Improve Error interceptor

master
Max Ehrlicher-Schmidt 4 years ago
parent 4a09780ec3
commit cc5945a8cf

@ -1,23 +1,17 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { APOLLO_OPTIONS } from 'apollo-angular'; import { APOLLO_OPTIONS } from 'apollo-angular';
import { import { ApolloClientOptions, InMemoryCache } from '@apollo/client/core';
ApolloClientOptions,
InMemoryCache,
} from '@apollo/client/core';
import { environment } from '../environments/environment'; import { environment } from '../environments/environment';
import { DefaultOptions } from '@apollo/client/core/ApolloClient'; import { DefaultOptions } from '@apollo/client/core/ApolloClient';
import { onError } from '@apollo/link-error'; 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 { SnackBarService } from './services/snackbar.service';
import { getErrorMessage } from './helper/getErrorMessage';
const uri = environment.apiUrl + '/graphql'; // <-- add the URL of the GraphQL server here const uri = environment.apiUrl + '/graphql'; // <-- add the URL of the GraphQL server here
const defaultOptions: DefaultOptions = { const defaultOptions: DefaultOptions = {
watchQuery: { watchQuery: {
fetchPolicy: 'no-cache', fetchPolicy: 'no-cache',
@ -27,29 +21,24 @@ const defaultOptions: DefaultOptions = {
fetchPolicy: 'no-cache', fetchPolicy: 'no-cache',
errorPolicy: 'all', errorPolicy: 'all',
}, },
} };
export function createApollo(httpLink: HttpLink, snackBar: SnackBarService): ApolloClientOptions<any> {
export function createApollo(
httpLink: HttpLink,
snackBar: SnackBarService
): ApolloClientOptions<any> {
const errorLink = onError(({ graphQLErrors, networkError }) => { const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) if (graphQLErrors) {
graphQLErrors.map(({ message, locations, path }) => { snackBar.openSnackBar(getErrorMessage(graphQLErrors[0] || null), 'Ok', true);
snackBar.openSnackBar(JSON.stringify(message), "Ok", true); }
/*console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
)*/
},
); if (networkError) {
if (networkError) { //console.log(`[Network error]: ${networkError}`);
//console.log(`[Network error]: ${networkError}`); // THE FOLLOWING CODE IS UNTESTED
// THE FOLLOWING CODE IS UNTESTED //snackBar.openSnackBar("Ein NetzwerkFehler ist aufgetreten", "Ok", true, [{"message": networkError}] );
//snackBar.openSnackBar("Ein NetzwerkFehler ist aufgetreten", "Ok", true, [{"message": networkError}] ); snackBar.openSnackBar(JSON.stringify(networkError), '', true);
snackBar.openSnackBar(JSON.stringify(networkError), "", true); }
} });
});
return { return {
link: errorLink.concat(httpLink.create({ uri })), link: errorLink.concat(httpLink.create({ uri })),
@ -64,9 +53,7 @@ export function createApollo(httpLink: HttpLink, snackBar: SnackBarService): Apo
provide: APOLLO_OPTIONS, provide: APOLLO_OPTIONS,
useFactory: createApollo, useFactory: createApollo,
deps: [HttpLink, SnackBarService], deps: [HttpLink, SnackBarService],
} },
], ],
}) })
export class GraphQLModule { export class GraphQLModule {}
}

@ -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.';
}

