/** * This function handles automatic retry when 401 error happens. * The returned Observable throws errors with user friendly messages. */ private withRetry(initialResult: Observable<Response>, func: (...params: any[]) => Observable<Response>, ...params: any[]): Observable<Response> { return initialResult .catch(error => { if (error.status === 401) { this.authService.unauthenticated(); // 401 means authentication required let message = this.authService.isOrgApiKeySet ? 'Incorrect organization API key' : 'Authentication is required'; return Observable.fromPromise(this.authService.login(message)) .mergeMap(confirmed => { if (confirmed) { // logged in again, or updated api key this.authService.authenticationMayChange(); switch (params.length) { case 1: return func(params[0]); case 2: return func(params[0], params[1]); case 3: return func(params[0], params[1], params[2]); default: return Observable.throw(new Error('Wrong number of parameters: ' + params)); } }else { // canceled return Observable.throw(new Error('User cancelled authentication')); } }); }else { let errorDetail = error.json(); if (errorDetail) { this.authService.authenticated(); // a valid API response received return Observable.throw(new Error(errorDetail.type + ': ' + errorDetail.message)); } console.log('API call failed: ' + JSON.stringify(error)); return Observable.throw(error); } }); }
intercept(observable: Observable<Response>): Observable<Response> { return observable.catch((err, source) => { if (err.status === 401 && !_.endsWith(err.url, '/login')) { this._router.navigate(['login']); return Observable.empty(null); } else { return Observable.throw(err); } }); }
/** * Creates a new Reactive Command. * @param canRun An observable that determines whether the given task is allowed to run at a given moment. * @param task A function that returns an observable that represents the asynchronous operation. * @param scheduler The scheduler that all of the results should be observed on. */ constructor(private task: (args: TArgs) => Observable<TResult>, private canRun: Observable<boolean>, private scheduler: Scheduler) { if (!task) { throw new Error("The task parameter must be supplied"); } if (!canRun) { throw new Error("The canRun parameter must be supplied"); } if (!scheduler) { throw new Error("The scheduler parameter must be supplied"); } this._executionInfo = new Subject<ExecutionInfo<TResult>>(); this._synchronizedExcecutionInfo = this._executionInfo; this._exceptions = new Subject<any>(); // Implementation mostly taken from: // https://github.com/reactiveui/ReactiveUI/blob/rxui7-master/ReactiveUI/ReactiveCommand.cs#L628 this._isExecuting = this._synchronizedExcecutionInfo .observeOn(scheduler) .map(info => info.demarcation === ExecutionDemarcation.Begin) .startWith(false) .distinctUntilChanged() .publishReplay(1) .refCount(); this._canExecute = this.canRun .catch(ex => { this._exceptions.next(ex); return Observable.of(false); }) .startWith(false) .combineLatest(this._isExecuting, (canRun, isExecuting) => { return canRun && !isExecuting; }) .distinctUntilChanged() .publishReplay(1) .refCount(); this._results = this._synchronizedExcecutionInfo .observeOn(scheduler) .filter(info => info.demarcation === ExecutionDemarcation.EndWithResult) .map(info => info.result); // Make sure that can execute is triggered to be a hot observable. this._canExecuteSubscription = this._canExecute.subscribe(); }
private _handle(observable: Observable<Response>, url: string, options?: RequestOptionsArgs): Observable<any> { return observable.catch((err: any): any => { console.log(err); if (err.status === 400 || err.status === 401 || err.status === 422) { console.log("Notifying..."); console.log(err.json()); this.growlMessageService.notifyError(err.json()); return Observable.empty(); } else { console.log("Redirecting..."); this.router.navigate(['/error-page']); return Observable.empty(); } }) .retryWhen(error => error.delay(500)) .timeout(2000, new Error('delay exceeded')) .finally(() => { this._afterCall(url, options); }); }