@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.
Files changed (202) hide show
  1. package/.npmignore +14 -0
  2. package/LICENSE.md +9 -0
  3. package/README.md +30 -0
  4. package/dist/cjs/helpers/enum.helper.d.ts +8 -0
  5. package/dist/cjs/helpers/enum.helper.js +79 -0
  6. package/dist/cjs/helpers/enum.helper.js.map +1 -0
  7. package/dist/cjs/helpers/header.helper.d.ts +13 -0
  8. package/dist/cjs/helpers/header.helper.js +24 -0
  9. package/dist/cjs/helpers/header.helper.js.map +1 -0
  10. package/dist/cjs/helpers/headers-helper.d.ts +3 -0
  11. package/dist/cjs/helpers/headers-helper.js +37 -0
  12. package/dist/cjs/helpers/headers-helper.js.map +1 -0
  13. package/dist/cjs/helpers/index.d.ts +5 -0
  14. package/dist/cjs/helpers/index.js +22 -0
  15. package/dist/cjs/helpers/index.js.map +1 -0
  16. package/dist/cjs/helpers/retry-helper.d.ts +37 -0
  17. package/dist/cjs/helpers/retry-helper.js +152 -0
  18. package/dist/cjs/helpers/retry-helper.js.map +1 -0
  19. package/dist/cjs/helpers/url.helper.d.ts +10 -0
  20. package/dist/cjs/helpers/url.helper.js +30 -0
  21. package/dist/cjs/helpers/url.helper.js.map +1 -0
  22. package/dist/cjs/http/http.debugger.d.ts +6 -0
  23. package/dist/cjs/http/http.debugger.js +26 -0
  24. package/dist/cjs/http/http.debugger.js.map +1 -0
  25. package/dist/cjs/http/http.functions.d.ts +11 -0
  26. package/dist/cjs/http/http.functions.js +381 -0
  27. package/dist/cjs/http/http.functions.js.map +1 -0
  28. package/dist/cjs/http/http.models.d.ts +73 -0
  29. package/dist/cjs/http/http.models.js +3 -0
  30. package/dist/cjs/http/http.models.js.map +1 -0
  31. package/dist/cjs/http/http.service.d.ts +19 -0
  32. package/dist/cjs/http/http.service.js +110 -0
  33. package/dist/cjs/http/http.service.js.map +1 -0
  34. package/dist/cjs/http/ihttp.service.d.ts +9 -0
  35. package/dist/cjs/http/ihttp.service.js +3 -0
  36. package/dist/cjs/http/ihttp.service.js.map +1 -0
  37. package/dist/cjs/http/index.d.ts +6 -0
  38. package/dist/cjs/http/index.js +23 -0
  39. package/dist/cjs/http/index.js.map +1 -0
  40. package/dist/cjs/http/test-http.service.d.ts +17 -0
  41. package/dist/cjs/http/test-http.service.js +47 -0
  42. package/dist/cjs/http/test-http.service.js.map +1 -0
  43. package/dist/cjs/index.d.ts +3 -0
  44. package/dist/cjs/index.js +21 -0
  45. package/dist/cjs/index.js.map +1 -0
  46. package/dist/cjs/models/index.d.ts +3 -0
  47. package/dist/cjs/models/index.js +20 -0
  48. package/dist/cjs/models/index.js.map +1 -0
  49. package/dist/cjs/models/isdk-info.d.ts +14 -0
  50. package/dist/cjs/models/isdk-info.js +3 -0
  51. package/dist/cjs/models/isdk-info.js.map +1 -0
  52. package/dist/cjs/models/parameters.d.ts +16 -0
  53. package/dist/cjs/models/parameters.js +30 -0
  54. package/dist/cjs/models/parameters.js.map +1 -0
  55. package/dist/cjs/models/url.models.d.ts +3 -0
  56. package/dist/cjs/models/url.models.js +3 -0
  57. package/dist/cjs/models/url.models.js.map +1 -0
  58. package/dist/cjs/sdk-info.generated.d.ts +2 -0
  59. package/dist/cjs/sdk-info.generated.js +9 -0
  60. package/dist/cjs/sdk-info.generated.js.map +1 -0
  61. package/dist/es2015/helpers/enum.helper.d.ts +8 -0
  62. package/dist/es2015/helpers/enum.helper.js +50 -0
  63. package/dist/es2015/helpers/enum.helper.js.map +1 -0
  64. package/dist/es2015/helpers/header.helper.d.ts +13 -0
  65. package/dist/es2015/helpers/header.helper.js +19 -0
  66. package/dist/es2015/helpers/header.helper.js.map +1 -0
  67. package/dist/es2015/helpers/headers-helper.d.ts +3 -0
  68. package/dist/es2015/helpers/headers-helper.js +11 -0
  69. package/dist/es2015/helpers/headers-helper.js.map +1 -0
  70. package/dist/es2015/helpers/index.d.ts +5 -0
  71. package/dist/es2015/helpers/index.js +6 -0
  72. package/dist/es2015/helpers/index.js.map +1 -0
  73. package/dist/es2015/helpers/retry-helper.d.ts +37 -0
  74. package/dist/es2015/helpers/retry-helper.js +145 -0
  75. package/dist/es2015/helpers/retry-helper.js.map +1 -0
  76. package/dist/es2015/helpers/url.helper.d.ts +10 -0
  77. package/dist/es2015/helpers/url.helper.js +23 -0
  78. package/dist/es2015/helpers/url.helper.js.map +1 -0
  79. package/dist/es2015/http/http.debugger.d.ts +6 -0
  80. package/dist/es2015/http/http.debugger.js +19 -0
  81. package/dist/es2015/http/http.debugger.js.map +1 -0
  82. package/dist/es2015/http/http.functions.d.ts +11 -0
  83. package/dist/es2015/http/http.functions.js +244 -0
  84. package/dist/es2015/http/http.functions.js.map +1 -0
  85. package/dist/es2015/http/http.models.d.ts +73 -0
  86. package/dist/es2015/http/http.models.js +2 -0
  87. package/dist/es2015/http/http.models.js.map +1 -0
  88. package/dist/es2015/http/http.service.d.ts +19 -0
  89. package/dist/es2015/http/http.service.js +45 -0
  90. package/dist/es2015/http/http.service.js.map +1 -0
  91. package/dist/es2015/http/ihttp.service.d.ts +9 -0
  92. package/dist/es2015/http/ihttp.service.js +2 -0
  93. package/dist/es2015/http/ihttp.service.js.map +1 -0
  94. package/dist/es2015/http/index.d.ts +6 -0
  95. package/dist/es2015/http/index.js +7 -0
  96. package/dist/es2015/http/index.js.map +1 -0
  97. package/dist/es2015/http/test-http.service.d.ts +17 -0
  98. package/dist/es2015/http/test-http.service.js +41 -0
  99. package/dist/es2015/http/test-http.service.js.map +1 -0
  100. package/dist/es2015/index.d.ts +3 -0
  101. package/dist/es2015/index.js +5 -0
  102. package/dist/es2015/index.js.map +1 -0
  103. package/dist/es2015/models/index.d.ts +3 -0
  104. package/dist/es2015/models/index.js +4 -0
  105. package/dist/es2015/models/index.js.map +1 -0
  106. package/dist/es2015/models/isdk-info.d.ts +14 -0
  107. package/dist/es2015/models/isdk-info.js +2 -0
  108. package/dist/es2015/models/isdk-info.js.map +1 -0
  109. package/dist/es2015/models/parameters.d.ts +16 -0
  110. package/dist/es2015/models/parameters.js +26 -0
  111. package/dist/es2015/models/parameters.js.map +1 -0
  112. package/dist/es2015/models/url.models.d.ts +3 -0
  113. package/dist/es2015/models/url.models.js +2 -0
  114. package/dist/es2015/models/url.models.js.map +1 -0
  115. package/dist/es2015/sdk-info.generated.d.ts +2 -0
  116. package/dist/es2015/sdk-info.generated.js +6 -0
  117. package/dist/es2015/sdk-info.generated.js.map +1 -0
  118. package/dist/es5/helpers/enum.helper.d.ts +8 -0
  119. package/dist/es5/helpers/enum.helper.js +66 -0
  120. package/dist/es5/helpers/enum.helper.js.map +1 -0
  121. package/dist/es5/helpers/header.helper.d.ts +13 -0
  122. package/dist/es5/helpers/header.helper.js +21 -0
  123. package/dist/es5/helpers/header.helper.js.map +1 -0
  124. package/dist/es5/helpers/headers-helper.d.ts +3 -0
  125. package/dist/es5/helpers/headers-helper.js +23 -0
  126. package/dist/es5/helpers/headers-helper.js.map +1 -0
  127. package/dist/es5/helpers/index.d.ts +5 -0
  128. package/dist/es5/helpers/index.js +6 -0
  129. package/dist/es5/helpers/index.js.map +1 -0
  130. package/dist/es5/helpers/retry-helper.d.ts +37 -0
  131. package/dist/es5/helpers/retry-helper.js +149 -0
  132. package/dist/es5/helpers/retry-helper.js.map +1 -0
  133. package/dist/es5/helpers/url.helper.d.ts +10 -0
  134. package/dist/es5/helpers/url.helper.js +27 -0
  135. package/dist/es5/helpers/url.helper.js.map +1 -0
  136. package/dist/es5/http/http.debugger.d.ts +6 -0
  137. package/dist/es5/http/http.debugger.js +23 -0
  138. package/dist/es5/http/http.debugger.js.map +1 -0
  139. package/dist/es5/http/http.functions.d.ts +11 -0
  140. package/dist/es5/http/http.functions.js +337 -0
  141. package/dist/es5/http/http.functions.js.map +1 -0
  142. package/dist/es5/http/http.models.d.ts +73 -0
  143. package/dist/es5/http/http.models.js +2 -0
  144. package/dist/es5/http/http.models.js.map +1 -0
  145. package/dist/es5/http/http.service.d.ts +19 -0
  146. package/dist/es5/http/http.service.js +72 -0
  147. package/dist/es5/http/http.service.js.map +1 -0
  148. package/dist/es5/http/ihttp.service.d.ts +9 -0
  149. package/dist/es5/http/ihttp.service.js +2 -0
  150. package/dist/es5/http/ihttp.service.js.map +1 -0
  151. package/dist/es5/http/index.d.ts +6 -0
  152. package/dist/es5/http/index.js +7 -0
  153. package/dist/es5/http/index.js.map +1 -0
  154. package/dist/es5/http/test-http.service.d.ts +17 -0
  155. package/dist/es5/http/test-http.service.js +44 -0
  156. package/dist/es5/http/test-http.service.js.map +1 -0
  157. package/dist/es5/index.d.ts +3 -0
  158. package/dist/es5/index.js +5 -0
  159. package/dist/es5/index.js.map +1 -0
  160. package/dist/es5/models/index.d.ts +3 -0
  161. package/dist/es5/models/index.js +4 -0
  162. package/dist/es5/models/index.js.map +1 -0
  163. package/dist/es5/models/isdk-info.d.ts +14 -0
  164. package/dist/es5/models/isdk-info.js +2 -0
  165. package/dist/es5/models/isdk-info.js.map +1 -0
  166. package/dist/es5/models/parameters.d.ts +16 -0
  167. package/dist/es5/models/parameters.js +27 -0
  168. package/dist/es5/models/parameters.js.map +1 -0
  169. package/dist/es5/models/url.models.d.ts +3 -0
  170. package/dist/es5/models/url.models.js +2 -0
  171. package/dist/es5/models/url.models.js.map +1 -0
  172. package/dist/es5/sdk-info.generated.d.ts +2 -0
  173. package/dist/es5/sdk-info.generated.js +6 -0
  174. package/dist/es5/sdk-info.generated.js.map +1 -0
  175. package/dist/umd/kontent-core.umd.js +3476 -0
  176. package/dist/umd/kontent-core.umd.js.map +1 -0
  177. package/dist/umd/kontent-core.umd.min.js +2 -0
  178. package/dist/umd/kontent-core.umd.min.js.map +1 -0
  179. package/dist/umd/report.json +1 -0
  180. package/dist/umd/report.min.json +1 -0
  181. package/dist/umd/stats.json +11920 -0
  182. package/dist/umd/stats.min.json +13789 -0
  183. package/lib/helpers/enum.helper.ts +63 -0
  184. package/lib/helpers/header.helper.ts +23 -0
  185. package/lib/helpers/headers-helper.ts +15 -0
  186. package/lib/helpers/index.ts +5 -0
  187. package/lib/helpers/retry-helper.ts +204 -0
  188. package/lib/helpers/url.helper.ts +26 -0
  189. package/lib/http/http.debugger.ts +21 -0
  190. package/lib/http/http.functions.ts +312 -0
  191. package/lib/http/http.models.ts +83 -0
  192. package/lib/http/http.service.ts +91 -0
  193. package/lib/http/ihttp.service.ts +20 -0
  194. package/lib/http/index.ts +6 -0
  195. package/lib/http/test-http.service.ts +70 -0
  196. package/lib/index.ts +4 -0
  197. package/lib/models/index.ts +3 -0
  198. package/lib/models/isdk-info.ts +15 -0
  199. package/lib/models/parameters.ts +25 -0
  200. package/lib/models/url.models.ts +3 -0
  201. package/lib/sdk-info.generated.ts +7 -0
  202. 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,5 @@
1
+ export * from './url.helper';
2
+ export * from './header.helper';
3
+ export * from './enum.helper';
4
+ export * from './retry-helper';
5
+ export * from './headers-helper';
@@ -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
+ }