Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / packages / metrics / lib / MetricsApi.ts
blob7a79f45af0b0627bf4a4d28db5c5f2e65d9eab04
1 import { HTTP_STATUS_CODE, SECOND } from '@proton/shared/lib/constants';
2 import { getAppVersionHeaders, getAuthHeaders, getUIDHeaders } from '@proton/shared/lib/fetch/headers';
3 import { wait } from '@proton/shared/lib/helpers/promise';
5 import { METRICS_DEFAULT_RETRY_SECONDS, METRICS_MAX_ATTEMPTS, METRICS_REQUEST_TIMEOUT_SECONDS } from './../constants';
6 import type IMetricsApi from './types/IMetricsApi';
8 class TooManyRequestsError extends Error {
9     constructor() {
10         super('Too many requests');
11         this.name = 'TooManyRequestsError';
12     }
15 class MetricsApi implements IMetricsApi {
16     private _authHeaders: { [key: string]: string };
18     private _versionHeaders: { [key: string]: string };
20     constructor({ uid, clientID, appVersion }: { uid?: string; clientID?: string; appVersion?: string } = {}) {
21         this._authHeaders = this.getAuthHeaders(uid);
22         this._versionHeaders = this.getVersionHeaders(clientID, appVersion);
23     }
25     public setAuthHeaders(uid: string, accessToken?: string) {
26         this._authHeaders = this.getAuthHeaders(uid, accessToken);
27     }
29     public setVersionHeaders(clientID: string, appVersion: string) {
30         this._versionHeaders = this.getVersionHeaders(clientID, appVersion);
31     }
33     private async _fetchWithTimeout(requestInfo: RequestInfo | URL, requestInit: RequestInit) {
34         const abortController = new AbortController();
36         const timeoutId = setTimeout(() => {
37             abortController.abort();
38         }, METRICS_REQUEST_TIMEOUT_SECONDS * SECOND);
40         return fetch(requestInfo, {
41             signal: abortController.signal,
42             ...requestInit,
43         }).finally(() => {
44             clearTimeout(timeoutId);
45         });
46     }
48     public async fetch(
49         requestInfo: RequestInfo | URL,
50         requestInit: RequestInit = {},
51         attempt: number = 1
52     ): Promise<Response | undefined> {
53         try {
54             const response = await this._fetchWithTimeout(requestInfo, {
55                 ...requestInit,
56                 headers: {
57                     ...requestInit?.headers,
58                     'content-type': 'application/json',
59                     priority: 'u=6',
60                     ...this._authHeaders,
61                     ...this._versionHeaders,
62                 },
63             });
65             if (attempt >= METRICS_MAX_ATTEMPTS) {
66                 throw new TooManyRequestsError();
67             }
69             if (response.status === HTTP_STATUS_CODE.TOO_MANY_REQUESTS) {
70                 const retryAfterSeconds = (() => {
71                     const retryHeader = response?.headers?.get('retry-after') || '';
73                     const parsedInt = parseInt(retryHeader, 10);
75                     if (parsedInt <= 0 || isNaN(parsedInt)) {
76                         return METRICS_DEFAULT_RETRY_SECONDS;
77                     }
79                     return parsedInt;
80                 })();
82                 await wait(retryAfterSeconds * SECOND);
83                 return await this.fetch(requestInfo, requestInit, attempt + 1);
84             }
86             return response;
87         } catch (error: any) {
88             if (error.name !== 'AbortError' || error.name === 'TooManyRequestsError') {
89                 throw error;
90             }
92             if (attempt >= METRICS_MAX_ATTEMPTS) {
93                 throw new TooManyRequestsError();
94             }
96             await wait(METRICS_DEFAULT_RETRY_SECONDS * SECOND);
97             return this.fetch(requestInfo, requestInit, attempt + 1);
98         }
99     }
101     private getAuthHeaders(uid?: string, accessToken?: string) {
102         if (!uid) {
103             return {};
104         }
106         if (uid && accessToken) {
107             return getAuthHeaders(uid, accessToken);
108         }
110         return getUIDHeaders(uid);
111     }
113     private getVersionHeaders(clientID?: string, appVersion?: string) {
114         if (!clientID || !appVersion) {
115             return {};
116         }
118         return getAppVersionHeaders(clientID, appVersion);
119     }
122 export default MetricsApi;