@ -1,92 +1,98 @@
import { Injectable } from '@angular/core'; 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 { AuthService } from '../services/auth.service';
import { Observable, throwError, BehaviorSubject } from 'rxjs'; import { Observable, throwError, BehaviorSubject } from 'rxjs';
import { catchError, filter, take, switchMap } from 'rxjs/operators'; import { catchError, filter, take, switchMap } from 'rxjs/operators';
import { SnackBarService } from '../services/snackbar.service'; import { SnackBarService } from '../services/snackbar.service';
import { Router, RouterStateSnapshot } from '@angular/router'; import { Router, RouterStateSnapshot } from '@angular/router';
import { JsonPipe } from '@angular/common'; import { getErrorMessage } from './getErrorMessage';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
} })
)
export class TokenInterceptor implements HttpInterceptor { export class TokenInterceptor implements HttpInterceptor {
private isRefreshing = false; private isRefreshing = false;
private requestTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null); private requestTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(
null
constructor(private authService: AuthService, private snackBar : SnackBarService, private router: Router) { } );
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { constructor(
private authService: AuthService,
private snackBar: SnackBarService,
private router: Router
) {}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
//console.log("i intercepted something"); //console.log("i intercepted something");
if (this.authService.getRequestToken()) { if (this.authService.getRequestToken()) {
request = this.addToken(request, this.authService.getRequestToken()); request = this.addToken(request, this.authService.getRequestToken());
} }
return next.handle(request).pipe(catchError((error: HttpErrorResponse) => { return next.handle(request).pipe(
let errorMessage = ''; catchError((error: HttpErrorResponse) => {
if (error.error instanceof ErrorEvent) { let errorMessage = '';
//client error //server error
//console.log("Client Error: " + JSON.stringify(error)); //console.log("Server Error: " + JSON.stringify(error));
console.log(error);
errorMessage = `Error: ${error.error.message}`; if (error?.status === 400) {
} else { switch (error?.error?.message) {
//server error case 'Invalid refresh token!':
//console.log("Server Error: " + JSON.stringify(error)); this.authService.logout();
if (error.status === 400){ errorMessage =
switch (error.error.message) { 'Die aktuelle Sitzung ist abgelaufen. Bitte loggen sie sich erneut ein.';
case "Invalid refresh token!": this.router.navigate(['/login'], {
this.authService.logout(); queryParams: {
errorMessage = "Die aktuelle Sitzung ist abgelaufen. Bitte loggen sie sich erneut ein." returnUrl: this.router.routerState.snapshot.url,
this.router.navigate(["/login"], { queryParams: { returnUrl: this.router.routerState.snapshot.url } }); },
break; });
break;
default:
errorMessage = this.serverErrorMessageGenerator(error); default:
break; errorMessage = getErrorMessage(error);
} break;
}
} else if (error.status === 401) { } else if (error?.status === 401) {
var urlSplit : string[] = error.url.split("/"); 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 if (urlSplit[3] === 'users' && urlSplit[5] === 'update') {
errorMessage = "Das aktuelle Passwort ist inkorrekt."; // Allow user pw updates to be processed correctly
} else { errorMessage = 'Das aktuelle Passwort ist inkorrekt.';
return this.handle401Error(request, next);
}
} else { } 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?.errors?.length > 1) {
if (error.error.message === undefined){ this.snackBar.openSnackBar(
if (error.error.errors[0].message.includes("not provided")) { errorMessage,
return "Nicht alle benötigten Felder wurden ausgefüllt."; 'Erweitert',
} else { true,
return "Viele Fehler sind aufgetreten." // If you change this you have to change it over this aswell ( I know its terrible to @ ) error.error.errors
} );
} else { } else {
return `${error.error.message}.`; //`${error.error.message}. Fehlercode: ${error.status}.` this.snackBar.openSnackBar(errorMessage, 'Ok', true);
} }
return throwError(errorMessage);
})
);
} }
private addToken(request: HttpRequest<any>, token: string) { private addToken(request: HttpRequest<any>, token: string) {
return request.clone({ return request.clone({
setHeaders: { setHeaders: {
'Authorization': `Bearer ${token}` Authorization: `Bearer ${token}`,
} },
}); });
} }
@ -100,15 +106,16 @@ export class TokenInterceptor implements HttpInterceptor {
this.isRefreshing = false; this.isRefreshing = false;
this.requestTokenSubject.next(token.request_token); this.requestTokenSubject.next(token.request_token);
return next.handle(this.addToken(request, token.request_token)); return next.handle(this.addToken(request, token.request_token));
})); })
);
} else { } else {
return this.requestTokenSubject.pipe( return this.requestTokenSubject.pipe(
filter(token => token != null), filter((token) => token != null),
take(1), take(1),
switchMap(jwt => { switchMap((jwt) => {
return next.handle(this.addToken(request, jwt)); return next.handle(this.addToken(request, jwt));
})); })
);
} }
} }
} }
Loading…
Cancel
Save