Search code examples
rxjs5rxjs-lettable-operators

Handling retryWhen status code in RxJS 5.5


I have some code I'm migrating to RxJS 5.5, which already worked.

public getMentor(id: number): Observable<Employee> {
    const url = `${this.employeeUrl}/${id}/mentor`;
    return this.http
        .get(url, this.authService.getOptionsWithToken())
        .retryWhen(errors => {
            return errors
                .mergeMap(error => (error.status === 404) ? Observable.throw(error) : Observable.of(error))
                .take(this.maxRetries);
        })
        .map(response => response.json() as Employee)
        .catch(ErrorHandlerService.handleError);
}

In any case, if this request fails with 404, it's OK according to the business logic. Now, this would be a near equivalent in 5.5:

public getMentor(id: number): Observable<Employee> {
  const url = `${this.employeeUrl}/${id}/mentor`;
  return this.http.get<Employee>(url)
    .pipe(
      retryWhen(errors => {
        console.log('errorInService', errors);
        return errors.pipe(
          mergeMap(error => (error.status === 404) ? _throw(error) : of(error)),
          take(this.maxRetries)
        )
      }),
      catchError(ErrorHandlerService.handleError)
    );
}

Here, the flow gets interrupted because inside retryWhen, errors is now a Subject and I cannot extract the status as before.

this.employeeService.getMentor(this.mentee.id).subscribe(
    mentor => {
      this.existingMentor = mentor;
      this.modalAddConfirmation(addConfirmation, mentee, form);
    },
    e => {
      console.log('errorInMentor', e);
      if (e.status === 404) {
        // console.log('No mentor');
        this.employeeService.putMentor(mentee.id, this.mentor)
          .subscribe(() => this.mentees.push(mentee));
      } else {
        return null;
      }
    }
  );

In the original caller, "e" is now a string which says Http failure response for http://localhost:8888/employees/1/mentor: 404 OK instead of an object. Obviously, the putMentor() call never gets made. I'm just learning so it's most likely I haven't understood fully the new pipeable operators.

Update

At least this code is throwing an ErrorObservable, but on the caller end (errorInMentor) it's still a string:

public getMentor(id: number): Observable<Employee> {
  const url = `${this.employeeUrl}/${id}/mentor`;
  return this.http.get<Employee>(url)
    .pipe(
      retryWhen(errors => {
        return errors.pipe(
          mergeMap(error => {
            console.log('error MergeMap', error);
            return error.status === 404 ? _throw(error) : of(error);
          }),
          take(this.maxRetries)
        )
      }),
      catchError(ErrorHandlerService.handleError)
    );
}

Solution

  • It turned out that the culprit was the ErrorHandlerService.handleError() function. Previously it worked like this:

    public static handleError(error: any): Promise<any> {
        console.error('An error occurred', error);
        return Promise.reject(error.message || error);
    }
    

    Promises and Pipeable operators do not get along well, so I updated the code to this:

    public static handleError(error: any): ErrorObservable {
        console.error('An error occurred', error);
        return ErrorObservable.create(error.message || error);
    }
    

    However I only needed an additional adjustment:

    public static handleError(error: any): ErrorObservable {
        console.error('An error occurred', error);
        return ErrorObservable.create(error);
    }
    

    Don't know how I missed that until now.