· 6 years ago · Oct 03, 2019, 02:42 PM
1/* tslint:disable:no-any */
2
3import axios, { AxiosInstance, AxiosPromise, AxiosRequestConfig } from 'axios';
4import { appStore } from '../AppStore';
5import { NotificationHandler } from '../Components/ToastNotification';
6import { AzureAuth } from '../Components/AzureAuth';
7import { modalStore } from '../Stores/ModalStore';
8import DateTimeService from '@app/Services/DateTimeService';
9import { DateTime } from '@app/AppConstants';
10
11class ApiHeaders {
12 [key: string]: string | number;
13}
14
15export interface IAjaxOptions {
16 responseType?: string;
17 hideModalLoader?: boolean;
18 requestKey?: string;
19 transformDateTime?:boolean;
20 operationName?:string;
21 noDateTransform?: boolean;
22 supressErrorHandling?: boolean;
23}
24
25type AxiosMethodDefinition = (url: string, data?: any, config?: AxiosRequestConfig) => AxiosPromise;
26type AxiosMethodChooser = (instance: AxiosInstance) => AxiosMethodDefinition;
27
28export default class ApiService {
29
30 private static _callMethod<TResponse>(methodChooser: AxiosMethodChooser, url: string, data?: any, options?: IAjaxOptions): AxiosPromise<TResponse> {
31 const responseType = options && options.responseType || 'json';
32 const instance = ApiService._getInstance(responseType, options);
33 const result = methodChooser(instance)(this._getAbsoluteUrl(url), data);
34 const requestTimestamp = DateTimeService.today().getTime();
35 const isHandleError = !(options && options.supressErrorHandling);
36 appStore.showAjaxSpinner(requestTimestamp);
37 result.catch((error: any) => {
38 if (isHandleError) this.handleError(error);
39 appStore.hideAjaxSpinner(requestTimestamp);
40 });
41 result.then(() => {
42 appStore.hideAjaxSpinner(requestTimestamp);
43 });
44 return result;
45 }
46
47 public static getData(url: string, getData?: any, options?: IAjaxOptions): AxiosPromise {
48 return this.getTypedData<any>(url, getData, options);
49 }
50
51 public static getTypedData<TResponse>(url: string, getData?: any, options?: IAjaxOptions): AxiosPromise<TResponse> {
52 return ApiService._callMethod<TResponse>((instance: AxiosInstance) => instance.get, url, { params: getData }, options);
53 }
54
55 public static putData(url: string, putData?: any, options?: IAjaxOptions): AxiosPromise {
56 return ApiService._callMethod((instance: AxiosInstance) => instance.put, url, putData, options);
57 }
58
59 public static putTypedData<TResponse>(url: string, putData?: any, options?: IAjaxOptions): AxiosPromise<TResponse> {
60 return ApiService._callMethod((instance: AxiosInstance) => instance.put, url, putData, options);
61 }
62
63 public static postData(url: string, postData?: any, options?: IAjaxOptions): AxiosPromise {
64 return ApiService._callMethod((instance: AxiosInstance) => instance.post, url, postData, options);
65 }
66
67 public static postTypedData<TResponse>(url: string, postData?: any, options?: IAjaxOptions): AxiosPromise<TResponse> {
68 return ApiService._callMethod<TResponse>((instance: AxiosInstance) => instance.post, url, postData, options);
69 }
70
71 public static patchData(url: string, patchData?: any, options?: IAjaxOptions): AxiosPromise {
72 return ApiService._callMethod((instance: AxiosInstance) => instance.patch, url, patchData, options);
73 }
74
75 public static deleteData(url: string, options?: IAjaxOptions): AxiosPromise {
76 return ApiService._callMethod((instance: AxiosInstance) => instance.delete, url, void 0, options);
77 }
78
79 public static handleError(error: any) {
80 if (error && error.response && error.response.status === 409) {
81 return;
82 }
83 errorHandleService.showError(error);
84 }
85
86 public static toQueryString(params: { [key: string]: string }) {
87 if (typeof(params) !== 'object') return '';
88 return `?${Object.keys(params).map(k => `${k}=${params[k]}`).join('&')}`;
89 }
90
91 static typeCheck = (el: any, isDate?: boolean) => {
92 if (!el) return el;
93 switch (typeof el) {
94 case 'string':
95 el = ApiService.strCheck(el, isDate);
96 break;
97 case 'object':
98 el = Array.isArray(el) ? ApiService.arrCheck(el) : ApiService.objCheck(el);
99 break;
100 }
101 return el;
102 };
103
104 private static strCheck = (str: string, isDate?: boolean) => {
105 if (isDate && DateTimeService.ISO_8601_date.test(str)) return DateTimeService.fromString(str);
106 return str;
107 };
108
109 private static arrCheck = (array: any) => {
110 return array.map((el: any) => {
111 return ApiService.typeCheck(el);
112 });
113 };
114
115 private static objCheck = (obj: any) => {
116 Object.keys(obj).forEach(key => {
117 obj[key] = ApiService.typeCheck(obj[key], key.indexOf('date') === 0 || key.indexOf('Date') !== -1);
118 });
119 return obj;
120 };
121
122 private static _getInstance(responseType: string = 'json', options?: IAjaxOptions): AxiosInstance {
123 const headers = new ApiHeaders();
124 headers['Cache-Control'] = 'no-cache';
125 if (appStore.currentToken) {
126 headers['Authorization'] = 'Bearer ' + appStore.currentToken;
127 }
128 if (appStore.isHistorized) {
129 headers['App-Is-Historized'] = 'true';
130 headers['App-Historized-Date'] = appStore.releasedDate && DateTimeService.toCustomUiFormat(appStore.releasedDate, DateTime.jsonDateFormat) || '';
131 }
132 if (appStore.isChangeLogOff) {
133 headers['App-Is-Change-Log-Disabled'] = 'true';
134 }
135 if (options && options.operationName) {
136 headers['App-Operation-Name'] = encodeURIComponent(options.operationName);
137 }
138 if (appStore.signalRConnectionId) {
139 headers['App-SignalR-ConnectionId'] = appStore.signalRConnectionId;
140 }
141
142 const transformResponse = (data: any) => {
143 if (options && options.noDateTransform) return data;
144 data = ApiService.typeCheck(data);
145 return data;
146 };
147
148 const axiosInstance = axios.create({ responseType: responseType, headers: headers, transformResponse: transformResponse });
149 axiosInstance.interceptors.response.use((res) => {
150 const contentType = res.headers['content-type'];
151 if (contentType && contentType.indexOf('text/html') > -1 && responseType === 'json') {
152 throw new Error('API call to ' + res.config.url + ' returned not expected content type: ' + contentType);
153 }
154 return res;
155 }, (err) => {
156 if (err.response && err.response.status === 401) {
157 return new Promise(() => {
158 AzureAuth.login();
159 });
160 }
161 throw err;
162 });
163
164 return axiosInstance;
165 }
166
167 private static _getAbsoluteUrl(url: string): string {
168 if (url && url[0] !== '/') url = '/' + url;
169 if (!url.startsWith('/api')) url = '/api' + url;
170 return location.origin + url;
171 }
172}
173
174class ErrorHandleService {
175 private _lastError: { message: string, time: number };
176
177 showError(error: any) {
178 const currentTime = (new Date()).getTime();
179 console.log('--error AJAX: ', error);
180 if (!this._lastError || this._lastError.message !== error.message || (currentTime - this._lastError.time > 10000)) {
181 if (error.response && error.response.status === 403) {
182 window.location.href = location.origin + '/403'; //can not use navigationStore because of problem with class instantiation
183 } else {
184 NotificationHandler.showError(error.message, '--error AJAX');
185 if (error && error.response && (error.response.status === 400 || error.response.status === 500)) {
186 modalStore.showServerError(error);
187 }
188 this._lastError = { message: error.message, time: currentTime };
189 }
190 }
191 }
192
193}
194
195const errorHandleService = new ErrorHandleService();