Skip to content

Commit

Permalink
interceptors
Browse files Browse the repository at this point in the history
  • Loading branch information
nhaancs committed Mar 24, 2021
1 parent 4057a2a commit 754bebf
Show file tree
Hide file tree
Showing 20 changed files with 397 additions and 2 deletions.
25 changes: 25 additions & 0 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,31 @@
}
}
}
},
"shared-interceptors": {
"projectType": "library",
"root": "libs/shared/interceptors",
"sourceRoot": "libs/shared/interceptors/src",
"prefix": "realworld",
"architect": {
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"libs/shared/interceptors/src/**/*.ts",
"libs/shared/interceptors/src/**/*.html"
]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"outputs": ["coverage/libs/shared/interceptors"],
"options": {
"jestConfig": "libs/shared/interceptors/jest.config.js",
"passWithNoTests": true
}
}
}
}
}
}
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ module.exports = {
'<rootDir>/libs/shared/directives',
'<rootDir>/libs/shared/error-handler',
'<rootDir>/libs/shared/foundation',
'<rootDir>/libs/shared/interceptors',
],
};
31 changes: 31 additions & 0 deletions libs/shared/interceptors/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"extends": ["../../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts"],
"extends": [
"plugin:@nrwl/nx/angular",
"plugin:@angular-eslint/template/process-inline-templates"
],
"parserOptions": {
"project": ["libs/shared/interceptors/tsconfig.*?.json"]
},
"rules": {
"@angular-eslint/directive-selector": [
"error",
{ "type": "attribute", "prefix": "realworld", "style": "camelCase" }
],
"@angular-eslint/component-selector": [
"error",
{ "type": "element", "prefix": "realworld", "style": "kebab-case" }
]
}
},
{
"files": ["*.html"],
"extends": ["plugin:@nrwl/nx/angular-template"],
"rules": {}
}
]
}
7 changes: 7 additions & 0 deletions libs/shared/interceptors/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# shared-interceptors

This library was generated with [Nx](https://nx.dev).

## Running unit tests

Run `nx test shared-interceptors` to execute the unit tests.
23 changes: 23 additions & 0 deletions libs/shared/interceptors/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module.exports = {
displayName: 'shared-interceptors',
preset: '../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
globals: {
'ts-jest': {
tsConfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
astTransformers: {
before: [
'jest-preset-angular/build/InlineFilesTransformer',
'jest-preset-angular/build/StripStylesTransformer',
],
},
},
},
coverageDirectory: '../../../coverage/libs/shared/interceptors',
snapshotSerializers: [
'jest-preset-angular/build/AngularNoNgAttributesSnapshotSerializer.js',
'jest-preset-angular/build/AngularSnapshotSerializer.js',
'jest-preset-angular/build/HTMLCommentSerializer.js',
],
};
1 change: 1 addition & 0 deletions libs/shared/interceptors/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './lib/shared-interceptors.module'
18 changes: 18 additions & 0 deletions libs/shared/interceptors/src/lib/caching.interceptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest
} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable()
export class CachingInterceptor implements HttpInterceptor {
constructor() { }

intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
// console.info('CachingInterceptor')
return next.handle(request)
}
}
22 changes: 22 additions & 0 deletions libs/shared/interceptors/src/lib/error.interceptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest
} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { HTTP_METHOD } from '@realworld/shared/client-server';
import { Observable } from 'rxjs';
import { retry } from 'rxjs/operators';

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
// handling http errors
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
// console.info('ErrorInterceptor')
return next.handle(request).pipe(
// retry when an error happens with GET request
retry(request.method === HTTP_METHOD.GET ? 3 : 0),
)
}
}
39 changes: 39 additions & 0 deletions libs/shared/interceptors/src/lib/loading.interceptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest
} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ILoadingService } from '@realworld/shared/loading';
import { Observable } from 'rxjs';
import { finalize } from 'rxjs/operators';


@Injectable()
export class LoadingInterceptor implements HttpInterceptor {
constructor(private loadingService: ILoadingService) { }

intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
// console.info('LoadingInterceptor')
const loadingHeader = 'loading'
if (
request.headers.has(loadingHeader) &&
request.headers.get(loadingHeader) === 'show'
) {
request = request.clone(
// remove loading header from headers object
{headers: request.headers.delete(loadingHeader)}
)

this.loadingService.loading()

return next.handle(request).pipe(
finalize(() => {
this.loadingService.loaded()
})
)
}
return next.handle(request)
}
}
22 changes: 22 additions & 0 deletions libs/shared/interceptors/src/lib/logging.interceptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest
} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { ILoggingService } from '@realworld/shared/logging';

