Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Observable - 401 causing forkJoin to error out

I am using forkJoin to make several server requests. This is a pattern I have commonly been using through out my application and it has been working great. However we just started implementing user roles which is done on the backend. I am not sure what is the best practice for implementing roles as I am mostly a front end developer, nonetheless this is the problem I have encountered:

Our application has member and admin member roles.

  1. From each view I must make calls to the backend for both member and admin member roles regardless as roles are not determined on the frontend.

  2. Member data is always returned in for both roles as members and admin members both have personal data.

  3. Requests made for admin data is only returned when the user is an admin. Whenever the user does not have admin access the request returns a 401 error. This is where I am having a problem.

Whenever the call returns a 401, the error method in my subscribe method is invoked and I do not have access to any of the calls that were made including the calls associated to the member data.

In my included code within the forkJoin there are five calls passed into the method. The third and forth call only return data if the user is an admin while the rest of the calls are always returned for either member or admin.

When the user is not an admin the third call returns a 401 and the stream stops and the error handler in my subscribe method is invoked. This is obviously not what I want. I want the stream to continue so I can use the data in the _data method.

I have only been using RXJS for 6 months and am learning. Maybe I should be using a different pattern or maybe there is a way to fix this. Any help with code examples would be greatly appreciated. Below my code example I included another example of code in which I attempted to fix the problem by playing around with catch methods. It didn't work.

My View get method:

private getZone() {
  this.spinner.show();
  this.zonesService.getZone(this.zoneId)
    .map(response => {
      this.zone = response['group'];
      return this.zone;
    })
    .flatMap(() => {
      return Observable.forkJoin(
        this.teamsService.getTeam(this.zone['TeamId']),
        this.zonesService.getZoneAssociations(this.zone['id'], '/myDevices'),
        this.zonesService.getZoneAssociations(this.zone['id'], '/devices'),
        this.zonesService.getZoneAssociations(this.zone['id'], '/groupMembers'),
        this.sitesService.getSite(this.zone['SiteId'])
      );
    })
    .subscribe(
      _data => {
        // data handling...
      },
      _error => {
        // error handling ...
      }
    );
}

My attempt to fix:

private getZone() {
  this.spinner.show();
  this.zonesService.getZone(this.zoneId)
    .map(response => {
      this.zone = response['group'];
      return this.zone;
    })
    .flatMap(() => {
      return Observable.forkJoin(
        this.teamsService.getTeam(this.zone['TeamId']),
        this.zonesService.getZoneAssociations(this.zone['id'], '/myDevices'),
        this.zonesService.getZoneAssociations(this.zone['id'], '/devices')
          .catch(error => Observable.throw(error)),
        this.zonesService.getZoneAssociations(this.zone['id'], '/groupMembers')
          .catch(error => Observable.throw(error)),
        this.sitesService.getSite(this.zone['SiteId'])
      );
    })
    .subscribe(
      _data => {
        // data handling...
      },
      _error => {
        // error handling...
      }
    );
}
like image 661
Aaron Avatar asked Nov 08 '22 19:11

Aaron


1 Answers

Returning Observable.throw will just rethrow the caught error, which will see forkJoin emit the error.

Instead, you could use Observable.of(null) to emit null and then complete, which will see forkJoin emit a null for the observable that emitted the error:

  return Observable.forkJoin(
    this.teamsService.getTeam(this.zone['TeamId']),
    this.zonesService.getZoneAssociations(this.zone['id'], '/myDevices'),
    this.zonesService.getZoneAssociations(this.zone['id'], '/devices')
      .catch(error => Observable.of(null)),
    this.zonesService.getZoneAssociations(this.zone['id'], '/groupMembers')
      .catch(error => Observable.of(null)),
    this.sitesService.getSite(this.zone['SiteId'])
  );

Or, if you wanted to emit the error as a value, you could use Observable.of(error).

like image 178
cartant Avatar answered Nov 15 '22 11:11

cartant