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,27 +21,22 @@ 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);
} }
}); });
@ -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(
catchError((error: HttpErrorResponse) => {
let errorMessage = ''; let errorMessage = '';
if (error.error instanceof ErrorEvent) {
//client error
//console.log("Client Error: " + JSON.stringify(error));
errorMessage = `Error: ${error.error.message}`;
} else {
//server error //server error
//console.log("Server Error: " + JSON.stringify(error)); //console.log("Server Error: " + JSON.stringify(error));
if (error.status === 400){ console.log(error);
switch (error.error.message) { if (error?.status === 400) {
case "Invalid refresh token!": switch (error?.error?.message) {
case 'Invalid refresh token!':
this.authService.logout(); this.authService.logout();
errorMessage = "Die aktuelle Sitzung ist abgelaufen. Bitte loggen sie sich erneut ein." errorMessage =
this.router.navigate(["/login"], { queryParams: { returnUrl: this.router.routerState.snapshot.url } }); 'Die aktuelle Sitzung ist abgelaufen. Bitte loggen sie sich erneut ein.';
this.router.navigate(['/login'], {
queryParams: {
returnUrl: this.router.routerState.snapshot.url,
},
});
break; break;
default: default:
errorMessage = this.serverErrorMessageGenerator(error); errorMessage = getErrorMessage(error);
break; break;
} }
} else if (error?.status === 401) {
} else if (error.status === 401) { var urlSplit: string[] = error.url.split('/'); //TODO: UFF might lead to bug when deployed under sub path .../frontend/...
var urlSplit : string[] = error.url.split("/"); if (urlSplit[3] === 'users' && urlSplit[5] === 'update') {
if (urlSplit[3] === "users" && urlSplit[5] === "update"){ // Allow user pw updates to be processed correctly // Allow user pw updates to be processed correctly
errorMessage = "Das aktuelle Passwort ist inkorrekt."; errorMessage = 'Das aktuelle Passwort ist inkorrekt.';
} else { } else {
return this.handle401Error(request, next); return this.handle401Error(request, next);
} }
} else { } else {
errorMessage = this.serverErrorMessageGenerator(error); 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); if (error?.error?.errors?.length > 1) {
})); this.snackBar.openSnackBar(
} errorMessage,
'Erweitert',
private serverErrorMessageGenerator (error: HttpErrorResponse): string { true,
if (error.error.message === undefined){ error.error.errors
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 { } 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