@Injectable()
export class LoggingInterceptor implements HttpInterceptor {
constructor(private loggingService: ILoggingService) { }

intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
// console.info('LoggingInterceptor')
this.loggingService.info({
message: `making a ${request.method} request to ${request.url}`
})
return next.handle(request)
}
}
83 changes: 83 additions & 0 deletions libs/shared/interceptors/src/lib/notification.interceptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest,
HttpResponse
} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { HTTP_METHOD } from '@realworld/shared/client-server';
import { INotificationService, NotificationType } from '@realworld/shared/notification';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class NotificationInterceptor implements HttpInterceptor {
constructor(private notificationService: INotificationService) { }

intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
// console.info('NotificationInterceptor')

return next.handle(request).pipe(
tap((event: HttpEvent<unknown>) => {
if (event instanceof HttpResponse) {
this.notify(request, event)
}
})
)
}

private notify(
req: HttpRequest<unknown>,
res: HttpResponse<unknown>
) {
let noti = this.getNotification(req, res)
if (!noti || !noti.message) {
return
}

switch (noti.type) {
case NotificationType.sussess:
this.notificationService.showSuccess(noti.message)
break
case NotificationType.info:
this.notificationService.showInfo(noti.message)
break
case NotificationType.warning:
this.notificationService.showWarning(noti.message)
break
case NotificationType.error:
this.notificationService.showError(noti.message)
break
}
}

private getNotification(
req: HttpRequest<unknown>,
res: HttpResponse<any>
): {type: NotificationType, message: string} | null {
// errors will be handled in error intercepter
if (res.ok) {
switch (req.method) {
case HTTP_METHOD.DELETE:
return {
type: NotificationType.sussess,
message: res?.body?.message
}
case HTTP_METHOD.POST:
return {
type: NotificationType.sussess,
message: res?.body?.message
}
case HTTP_METHOD.PUT:
case HTTP_METHOD.PATCH:
return {
type: NotificationType.sussess,
message: res?.body?.message
}
default:
return null
}
}
}
}
24 changes: 24 additions & 0 deletions libs/shared/interceptors/src/lib/shared-interceptors.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { CommonModule } from '@angular/common';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { CachingInterceptor } from './caching.interceptor';
import { ErrorInterceptor } from './error.interceptor';
import { LoadingInterceptor } from './loading.interceptor';
import { LoggingInterceptor } from './logging.interceptor';
import { NotificationInterceptor } from './notification.interceptor';
import { TimeoutInterceptor } from './timeout.interceptor';
import { TokenInterceptor } from './token.interceptor';

@NgModule({
imports: [CommonModule],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: TimeoutInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: CachingInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: LoadingInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: NotificationInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
]
})
export class SharedInterceptorsModule {}
17 changes: 17 additions & 0 deletions libs/shared/interceptors/src/lib/timeout.interceptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { timeout } from 'rxjs/operators';

export const REQUEST_TIMEOUT = 30000

@Injectable()
export class TimeoutInterceptor implements HttpInterceptor {
constructor() { }

intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
// console.info('TimeoutInterceptor')

return next.handle(request).pipe(timeout(REQUEST_TIMEOUT))
}
}
35 changes: 35 additions & 0 deletions libs/shared/interceptors/src/lib/token.interceptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Injectable } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { HTTP_HEADER } from '@realworld/shared/client-server';
import { UserStorageUtil } from '@realworld/shared/storage';

@Injectable()
export class TokenInterceptor implements HttpInterceptor {
constructor(private userStorageUtil: UserStorageUtil) {}

intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
// console.info('TokenInterceptor')

let token = this.userStorageUtil.token
if (token) {
let newHeaders = request.headers;
// If we have a token, we append it to our new headers
newHeaders = newHeaders.append(HTTP_HEADER.AUTHORIZATION, 'Bearer ' + token);

// Finally we have to clone our request with our new headers
// This is required because HttpRequests are immutable
const authReq = request.clone({ headers: newHeaders });
// Then we return an Observable that will run the request
// or pass it to the next interceptor if any
return next.handle(authReq);
}

return next.handle(request)
}
}
1 change: 1 addition & 0 deletions libs/shared/interceptors/src/test-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import 'jest-preset-angular';
13 changes: 13 additions & 0 deletions libs/shared/interceptors/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "../../../tsconfig.base.json",
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}
Loading

0 comments on commit 754bebf

Please sign in to comment.