/
api.service.ts
154 lines (128 loc) · 4.35 KB
/
api.service.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import { Injectable, NgZone } from '@angular/core';
import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { Router } from '@angular/router';
import { processServiceError } from '../utils/errors';
import { environment } from 'src/environments/environment';
export enum ResponseTypes {
Json = 'json',
Text = 'text',
}
export enum RequestTypes {
Json = 'json',
}
export class RequestOptions {
responseType = ResponseTypes.Json;
requestType = RequestTypes.Json;
ignoreAuth = false;
public constructor(init?: Partial<RequestOptions>) {
Object.assign(this, init);
}
}
/**
* Allows to make requests to the backend API.
*/
@Injectable({
providedIn: 'root'
})
export class ApiService {
/**
* URL prefix for the API routes. The 'http-api/' prefix is used if the app is running
* with the dev server using the http protocol, because the dev server proxy uses it to
* route the request to the appropiate url.
*/
private readonly apiPrefix = !environment.production && location.protocol.indexOf('http:') !== -1 ?
'http-api/' : 'api/';
constructor(
private http: HttpClient,
private router: Router,
private ngZone: NgZone,
) { }
/**
* Makes a request to a GET endpoint.
* @param url Endpoint URL, after the "/api/" part.
*/
get(url: string, options: RequestOptions = null): Observable<any> {
return this.request('GET', url, {}, options);
}
/**
* Makes a request to a POST endpoint.
* @param url Endpoint URL, after the "/api/" part.
*/
post(url: string, body: any = {}, options: RequestOptions = null): Observable<any> {
return this.request('POST', url, body, options);
}
/**
* Makes a request to a PUT endpoint.
* @param url Endpoint URL, after the "/api/" part.
*/
put(url: string, body: any = {}, options: RequestOptions = null): Observable<any> {
return this.request('PUT', url, body, options);
}
/**
* Makes a request to a DELETE endpoint.
* @param url Endpoint URL, after the "/api/" part.
*/
delete(url: string, options: RequestOptions = null): Observable<any> {
return this.request('DELETE', url, {}, options);
}
/**
* Makes the actual call to the API.
*/
private request(method: string, url: string, body: any, options: RequestOptions) {
body = body ? body : {};
options = options ? options : new RequestOptions();
return this.http.request(method, this.apiPrefix + url, {
...this.getRequestOptions(options),
responseType: options.responseType,
// Use the session cookies.
withCredentials: true,
body: this.getPostBody(body, options),
}).pipe(
map(result => this.successHandler(result)),
catchError(error => this.errorHandler(error, options)),
);
}
/**
* Process the options to use them whem making the reques.
*/
private getRequestOptions(options: RequestOptions) {
const requestOptions: any = {};
requestOptions.headers = new HttpHeaders();
if (options.requestType === RequestTypes.Json) {
requestOptions.headers = requestOptions.headers.append('Content-Type', 'application/json');
}
return requestOptions;
}
/**
* Encode the content to send it to the backend.
*/
private getPostBody(body: any, options: RequestOptions) {
if (options.requestType === RequestTypes.Json) {
return JSON.stringify(body);
}
const formData = new FormData();
Object.keys(body).forEach(key => formData.append(key, body[key]));
return formData;
}
private successHandler(result: any) {
if (typeof result === 'string' && result === 'manager token is null') {
throw new Error(result);
}
return result;
}
private errorHandler(error: HttpErrorResponse, options: RequestOptions) {
// Normally, if the problem was due to the session cookie being invalid, the
// user is redirected to the login page.
if (!options.ignoreAuth) {
if (error.status === 401) {
this.ngZone.run(() => this.router.navigate(['login'], { replaceUrl: true }));
}
if (error.error && typeof error.error === 'string' && error.error.includes('change password')) {
this.ngZone.run(() => this.router.navigate(['login'], { replaceUrl: true }));
}
}
return throwError(processServiceError(error));
}
}