]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | import { |
2 | HttpErrorResponse, | |
3 | HttpEvent, | |
4 | HttpHandler, | |
5 | HttpInterceptor, | |
6 | HttpRequest | |
7 | } from '@angular/common/http'; | |
8 | import { Injectable } from '@angular/core'; | |
9 | import { Router } from '@angular/router'; | |
10 | ||
f67539c2 | 11 | import _ from 'lodash'; |
11fdf7f2 TL |
12 | import { Observable, throwError as observableThrowError } from 'rxjs'; |
13 | import { catchError } from 'rxjs/operators'; | |
14 | ||
15 | import { NotificationType } from '../enum/notification-type.enum'; | |
16 | import { CdNotificationConfig } from '../models/cd-notification'; | |
17 | import { FinishedTask } from '../models/finished-task'; | |
18 | import { AuthStorageService } from './auth-storage.service'; | |
19 | import { NotificationService } from './notification.service'; | |
11fdf7f2 | 20 | |
9f95a23c TL |
21 | export class CdHttpErrorResponse extends HttpErrorResponse { |
22 | preventDefault: Function; | |
23 | ignoreStatusCode: Function; | |
24 | } | |
25 | ||
11fdf7f2 | 26 | @Injectable({ |
81eedcae | 27 | providedIn: 'root' |
11fdf7f2 TL |
28 | }) |
29 | export class ApiInterceptorService implements HttpInterceptor { | |
30 | constructor( | |
31 | private router: Router, | |
32 | private authStorageService: AuthStorageService, | |
33 | public notificationService: NotificationService | |
34 | ) {} | |
35 | ||
36 | intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { | |
f67539c2 TL |
37 | const defaultVersion = '1.0'; |
38 | const acceptHeader = request.headers.get('Accept'); | |
39 | let reqWithVersion: HttpRequest<any>; | |
40 | if (acceptHeader && acceptHeader.startsWith('application/vnd.ceph.api.v')) { | |
41 | reqWithVersion = request.clone(); | |
42 | } else { | |
43 | reqWithVersion = request.clone({ | |
44 | setHeaders: { | |
45 | Accept: `application/vnd.ceph.api.v${defaultVersion}+json` | |
46 | } | |
47 | }); | |
48 | } | |
49 | return next.handle(reqWithVersion).pipe( | |
9f95a23c | 50 | catchError((resp: CdHttpErrorResponse) => { |
11fdf7f2 TL |
51 | if (resp instanceof HttpErrorResponse) { |
52 | let timeoutId: number; | |
53 | switch (resp.status) { | |
54 | case 400: | |
55 | const finishedTask = new FinishedTask(); | |
56 | ||
57 | const task = resp.error.task; | |
58 | if (_.isPlainObject(task)) { | |
59 | task.metadata.component = task.metadata.component || resp.error.component; | |
60 | ||
61 | finishedTask.name = task.name; | |
62 | finishedTask.metadata = task.metadata; | |
63 | } else { | |
64 | finishedTask.metadata = resp.error; | |
65 | } | |
66 | ||
67 | finishedTask.success = false; | |
68 | finishedTask.exception = resp.error; | |
69 | timeoutId = this.notificationService.notifyTask(finishedTask); | |
70 | break; | |
71 | case 401: | |
72 | this.authStorageService.remove(); | |
73 | this.router.navigate(['/login']); | |
74 | break; | |
75 | case 403: | |
f67539c2 TL |
76 | this.router.navigate(['error'], { |
77 | state: { | |
78 | message: $localize`Sorry, you don’t have permission to view this page or resource.`, | |
79 | header: $localize`Access Denied`, | |
80 | icon: 'fa fa-lock', | |
81 | source: 'forbidden' | |
82 | } | |
83 | }); | |
11fdf7f2 TL |
84 | break; |
85 | default: | |
86 | timeoutId = this.prepareNotification(resp); | |
87 | } | |
88 | ||
89 | /** | |
90 | * Decorated preventDefault method (in case error previously had | |
91 | * preventDefault method defined). If called, it will prevent a | |
92 | * notification to be shown. | |
93 | */ | |
9f95a23c | 94 | resp.preventDefault = () => { |
11fdf7f2 TL |
95 | this.notificationService.cancel(timeoutId); |
96 | }; | |
97 | ||
98 | /** | |
99 | * If called, it will prevent a notification for the specific status code. | |
100 | * @param {number} status The status code to be ignored. | |
101 | */ | |
e306af50 | 102 | resp.ignoreStatusCode = function (status: number) { |
11fdf7f2 TL |
103 | if (this.status === status) { |
104 | this.preventDefault(); | |
105 | } | |
106 | }; | |
107 | } | |
108 | // Return the error to the method that called it. | |
109 | return observableThrowError(resp); | |
110 | }) | |
111 | ); | |
112 | } | |
113 | ||
9f95a23c | 114 | private prepareNotification(resp: any): number { |
11fdf7f2 TL |
115 | return this.notificationService.show(() => { |
116 | let message = ''; | |
117 | if (_.isPlainObject(resp.error) && _.isString(resp.error.detail)) { | |
118 | message = resp.error.detail; // Error was triggered by the backend. | |
119 | } else if (_.isString(resp.error)) { | |
120 | message = resp.error; | |
121 | } else if (_.isString(resp.message)) { | |
122 | message = resp.message; | |
123 | } | |
124 | return new CdNotificationConfig( | |
125 | NotificationType.error, | |
126 | `${resp.status} - ${resp.statusText}`, | |
127 | message, | |
128 | undefined, | |
129 | resp['application'] | |
130 | ); | |
131 | }); | |
132 | } | |
133 | } |