UNPKG

@isaiahiroko/ng-interceptors

Version:

Angular interceptors for token authorization, local and remote store synchronization and global notification

183 lines (175 loc) 16.4 kB
import { Injectable } from '@angular/core'; import { AuthService } from '@isaiahiroko/ng-utils'; import { finalize, retryWhen, zip, mergeMap, catchError } from 'rxjs/operators'; import { range, timer, throwError } from 'rxjs'; import { HTTP_INTERCEPTORS } from '@angular/common/http'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class AuthInterceptor { /** * @param {?} auth */ constructor(auth) { this.auth = auth; } /** * @param {?} req * @param {?} next * @return {?} */ intercept(req, next) { /** @type {?} */ const authReq = req.clone({ headers: req.headers.set('Authorization', this.auth.token()), }); // send cloned request with header to the next handler. return next.handle(authReq); } } AuthInterceptor.decorators = [ { type: Injectable }, ]; /** @nocollapse */ AuthInterceptor.ctorParameters = () => [ { type: AuthService } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class LoaderServiceContract { /** * @param {?=} type * @return {?} */ start(type) { } /** * @return {?} */ stop() { } /** * @param {?} message * @param {?=} title * @return {?} */ notify(message, title) { } /** * @param {?} message * @param {?=} title * @return {?} */ toast(message, title) { } } class NotifInterceptor { /** * @param {?} notif */ constructor(notif) { this.notif = notif; } /** * @param {?} req * @param {?} next * @return {?} */ intercept(req, next) { this.notif.start(); // extend server response observable with logging return next.handle(req).pipe( // things don't always work as planned // if things go wrong, try again three times // however, delay trial in an incremental manner (multipl of 1 second) retryWhen((attempts) => { return attempts.pipe(zip(range(1, 4)), mergeMap(([error, i]) => { this.notif.start('query'); if (i > 2) { return throwError(error); } return timer(i * 1000); })); }), // catchError http errors and turn to observable of undefined catchError((e) => { /** @type {?} */ let title; /** @type {?} */ let message; this.notif.start('buffer'); if (e.error instanceof ErrorEvent) { // A client-side or network error occurred. Handle it accordingly. title = 'Client Error'; message = e.error.message; } else { // The backend returned an unsuccessful response code. // The response body may contain clues as to what went wrong, title = 'Network Info'; message = `${e.statusText} (${e.status})`; // message = e.message } if (e.status === 401) ; console.error({ title, message }); // catche //re throw // throw {title, message} // return of({ e: {title, message} }) // return empty() return throwError({ title, message }); }), finalize(() => { this.notif.stop(); })); } } NotifInterceptor.decorators = [ { type: Injectable }, ]; /** @nocollapse */ NotifInterceptor.ctorParameters = () => [ { type: LoaderServiceContract } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class StoreSyncInterceptor { /** * @param {?} req * @param {?} next * @return {?} */ intercept(req, next) { // continue if not cachable. // if (!isCachable(req)) { return next.handle(req); } { return next.handle(req); } // cache-or-fetch // const cachedResponse = this.cache.get(req); // return cachedResponse ? // of(cachedResponse) : sendRequest(req, next, this.cache); } } StoreSyncInterceptor.decorators = [ { type: Injectable }, ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** @type {?} */ const INTERCEPTORS = [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }, // { provide: HTTP_INTERCEPTORS, useClass: StoreSyncInterceptor, multi: true }, { provide: HTTP_INTERCEPTORS, useClass: NotifInterceptor, multi: true } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ export { INTERCEPTORS, StoreSyncInterceptor, AuthInterceptor, LoaderServiceContract, NotifInterceptor }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaXNhaWFoaXJva28tbmctaW50ZXJjZXB0b3JzLmpzLm1hcCIsInNvdXJjZXMiOlsibmc6Ly9AaXNhaWFoaXJva28vbmctaW50ZXJjZXB0b3JzL2xpYi9hdXRoLmludGVyY2VwdG9yLnRzIiwibmc6Ly9AaXNhaWFoaXJva28vbmctaW50ZXJjZXB0b3JzL2xpYi9ub3RpZi5pbnRlcmNlcHRvci50cyIsIm5nOi8vQGlzYWlhaGlyb2tvL25nLWludGVyY2VwdG9ycy9saWIvc3RvcmUtc3luYy5pbnRlcmNlcHRvci50cyIsIm5nOi8vQGlzYWlhaGlyb2tvL25nLWludGVyY2VwdG9ycy9saWIvaW5kZXgudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnXHJcbmltcG9ydCB7IEh0dHBJbnRlcmNlcHRvciwgSHR0cFJlcXVlc3QsIEh0dHBIYW5kbGVyLCBIdHRwRXZlbnQgfSBmcm9tICdAYW5ndWxhci9jb21tb24vaHR0cCdcclxuXHJcbmltcG9ydCB7IEF1dGhTZXJ2aWNlIH0gZnJvbSAnQGlzYWlhaGlyb2tvL25nLXV0aWxzJ1xyXG5pbXBvcnQgeyBPYnNlcnZhYmxlIH0gZnJvbSAncnhqcyc7XHJcblxyXG5ASW5qZWN0YWJsZSgpXHJcbmV4cG9ydCBjbGFzcyBBdXRoSW50ZXJjZXB0b3IgaW1wbGVtZW50cyBIdHRwSW50ZXJjZXB0b3Ige1xyXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgYXV0aDogQXV0aFNlcnZpY2UpIHt9XHJcblxyXG4gIGludGVyY2VwdChyZXE6IEh0dHBSZXF1ZXN0PGFueT4sIG5leHQ6IEh0dHBIYW5kbGVyKTogT2JzZXJ2YWJsZTxIdHRwRXZlbnQ8YW55Pj4ge1xyXG4gICAgLy8gR2V0IHRoZSBhdXRoIHRva2VuIGZyb20gdGhlIHNlcnZpY2UuXHJcbiAgICAvLyBDbG9uZSB0aGUgcmVxdWVzdCBhbmQgcmVwbGFjZSB0aGUgb3JpZ2luYWwgaGVhZGVycyB3aXRoXHJcbiAgICAvLyBjbG9uZWQgaGVhZGVycywgdXBkYXRlZCB3aXRoIHRoZSBhdXRob3JpemF0aW9uLlxyXG4gICAgY29uc3QgYXV0aFJlcSA9IHJlcS5jbG9uZSh7XHJcbiAgICAgIGhlYWRlcnM6IHJlcS5oZWFkZXJzLnNldCgnQXV0aG9yaXphdGlvbicsIHRoaXMuYXV0aC50b2tlbigpKSxcclxuICAgIH0pXHJcblxyXG4gICAgLy8gc2VuZCBjbG9uZWQgcmVxdWVzdCB3aXRoIGhlYWRlciB0byB0aGUgbmV4dCBoYW5kbGVyLlxyXG4gICAgcmV0dXJuIG5leHQuaGFuZGxlKGF1dGhSZXEpXHJcbiAgfVxyXG59XHJcbiIsImltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJ1xyXG5pbXBvcnQgeyBIdHRwUmVxdWVzdCwgSHR0cEhhbmRsZXIsIEh0dHBJbnRlcmNlcHRvciwgSHR0cEV2ZW50IH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uL2h0dHAnXHJcblxyXG5pbXBvcnQgeyBmaW5hbGl6ZSwgcmV0cnlXaGVuLCB6aXAsIG1lcmdlTWFwLCBjYXRjaEVycm9yIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnXHJcbmltcG9ydCB7IHJhbmdlLCAgdGltZXIsICB0aHJvd0Vycm9yLCBPYnNlcnZhYmxlIH0gZnJvbSAncnhqcydcclxuXHJcbmV4cG9ydCBjbGFzcyBMb2FkZXJTZXJ2aWNlQ29udHJhY3Qge1xyXG4gIHN0YXJ0ICh0eXBlPzogc3RyaW5nKSB7fVxyXG4gIHN0b3AgKCkge31cclxuICBub3RpZnkgKG1lc3NhZ2U6IHN0cmluZywgdGl0bGU/OiBzdHJpbmcpIHt9XHJcbiAgdG9hc3QgKG1lc3NhZ2U6IHN0cmluZywgdGl0bGU/OiBzdHJpbmcpIHt9XHJcbn1cclxuXHJcbkBJbmplY3RhYmxlKClcclxuZXhwb3J0IGNsYXNzIE5vdGlmSW50ZXJjZXB0b3IgaW1wbGVtZW50cyBIdHRwSW50ZXJjZXB0b3Ige1xyXG4gIGNvbnN0cnVjdG9yIChcclxuICAgIHByaXZhdGUgbm90aWY6IExvYWRlclNlcnZpY2VDb250cmFjdCxcclxuICApIHt9XHJcblxyXG4gIGludGVyY2VwdCAocmVxOiBIdHRwUmVxdWVzdDxhbnk+LCBuZXh0OiBIdHRwSGFuZGxlcik6IE9ic2VydmFibGU8SHR0cEV2ZW50PGFueT4+IHtcclxuICAgIHRoaXMubm90aWYuc3RhcnQoKVxyXG5cclxuICAgIC8vIGV4dGVuZCBzZXJ2ZXIgcmVzcG9uc2Ugb2JzZXJ2YWJsZSB3aXRoIGxvZ2dpbmdcclxuICAgIHJldHVybiBuZXh0LmhhbmRsZShyZXEpLnBpcGUoXHJcbiAgICAgIC8vIHRoaW5ncyBkb24ndCBhbHdheXMgd29yayBhcyBwbGFubmVkXHJcbiAgICAgIC8vIGlmIHRoaW5ncyBnbyB3cm9uZywgdHJ5IGFnYWluIHRocmVlIHRpbWVzXHJcbiAgICAgIC8vIGhvd2V2ZXIsIGRlbGF5IHRyaWFsIGluIGFuIGluY3JlbWVudGFsIG1hbm5lciAobXVsdGlwbCBvZiAxIHNlY29uZClcclxuICAgICAgcmV0cnlXaGVuKChhdHRlbXB0cykgPT4ge1xyXG4gICAgICAgIHJldHVybiBhdHRlbXB0cy5waXBlKFxyXG4gICAgICAgICAgemlwKHJhbmdlKDEsIDQpKSxcclxuICAgICAgICAgIG1lcmdlTWFwKChbZXJyb3IsIGldKSA9PiB7XHJcbiAgICAgICAgICAgIHRoaXMubm90aWYuc3RhcnQoJ3F1ZXJ5JylcclxuICAgICAgICAgICAgaWYgKGkgPiAyKSB7XHJcbiAgICAgICAgICAgICAgcmV0dXJuIHRocm93RXJyb3IoZXJyb3IpXHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgcmV0dXJuIHRpbWVyKGkgKiAxMDAwKVxyXG4gICAgICAgICAgfSlcclxuICAgICAgICApXHJcbiAgICAgIH0pLFxyXG5cclxuICAgICAgLy8gY2F0Y2hFcnJvciBodHRwIGVycm9ycyBhbmQgdHVybiB0byBvYnNlcnZhYmxlIG9mIHVuZGVmaW5lZFxyXG4gICAgICBjYXRjaEVycm9yKChlKSA9PiB7XHJcbiAgICAgICAgbGV0IHRpdGxlLCBtZXNzYWdlXHJcblxyXG4gICAgICAgIHRoaXMubm90aWYuc3RhcnQoJ2J1ZmZlcicpXHJcblxyXG4gICAgICAgIGlmIChlLmVycm9yIGluc3RhbmNlb2YgRXJyb3JFdmVudCkge1xyXG4gICAgICAgICAgLy8gQSBjbGllbnQtc2lkZSBvciBuZXR3b3JrIGVycm9yIG9jY3VycmVkLiBIYW5kbGUgaXQgYWNjb3JkaW5nbHkuXHJcbiAgICAgICAgICB0aXRsZSA9ICdDbGllbnQgRXJyb3InXHJcbiAgICAgICAgICBtZXNzYWdlID0gZS5lcnJvci5tZXNzYWdlXHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgIC8vIFRoZSBiYWNrZW5kIHJldHVybmVkIGFuIHVuc3VjY2Vzc2Z1bCByZXNwb25zZSBjb2RlLlxyXG4gICAgICAgICAgLy8gVGhlIHJlc3BvbnNlIGJvZHkgbWF5IGNvbnRhaW4gY2x1ZXMgYXMgdG8gd2hhdCB3ZW50IHdyb25nLFxyXG4gICAgICAgICAgdGl0bGUgPSAnTmV0d29yayBJbmZvJ1xyXG4gICAgICAgICAgbWVzc2FnZSA9IGAke2Uuc3RhdHVzVGV4dH0gKCR7ZS5zdGF0dXN9KWBcclxuICAgICAgICAgIC8vIG1lc3NhZ2UgPSBlLm1lc3NhZ2VcclxuICAgICAgICB9XHJcbiAgICAgICAgXHJcbiAgICAgICAgaWYoZS5zdGF0dXMgPT09IDQwMSl7XHJcbiAgICAgICAgICAvLyB0aGlzLm5vdGlmLm5vdGlmeSgnVGhlIHVzZXJuYW1lL3Bhc3N3b3JkIGlzIHdyb25nJywgJ1VuYXV0aG9yaXplZCcpXHJcbiAgICAgICAgfVxyXG4gICAgICAgIGVsc2V7XHJcbiAgICAgICAgICAvLyB0aGlzLm5vdGlmLnRvYXN0KG1lc3NhZ2UsIHRpdGxlKVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgY29uc29sZS5lcnJvcih7IHRpdGxlLCBtZXNzYWdlIH0pO1xyXG4gICAgICAgIFxyXG4gICAgICAgIC8vIGNhdGNoZSAvL3JlIHRocm93XHJcbiAgICAgICAgLy8gdGhyb3cge3RpdGxlLCBtZXNzYWdlfVxyXG4gICAgICAgIC8vIHJldHVybiBvZih7IGU6IHt0aXRsZSwgbWVzc2FnZX0gfSlcclxuICAgICAgICAvLyByZXR1cm4gZW1wdHkoKVxyXG4gICAgICAgIHJldHVybiB0aHJvd0Vycm9yKHsgdGl0bGUsIG1lc3NhZ2V9KVxyXG4gICAgICB9KSxcclxuICAgICAgZmluYWxpemUoKCkgPT4ge1xyXG4gICAgICAgIHRoaXMubm90aWYuc3RvcCgpXHJcbiAgICAgIH0pLFxyXG4gICAgKVxyXG4gIH1cclxufVxyXG4iLCJpbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSdcclxuaW1wb3J0IHtcclxuICBIdHRwSW50ZXJjZXB0b3IsXHJcbiAgSHR0cFJlcXVlc3QsXHJcbiAgSHR0cEhhbmRsZXIsXHJcbiAgSHR0cEV2ZW50LFxyXG59IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbi9odHRwJ1xyXG5pbXBvcnQgeyBPYnNlcnZhYmxlIH0gZnJvbSAncnhqcyc7XHJcblxyXG5ASW5qZWN0YWJsZSgpXHJcbmV4cG9ydCBjbGFzcyBTdG9yZVN5bmNJbnRlcmNlcHRvciBpbXBsZW1lbnRzIEh0dHBJbnRlcmNlcHRvciB7XHJcbiAgLy8gY29uc3RydWN0b3IocHJpdmF0ZSBjYWNoZTogUmVxdWVzdENhY2hlKSB7fVxyXG5cclxuICBpbnRlcmNlcHQocmVxOiBIdHRwUmVxdWVzdDxhbnk+LCBuZXh0OiBIdHRwSGFuZGxlcik6IE9ic2VydmFibGU8SHR0cEV2ZW50PGFueT4+ICB7XHJcbiAgICAvLyBjb250aW51ZSBpZiBub3QgY2FjaGFibGUuXHJcbiAgICAvLyBpZiAoIWlzQ2FjaGFibGUocmVxKSkgeyByZXR1cm4gbmV4dC5oYW5kbGUocmVxKTsgfVxyXG4gICAgaWYgKHRydWUpIHtcclxuICAgICAgcmV0dXJuIG5leHQuaGFuZGxlKHJlcSlcclxuICAgIH1cclxuXHJcbiAgICAvLyBjYWNoZS1vci1mZXRjaFxyXG4gICAgLy8gY29uc3QgY2FjaGVkUmVzcG9uc2UgPSB0aGlzLmNhY2hlLmdldChyZXEpO1xyXG4gICAgLy8gcmV0dXJuIGNhY2hlZFJlc3BvbnNlID9cclxuICAgIC8vICAgb2YoY2FjaGVkUmVzcG9uc2UpIDogc2VuZFJlcXVlc3QocmVxLCBuZXh0LCB0aGlzLmNhY2hlKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEdldCBzZXJ2ZXIgcmVzcG9uc2Ugb2JzZXJ2YWJsZSBieSBzZW5kaW5nIHJlcXVlc3QgdG8gYG5leHQoKWAuXHJcbiAgICogV2lsbCBhZGQgdGhlIHJlc3BvbnNlIHRvIHRoZSBjYWNoZSBvbiB0aGUgd2F5IG91dC5cclxuICAgKi9cclxuICAvLyAgc2VuZFJlcXVlc3QoXHJcbiAgLy8gICAgIHJlcTogSHR0cFJlcXVlc3Q8YW55PixcclxuICAvLyAgICAgbmV4dDogSHR0cEhhbmRsZXIsXHJcbiAgLy8gICAgIGNhY2hlOiBSZXF1ZXN0Q2FjaGUpOiBPYnNlcnZhYmxlPEh0dHBFdmVudDxhbnk+PiB7XHJcblxyXG4gIC8vICAgICAvLyBObyBoZWFkZXJzIGFsbG93ZWQgaW4gbnBtIHNlYXJjaCByZXF1ZXN0XHJcbiAgLy8gICAgIGNvbnN0IG5vSGVhZGVyUmVxID0gcmVxLmNsb25lKHsgaGVhZGVyczogbmV3IEh0dHBIZWFkZXJzKCkgfSk7XHJcblxyXG4gIC8vICAgICByZXR1cm4gbmV4dC5oYW5kbGUobm9IZWFkZXJSZXEpLnBpcGUoXHJcbiAgLy8gICAgICAgdGFwKGV2ZW50ID0+IHtcclxuICAvLyAgICAgICAgIC8vIFRoZXJlIG1heSBiZSBvdGhlciBldmVudHMgYmVzaWRlcyB0aGUgcmVzcG9uc2UuXHJcbiAgLy8gICAgICAgICBpZiAoZXZlbnQgaW5zdGFuY2VvZiBIdHRwUmVzcG9uc2UpIHtcclxuICAvLyAgICAgICAgICAgY2FjaGUucHV0KHJlcSwgZXZlbnQpOyAvLyBVcGRhdGUgdGhlIGNhY2hlLlxyXG4gIC8vICAgICAgICAgfVxyXG4gIC8vICAgICAgIH0pXHJcbiAgLy8gICAgICk7XHJcbiAgLy8gICB9XHJcblxyXG4gIC8vIGNhY2hlLXRoZW4tcmVmcmVzaFxyXG4gIC8vIGlmIChyZXEuaGVhZGVycy5nZXQoJ3gtcmVmcmVzaCcpKSB7XHJcbiAgLy8gICAgIGNvbnN0IHJlc3VsdHMkID0gc2VuZFJlcXVlc3QocmVxLCBuZXh0LCB0aGlzLmNhY2hlKTtcclxuICAvLyAgICAgcmV0dXJuIGNhY2hlZFJlc3BvbnNlID9cclxuICAvLyAgICAgICByZXN1bHRzJC5waXBlKCBzdGFydFdpdGgoY2FjaGVkUmVzcG9uc2UpICkgOlxyXG4gIC8vICAgICAgIHJlc3VsdHMkO1xyXG4gIC8vICAgfVxyXG59XHJcbiIsImltcG9ydCB7IFN0b3JlU3luY0ludGVyY2VwdG9yIH0gZnJvbSAnLi9zdG9yZS1zeW5jLmludGVyY2VwdG9yJ1xyXG5pbXBvcnQgeyBBdXRoSW50ZXJjZXB0b3IgfSBmcm9tICcuL2F1dGguaW50ZXJjZXB0b3InXHJcbmltcG9ydCB7IE5vdGlmSW50ZXJjZXB0b3IgfSBmcm9tICcuL25vdGlmLmludGVyY2VwdG9yJ1xyXG5pbXBvcnQgeyBIVFRQX0lOVEVSQ0VQVE9SUyB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbi9odHRwJ1xyXG5cclxuZXhwb3J0IGNvbnN0IElOVEVSQ0VQVE9SUzogYW55W10gPSBbXHJcbiAgeyBwcm92aWRlOiBIVFRQX0lOVEVSQ0VQVE9SUywgdXNlQ2xhc3M6IEF1dGhJbnRlcmNlcHRvciwgbXVsdGk6IHRydWUgfSxcclxuICAvLyB7IHByb3ZpZGU6IEhUVFBfSU5URVJDRVBUT1JTLCB1c2VDbGFzczogU3RvcmVTeW5jSW50ZXJjZXB0b3IsIG11bHRpOiB0cnVlIH0sXHJcbiAgeyBwcm92aWRlOiBIVFRQX0lOVEVSQ0VQVE9SUywgdXNlQ2xhc3M6IE5vdGlmSW50ZXJjZXB0b3IsIG11bHRpOiB0cnVlIH1cclxuXVxyXG5cclxuZXhwb3J0ICogZnJvbSAnLi9zdG9yZS1zeW5jLmludGVyY2VwdG9yJ1xyXG5leHBvcnQgKiBmcm9tICcuL2F1dGguaW50ZXJjZXB0b3InXHJcbmV4cG9ydCAqIGZyb20g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