@pushwoosh/http-client 1.5.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/http-client.errors.d.ts +231 -0
- package/dist/http-client.errors.d.ts.map +1 -0
- package/dist/http-client.main.d.ts +145 -0
- package/dist/http-client.main.d.ts.map +1 -0
- package/dist/http-client.types.d.ts +89 -0
- package/dist/http-client.types.d.ts.map +1 -0
- package/dist/http-client.utilities.d.ts +20 -0
- package/dist/http-client.utilities.d.ts.map +1 -0
- package/{src/index.ts → dist/index.d.ts} +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.LICENSE.txt +1 -0
- package/dist/index.js.map +1 -0
- package/package.json +4 -1
- package/.browserslistrc +0 -5
- package/.dockerignore +0 -2
- package/.env.example +0 -3
- package/.eslintignore +0 -3
- package/.eslintrc +0 -38
- package/.gitlab-ci.yml +0 -122
- package/babel.config.js +0 -24
- package/jest.config.js +0 -3
- package/src/http-client.errors.ts +0 -324
- package/src/http-client.main.ts +0 -451
- package/src/http-client.types.ts +0 -102
- package/src/http-client.utilities.ts +0 -84
- package/tsconfig.eslint.json +0 -9
- package/tsconfig.json +0 -51
- package/webpack.base.config.js +0 -40
- package/webpack.development.config.js +0 -33
- package/webpack.production.config.js +0 -24
- package/webpack.utilities.js +0 -12
package/src/http-client.main.ts
DELETED
|
@@ -1,451 +0,0 @@
|
|
|
1
|
-
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
|
|
2
|
-
import { History } from 'history';
|
|
3
|
-
import Cookies from 'js-cookie';
|
|
4
|
-
import decodeJwt from 'jwt-decode';
|
|
5
|
-
import { compile } from 'path-to-regexp';
|
|
6
|
-
import { stringify } from 'qs';
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
LogicError,
|
|
10
|
-
UnauthorizedError,
|
|
11
|
-
UnprocessableEntityError,
|
|
12
|
-
} from './http-client.errors';
|
|
13
|
-
import {
|
|
14
|
-
AccessToken,
|
|
15
|
-
BasicAuthorization,
|
|
16
|
-
Body,
|
|
17
|
-
Config,
|
|
18
|
-
DeleteOptions,
|
|
19
|
-
GetOptions,
|
|
20
|
-
Introspect,
|
|
21
|
-
Method,
|
|
22
|
-
Params,
|
|
23
|
-
PatchOptions,
|
|
24
|
-
PostOptions,
|
|
25
|
-
PutOptions,
|
|
26
|
-
Query,
|
|
27
|
-
RefreshToken,
|
|
28
|
-
RequestDetails,
|
|
29
|
-
RequestOptions,
|
|
30
|
-
Result,
|
|
31
|
-
} from './http-client.types';
|
|
32
|
-
|
|
33
|
-
type UpdateTokensByAuthorizationCodeQuery = {
|
|
34
|
-
readonly grant_type: 'authorization_code';
|
|
35
|
-
readonly code: string;
|
|
36
|
-
readonly redirect_uri: string;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
type UpdateTokensByRefreshTokenQuery = {
|
|
40
|
-
readonly grant_type: 'refresh_token';
|
|
41
|
-
readonly refresh_token: string;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
type UpdateTokensQuery =
|
|
45
|
-
| UpdateTokensByAuthorizationCodeQuery
|
|
46
|
-
| UpdateTokensByRefreshTokenQuery;
|
|
47
|
-
|
|
48
|
-
type UpdateTokensPairResult = {
|
|
49
|
-
readonly access_token: string;
|
|
50
|
-
readonly refresh_token: string;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export class HttpClient {
|
|
54
|
-
/**
|
|
55
|
-
* Имя куки, используемое для сохранения состояния авторизации
|
|
56
|
-
* Для более подробной информации смотри https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1.1
|
|
57
|
-
*/
|
|
58
|
-
public static AUTHORIZATION_STATE_COOKIE_NAME = '__PW_AUTHORIZATION_STATE';
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Имя куки, используемое для сохранения адреса страницы перед авторизацией
|
|
62
|
-
*/
|
|
63
|
-
public static URL_BEFORE_AUTHORIZATION_COOKIE_NAME = '__PW_URL_BEFORE_AUTHORIZATION';
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Экземпляр Axios
|
|
67
|
-
*/
|
|
68
|
-
private readonly instance: AxiosInstance;
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* API для работы с навигацией
|
|
72
|
-
*/
|
|
73
|
-
private readonly history: History;
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Конфигурация Http Client
|
|
77
|
-
*/
|
|
78
|
-
private readonly config: Config;
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Токен доступа, используемый для доступа к защищенным ресурсам
|
|
82
|
-
* Для более подробной информации смотри https://www.rfc-editor.org/rfc/rfc6749.html#section-1.4
|
|
83
|
-
*/
|
|
84
|
-
private accessToken: AccessToken | null;
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Токен обновления, используемый для обновления токена доступа.
|
|
88
|
-
* Для более подробной информации смотри https://www.rfc-editor.org/rfc/rfc6749.html#section-1.5
|
|
89
|
-
*/
|
|
90
|
-
private refreshToken: RefreshToken | null;
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Происходит ли сейчас обновление токенов
|
|
94
|
-
*/
|
|
95
|
-
private isRefreshing: boolean;
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Проверить наличие авторизации
|
|
99
|
-
*
|
|
100
|
-
* @param details - детали запроса к защищенным ресурсам
|
|
101
|
-
*/
|
|
102
|
-
private checkAuthorization = async (details: RequestDetails | null): Promise<void> => {
|
|
103
|
-
// если происходит процесс обновления токенов, ждем 100 мс и пробуем проверить авторизацию заново
|
|
104
|
-
if (this.isRefreshing) {
|
|
105
|
-
await new Promise((resolve) => {
|
|
106
|
-
setTimeout(resolve, 100);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
await this.checkAuthorization(details);
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// если нет токена доступа, то клиент точно не авторизован
|
|
114
|
-
if (!this.accessToken) {
|
|
115
|
-
throw new UnauthorizedError('Unauthorized!', details);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// если время жизни токена вышло, нужно попробовать обновить его с помощью токена обновления
|
|
119
|
-
const currentTime = new Date().getTime() / 1000;
|
|
120
|
-
const introspect = decodeJwt<any>(this.accessToken);
|
|
121
|
-
const isTokenExpired = introspect.expired_at < currentTime;
|
|
122
|
-
if (isTokenExpired) {
|
|
123
|
-
await this.updateTokenByRefreshToken(details);
|
|
124
|
-
await this.checkAuthorization(details);
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Обновить токен доступа
|
|
130
|
-
* Для более подробной информации смотри:
|
|
131
|
-
* https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1.3
|
|
132
|
-
* https://www.rfc-editor.org/rfc/rfc6749.html#section-6
|
|
133
|
-
*
|
|
134
|
-
* @param query - параметры запроса, участвующие в обновлении
|
|
135
|
-
*/
|
|
136
|
-
private updateToken = async (query: UpdateTokensQuery): Promise<void> => {
|
|
137
|
-
// идентификация клиента происходит с помощью заголовка
|
|
138
|
-
const clientIdInBase64 = btoa(`${this.config.clientId}:`);
|
|
139
|
-
const authorizationHeader = `Basic ${clientIdInBase64}`;
|
|
140
|
-
|
|
141
|
-
try {
|
|
142
|
-
this.isRefreshing = true;
|
|
143
|
-
|
|
144
|
-
const tokens = await this.post<UpdateTokensPairResult, never, UpdateTokensQuery, never>(
|
|
145
|
-
this.config.urls.token,
|
|
146
|
-
{
|
|
147
|
-
headers: {
|
|
148
|
-
Authorization: authorizationHeader,
|
|
149
|
-
},
|
|
150
|
-
query,
|
|
151
|
-
withAuthorization: false,
|
|
152
|
-
withCredentials: true,
|
|
153
|
-
},
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
this.accessToken = tokens.access_token;
|
|
157
|
-
this.refreshToken = tokens.refresh_token;
|
|
158
|
-
this.isRefreshing = false;
|
|
159
|
-
} catch (error) {
|
|
160
|
-
this.accessToken = null;
|
|
161
|
-
this.refreshToken = null;
|
|
162
|
-
this.isRefreshing = false;
|
|
163
|
-
|
|
164
|
-
throw error;
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Обновить токен доступа с помощью кода авторизации
|
|
170
|
-
* Для более подробной информации смотри: https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1.3
|
|
171
|
-
*
|
|
172
|
-
* @param authorizationCode - код авторизации, полученный от сервера авторизации
|
|
173
|
-
*/
|
|
174
|
-
public updateTokenByAuthorizationCode = async (authorizationCode: string): Promise<void> => {
|
|
175
|
-
await this.updateToken({
|
|
176
|
-
grant_type: 'authorization_code',
|
|
177
|
-
code: authorizationCode,
|
|
178
|
-
redirect_uri: `${window.location.origin}${window.location.pathname}`,
|
|
179
|
-
});
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Обновить токен доступа с помощью токена обновления
|
|
184
|
-
* Для более подробной информации смотри: https://www.rfc-editor.org/rfc/rfc6749.html#section-6
|
|
185
|
-
*
|
|
186
|
-
* @param details - детали запроса к защищенным ресурсам
|
|
187
|
-
*/
|
|
188
|
-
public updateTokenByRefreshToken = async (details: RequestDetails | null): Promise<void> => {
|
|
189
|
-
if (!this.refreshToken) {
|
|
190
|
-
this.accessToken = null;
|
|
191
|
-
throw new UnauthorizedError('Unauthorized!', details);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
await this.updateToken({
|
|
195
|
-
grant_type: 'refresh_token',
|
|
196
|
-
refresh_token: this.refreshToken,
|
|
197
|
-
});
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Создать экземпляр класса
|
|
202
|
-
*
|
|
203
|
-
* @param history - API для работы с навигацией
|
|
204
|
-
* @param config - конфигурация Http Client
|
|
205
|
-
*/
|
|
206
|
-
public constructor(history: History, config: Config) {
|
|
207
|
-
this.instance = axios.create({ paramsSerializer: stringify });
|
|
208
|
-
|
|
209
|
-
this.history = history;
|
|
210
|
-
this.config = config;
|
|
211
|
-
this.accessToken = null;
|
|
212
|
-
this.refreshToken = null;
|
|
213
|
-
this.isRefreshing = false;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Выполнить запрос
|
|
218
|
-
*
|
|
219
|
-
* @param url - адрес запроса
|
|
220
|
-
* @param method - метод запроса
|
|
221
|
-
* @param options - конфигурация запроса
|
|
222
|
-
*/
|
|
223
|
-
public request = async <M extends Method, R extends Result, P extends Params | never, Q extends Query | never, B extends Body | never>(url: string, method: M, options: RequestOptions<M, R, P, Q, B>): Promise<R> => {
|
|
224
|
-
const { basicErrorMiddleware, basicResponseMiddleware } = this.config;
|
|
225
|
-
const { errorMiddleware = basicErrorMiddleware, responseMiddleware = basicResponseMiddleware } = options;
|
|
226
|
-
// формируем детали запроса
|
|
227
|
-
const details = {
|
|
228
|
-
url,
|
|
229
|
-
method,
|
|
230
|
-
headers: options.headers,
|
|
231
|
-
params: (options as any).params,
|
|
232
|
-
query: (options as any).query,
|
|
233
|
-
body: (options as any).body,
|
|
234
|
-
withCredentials: options.withCredentials,
|
|
235
|
-
withAuthorization: options.withAuthorization,
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
// формируем конфигурацию запроса
|
|
239
|
-
const config: AxiosRequestConfig = {
|
|
240
|
-
url,
|
|
241
|
-
method,
|
|
242
|
-
headers: options.headers,
|
|
243
|
-
validateStatus: null,
|
|
244
|
-
withCredentials: options.withCredentials,
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
if ('params' in options) {
|
|
248
|
-
config.url = compile(url)(options.params);
|
|
249
|
-
}
|
|
250
|
-
if ('query' in options) {
|
|
251
|
-
config.params = options.query;
|
|
252
|
-
}
|
|
253
|
-
if ('body' in options) {
|
|
254
|
-
config.data = options.body;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
if (options.withAuthorization) {
|
|
258
|
-
await this.checkAuthorization(details);
|
|
259
|
-
config.headers = config.headers || {};
|
|
260
|
-
config.headers['Authorization'] = `Bearer ${this.accessToken}`;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// запрос может быть выполнен дважды, если произошла ошибка UnauthorizedError,
|
|
264
|
-
// чтобы была возможность автоматически обновить access token с помощью refresh token
|
|
265
|
-
let result;
|
|
266
|
-
try {
|
|
267
|
-
const response = await this.instance.request<R>(config);
|
|
268
|
-
result = await responseMiddleware({
|
|
269
|
-
result: response.data,
|
|
270
|
-
status: response.status,
|
|
271
|
-
message: response.statusText,
|
|
272
|
-
details,
|
|
273
|
-
});
|
|
274
|
-
} catch (firstError) {
|
|
275
|
-
// если ошибка возникшая во время первого запроса не Unauthorized, то прокидываем ее дальше
|
|
276
|
-
if (!(firstError instanceof UnauthorizedError)) {
|
|
277
|
-
await errorMiddleware(firstError);
|
|
278
|
-
// если вдруг в errorMiddleware не кинуто исключение, делаем это за него
|
|
279
|
-
throw firstError;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// вторая попытка выполнить запрос, но уже с обновлением токенов
|
|
283
|
-
try {
|
|
284
|
-
if (options.withAuthorization) {
|
|
285
|
-
await this.updateTokenByRefreshToken(details);
|
|
286
|
-
await this.checkAuthorization(details);
|
|
287
|
-
config.headers = config.headers || {};
|
|
288
|
-
config.headers['Authorization'] = `Bearer ${this.accessToken}`;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
const response = await this.instance.request<R>(config);
|
|
292
|
-
result = await responseMiddleware({
|
|
293
|
-
result: response.data,
|
|
294
|
-
status: response.status,
|
|
295
|
-
message: response.statusText,
|
|
296
|
-
details,
|
|
297
|
-
});
|
|
298
|
-
} catch (secondError) {
|
|
299
|
-
await errorMiddleware(secondError);
|
|
300
|
-
// если вдруг в errorMiddleware не кинуто исключение, делаем это за него
|
|
301
|
-
throw secondError;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
return result;
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Выполнить GET запрос
|
|
310
|
-
*
|
|
311
|
-
* @param url - адрес запроса
|
|
312
|
-
* @param options - конфигурация GET запроса
|
|
313
|
-
*/
|
|
314
|
-
public get = <R extends Result, P extends Params | never, Q extends Query | never>(url: string, options: GetOptions<R, P, Q>): Promise<R> => (
|
|
315
|
-
this.request<Method.GET, R, P, Q, never>(url, Method.GET, options as RequestOptions<Method.GET, R, P, Q, never>)
|
|
316
|
-
);
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* Выполнить POST запрос
|
|
320
|
-
*
|
|
321
|
-
* @param url - адрес запроса
|
|
322
|
-
* @param options - конфигурация POST запроса
|
|
323
|
-
*/
|
|
324
|
-
public post = <R extends Result, P extends Params | never, Q extends Query | never, B extends Body>(url: string, options: PostOptions<R, P, Q, B>): Promise<R> => (
|
|
325
|
-
this.request<Method.POST, R, P, Q, B>(url, Method.POST, options as RequestOptions<Method.POST, R, P, Q, B>)
|
|
326
|
-
);
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* Выполнить PUT запрос
|
|
330
|
-
*
|
|
331
|
-
* @param url - адрес запроса
|
|
332
|
-
* @param options - конфигурация PUT запроса
|
|
333
|
-
*/
|
|
334
|
-
public put = <R extends Result, P extends Params | never, Q extends Query | never, B extends Body>(url: string, options: PutOptions<R, P, Q, B>): Promise<R> => (
|
|
335
|
-
this.request<Method.PUT, R, P, Q, B>(url, Method.PUT, options as RequestOptions<Method.PUT, R, P, Q, B>)
|
|
336
|
-
);
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Выполнить PATCH запрос
|
|
340
|
-
*
|
|
341
|
-
* @param url - адрес запроса
|
|
342
|
-
* @param options - конфигурация PATCH запроса
|
|
343
|
-
*/
|
|
344
|
-
public patch = <R extends Result, P extends Params | never, Q extends Query | never, B extends Body>(url: string, options: PatchOptions<R, P, Q, B>): Promise<R> => (
|
|
345
|
-
this.request<Method.PATCH, R, P, Q, B>(url, Method.PATCH, options as RequestOptions<Method.PATCH, R, P, Q, B>)
|
|
346
|
-
);
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
* Выполнить DELETE запрос
|
|
350
|
-
*
|
|
351
|
-
* @param url - адрес запроса
|
|
352
|
-
* @param options - конфигурация DELETE запроса
|
|
353
|
-
*/
|
|
354
|
-
public delete = <R extends Result, P extends Params | never, Q extends Query | never, B extends Body>(url: string, options: DeleteOptions<R, P, Q, B>): Promise<R> => (
|
|
355
|
-
this.request<Method.DELETE, R, P, Q, B>(url, Method.DELETE, options as RequestOptions<Method.DELETE, R, P, Q, B>)
|
|
356
|
-
);
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* Выполнить первый шаг авторизации клиента (базовая авторизация)
|
|
360
|
-
* Для более подробной информации смотри https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1.1
|
|
361
|
-
*/
|
|
362
|
-
public login = async (): Promise<void> => {
|
|
363
|
-
const newAuthorizationState = Math.random().toString().slice(2, 12);
|
|
364
|
-
Cookies.set(HttpClient.AUTHORIZATION_STATE_COOKIE_NAME, newAuthorizationState);
|
|
365
|
-
Cookies.set(HttpClient.URL_BEFORE_AUTHORIZATION_COOKIE_NAME, window.location.href, this.config.cookie);
|
|
366
|
-
|
|
367
|
-
const query = stringify({
|
|
368
|
-
response_type: 'code',
|
|
369
|
-
client_id: this.config.clientId,
|
|
370
|
-
// scope: this.config.scope,
|
|
371
|
-
redirect_uri: `${window.location.origin}${window.location.pathname}`,
|
|
372
|
-
state: newAuthorizationState,
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
window.location.href = `${this.config.urls.authorize}?${query}`;
|
|
376
|
-
|
|
377
|
-
return new Promise(() => {
|
|
378
|
-
});
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Выполнить второй шаг авторизации клиента (получить access token и refresh token)
|
|
383
|
-
* Для более подробной информации смотри https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1.3
|
|
384
|
-
*
|
|
385
|
-
* @param authorization - данные о базовой авторизации клиента.
|
|
386
|
-
*/
|
|
387
|
-
public authorize = async <I extends Introspect>(authorization: BasicAuthorization): Promise<I> => {
|
|
388
|
-
const storiedAuthorizationState = Cookies.get(HttpClient.AUTHORIZATION_STATE_COOKIE_NAME);
|
|
389
|
-
const storiedAuthorizationUrl = Cookies.get(HttpClient.URL_BEFORE_AUTHORIZATION_COOKIE_NAME);
|
|
390
|
-
|
|
391
|
-
Cookies.remove(HttpClient.AUTHORIZATION_STATE_COOKIE_NAME);
|
|
392
|
-
Cookies.remove(HttpClient.URL_BEFORE_AUTHORIZATION_COOKIE_NAME);
|
|
393
|
-
|
|
394
|
-
if (storiedAuthorizationUrl) {
|
|
395
|
-
const url = new URL(storiedAuthorizationUrl);
|
|
396
|
-
this.history.replace({
|
|
397
|
-
pathname: url.pathname,
|
|
398
|
-
search: url.search,
|
|
399
|
-
hash: url.hash,
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
if (storiedAuthorizationState !== authorization.state) {
|
|
404
|
-
throw new UnprocessableEntityError('Storied authorization state and received authorization state NOT equals!');
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
await this.updateTokenByAuthorizationCode(authorization.code);
|
|
408
|
-
if (!this.accessToken) {
|
|
409
|
-
throw new LogicError('Access token is null after update tokens!');
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
return decodeJwt<I>(this.accessToken);
|
|
413
|
-
};
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Закончить сеанс
|
|
417
|
-
*/
|
|
418
|
-
public logout = async (): Promise<void> => {
|
|
419
|
-
this.accessToken = null;
|
|
420
|
-
this.refreshToken = null;
|
|
421
|
-
|
|
422
|
-
await this.get<void, never, never>(
|
|
423
|
-
this.config.urls.logout,
|
|
424
|
-
{
|
|
425
|
-
withAuthorization: false,
|
|
426
|
-
withCredentials: true,
|
|
427
|
-
},
|
|
428
|
-
);
|
|
429
|
-
};
|
|
430
|
-
|
|
431
|
-
/**
|
|
432
|
-
* Получить токен доступа
|
|
433
|
-
*/
|
|
434
|
-
public getAccessToken = async (): Promise<AccessToken> => {
|
|
435
|
-
await this.checkAuthorization(null);
|
|
436
|
-
if (!this.accessToken) {
|
|
437
|
-
throw new LogicError('Access token is null after check authorization!');
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
return this.accessToken;
|
|
441
|
-
};
|
|
442
|
-
|
|
443
|
-
/**
|
|
444
|
-
* Установить токен доступа вручную
|
|
445
|
-
*
|
|
446
|
-
* @param accessToken - токен доступа
|
|
447
|
-
*/
|
|
448
|
-
public setAccessToken = (accessToken: string): void => {
|
|
449
|
-
this.accessToken = accessToken;
|
|
450
|
-
};
|
|
451
|
-
}
|
package/src/http-client.types.ts
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { CookieAttributes } from 'js-cookie';
|
|
2
|
-
|
|
3
|
-
export type AccessToken = string;
|
|
4
|
-
export type RefreshToken = string;
|
|
5
|
-
|
|
6
|
-
export enum Method {
|
|
7
|
-
GET = 'GET',
|
|
8
|
-
POST = 'POST',
|
|
9
|
-
PUT = 'PUT',
|
|
10
|
-
PATCH = 'PATCH',
|
|
11
|
-
DELETE = 'DELETE',
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export type Headers = Record<string, string | number | boolean>;
|
|
15
|
-
export type Params = Record<string, string | number>;
|
|
16
|
-
export type Query = Record<string, string> | URLSearchParams | string;
|
|
17
|
-
export type Body = Record<string, unknown> | FormData;
|
|
18
|
-
export type Result = unknown;
|
|
19
|
-
|
|
20
|
-
export type RequestDetails = {
|
|
21
|
-
readonly url: string;
|
|
22
|
-
readonly method: Method;
|
|
23
|
-
readonly params: Params;
|
|
24
|
-
readonly query: Query;
|
|
25
|
-
readonly body: Body;
|
|
26
|
-
readonly withCredentials?: boolean;
|
|
27
|
-
readonly withAuthorization: boolean;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export type Response = {
|
|
31
|
-
readonly result: any;
|
|
32
|
-
readonly status: number;
|
|
33
|
-
readonly message?: string;
|
|
34
|
-
readonly details: RequestDetails;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export type CommonRequestOptions<R extends Result> = {
|
|
38
|
-
readonly headers?: Headers;
|
|
39
|
-
readonly withAuthorization: boolean;
|
|
40
|
-
readonly withCredentials?: boolean;
|
|
41
|
-
readonly responseMiddleware?: (response: Response) => Promise<R> | R;
|
|
42
|
-
readonly errorMiddleware?: (error: unknown) => Promise<never> | never;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export type RequestOptions<
|
|
46
|
-
M extends Method,
|
|
47
|
-
R extends Result,
|
|
48
|
-
P extends Params | never,
|
|
49
|
-
Q extends Query | never,
|
|
50
|
-
B extends Body | never,
|
|
51
|
-
> =
|
|
52
|
-
M extends Method.GET
|
|
53
|
-
? [P] extends [never]
|
|
54
|
-
? [Q] extends [never]
|
|
55
|
-
? CommonRequestOptions<R>
|
|
56
|
-
: CommonRequestOptions<R> & { readonly query: Q; }
|
|
57
|
-
: [Q] extends [never]
|
|
58
|
-
? CommonRequestOptions<R> & { readonly params: P; }
|
|
59
|
-
: CommonRequestOptions<R> & { readonly query: Q; readonly params: P; }
|
|
60
|
-
: [P] extends [never]
|
|
61
|
-
? [Q] extends [never]
|
|
62
|
-
? [B] extends [never]
|
|
63
|
-
? CommonRequestOptions<R>
|
|
64
|
-
: CommonRequestOptions<R> & { readonly body: B; }
|
|
65
|
-
: [B] extends [never]
|
|
66
|
-
? CommonRequestOptions<R> & { readonly query: Q; }
|
|
67
|
-
: CommonRequestOptions<R> & { readonly query: Q; readonly body: B; }
|
|
68
|
-
: [Q] extends [never]
|
|
69
|
-
? [B] extends [never]
|
|
70
|
-
? CommonRequestOptions<R> & { readonly params: P; }
|
|
71
|
-
: CommonRequestOptions<R> & { readonly params: P; readonly body: B; }
|
|
72
|
-
: [B] extends [never]
|
|
73
|
-
? CommonRequestOptions<R> & { readonly params: P; readonly query: Q; }
|
|
74
|
-
: CommonRequestOptions<R> & { readonly params: P; readonly query: Q, readonly body: B; };
|
|
75
|
-
|
|
76
|
-
export type GetOptions<R extends Result, P extends Params | never, Q extends Query | never> = Omit<RequestOptions<Method.GET, R, P, Q, never>, 'method'>;
|
|
77
|
-
export type PostOptions<R extends Result, P extends Params | never, Q extends Query | never, B extends Body> = Omit<RequestOptions<Method.POST, R, P, Q, B>, 'method'>;
|
|
78
|
-
export type PutOptions<R extends Result, P extends Params | never, Q extends Query | never, B extends Body> = Omit<RequestOptions<Method.PUT, R, P, Q, B>, 'method'>;
|
|
79
|
-
export type PatchOptions<R extends Result, P extends Params | never, Q extends Query | never, B extends Body> = Omit<RequestOptions<Method.PATCH, R, P, Q, B>, 'method'>;
|
|
80
|
-
export type DeleteOptions<R extends Result, P extends Params | never, Q extends Query | never, B extends Body> = Omit<RequestOptions<Method.DELETE, R, P, Q, B>, 'method'>;
|
|
81
|
-
|
|
82
|
-
export type Config = {
|
|
83
|
-
readonly urls: {
|
|
84
|
-
readonly authorize: string;
|
|
85
|
-
readonly token: string;
|
|
86
|
-
readonly logout: string;
|
|
87
|
-
};
|
|
88
|
-
readonly clientId: string;
|
|
89
|
-
readonly scope: string;
|
|
90
|
-
readonly cookie: Omit<CookieAttributes, 'domain'> & {
|
|
91
|
-
readonly domain: string;
|
|
92
|
-
};
|
|
93
|
-
readonly basicResponseMiddleware: <R extends Result>(response: Response) => Promise<R> | R;
|
|
94
|
-
readonly basicErrorMiddleware: (error: unknown) => Promise<never> | never;
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
export type BasicAuthorization = {
|
|
98
|
-
readonly state: string;
|
|
99
|
-
readonly code: string;
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
export type Introspect = Record<string, unknown>;
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
BadGatewayError,
|
|
3
|
-
BadRequestError,
|
|
4
|
-
ForbiddenError,
|
|
5
|
-
GatewayTimeoutError,
|
|
6
|
-
InternalServerError,
|
|
7
|
-
MethodNotAllowedError,
|
|
8
|
-
NotFoundError,
|
|
9
|
-
NotImplementedError,
|
|
10
|
-
PayloadTooLargeError,
|
|
11
|
-
ServiceUnavailableError,
|
|
12
|
-
TooManyRequestsError,
|
|
13
|
-
UnauthorizedError,
|
|
14
|
-
UnprocessableEntityError,
|
|
15
|
-
UnsupportedMediaTypeError,
|
|
16
|
-
UntiledError,
|
|
17
|
-
} from './http-client.errors';
|
|
18
|
-
import {
|
|
19
|
-
Response,
|
|
20
|
-
Result,
|
|
21
|
-
} from './http-client.types';
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Проверить, выполнился ли запрос успешно.
|
|
25
|
-
*
|
|
26
|
-
* @param response - ответ от сервера.
|
|
27
|
-
*/
|
|
28
|
-
export function checkIsResponseSuccess(response: Response): boolean {
|
|
29
|
-
return response.status >= 200 && response.status < 300;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Обработчик ответа от сервера по умолчанию.
|
|
34
|
-
*
|
|
35
|
-
* @param response - ответ от сервера.
|
|
36
|
-
*/
|
|
37
|
-
export function defaultResponseMiddleware<R extends Result>(response: Response): R {
|
|
38
|
-
if (checkIsResponseSuccess(response)) {
|
|
39
|
-
return response.result;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const message: string | undefined = response.message || response.result?.message;
|
|
43
|
-
switch (response.status) {
|
|
44
|
-
case 400:
|
|
45
|
-
throw new BadRequestError(message, response.details);
|
|
46
|
-
case 401:
|
|
47
|
-
throw new UnauthorizedError(message, response.details);
|
|
48
|
-
case 403:
|
|
49
|
-
throw new ForbiddenError(message, response.details);
|
|
50
|
-
case 404:
|
|
51
|
-
throw new NotFoundError(message, response.details);
|
|
52
|
-
case 405:
|
|
53
|
-
throw new MethodNotAllowedError(message, response.details);
|
|
54
|
-
case 413:
|
|
55
|
-
throw new PayloadTooLargeError(message, response.details);
|
|
56
|
-
case 415:
|
|
57
|
-
throw new UnsupportedMediaTypeError(message, response.details);
|
|
58
|
-
case 422:
|
|
59
|
-
throw new UnprocessableEntityError(message, response.details);
|
|
60
|
-
case 429:
|
|
61
|
-
throw new TooManyRequestsError(message, response.details);
|
|
62
|
-
case 500:
|
|
63
|
-
throw new InternalServerError(message, response.details);
|
|
64
|
-
case 501:
|
|
65
|
-
throw new NotImplementedError(message, response.details);
|
|
66
|
-
case 502:
|
|
67
|
-
throw new BadGatewayError(message, response.details);
|
|
68
|
-
case 503:
|
|
69
|
-
throw new ServiceUnavailableError(message, response.details);
|
|
70
|
-
case 504:
|
|
71
|
-
throw new GatewayTimeoutError(message, response.details);
|
|
72
|
-
default:
|
|
73
|
-
throw new UntiledError(message || 'Untiled Error!', response.status, response.details);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Обработчик ошибки по умолчанию.
|
|
79
|
-
*
|
|
80
|
-
* @param error - ошибка.
|
|
81
|
-
*/
|
|
82
|
-
export function defaultErrorMiddleware(error: unknown): never {
|
|
83
|
-
throw error;
|
|
84
|
-
}
|
package/tsconfig.eslint.json
DELETED
package/tsconfig.json
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES5",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"outDir": "./dist",
|
|
6
|
-
"rootDir": "./src",
|
|
7
|
-
"baseUrl": "./",
|
|
8
|
-
"lib": [
|
|
9
|
-
"ESNext",
|
|
10
|
-
"DOM"
|
|
11
|
-
],
|
|
12
|
-
/* Type Checking */
|
|
13
|
-
"strict": true,
|
|
14
|
-
"allowUnreachableCode": false,
|
|
15
|
-
"allowUnusedLabels": false,
|
|
16
|
-
"exactOptionalPropertyTypes": false,
|
|
17
|
-
"noFallthroughCasesInSwitch": true,
|
|
18
|
-
"noImplicitReturns": true,
|
|
19
|
-
"noPropertyAccessFromIndexSignature": true,
|
|
20
|
-
"noUncheckedIndexedAccess": true,
|
|
21
|
-
"noUnusedLocals": true,
|
|
22
|
-
"noUnusedParameters": true,
|
|
23
|
-
/* Modules */
|
|
24
|
-
"moduleResolution": "node",
|
|
25
|
-
"resolveJsonModule": true,
|
|
26
|
-
/* Emit */
|
|
27
|
-
"declaration": true,
|
|
28
|
-
"declarationMap": true,
|
|
29
|
-
"importHelpers": true,
|
|
30
|
-
"preserveValueImports": false,
|
|
31
|
-
"sourceMap": true,
|
|
32
|
-
/* JavaScript Support */
|
|
33
|
-
"allowJs": false,
|
|
34
|
-
/* Interop Constraints */
|
|
35
|
-
"allowSyntheticDefaultImports": true,
|
|
36
|
-
"esModuleInterop": true,
|
|
37
|
-
"forceConsistentCasingInFileNames": true,
|
|
38
|
-
"isolatedModules": true,
|
|
39
|
-
/* Language and Environment */
|
|
40
|
-
"emitDecoratorMetadata": true,
|
|
41
|
-
"experimentalDecorators": true
|
|
42
|
-
},
|
|
43
|
-
"include": [
|
|
44
|
-
"./src/**/*",
|
|
45
|
-
"./tests/**/*",
|
|
46
|
-
],
|
|
47
|
-
"exclude": [
|
|
48
|
-
"./dist/**/*",
|
|
49
|
-
"./node_modules/**/*"
|
|
50
|
-
]
|
|
51
|
-
}
|