From cc5945a8cfd53f6f1f4a9199a1276bfe5f649249 Mon Sep 17 00:00:00 2001 From: Max Ehrlicher-Schmidt Date: Sat, 26 Dec 2020 22:44:33 +0100 Subject: [PATCH] Improve Error interceptor --- src/app/graphql.module.ts | 55 +++++------ src/app/helper/getErrorMessage.ts | 17 ++++ src/app/helper/token.interceptor.ts | 139 +++++++++++++++------------- 3 files changed, 111 insertions(+), 100 deletions(-) create mode 100644 src/app/helper/getErrorMessage.ts diff --git a/src/app/graphql.module.ts b/src/app/graphql.module.ts index eca37c2..bbac287 100644 --- a/src/app/graphql.module.ts +++ b/src/app/graphql.module.ts @@ -1,23 +1,17 @@ import { NgModule } from '@angular/core'; import { APOLLO_OPTIONS } from 'apollo-angular'; -import { - ApolloClientOptions, - InMemoryCache, -} from '@apollo/client/core'; +import { ApolloClientOptions, InMemoryCache } from '@apollo/client/core'; import { environment } from '../environments/environment'; import { DefaultOptions } from '@apollo/client/core/ApolloClient'; import { onError } from '@apollo/link-error'; -import {Apollo, gql} from 'apollo-angular'; -import {HttpLink} from 'apollo-angular/http'; +import { HttpLink } from 'apollo-angular/http'; import { SnackBarService } from './services/snackbar.service'; - +import { getErrorMessage } from './helper/getErrorMessage'; const uri = environment.apiUrl + '/graphql'; // <-- add the URL of the GraphQL server here - - const defaultOptions: DefaultOptions = { watchQuery: { fetchPolicy: 'no-cache', @@ -27,29 +21,24 @@ const defaultOptions: DefaultOptions = { fetchPolicy: 'no-cache', errorPolicy: 'all', }, -} - - - -export function createApollo(httpLink: HttpLink, snackBar: SnackBarService): ApolloClientOptions { +}; +export function createApollo( + httpLink: HttpLink, + snackBar: SnackBarService +): ApolloClientOptions { const errorLink = onError(({ graphQLErrors, networkError }) => { - if (graphQLErrors) - graphQLErrors.map(({ message, locations, path }) => { - snackBar.openSnackBar(JSON.stringify(message), "Ok", true); - /*console.log( - `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`, - )*/ - }, - - ); - if (networkError) { - //console.log(`[Network error]: ${networkError}`); - // THE FOLLOWING CODE IS UNTESTED - //snackBar.openSnackBar("Ein NetzwerkFehler ist aufgetreten", "Ok", true, [{"message": networkError}] ); - snackBar.openSnackBar(JSON.stringify(networkError), "", true); - } - }); + if (graphQLErrors) { + snackBar.openSnackBar(getErrorMessage(graphQLErrors[0] || null), 'Ok', true); + } + + if (networkError) { + //console.log(`[Network error]: ${networkError}`); + // THE FOLLOWING CODE IS UNTESTED + //snackBar.openSnackBar("Ein NetzwerkFehler ist aufgetreten", "Ok", true, [{"message": networkError}] ); + snackBar.openSnackBar(JSON.stringify(networkError), '', true); + } + }); return { link: errorLink.concat(httpLink.create({ uri })), @@ -64,9 +53,7 @@ export function createApollo(httpLink: HttpLink, snackBar: SnackBarService): Apo provide: APOLLO_OPTIONS, useFactory: createApollo, deps: [HttpLink, SnackBarService], - } + }, ], }) -export class GraphQLModule { - -} +export class GraphQLModule {} diff --git a/src/app/helper/getErrorMessage.ts b/src/app/helper/getErrorMessage.ts new file mode 100644 index 0000000..8e8196b --- /dev/null +++ b/src/app/helper/getErrorMessage.ts @@ -0,0 +1,17 @@ +export function getErrorMessage(error: any): string { + const message = + error?.error?.errors[0]?.message || + error?.error?.message || + error?.message || + ''; + if ( + message.includes('not provided') || + message.includes('violates not-null constraint') + ) { + return 'Nicht alle benötigten Felder wurden ausgefüllt.'; + } + if (message.includes("TimeFrames with ids") && message.includes("overlapping")) { + return "Überlappende Zeitscheibe - Ein Lastenrad kann zu einem Zeitpunkt nur an einem Standort stehen." + } + return message || 'Es ist ein Fehler aufgetreten. Prüfen Sie Ihre Eingaben oder versuchen Sie es später erneut.'; +} diff --git a/src/app/helper/token.interceptor.ts b/src/app/helper/token.interceptor.ts index 5b017a5..d50bc88 100644 --- a/src/app/helper/token.interceptor.ts +++ b/src/app/helper/token.interceptor.ts @@ -1,92 +1,98 @@ import { Injectable } from '@angular/core'; -import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http'; +import { + HttpRequest, + HttpHandler, + HttpEvent, + HttpInterceptor, + HttpErrorResponse, +} from '@angular/common/http'; import { AuthService } from '../services/auth.service'; import { Observable, throwError, BehaviorSubject } from 'rxjs'; import { catchError, filter, take, switchMap } from 'rxjs/operators'; import { SnackBarService } from '../services/snackbar.service'; import { Router, RouterStateSnapshot } from '@angular/router'; -import { JsonPipe } from '@angular/common'; +import { getErrorMessage } from './getErrorMessage'; @Injectable({ providedIn: 'root', -} -) +}) export class TokenInterceptor implements HttpInterceptor { - private isRefreshing = false; - private requestTokenSubject: BehaviorSubject = new BehaviorSubject(null); + private requestTokenSubject: BehaviorSubject = new BehaviorSubject( + null + ); - constructor(private authService: AuthService, private snackBar : SnackBarService, private router: Router) { } + constructor( + private authService: AuthService, + private snackBar: SnackBarService, + private router: Router + ) {} - intercept(request: HttpRequest, next: HttpHandler): Observable> { + intercept( + request: HttpRequest, + next: HttpHandler + ): Observable> { //console.log("i intercepted something"); if (this.authService.getRequestToken()) { request = this.addToken(request, this.authService.getRequestToken()); } - return next.handle(request).pipe(catchError((error: HttpErrorResponse) => { - let errorMessage = ''; - if (error.error instanceof ErrorEvent) { - //client error - //console.log("Client Error: " + JSON.stringify(error)); + return next.handle(request).pipe( + catchError((error: HttpErrorResponse) => { + let errorMessage = ''; + //server error + //console.log("Server Error: " + JSON.stringify(error)); + console.log(error); + if (error?.status === 400) { + switch (error?.error?.message) { + case 'Invalid refresh token!': + this.authService.logout(); + errorMessage = + 'Die aktuelle Sitzung ist abgelaufen. Bitte loggen sie sich erneut ein.'; + this.router.navigate(['/login'], { + queryParams: { + returnUrl: this.router.routerState.snapshot.url, + }, + }); + break; - errorMessage = `Error: ${error.error.message}`; - } else { - //server error - //console.log("Server Error: " + JSON.stringify(error)); - if (error.status === 400){ - switch (error.error.message) { - case "Invalid refresh token!": - this.authService.logout(); - errorMessage = "Die aktuelle Sitzung ist abgelaufen. Bitte loggen sie sich erneut ein." - this.router.navigate(["/login"], { queryParams: { returnUrl: this.router.routerState.snapshot.url } }); - break; - - default: - errorMessage = this.serverErrorMessageGenerator(error); - break; - } - - } else if (error.status === 401) { - var urlSplit : string[] = error.url.split("/"); - if (urlSplit[3] === "users" && urlSplit[5] === "update"){ // Allow user pw updates to be processed correctly - errorMessage = "Das aktuelle Passwort ist inkorrekt."; - } else { - return this.handle401Error(request, next); - } + default: + errorMessage = getErrorMessage(error); + break; + } + } else if (error?.status === 401) { + var urlSplit: string[] = error.url.split('/'); //TODO: UFF might lead to bug when deployed under sub path .../frontend/... + if (urlSplit[3] === 'users' && urlSplit[5] === 'update') { + // Allow user pw updates to be processed correctly + errorMessage = 'Das aktuelle Passwort ist inkorrekt.'; } else { - errorMessage = this.serverErrorMessageGenerator(error); + return this.handle401Error(request, next); } + } else { + errorMessage = getErrorMessage(error); } - if (errorMessage === "Viele Fehler sind aufgetreten.") { // Here is the thing I ment - this.snackBar.openSnackBar(errorMessage, "Erweitert", true, error.error.errors); - } else { - this.snackBar.openSnackBar(errorMessage, "Ok", true); - } - - return throwError(errorMessage); - })); - } - private serverErrorMessageGenerator (error: HttpErrorResponse): string { - if (error.error.message === undefined){ - if (error.error.errors[0].message.includes("not provided")) { - return "Nicht alle benötigten Felder wurden ausgefüllt."; - } else { - return "Viele Fehler sind aufgetreten." // If you change this you have to change it over this aswell ( I know its terrible to @ ) - } - } else { - return `${error.error.message}.`; //`${error.error.message}. Fehlercode: ${error.status}.` - } + if (error?.error?.errors?.length > 1) { + this.snackBar.openSnackBar( + errorMessage, + 'Erweitert', + true, + error.error.errors + ); + } else { + this.snackBar.openSnackBar(errorMessage, 'Ok', true); + } + return throwError(errorMessage); + }) + ); } - private addToken(request: HttpRequest, token: string) { return request.clone({ setHeaders: { - 'Authorization': `Bearer ${token}` - } + Authorization: `Bearer ${token}`, + }, }); } @@ -100,15 +106,16 @@ export class TokenInterceptor implements HttpInterceptor { this.isRefreshing = false; this.requestTokenSubject.next(token.request_token); return next.handle(this.addToken(request, token.request_token)); - })); - + }) + ); } else { return this.requestTokenSubject.pipe( - filter(token => token != null), + filter((token) => token != null), take(1), - switchMap(jwt => { + switchMap((jwt) => { return next.handle(this.addToken(request, jwt)); - })); + }) + ); } } -} \ No newline at end of file +}