@kontent-ai/core-sdk 10.0.0-0
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/.npmignore +14 -0
- package/LICENSE.md +9 -0
- package/README.md +30 -0
- package/dist/cjs/helpers/enum.helper.d.ts +8 -0
- package/dist/cjs/helpers/enum.helper.js +79 -0
- package/dist/cjs/helpers/enum.helper.js.map +1 -0
- package/dist/cjs/helpers/header.helper.d.ts +13 -0
- package/dist/cjs/helpers/header.helper.js +24 -0
- package/dist/cjs/helpers/header.helper.js.map +1 -0
- package/dist/cjs/helpers/headers-helper.d.ts +3 -0
- package/dist/cjs/helpers/headers-helper.js +37 -0
- package/dist/cjs/helpers/headers-helper.js.map +1 -0
- package/dist/cjs/helpers/index.d.ts +5 -0
- package/dist/cjs/helpers/index.js +22 -0
- package/dist/cjs/helpers/index.js.map +1 -0
- package/dist/cjs/helpers/retry-helper.d.ts +37 -0
- package/dist/cjs/helpers/retry-helper.js +152 -0
- package/dist/cjs/helpers/retry-helper.js.map +1 -0
- package/dist/cjs/helpers/url.helper.d.ts +10 -0
- package/dist/cjs/helpers/url.helper.js +30 -0
- package/dist/cjs/helpers/url.helper.js.map +1 -0
- package/dist/cjs/http/http.debugger.d.ts +6 -0
- package/dist/cjs/http/http.debugger.js +26 -0
- package/dist/cjs/http/http.debugger.js.map +1 -0
- package/dist/cjs/http/http.functions.d.ts +11 -0
- package/dist/cjs/http/http.functions.js +381 -0
- package/dist/cjs/http/http.functions.js.map +1 -0
- package/dist/cjs/http/http.models.d.ts +73 -0
- package/dist/cjs/http/http.models.js +3 -0
- package/dist/cjs/http/http.models.js.map +1 -0
- package/dist/cjs/http/http.service.d.ts +19 -0
- package/dist/cjs/http/http.service.js +110 -0
- package/dist/cjs/http/http.service.js.map +1 -0
- package/dist/cjs/http/ihttp.service.d.ts +9 -0
- package/dist/cjs/http/ihttp.service.js +3 -0
- package/dist/cjs/http/ihttp.service.js.map +1 -0
- package/dist/cjs/http/index.d.ts +6 -0
- package/dist/cjs/http/index.js +23 -0
- package/dist/cjs/http/index.js.map +1 -0
- package/dist/cjs/http/test-http.service.d.ts +17 -0
- package/dist/cjs/http/test-http.service.js +47 -0
- package/dist/cjs/http/test-http.service.js.map +1 -0
- package/dist/cjs/index.d.ts +3 -0
- package/dist/cjs/index.js +21 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/models/index.d.ts +3 -0
- package/dist/cjs/models/index.js +20 -0
- package/dist/cjs/models/index.js.map +1 -0
- package/dist/cjs/models/isdk-info.d.ts +14 -0
- package/dist/cjs/models/isdk-info.js +3 -0
- package/dist/cjs/models/isdk-info.js.map +1 -0
- package/dist/cjs/models/parameters.d.ts +16 -0
- package/dist/cjs/models/parameters.js +30 -0
- package/dist/cjs/models/parameters.js.map +1 -0
- package/dist/cjs/models/url.models.d.ts +3 -0
- package/dist/cjs/models/url.models.js +3 -0
- package/dist/cjs/models/url.models.js.map +1 -0
- package/dist/cjs/sdk-info.generated.d.ts +2 -0
- package/dist/cjs/sdk-info.generated.js +9 -0
- package/dist/cjs/sdk-info.generated.js.map +1 -0
- package/dist/es2015/helpers/enum.helper.d.ts +8 -0
- package/dist/es2015/helpers/enum.helper.js +50 -0
- package/dist/es2015/helpers/enum.helper.js.map +1 -0
- package/dist/es2015/helpers/header.helper.d.ts +13 -0
- package/dist/es2015/helpers/header.helper.js +19 -0
- package/dist/es2015/helpers/header.helper.js.map +1 -0
- package/dist/es2015/helpers/headers-helper.d.ts +3 -0
- package/dist/es2015/helpers/headers-helper.js +11 -0
- package/dist/es2015/helpers/headers-helper.js.map +1 -0
- package/dist/es2015/helpers/index.d.ts +5 -0
- package/dist/es2015/helpers/index.js +6 -0
- package/dist/es2015/helpers/index.js.map +1 -0
- package/dist/es2015/helpers/retry-helper.d.ts +37 -0
- package/dist/es2015/helpers/retry-helper.js +145 -0
- package/dist/es2015/helpers/retry-helper.js.map +1 -0
- package/dist/es2015/helpers/url.helper.d.ts +10 -0
- package/dist/es2015/helpers/url.helper.js +23 -0
- package/dist/es2015/helpers/url.helper.js.map +1 -0
- package/dist/es2015/http/http.debugger.d.ts +6 -0
- package/dist/es2015/http/http.debugger.js +19 -0
- package/dist/es2015/http/http.debugger.js.map +1 -0
- package/dist/es2015/http/http.functions.d.ts +11 -0
- package/dist/es2015/http/http.functions.js +244 -0
- package/dist/es2015/http/http.functions.js.map +1 -0
- package/dist/es2015/http/http.models.d.ts +73 -0
- package/dist/es2015/http/http.models.js +2 -0
- package/dist/es2015/http/http.models.js.map +1 -0
- package/dist/es2015/http/http.service.d.ts +19 -0
- package/dist/es2015/http/http.service.js +45 -0
- package/dist/es2015/http/http.service.js.map +1 -0
- package/dist/es2015/http/ihttp.service.d.ts +9 -0
- package/dist/es2015/http/ihttp.service.js +2 -0
- package/dist/es2015/http/ihttp.service.js.map +1 -0
- package/dist/es2015/http/index.d.ts +6 -0
- package/dist/es2015/http/index.js +7 -0
- package/dist/es2015/http/index.js.map +1 -0
- package/dist/es2015/http/test-http.service.d.ts +17 -0
- package/dist/es2015/http/test-http.service.js +41 -0
- package/dist/es2015/http/test-http.service.js.map +1 -0
- package/dist/es2015/index.d.ts +3 -0
- package/dist/es2015/index.js +5 -0
- package/dist/es2015/index.js.map +1 -0
- package/dist/es2015/models/index.d.ts +3 -0
- package/dist/es2015/models/index.js +4 -0
- package/dist/es2015/models/index.js.map +1 -0
- package/dist/es2015/models/isdk-info.d.ts +14 -0
- package/dist/es2015/models/isdk-info.js +2 -0
- package/dist/es2015/models/isdk-info.js.map +1 -0
- package/dist/es2015/models/parameters.d.ts +16 -0
- package/dist/es2015/models/parameters.js +26 -0
- package/dist/es2015/models/parameters.js.map +1 -0
- package/dist/es2015/models/url.models.d.ts +3 -0
- package/dist/es2015/models/url.models.js +2 -0
- package/dist/es2015/models/url.models.js.map +1 -0
- package/dist/es2015/sdk-info.generated.d.ts +2 -0
- package/dist/es2015/sdk-info.generated.js +6 -0
- package/dist/es2015/sdk-info.generated.js.map +1 -0
- package/dist/es5/helpers/enum.helper.d.ts +8 -0
- package/dist/es5/helpers/enum.helper.js +66 -0
- package/dist/es5/helpers/enum.helper.js.map +1 -0
- package/dist/es5/helpers/header.helper.d.ts +13 -0
- package/dist/es5/helpers/header.helper.js +21 -0
- package/dist/es5/helpers/header.helper.js.map +1 -0
- package/dist/es5/helpers/headers-helper.d.ts +3 -0
- package/dist/es5/helpers/headers-helper.js +23 -0
- package/dist/es5/helpers/headers-helper.js.map +1 -0
- package/dist/es5/helpers/index.d.ts +5 -0
- package/dist/es5/helpers/index.js +6 -0
- package/dist/es5/helpers/index.js.map +1 -0
- package/dist/es5/helpers/retry-helper.d.ts +37 -0
- package/dist/es5/helpers/retry-helper.js +149 -0
- package/dist/es5/helpers/retry-helper.js.map +1 -0
- package/dist/es5/helpers/url.helper.d.ts +10 -0
- package/dist/es5/helpers/url.helper.js +27 -0
- package/dist/es5/helpers/url.helper.js.map +1 -0
- package/dist/es5/http/http.debugger.d.ts +6 -0
- package/dist/es5/http/http.debugger.js +23 -0
- package/dist/es5/http/http.debugger.js.map +1 -0
- package/dist/es5/http/http.functions.d.ts +11 -0
- package/dist/es5/http/http.functions.js +337 -0
- package/dist/es5/http/http.functions.js.map +1 -0
- package/dist/es5/http/http.models.d.ts +73 -0
- package/dist/es5/http/http.models.js +2 -0
- package/dist/es5/http/http.models.js.map +1 -0
- package/dist/es5/http/http.service.d.ts +19 -0
- package/dist/es5/http/http.service.js +72 -0
- package/dist/es5/http/http.service.js.map +1 -0
- package/dist/es5/http/ihttp.service.d.ts +9 -0
- package/dist/es5/http/ihttp.service.js +2 -0
- package/dist/es5/http/ihttp.service.js.map +1 -0
- package/dist/es5/http/index.d.ts +6 -0
- package/dist/es5/http/index.js +7 -0
- package/dist/es5/http/index.js.map +1 -0
- package/dist/es5/http/test-http.service.d.ts +17 -0
- package/dist/es5/http/test-http.service.js +44 -0
- package/dist/es5/http/test-http.service.js.map +1 -0
- package/dist/es5/index.d.ts +3 -0
- package/dist/es5/index.js +5 -0
- package/dist/es5/index.js.map +1 -0
- package/dist/es5/models/index.d.ts +3 -0
- package/dist/es5/models/index.js +4 -0
- package/dist/es5/models/index.js.map +1 -0
- package/dist/es5/models/isdk-info.d.ts +14 -0
- package/dist/es5/models/isdk-info.js +2 -0
- package/dist/es5/models/isdk-info.js.map +1 -0
- package/dist/es5/models/parameters.d.ts +16 -0
- package/dist/es5/models/parameters.js +27 -0
- package/dist/es5/models/parameters.js.map +1 -0
- package/dist/es5/models/url.models.d.ts +3 -0
- package/dist/es5/models/url.models.js +2 -0
- package/dist/es5/models/url.models.js.map +1 -0
- package/dist/es5/sdk-info.generated.d.ts +2 -0
- package/dist/es5/sdk-info.generated.js +6 -0
- package/dist/es5/sdk-info.generated.js.map +1 -0
- package/dist/umd/kontent-core.umd.js +3476 -0
- package/dist/umd/kontent-core.umd.js.map +1 -0
- package/dist/umd/kontent-core.umd.min.js +2 -0
- package/dist/umd/kontent-core.umd.min.js.map +1 -0
- package/dist/umd/report.json +1 -0
- package/dist/umd/report.min.json +1 -0
- package/dist/umd/stats.json +11920 -0
- package/dist/umd/stats.min.json +13789 -0
- package/lib/helpers/enum.helper.ts +63 -0
- package/lib/helpers/header.helper.ts +23 -0
- package/lib/helpers/headers-helper.ts +15 -0
- package/lib/helpers/index.ts +5 -0
- package/lib/helpers/retry-helper.ts +204 -0
- package/lib/helpers/url.helper.ts +26 -0
- package/lib/http/http.debugger.ts +21 -0
- package/lib/http/http.functions.ts +312 -0
- package/lib/http/http.models.ts +83 -0
- package/lib/http/http.service.ts +91 -0
- package/lib/http/ihttp.service.ts +20 -0
- package/lib/http/index.ts +6 -0
- package/lib/http/test-http.service.ts +70 -0
- package/lib/index.ts +4 -0
- package/lib/models/index.ts +3 -0
- package/lib/models/isdk-info.ts +15 -0
- package/lib/models/parameters.ts +25 -0
- package/lib/models/url.models.ts +3 -0
- package/lib/sdk-info.generated.ts +7 -0
- package/package.json +87 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export class EnumHelper {
|
|
2
|
+
|
|
3
|
+
getAllNames(T: any): any[] {
|
|
4
|
+
const enumNames: any[] = [];
|
|
5
|
+
|
|
6
|
+
for (const key in T) {
|
|
7
|
+
if (T.hasOwnProperty(key)) {
|
|
8
|
+
enumNames.push(key);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return enumNames;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
getAllValues(T: any): any[] {
|
|
16
|
+
const allEnumValues: any[] = Object.keys(T).map(key => T[key]);
|
|
17
|
+
|
|
18
|
+
return allEnumValues;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
getEnumFromValue<T>(T: any, value: string | number): T | undefined {
|
|
22
|
+
try {
|
|
23
|
+
if (!value) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// we can map back from index number directly
|
|
28
|
+
if (this.isNumeric(value)) {
|
|
29
|
+
return <T>T[value];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// for strings, we need to compare each value separately
|
|
33
|
+
const allEnumValues = this.getAllValues(T);
|
|
34
|
+
|
|
35
|
+
const result = allEnumValues.find(m => m.toLowerCase() === value.toString().toLowerCase());
|
|
36
|
+
|
|
37
|
+
if (!result) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return result as T;
|
|
42
|
+
} catch (err) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getEnumFromName<T>(T: any, name: string): T | undefined {
|
|
48
|
+
const allNames = this.getAllNames(T);
|
|
49
|
+
|
|
50
|
+
for (const enumName of allNames) {
|
|
51
|
+
if (enumName.toLowerCase() === name.toLowerCase()) {
|
|
52
|
+
return T[enumName];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private isNumeric(value: any): boolean {
|
|
59
|
+
return !isNaN(parseFloat(value)) && isFinite(value);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const enumHelper = new EnumHelper();
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ISDKInfo } from '../models';
|
|
2
|
+
|
|
3
|
+
import { IHeader } from '../http/http.models';
|
|
4
|
+
|
|
5
|
+
export class HeaderHelper {
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Header name for SDK usage
|
|
9
|
+
*/
|
|
10
|
+
private readonly sdkVersionHeader: string = 'X-KC-SDKID';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Header identifying SDK type & version for internal purposes of Kontent.ai
|
|
14
|
+
*/
|
|
15
|
+
getSdkIdHeader(info: ISDKInfo): IHeader {
|
|
16
|
+
return {
|
|
17
|
+
header: this.sdkVersionHeader,
|
|
18
|
+
value: `${info.host};${info.name};${info.version}`
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const headerHelper = new HeaderHelper();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { AxiosResponse } from 'axios';
|
|
2
|
+
import { IHeader } from '../http/http.models';
|
|
3
|
+
|
|
4
|
+
export function extractHeadersFromAxiosResponse(response: AxiosResponse): IHeader[] {
|
|
5
|
+
const headers: IHeader[] = [];
|
|
6
|
+
|
|
7
|
+
for (const headerKey of Object.keys(response.headers)) {
|
|
8
|
+
headers.push({
|
|
9
|
+
header: headerKey,
|
|
10
|
+
value: response.headers[headerKey]
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return headers;
|
|
15
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { AxiosError } from 'axios';
|
|
2
|
+
|
|
3
|
+
import { extractHeadersFromAxiosResponse } from './headers-helper';
|
|
4
|
+
import { IHeader, IRetryStrategyOptions } from '../http/http.models';
|
|
5
|
+
|
|
6
|
+
export class RetryHelper {
|
|
7
|
+
public readonly requestCancelledMessagePrefix: string = 'Request cancelled';
|
|
8
|
+
public readonly retryAfterHeaderName: string = 'Retry-After';
|
|
9
|
+
public readonly defaultRetryStatusCodes: number[] = [408, 429, 500, 502, 503, 504];
|
|
10
|
+
public readonly defaultRetryStrategy = {
|
|
11
|
+
addJitter: true,
|
|
12
|
+
deltaBackoffMs: 1000, // 1 sec
|
|
13
|
+
maxAttempts: 5,
|
|
14
|
+
canRetryError: (error: any) => this.canRetryErrorDefault(error)
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
getRetryErrorResult(data: {
|
|
18
|
+
retryAttempt: number;
|
|
19
|
+
error: any;
|
|
20
|
+
retryStrategy: IRetryStrategyOptions;
|
|
21
|
+
}): {
|
|
22
|
+
retryInMs: number;
|
|
23
|
+
canRetry: boolean;
|
|
24
|
+
maxRetries: number;
|
|
25
|
+
} {
|
|
26
|
+
if (data.error && data.error.message) {
|
|
27
|
+
if ((<string>data.error.message).startsWith(this.requestCancelledMessagePrefix)) {
|
|
28
|
+
// request was cancelled by user, do not retry it
|
|
29
|
+
return {
|
|
30
|
+
canRetry: false,
|
|
31
|
+
retryInMs: 0,
|
|
32
|
+
maxRetries: 0
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const canRetryError: boolean = data.retryStrategy.canRetryError
|
|
38
|
+
? data.retryStrategy.canRetryError(data.error)
|
|
39
|
+
: this.defaultRetryStrategy.canRetryError(data.error);
|
|
40
|
+
|
|
41
|
+
if (!canRetryError) {
|
|
42
|
+
// request cannot be retried
|
|
43
|
+
return {
|
|
44
|
+
canRetry: false,
|
|
45
|
+
retryInMs: 0,
|
|
46
|
+
maxRetries: 0
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const maxRetries: number = (data.retryStrategy.maxAttempts ?? this.defaultRetryStrategy.maxAttempts);
|
|
51
|
+
|
|
52
|
+
const maxRetriesReached: boolean =
|
|
53
|
+
data.retryAttempt >= maxRetries;
|
|
54
|
+
|
|
55
|
+
if (maxRetriesReached) {
|
|
56
|
+
// request cannot be retried anymore due to maximum attempts
|
|
57
|
+
return {
|
|
58
|
+
canRetry: false,
|
|
59
|
+
retryInMs: 0,
|
|
60
|
+
maxRetries: maxRetries
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// get wait time
|
|
64
|
+
const retryResult: number | undefined = this.tryGetRetryAfterInMsFromError(data.error);
|
|
65
|
+
|
|
66
|
+
if (retryResult) {
|
|
67
|
+
// retry after header was provided
|
|
68
|
+
return {
|
|
69
|
+
canRetry: true,
|
|
70
|
+
retryInMs: retryResult,
|
|
71
|
+
maxRetries: maxRetries
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// wait time was not provided in header
|
|
76
|
+
const waitTimeMs = this.getNextWaitTimeMs(
|
|
77
|
+
data.retryStrategy.addJitter ?? this.defaultRetryStrategy.addJitter,
|
|
78
|
+
data.retryStrategy.deltaBackoffMs ?? this.defaultRetryStrategy.deltaBackoffMs,
|
|
79
|
+
data.retryAttempt
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
canRetry: true,
|
|
84
|
+
retryInMs: waitTimeMs,
|
|
85
|
+
maxRetries: maxRetries
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
getRetryStrategyFromStrategyOptions(retryOptions?: IRetryStrategyOptions): IRetryStrategyOptions {
|
|
90
|
+
if (!retryOptions) {
|
|
91
|
+
return this.defaultRetryStrategy;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return retryOptions;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
canRetryInTime(
|
|
98
|
+
startTime: Date,
|
|
99
|
+
maxCumulativeWaitTimeMs: number
|
|
100
|
+
): {
|
|
101
|
+
canRetry: boolean;
|
|
102
|
+
differenceInMs: number;
|
|
103
|
+
} {
|
|
104
|
+
const start = startTime.getTime();
|
|
105
|
+
const now = new Date().getTime();
|
|
106
|
+
|
|
107
|
+
const differenceInMs = now - start;
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
canRetry: differenceInMs < maxCumulativeWaitTimeMs,
|
|
111
|
+
differenceInMs: differenceInMs
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private getNextWaitTimeMs(addJitter: boolean, deltaBackoffMs: number, retryAttempts: number): number {
|
|
116
|
+
if (!addJitter) {
|
|
117
|
+
return deltaBackoffMs * Math.pow(2, retryAttempts);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const from: number = 0.8 * deltaBackoffMs;
|
|
121
|
+
const to: number = 1.2 * deltaBackoffMs * Math.pow(2, retryAttempts);
|
|
122
|
+
|
|
123
|
+
return this.randomNumberFromInterval(from, to);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private canRetryErrorDefault(error: any): boolean {
|
|
127
|
+
const axiosError = this.tryGetAxiosError(error);
|
|
128
|
+
|
|
129
|
+
if (!axiosError) {
|
|
130
|
+
// by default non-axios errors are not retried
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const statusCode: number = this.getStatusCodeFromError(error);
|
|
135
|
+
const canRetryStatusCode: boolean = this.canRetryStatusCode(statusCode, this.defaultRetryStatusCodes);
|
|
136
|
+
|
|
137
|
+
if (canRetryStatusCode) {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private tryGetRetryAfterInMsFromError(error: any): number | undefined {
|
|
145
|
+
const axiosError = this.tryGetAxiosError(error);
|
|
146
|
+
|
|
147
|
+
if (!axiosError || !axiosError.response) {
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const headers: IHeader[] = extractHeadersFromAxiosResponse(axiosError.response);
|
|
152
|
+
|
|
153
|
+
const retryValueHeader = headers.find(
|
|
154
|
+
(m) => m.header.toLowerCase() === this.retryAfterHeaderName.toLowerCase()
|
|
155
|
+
);
|
|
156
|
+
if (!retryValueHeader) {
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const retryInSeconds = +retryValueHeader.value;
|
|
161
|
+
|
|
162
|
+
return retryInSeconds * 1000;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private canRetryStatusCode(statusCode: number, useRetryForResponseCodes: number[]): boolean {
|
|
166
|
+
return useRetryForResponseCodes.includes(statusCode);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private getStatusCodeFromError(error: any): number {
|
|
170
|
+
const axiosError = this.tryGetAxiosError(error);
|
|
171
|
+
|
|
172
|
+
if (!axiosError || !axiosError.response) {
|
|
173
|
+
return 0;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return axiosError.response.status;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private tryGetAxiosError(error: any): AxiosError | undefined {
|
|
180
|
+
if (!error) {
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (error.isAxiosError) {
|
|
185
|
+
return error as AxiosError;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const originalError = error.originalError;
|
|
189
|
+
if (originalError && originalError.isAxiosError) {
|
|
190
|
+
return originalError as AxiosError;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* min and max included
|
|
198
|
+
*/
|
|
199
|
+
private randomNumberFromInterval(min: number, max: number): number {
|
|
200
|
+
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export const retryHelper = new RetryHelper();
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { IQueryParameter } from '../models';
|
|
2
|
+
|
|
3
|
+
export class UrlHelper {
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Adds query parameters to given url
|
|
7
|
+
* @param url Url to which options will be added
|
|
8
|
+
* @param options Query parameters to add
|
|
9
|
+
*/
|
|
10
|
+
addOptionsToUrl(url: string, options?: IQueryParameter[]): string {
|
|
11
|
+
if (options) {
|
|
12
|
+
options.forEach(filter => {
|
|
13
|
+
if (url.indexOf('?') > -1) {
|
|
14
|
+
url += '&';
|
|
15
|
+
} else {
|
|
16
|
+
url += '?';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
url += filter.getParam();
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return url;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const urlHelper = new UrlHelper();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export class HttpDebugger {
|
|
2
|
+
/*
|
|
3
|
+
Called when http request is started
|
|
4
|
+
*/
|
|
5
|
+
debugStartHttpRequest(): void {
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
Called when http request is resolved
|
|
10
|
+
*/
|
|
11
|
+
debugSuccessHttpRequest(): void {
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
Called when http request is being retried
|
|
16
|
+
*/
|
|
17
|
+
debugRetryHttpRequest(): void {
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const httpDebugger = new HttpDebugger();
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import axios, { AxiosInstance, Canceler, CancelToken } from 'axios';
|
|
2
|
+
import { extractHeadersFromAxiosResponse } from '../helpers/headers-helper';
|
|
3
|
+
|
|
4
|
+
import { httpDebugger } from './http.debugger';
|
|
5
|
+
import {
|
|
6
|
+
IHttpCancelRequestToken,
|
|
7
|
+
IHeader,
|
|
8
|
+
IHttpDeleteQueryCall,
|
|
9
|
+
IHttpGetQueryCall,
|
|
10
|
+
IHttpPatchQueryCall,
|
|
11
|
+
IHttpPostQueryCall,
|
|
12
|
+
IHttpPutQueryCall,
|
|
13
|
+
IHttpQueryOptions,
|
|
14
|
+
IResponse,
|
|
15
|
+
IRetryStrategyOptions
|
|
16
|
+
} from './http.models';
|
|
17
|
+
import { retryHelper } from '../helpers/retry-helper';
|
|
18
|
+
|
|
19
|
+
export interface IHttpFunctionsConfig {
|
|
20
|
+
logErrorsToConsole: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function getWithRetryAsync<TRawData>(
|
|
24
|
+
instance: AxiosInstance,
|
|
25
|
+
call: IHttpGetQueryCall,
|
|
26
|
+
functionsConfig: IHttpFunctionsConfig,
|
|
27
|
+
options?: IHttpQueryOptions<CancelToken>
|
|
28
|
+
): Promise<IResponse<TRawData>> {
|
|
29
|
+
const retryStrategyOptions = options?.retryStrategy ?? retryHelper.defaultRetryStrategy;
|
|
30
|
+
|
|
31
|
+
return await runWithRetryAsync<TRawData>({
|
|
32
|
+
retryAttempt: 0,
|
|
33
|
+
url: call.url,
|
|
34
|
+
retryStrategy: retryStrategyOptions,
|
|
35
|
+
functionsConfig: functionsConfig,
|
|
36
|
+
call: async (retryAttempt) => {
|
|
37
|
+
httpDebugger.debugStartHttpRequest();
|
|
38
|
+
|
|
39
|
+
const axiosResponse = await instance.get<TRawData>(call.url, {
|
|
40
|
+
headers: getHeadersJson(options?.headers ?? [], false),
|
|
41
|
+
responseType: options?.responseType,
|
|
42
|
+
cancelToken: options?.cancelToken?.token
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const response: IResponse<TRawData> = {
|
|
46
|
+
data: axiosResponse.data,
|
|
47
|
+
rawResponse: axiosResponse,
|
|
48
|
+
headers: extractHeadersFromAxiosResponse(axiosResponse),
|
|
49
|
+
status: axiosResponse.status,
|
|
50
|
+
retryStrategy: {
|
|
51
|
+
options: retryStrategyOptions,
|
|
52
|
+
retryAttempts: retryAttempt
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
httpDebugger.debugSuccessHttpRequest();
|
|
57
|
+
return response;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function postWithRetryAsync<TRawData>(
|
|
63
|
+
instance: AxiosInstance,
|
|
64
|
+
call: IHttpPostQueryCall,
|
|
65
|
+
functionsConfig: IHttpFunctionsConfig,
|
|
66
|
+
options?: IHttpQueryOptions<CancelToken>
|
|
67
|
+
): Promise<IResponse<TRawData>> {
|
|
68
|
+
const retryStrategyOptions = options?.retryStrategy ?? retryHelper.defaultRetryStrategy;
|
|
69
|
+
|
|
70
|
+
return await runWithRetryAsync<TRawData>({
|
|
71
|
+
retryAttempt: 0,
|
|
72
|
+
url: call.url,
|
|
73
|
+
retryStrategy: retryStrategyOptions,
|
|
74
|
+
functionsConfig: functionsConfig,
|
|
75
|
+
call: async (retryAttempt) => {
|
|
76
|
+
httpDebugger.debugStartHttpRequest();
|
|
77
|
+
|
|
78
|
+
const axiosResponse = await instance.post<TRawData>(call.url, call.body, {
|
|
79
|
+
headers: getHeadersJson(options?.headers ?? [], false),
|
|
80
|
+
responseType: options?.responseType,
|
|
81
|
+
// required for uploading large files
|
|
82
|
+
// https://github.com/axios/axios/issues/1362
|
|
83
|
+
maxContentLength: 'Infinity' as any,
|
|
84
|
+
maxBodyLength: 'Infinity' as any,
|
|
85
|
+
cancelToken: options?.cancelToken?.token
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const response: IResponse<TRawData> = {
|
|
89
|
+
data: axiosResponse.data,
|
|
90
|
+
rawResponse: axiosResponse,
|
|
91
|
+
headers: extractHeadersFromAxiosResponse(axiosResponse),
|
|
92
|
+
status: axiosResponse.status,
|
|
93
|
+
retryStrategy: {
|
|
94
|
+
options: retryStrategyOptions,
|
|
95
|
+
retryAttempts: retryAttempt
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
httpDebugger.debugSuccessHttpRequest();
|
|
100
|
+
return response;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export async function putWithRetryAsync<TRawData>(
|
|
106
|
+
instance: AxiosInstance,
|
|
107
|
+
call: IHttpPutQueryCall,
|
|
108
|
+
functionsConfig: IHttpFunctionsConfig,
|
|
109
|
+
options?: IHttpQueryOptions<CancelToken>
|
|
110
|
+
): Promise<IResponse<TRawData>> {
|
|
111
|
+
const retryStrategyOptions = options?.retryStrategy ?? retryHelper.defaultRetryStrategy;
|
|
112
|
+
|
|
113
|
+
return await runWithRetryAsync<TRawData>({
|
|
114
|
+
retryAttempt: 0,
|
|
115
|
+
url: call.url,
|
|
116
|
+
retryStrategy: retryStrategyOptions,
|
|
117
|
+
functionsConfig: functionsConfig,
|
|
118
|
+
call: async (retryAttempt) => {
|
|
119
|
+
httpDebugger.debugStartHttpRequest();
|
|
120
|
+
|
|
121
|
+
const axiosResponse = await instance.put<TRawData>(call.url, call.body, {
|
|
122
|
+
headers: getHeadersJson(options?.headers ?? [], false),
|
|
123
|
+
responseType: options?.responseType,
|
|
124
|
+
// required for uploading large files
|
|
125
|
+
// https://github.com/axios/axios/issues/1362
|
|
126
|
+
maxContentLength: 'Infinity' as any,
|
|
127
|
+
maxBodyLength: 'Infinity' as any,
|
|
128
|
+
cancelToken: options?.cancelToken?.token
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const response: IResponse<TRawData> = {
|
|
132
|
+
data: axiosResponse.data,
|
|
133
|
+
rawResponse: axiosResponse,
|
|
134
|
+
headers: extractHeadersFromAxiosResponse(axiosResponse),
|
|
135
|
+
status: axiosResponse.status,
|
|
136
|
+
retryStrategy: {
|
|
137
|
+
options: retryStrategyOptions,
|
|
138
|
+
retryAttempts: retryAttempt
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
httpDebugger.debugSuccessHttpRequest();
|
|
143
|
+
return response;
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export async function patchWithRetryAsync<TRawData>(
|
|
149
|
+
instance: AxiosInstance,
|
|
150
|
+
call: IHttpPatchQueryCall,
|
|
151
|
+
functionsConfig: IHttpFunctionsConfig,
|
|
152
|
+
options?: IHttpQueryOptions<CancelToken>
|
|
153
|
+
): Promise<IResponse<TRawData>> {
|
|
154
|
+
const retryStrategyOptions = options?.retryStrategy ?? retryHelper.defaultRetryStrategy;
|
|
155
|
+
|
|
156
|
+
return await runWithRetryAsync<TRawData>({
|
|
157
|
+
retryAttempt: 0,
|
|
158
|
+
url: call.url,
|
|
159
|
+
retryStrategy: retryStrategyOptions,
|
|
160
|
+
functionsConfig: functionsConfig,
|
|
161
|
+
call: async (retryAttempt) => {
|
|
162
|
+
httpDebugger.debugStartHttpRequest();
|
|
163
|
+
|
|
164
|
+
const axiosResponse = await instance.patch<TRawData>(call.url, call.body, {
|
|
165
|
+
headers: getHeadersJson(options?.headers ?? [], false),
|
|
166
|
+
responseType: options?.responseType,
|
|
167
|
+
// required for uploading large files
|
|
168
|
+
// https://github.com/axios/axios/issues/1362
|
|
169
|
+
maxContentLength: 'Infinity' as any,
|
|
170
|
+
maxBodyLength: 'Infinity' as any,
|
|
171
|
+
cancelToken: options?.cancelToken?.token
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const response: IResponse<TRawData> = {
|
|
175
|
+
data: axiosResponse.data,
|
|
176
|
+
rawResponse: axiosResponse,
|
|
177
|
+
headers: extractHeadersFromAxiosResponse(axiosResponse),
|
|
178
|
+
status: axiosResponse.status,
|
|
179
|
+
retryStrategy: {
|
|
180
|
+
options: retryStrategyOptions,
|
|
181
|
+
retryAttempts: retryAttempt
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
httpDebugger.debugSuccessHttpRequest();
|
|
186
|
+
return response;
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export async function deleteWithRetryAsync<TRawData>(
|
|
192
|
+
instance: AxiosInstance,
|
|
193
|
+
call: IHttpDeleteQueryCall,
|
|
194
|
+
functionsConfig: IHttpFunctionsConfig,
|
|
195
|
+
options?: IHttpQueryOptions<CancelToken>
|
|
196
|
+
): Promise<IResponse<TRawData>> {
|
|
197
|
+
const retryStrategyOptions = options?.retryStrategy ?? retryHelper.defaultRetryStrategy;
|
|
198
|
+
|
|
199
|
+
return await runWithRetryAsync<TRawData>({
|
|
200
|
+
retryAttempt: 0,
|
|
201
|
+
url: call.url,
|
|
202
|
+
retryStrategy: retryStrategyOptions,
|
|
203
|
+
functionsConfig: functionsConfig,
|
|
204
|
+
call: async (retryAttempt) => {
|
|
205
|
+
httpDebugger.debugStartHttpRequest();
|
|
206
|
+
|
|
207
|
+
const axiosResponse = await instance.delete<TRawData>(call.url, {
|
|
208
|
+
headers: getHeadersJson(options?.headers ?? [], false),
|
|
209
|
+
responseType: options?.responseType,
|
|
210
|
+
// required for uploading large files
|
|
211
|
+
// https://github.com/axios/axios/issues/1362
|
|
212
|
+
maxContentLength: 'Infinity' as any,
|
|
213
|
+
maxBodyLength: 'Infinity' as any,
|
|
214
|
+
cancelToken: options?.cancelToken?.token
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const response: IResponse<TRawData> = {
|
|
218
|
+
data: axiosResponse.data,
|
|
219
|
+
rawResponse: axiosResponse,
|
|
220
|
+
headers: extractHeadersFromAxiosResponse(axiosResponse),
|
|
221
|
+
status: axiosResponse.status,
|
|
222
|
+
retryStrategy: {
|
|
223
|
+
options: retryStrategyOptions,
|
|
224
|
+
retryAttempts: retryAttempt
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
httpDebugger.debugSuccessHttpRequest();
|
|
229
|
+
return response;
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function createCancelToken(): IHttpCancelRequestToken<CancelToken> {
|
|
235
|
+
let canceler: Canceler;
|
|
236
|
+
|
|
237
|
+
const token = new axios.CancelToken((c) => {
|
|
238
|
+
// An executor function receives a cancel function as a parameter
|
|
239
|
+
canceler = c;
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
cancel: (cancelMessage) =>
|
|
244
|
+
canceler(`${retryHelper.requestCancelledMessagePrefix}: ${cancelMessage ?? 'User cancel'}`),
|
|
245
|
+
token: token
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async function runWithRetryAsync<TRawData>(data: {
|
|
250
|
+
url: string;
|
|
251
|
+
retryAttempt: number;
|
|
252
|
+
call: (retryAttempt: number) => Promise<IResponse<TRawData>>;
|
|
253
|
+
retryStrategy: IRetryStrategyOptions;
|
|
254
|
+
functionsConfig: IHttpFunctionsConfig;
|
|
255
|
+
}): Promise<IResponse<TRawData>> {
|
|
256
|
+
try {
|
|
257
|
+
return await data.call(data.retryAttempt);
|
|
258
|
+
} catch (error) {
|
|
259
|
+
const retryResult = retryHelper.getRetryErrorResult({
|
|
260
|
+
error: error,
|
|
261
|
+
retryAttempt: data.retryAttempt,
|
|
262
|
+
retryStrategy: data.retryStrategy
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
if (retryResult.canRetry) {
|
|
266
|
+
httpDebugger.debugRetryHttpRequest();
|
|
267
|
+
|
|
268
|
+
// wait time before retrying
|
|
269
|
+
await new Promise((resolve) => setTimeout(resolve, retryResult.retryInMs));
|
|
270
|
+
|
|
271
|
+
// retry request
|
|
272
|
+
console.warn(
|
|
273
|
+
`Retry attempt '${data.retryAttempt + 1}' from a maximum of '${
|
|
274
|
+
retryResult.maxRetries
|
|
275
|
+
}' retries. Request url: '${data.url}'`
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
return await runWithRetryAsync({
|
|
279
|
+
call: data.call,
|
|
280
|
+
retryStrategy: data.retryStrategy,
|
|
281
|
+
retryAttempt: data.retryAttempt + 1,
|
|
282
|
+
url: data.url,
|
|
283
|
+
functionsConfig: data.functionsConfig
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (data.functionsConfig.logErrorsToConsole) {
|
|
288
|
+
console.error(`Executing '${data.url}' failed. Request was retried '${data.retryAttempt}' times. `, error);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
throw error;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function getHeadersJson(headers: IHeader[], addContentTypeHeader: boolean): { [header: string]: string } {
|
|
296
|
+
const headerJson: { [header: string]: string } = {};
|
|
297
|
+
|
|
298
|
+
headers.forEach((header) => {
|
|
299
|
+
headerJson[header.header] = header.value;
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
if (addContentTypeHeader) {
|
|
303
|
+
// add default content type header if not present
|
|
304
|
+
const contentTypeHeader = headers.find((m) => m.header.toLowerCase() === 'Content-Type'.toLowerCase());
|
|
305
|
+
|
|
306
|
+
if (!contentTypeHeader) {
|
|
307
|
+
headerJson['Content-Type'] = 'application/json';
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return headerJson;
|
|
312
|
+
}
|