2

I'm new with rxjs and redux-observable and I'm facing the following problem:

In an epic, I request something but I got a 401 because my current access_token is expired. So I need to get a new one and then retry the request. For this I use my stored refresh_token. I've found a solution that seems to work with rxjs verion 5.x and I'm trying to implement it using version 6 but I'm probably missing something and the operation never gets completed:

I make the request, it fails because of 401. Then, I've managed to request the new access_token, I get the new access_token back but that's it, the stream that was listening to recall the source never executes.

I've tried to adapt this: rxjs - How to retry after catching and processing an error with emitting something which is implemented with version 5.x of rxjs

Here's the epic that I'm writing:

export const fetchItems= (action$: any) => action$.pipe(
  ofType(ActionTypes.REQUEST_ITEMS),
  switchMap((action: any) => {
    return request('get', '/items', '', true);
  }),
  map((response: any) => successResponse(response.data)),
  catchError((error: AxiosError, source: any) => {
    if (isError401(error) {
      return action$.pipe(
        ofType(ActionTypes.SUCCESS_REFRESH_TOKEN),
        takeUntil(action$.ofType(ActionTypes.FAILURE_REFRESH_TOKEN)),
        take(1),
        mergeMapTo(source),
        merge(
          of(requestRefreshToken())
        )
      );
    } else {
      return failureResponse(error);
    }
  })
);

I know that I have a few any that should not be there and that I'm not giving much context for you to help me but maybe you can give me a clue on where to look for the problem.

From the link that I've showed before, I'm not using the Observable.defer() part but I guess that's not the problem here. Anyways I also don't know how to implement that part using rxjs 6.

In my redux dev tools I see this actions:

REQUEST_ITEMS
REQUEST_REFRESH_TOKEN (this is the request of a new access_token passing the current refresh_token)
SUCCESS_REFRESH_TOKEN (this means that I've got a new access_token stored)

after this, I was expecting that the mergeMapTo(source) will fire again REQUEST_ITEMS (and having a new valid access_token the action can be completed, falling this time into successResponse(response.data)), but it never gets fired.

andreslh
  • 21
  • 4

1 Answers1

0

In case that someone needs to know the answer to this problem, it turns out that I was using the catchError in a wrong way. As explained here: RxJs Observables: run retryWhen after some more async requests

Also, I had to replace axios (which I was using for the http request) by the ajax provided by rxjs.

The working code is this:

export const fetchItems = (action$: any, store: any) => action$.pipe(
  ofType(ActionTypes.REQUEST_ITEMS),
  mergeMap((action: any) => {
    return defer(() => {
        const access_token = store.value.data.auth.access_token;
        const options = {
          headers: {
            Authorization: `Bearer ${access_token}`,
          },
          url: '/items',
        };
        return ajax(options);
      })
      .pipe(
        map((response: any) => successItems(response.response)),
        catchError((error: any, source: any) => {
          if (isError401(error)) {
            return action$.pipe(
              ofType(ActionTypes.SUCCESS_REFRESH_TOKEN),
              takeUntil(action$.ofType(ActionTypes.FAILURE_REFRESH_TOKEN)),
              take(1),
              mergeMapTo(source),
              merge(
                of(requestRefreshToken())
              ),
            );
          } else {
            return of(failureItems(error));
          }
        })
      );
  })
);
andreslh
  • 21
  • 4