@kontent-ai/delivery-sdk 16.4.3 → 16.4.5
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 +15 -15
- package/LICENSE.md +9 -9
- package/dist/bundles/kontent-delivery.umd.js +5076 -4619
- package/dist/bundles/kontent-delivery.umd.js.map +1 -1
- package/dist/bundles/kontent-delivery.umd.min.js +1 -1
- package/dist/bundles/kontent-delivery.umd.min.js.map +1 -1
- package/dist/bundles/report.json +1 -1
- package/dist/bundles/report.min.json +1 -1
- package/dist/bundles/stats.json +7015 -5766
- package/dist/bundles/stats.min.json +11376 -10250
- package/dist/cjs/query/element/element-query.class.js +1 -1
- package/dist/cjs/query/element/element-query.class.js.map +1 -1
- package/dist/cjs/query/item/single-item-query.class.js +1 -1
- package/dist/cjs/query/item/single-item-query.class.js.map +1 -1
- package/dist/cjs/query/taxonomy/taxonomy-query.class.js +1 -1
- package/dist/cjs/query/taxonomy/taxonomy-query.class.js.map +1 -1
- package/dist/cjs/query/type/single-type-query.class.js +1 -1
- package/dist/cjs/query/type/single-type-query.class.js.map +1 -1
- package/dist/cjs/query/used-in/used-in-query.class.js +1 -1
- package/dist/cjs/query/used-in/used-in-query.class.js.map +1 -1
- package/dist/cjs/sdk-info.generated.js +1 -1
- package/dist/es6/query/element/element-query.class.js +1 -1
- package/dist/es6/query/element/element-query.class.js.map +1 -1
- package/dist/es6/query/item/single-item-query.class.js +1 -1
- package/dist/es6/query/item/single-item-query.class.js.map +1 -1
- package/dist/es6/query/taxonomy/taxonomy-query.class.js +1 -1
- package/dist/es6/query/taxonomy/taxonomy-query.class.js.map +1 -1
- package/dist/es6/query/type/single-type-query.class.js +1 -1
- package/dist/es6/query/type/single-type-query.class.js.map +1 -1
- package/dist/es6/query/used-in/used-in-query.class.js +1 -1
- package/dist/es6/query/used-in/used-in-query.class.js.map +1 -1
- package/dist/es6/sdk-info.generated.js +1 -1
- package/dist/esnext/query/element/element-query.class.js +1 -1
- package/dist/esnext/query/element/element-query.class.js.map +1 -1
- package/dist/esnext/query/item/single-item-query.class.js +1 -1
- package/dist/esnext/query/item/single-item-query.class.js.map +1 -1
- package/dist/esnext/query/taxonomy/taxonomy-query.class.js +1 -1
- package/dist/esnext/query/taxonomy/taxonomy-query.class.js.map +1 -1
- package/dist/esnext/query/type/single-type-query.class.js +1 -1
- package/dist/esnext/query/type/single-type-query.class.js.map +1 -1
- package/dist/esnext/query/used-in/used-in-query.class.js +1 -1
- package/dist/esnext/query/used-in/used-in-query.class.js.map +1 -1
- package/dist/esnext/sdk-info.generated.js +1 -1
- package/lib/client/delivery-client.factory.ts +9 -9
- package/lib/client/delivery-client.ts +163 -163
- package/lib/client/idelivery-client.interface.ts +110 -110
- package/lib/client/index.ts +3 -3
- package/lib/config/delivery-configs.ts +98 -98
- package/lib/config/index.ts +1 -1
- package/lib/contracts/contracts.ts +265 -265
- package/lib/contracts/index.ts +1 -1
- package/lib/elements/element-models.ts +89 -89
- package/lib/elements/element-resolver.ts +3 -3
- package/lib/elements/element-type.ts +16 -16
- package/lib/elements/elements.ts +70 -70
- package/lib/elements/index.ts +4 -4
- package/lib/images/image-url-transformation-builder.factory.ts +5 -5
- package/lib/images/image-url-transformation-builder.ts +181 -181
- package/lib/images/image.models.ts +4 -4
- package/lib/images/index.ts +3 -3
- package/lib/index.ts +12 -12
- package/lib/mappers/element.mapper.ts +504 -504
- package/lib/mappers/generic-element.mapper.ts +20 -20
- package/lib/mappers/index.ts +8 -8
- package/lib/mappers/item.mapper.ts +181 -181
- package/lib/mappers/language.mapper.ts +24 -24
- package/lib/mappers/sync.mapper.ts +32 -32
- package/lib/mappers/taxonomy.mapper.ts +77 -77
- package/lib/mappers/type.mapper.ts +76 -76
- package/lib/mappers/used-in.mapper.ts +20 -20
- package/lib/models/common/base-responses.ts +13 -13
- package/lib/models/common/common-models.ts +118 -118
- package/lib/models/common/filters.ts +280 -280
- package/lib/models/common/headers.ts +5 -5
- package/lib/models/common/index.ts +7 -7
- package/lib/models/common/pagination.class.ts +7 -7
- package/lib/models/common/parameters.ts +189 -189
- package/lib/models/common/sort-order.ts +1 -1
- package/lib/models/content-type-models.ts +42 -42
- package/lib/models/element-models.ts +51 -51
- package/lib/models/index.ts +9 -9
- package/lib/models/item-models.ts +192 -192
- package/lib/models/language-models.ts +17 -17
- package/lib/models/responses.ts +137 -137
- package/lib/models/sync-models.ts +18 -18
- package/lib/models/taxonomy-models.ts +25 -25
- package/lib/query/common/base-item-listing-query.class.ts +274 -274
- package/lib/query/common/base-listing-query.class.ts +95 -95
- package/lib/query/common/base-query.class.ts +148 -148
- package/lib/query/element/element-query.class.ts +47 -45
- package/lib/query/index.ts +13 -13
- package/lib/query/item/multiple-items-query.class.ts +183 -183
- package/lib/query/item/single-item-query.class.ts +106 -106
- package/lib/query/items-feed/items-feed-query.class.ts +191 -191
- package/lib/query/language/languages-query.class.ts +91 -91
- package/lib/query/sync/initialize-sync-query.class.ts +83 -83
- package/lib/query/sync/sync-changes-query.class.ts +56 -56
- package/lib/query/taxonomy/taxonomies-query.class.ts +82 -82
- package/lib/query/taxonomy/taxonomy-query.class.ts +50 -50
- package/lib/query/type/multiple-type-query.class.ts +95 -95
- package/lib/query/type/single-type-query.class.ts +45 -45
- package/lib/query/used-in/used-in-query.class.ts +128 -126
- package/lib/sdk-info.generated.ts +1 -1
- package/lib/services/base-delivery-query.service.ts +375 -375
- package/lib/services/delivery-query.service.ts +336 -336
- package/lib/services/index.ts +2 -2
- package/lib/services/mapping.service.ts +223 -223
- package/lib/utilities/codename.helper.ts +10 -10
- package/lib/utilities/delivery-url.helper.ts +11 -11
- package/lib/utilities/enum.helper.ts +38 -38
- package/lib/utilities/index.ts +3 -3
- package/package.json +96 -96
- package/readme.md +531 -531
|
@@ -1,375 +1,375 @@
|
|
|
1
|
-
import { IResponse, IHeader, IHttpService, IQueryParameter, urlHelper } from '@kontent-ai/core-sdk';
|
|
2
|
-
import { AxiosError } from 'axios';
|
|
3
|
-
import {
|
|
4
|
-
waitForLoadingNewContentHeader,
|
|
5
|
-
IQueryConfig,
|
|
6
|
-
ISDKInfo,
|
|
7
|
-
IDeliveryErrorRaw,
|
|
8
|
-
DeliveryError,
|
|
9
|
-
sdkVersionHeader,
|
|
10
|
-
staleContentHeaderName,
|
|
11
|
-
continuationTokenHeaderName,
|
|
12
|
-
IDeliveryNetworkResponse,
|
|
13
|
-
ClientTypes
|
|
14
|
-
} from '../models';
|
|
15
|
-
|
|
16
|
-
import { IDeliveryClientConfig } from '../config';
|
|
17
|
-
import { IMappingService } from './mapping.service';
|
|
18
|
-
|
|
19
|
-
export abstract class BaseDeliveryQueryService<TClientTypes extends ClientTypes> {
|
|
20
|
-
/**
|
|
21
|
-
* Default base Url to Kontent.ai Delivery API
|
|
22
|
-
*/
|
|
23
|
-
private readonly defaultBaseDeliveryApiUrl: string = 'https://deliver.kontent.ai';
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Default preview url to Kontent.ai Delivery API
|
|
27
|
-
*/
|
|
28
|
-
private readonly defaultPreviewDeliveryApiUrl: string = 'https://preview-deliver.kontent.ai';
|
|
29
|
-
|
|
30
|
-
constructor(
|
|
31
|
-
/**
|
|
32
|
-
* Delivery client configuration
|
|
33
|
-
*/
|
|
34
|
-
public readonly config: IDeliveryClientConfig,
|
|
35
|
-
/**
|
|
36
|
-
* Http service for fetching data
|
|
37
|
-
*/
|
|
38
|
-
public readonly httpService: IHttpService<any>,
|
|
39
|
-
/**
|
|
40
|
-
* Information about the SDK
|
|
41
|
-
*/
|
|
42
|
-
public readonly sdkInfo: ISDKInfo,
|
|
43
|
-
/**
|
|
44
|
-
* Mapping service
|
|
45
|
-
*/
|
|
46
|
-
public readonly mappingService: IMappingService<TClientTypes>
|
|
47
|
-
) {}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Gets url based on the action, query configuration and options (parameters)
|
|
51
|
-
* @param action Action (= url part) that will be hit
|
|
52
|
-
* @param queryConfig Query configuration
|
|
53
|
-
* @param options Query options
|
|
54
|
-
*/
|
|
55
|
-
getUrl(action: string, queryConfig: IQueryConfig, options?: IQueryParameter[]): string {
|
|
56
|
-
if (!this.config.proxy || !this.config.proxy.advancedProxyUrlResolver) {
|
|
57
|
-
return urlHelper.addOptionsToUrl(this.getBaseUrl(queryConfig) + action, options);
|
|
58
|
-
}
|
|
59
|
-
return this.config.proxy.advancedProxyUrlResolver({
|
|
60
|
-
queryParameters: options ? options : [],
|
|
61
|
-
queryString: urlHelper.addOptionsToUrl('', options),
|
|
62
|
-
action: action,
|
|
63
|
-
domain: this.getDomain(queryConfig),
|
|
64
|
-
queryConfig: queryConfig,
|
|
65
|
-
environmentId: this.config.environmentId
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Gets proper set of headers for given request.
|
|
71
|
-
* @param queryConfig Query configuration
|
|
72
|
-
* @param additionalHeaders Custom headers
|
|
73
|
-
*/
|
|
74
|
-
getHeaders(queryConfig: IQueryConfig, additionalHeaders?: IHeader[]): IHeader[] {
|
|
75
|
-
const headers: IHeader[] = [];
|
|
76
|
-
|
|
77
|
-
if (additionalHeaders) {
|
|
78
|
-
headers.push(...additionalHeaders);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// add SDK Id header for monitoring SDK usage
|
|
82
|
-
headers.push(this.getSdkIdHeader());
|
|
83
|
-
|
|
84
|
-
// add headers from global config
|
|
85
|
-
if (this.config.globalHeaders) {
|
|
86
|
-
headers.push(...this.config.globalHeaders(queryConfig));
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// add query / global headers from query config
|
|
90
|
-
headers.push(...this.getQueryHeaders(queryConfig));
|
|
91
|
-
|
|
92
|
-
if (this.isPreviewModeEnabled(queryConfig) && this.isSecuredModeEnabled(queryConfig)) {
|
|
93
|
-
throw Error(`Preview & secured modes cannot be used at the same time.`);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// add preview header is required
|
|
97
|
-
if (this.isPreviewModeEnabled(queryConfig) && this.config.previewApiKey) {
|
|
98
|
-
headers.push(this.getAuthorizationHeader(this.config.previewApiKey));
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// add secured mode header is required
|
|
102
|
-
if (this.isSecuredModeEnabled(queryConfig) && this.config.secureApiKey) {
|
|
103
|
-
headers.push(this.getAuthorizationHeader(this.config.secureApiKey));
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// add 'X-KC-Wait-For-Loading-New-Content' header if required
|
|
107
|
-
if (this.shouldAddWaitForLoadingNewContentHeader(queryConfig)) {
|
|
108
|
-
headers.push({
|
|
109
|
-
header: waitForLoadingNewContentHeader,
|
|
110
|
-
value: 'true'
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return headers;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Http POST response
|
|
119
|
-
* @param url Url of request
|
|
120
|
-
* @param queryConfig Query config configuration
|
|
121
|
-
*/
|
|
122
|
-
protected async postResponseAsync<TRawData>(
|
|
123
|
-
url: string,
|
|
124
|
-
body: any,
|
|
125
|
-
queryConfig?: IQueryConfig,
|
|
126
|
-
serviceConfig?: {
|
|
127
|
-
headers?: IHeader[];
|
|
128
|
-
}
|
|
129
|
-
): Promise<IResponse<TRawData>> {
|
|
130
|
-
if (!queryConfig) {
|
|
131
|
-
queryConfig = {};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (!serviceConfig) {
|
|
135
|
-
serviceConfig = {};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
try {
|
|
139
|
-
return await this.httpService.postAsync<TRawData>(
|
|
140
|
-
{
|
|
141
|
-
url: url,
|
|
142
|
-
body: body
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
cancelToken: queryConfig?.cancelToken,
|
|
146
|
-
responseType: 'json',
|
|
147
|
-
retryStrategy: this.config.retryStrategy,
|
|
148
|
-
headers: this.getHeaders(queryConfig, serviceConfig.headers ? serviceConfig.headers : [])
|
|
149
|
-
}
|
|
150
|
-
);
|
|
151
|
-
} catch (error) {
|
|
152
|
-
throw this.mapDeliveryError(error);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Http GET response
|
|
158
|
-
* @param url Url of request
|
|
159
|
-
* @param queryConfig Query config configuration
|
|
160
|
-
*/
|
|
161
|
-
protected async getResponseAsync<TRawData>(
|
|
162
|
-
url: string,
|
|
163
|
-
queryConfig?: IQueryConfig,
|
|
164
|
-
serviceConfig?: {
|
|
165
|
-
headers?: IHeader[];
|
|
166
|
-
}
|
|
167
|
-
): Promise<IResponse<TRawData>> {
|
|
168
|
-
if (!queryConfig) {
|
|
169
|
-
queryConfig = {};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (!serviceConfig) {
|
|
173
|
-
serviceConfig = {};
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
try {
|
|
177
|
-
return await this.httpService.getAsync<TRawData>(
|
|
178
|
-
{
|
|
179
|
-
url: url
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
cancelToken: queryConfig?.cancelToken,
|
|
183
|
-
responseType: 'json',
|
|
184
|
-
retryStrategy: this.config.retryStrategy,
|
|
185
|
-
headers: this.getHeaders(queryConfig, serviceConfig.headers ? serviceConfig.headers : [])
|
|
186
|
-
}
|
|
187
|
-
);
|
|
188
|
-
} catch (error) {
|
|
189
|
-
throw this.mapDeliveryError(error);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Gets base URL of the request including the environment Id
|
|
195
|
-
* @param queryConfig Query configuration
|
|
196
|
-
*/
|
|
197
|
-
protected getBaseUrl(queryConfig: IQueryConfig): string {
|
|
198
|
-
return this.getDomain(queryConfig) + '/' + this.config.environmentId;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
protected mapNetworkResponse<TData, TContract>(
|
|
202
|
-
data: TData,
|
|
203
|
-
response: IResponse<any>
|
|
204
|
-
): IDeliveryNetworkResponse<TData, TContract> {
|
|
205
|
-
return {
|
|
206
|
-
data: data,
|
|
207
|
-
response: response,
|
|
208
|
-
hasStaleContent: this.getHasStaleContent(response.headers),
|
|
209
|
-
xContinuationToken: this.getContinuationToken(response.headers)
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Indicates if current query should use preview mode
|
|
215
|
-
* @param queryConfig Query configuration
|
|
216
|
-
*/
|
|
217
|
-
private isPreviewModeEnabled(queryConfig: IQueryConfig): boolean {
|
|
218
|
-
if (queryConfig.usePreviewMode !== undefined) {
|
|
219
|
-
return queryConfig.usePreviewMode;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
if (!this.config.defaultQueryConfig) {
|
|
223
|
-
return false;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (this.config.defaultQueryConfig.usePreviewMode === true) {
|
|
227
|
-
return true;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return false;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
private getQueryHeaders(queryConfig: IQueryConfig): IHeader[] {
|
|
234
|
-
if (queryConfig.customHeaders) {
|
|
235
|
-
return queryConfig.customHeaders;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (this.config.defaultQueryConfig?.customHeaders) {
|
|
239
|
-
return this.config.defaultQueryConfig.customHeaders;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
return [];
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
private shouldAddWaitForLoadingNewContentHeader(queryConfig: IQueryConfig): boolean {
|
|
246
|
-
if (queryConfig.waitForLoadingNewContent !== undefined) {
|
|
247
|
-
return queryConfig.waitForLoadingNewContent;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (!this.config.defaultQueryConfig) {
|
|
251
|
-
return false;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (this.config.defaultQueryConfig.waitForLoadingNewContent === true) {
|
|
255
|
-
return true;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return false;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Indicates if current query should use secured mode
|
|
263
|
-
* @param queryConfig Query configuration
|
|
264
|
-
*/
|
|
265
|
-
private isSecuredModeEnabled(queryConfig: IQueryConfig): boolean {
|
|
266
|
-
if (queryConfig.useSecuredMode !== undefined) {
|
|
267
|
-
return queryConfig.useSecuredMode;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (!this.config.defaultQueryConfig) {
|
|
271
|
-
return false;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (this.config.defaultQueryConfig.useSecuredMode === true) {
|
|
275
|
-
return true;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return false;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Gets preview or standard URL based on client and query configuration
|
|
283
|
-
* @param queryConfig Query configuration
|
|
284
|
-
*/
|
|
285
|
-
private getDomain(queryConfig: IQueryConfig): string {
|
|
286
|
-
if (this.isPreviewModeEnabled(queryConfig)) {
|
|
287
|
-
if (!this.config.previewApiKey) {
|
|
288
|
-
throw Error(`Preview API key is not configured.`);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// check custom preview url
|
|
292
|
-
if (this.config.proxy && this.config.proxy.basePreviewUrl) {
|
|
293
|
-
return this.config.proxy.basePreviewUrl;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// use default preview url
|
|
297
|
-
return this.defaultPreviewDeliveryApiUrl;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// check custom base url
|
|
301
|
-
if (this.config.proxy && this.config.proxy.baseUrl) {
|
|
302
|
-
return this.config.proxy.baseUrl;
|
|
303
|
-
}
|
|
304
|
-
return this.defaultBaseDeliveryApiUrl;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Gets authorization header. This is used for 'preview' functionality
|
|
309
|
-
*/
|
|
310
|
-
private getAuthorizationHeader(key?: string): IHeader {
|
|
311
|
-
if (!key) {
|
|
312
|
-
throw Error(`Cannot get authorization header because key is invalid`);
|
|
313
|
-
}
|
|
314
|
-
// authorization header required for preview mode
|
|
315
|
-
return {
|
|
316
|
-
header: 'authorization',
|
|
317
|
-
value: `bearer ${key}`
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Header identifying SDK type & version for internal purposes of Kontent.ai
|
|
323
|
-
*/
|
|
324
|
-
private getSdkIdHeader(): IHeader {
|
|
325
|
-
return {
|
|
326
|
-
header: sdkVersionHeader,
|
|
327
|
-
value: `${this.sdkInfo.host};${this.sdkInfo.name};${this.sdkInfo.version}`
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
private mapDeliveryError(error: any): any {
|
|
332
|
-
let axiosError: AxiosError | undefined;
|
|
333
|
-
|
|
334
|
-
if (error.error) {
|
|
335
|
-
axiosError = error.error;
|
|
336
|
-
} else {
|
|
337
|
-
axiosError = error;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
if (!axiosError || !axiosError.isAxiosError) {
|
|
341
|
-
return error;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const deliveryErrorData = axiosError.response?.data as IDeliveryErrorRaw;
|
|
345
|
-
|
|
346
|
-
if (!deliveryErrorData || !deliveryErrorData.error_code) {
|
|
347
|
-
return error;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
return new DeliveryError({
|
|
351
|
-
errorCode: deliveryErrorData.error_code,
|
|
352
|
-
message: deliveryErrorData.message,
|
|
353
|
-
specificCode: deliveryErrorData.specific_code,
|
|
354
|
-
requestId: deliveryErrorData.request_id
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
private getHasStaleContent(headers: IHeader[]): boolean {
|
|
359
|
-
const hasStaleContentHeader = headers.find(
|
|
360
|
-
(m) => m.header.toLowerCase() === staleContentHeaderName.toLowerCase()
|
|
361
|
-
);
|
|
362
|
-
|
|
363
|
-
if (hasStaleContentHeader) {
|
|
364
|
-
if (hasStaleContentHeader.value.toString() === '1') {
|
|
365
|
-
return true;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
return false;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
private getContinuationToken(headers: IHeader[]): string | undefined {
|
|
372
|
-
const header = headers.find((m) => m.header.toLowerCase() === continuationTokenHeaderName.toLowerCase());
|
|
373
|
-
return header ? header.value : undefined;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
1
|
+
import { IResponse, IHeader, IHttpService, IQueryParameter, urlHelper } from '@kontent-ai/core-sdk';
|
|
2
|
+
import { AxiosError } from 'axios';
|
|
3
|
+
import {
|
|
4
|
+
waitForLoadingNewContentHeader,
|
|
5
|
+
IQueryConfig,
|
|
6
|
+
ISDKInfo,
|
|
7
|
+
IDeliveryErrorRaw,
|
|
8
|
+
DeliveryError,
|
|
9
|
+
sdkVersionHeader,
|
|
10
|
+
staleContentHeaderName,
|
|
11
|
+
continuationTokenHeaderName,
|
|
12
|
+
IDeliveryNetworkResponse,
|
|
13
|
+
ClientTypes
|
|
14
|
+
} from '../models';
|
|
15
|
+
|
|
16
|
+
import { IDeliveryClientConfig } from '../config';
|
|
17
|
+
import { IMappingService } from './mapping.service';
|
|
18
|
+
|
|
19
|
+
export abstract class BaseDeliveryQueryService<TClientTypes extends ClientTypes> {
|
|
20
|
+
/**
|
|
21
|
+
* Default base Url to Kontent.ai Delivery API
|
|
22
|
+
*/
|
|
23
|
+
private readonly defaultBaseDeliveryApiUrl: string = 'https://deliver.kontent.ai';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Default preview url to Kontent.ai Delivery API
|
|
27
|
+
*/
|
|
28
|
+
private readonly defaultPreviewDeliveryApiUrl: string = 'https://preview-deliver.kontent.ai';
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
/**
|
|
32
|
+
* Delivery client configuration
|
|
33
|
+
*/
|
|
34
|
+
public readonly config: IDeliveryClientConfig,
|
|
35
|
+
/**
|
|
36
|
+
* Http service for fetching data
|
|
37
|
+
*/
|
|
38
|
+
public readonly httpService: IHttpService<any>,
|
|
39
|
+
/**
|
|
40
|
+
* Information about the SDK
|
|
41
|
+
*/
|
|
42
|
+
public readonly sdkInfo: ISDKInfo,
|
|
43
|
+
/**
|
|
44
|
+
* Mapping service
|
|
45
|
+
*/
|
|
46
|
+
public readonly mappingService: IMappingService<TClientTypes>
|
|
47
|
+
) {}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Gets url based on the action, query configuration and options (parameters)
|
|
51
|
+
* @param action Action (= url part) that will be hit
|
|
52
|
+
* @param queryConfig Query configuration
|
|
53
|
+
* @param options Query options
|
|
54
|
+
*/
|
|
55
|
+
getUrl(action: string, queryConfig: IQueryConfig, options?: IQueryParameter[]): string {
|
|
56
|
+
if (!this.config.proxy || !this.config.proxy.advancedProxyUrlResolver) {
|
|
57
|
+
return urlHelper.addOptionsToUrl(this.getBaseUrl(queryConfig) + action, options);
|
|
58
|
+
}
|
|
59
|
+
return this.config.proxy.advancedProxyUrlResolver({
|
|
60
|
+
queryParameters: options ? options : [],
|
|
61
|
+
queryString: urlHelper.addOptionsToUrl('', options),
|
|
62
|
+
action: action,
|
|
63
|
+
domain: this.getDomain(queryConfig),
|
|
64
|
+
queryConfig: queryConfig,
|
|
65
|
+
environmentId: this.config.environmentId
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Gets proper set of headers for given request.
|
|
71
|
+
* @param queryConfig Query configuration
|
|
72
|
+
* @param additionalHeaders Custom headers
|
|
73
|
+
*/
|
|
74
|
+
getHeaders(queryConfig: IQueryConfig, additionalHeaders?: IHeader[]): IHeader[] {
|
|
75
|
+
const headers: IHeader[] = [];
|
|
76
|
+
|
|
77
|
+
if (additionalHeaders) {
|
|
78
|
+
headers.push(...additionalHeaders);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// add SDK Id header for monitoring SDK usage
|
|
82
|
+
headers.push(this.getSdkIdHeader());
|
|
83
|
+
|
|
84
|
+
// add headers from global config
|
|
85
|
+
if (this.config.globalHeaders) {
|
|
86
|
+
headers.push(...this.config.globalHeaders(queryConfig));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// add query / global headers from query config
|
|
90
|
+
headers.push(...this.getQueryHeaders(queryConfig));
|
|
91
|
+
|
|
92
|
+
if (this.isPreviewModeEnabled(queryConfig) && this.isSecuredModeEnabled(queryConfig)) {
|
|
93
|
+
throw Error(`Preview & secured modes cannot be used at the same time.`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// add preview header is required
|
|
97
|
+
if (this.isPreviewModeEnabled(queryConfig) && this.config.previewApiKey) {
|
|
98
|
+
headers.push(this.getAuthorizationHeader(this.config.previewApiKey));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// add secured mode header is required
|
|
102
|
+
if (this.isSecuredModeEnabled(queryConfig) && this.config.secureApiKey) {
|
|
103
|
+
headers.push(this.getAuthorizationHeader(this.config.secureApiKey));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// add 'X-KC-Wait-For-Loading-New-Content' header if required
|
|
107
|
+
if (this.shouldAddWaitForLoadingNewContentHeader(queryConfig)) {
|
|
108
|
+
headers.push({
|
|
109
|
+
header: waitForLoadingNewContentHeader,
|
|
110
|
+
value: 'true'
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return headers;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Http POST response
|
|
119
|
+
* @param url Url of request
|
|
120
|
+
* @param queryConfig Query config configuration
|
|
121
|
+
*/
|
|
122
|
+
protected async postResponseAsync<TRawData>(
|
|
123
|
+
url: string,
|
|
124
|
+
body: any,
|
|
125
|
+
queryConfig?: IQueryConfig,
|
|
126
|
+
serviceConfig?: {
|
|
127
|
+
headers?: IHeader[];
|
|
128
|
+
}
|
|
129
|
+
): Promise<IResponse<TRawData>> {
|
|
130
|
+
if (!queryConfig) {
|
|
131
|
+
queryConfig = {};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!serviceConfig) {
|
|
135
|
+
serviceConfig = {};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
return await this.httpService.postAsync<TRawData>(
|
|
140
|
+
{
|
|
141
|
+
url: url,
|
|
142
|
+
body: body
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
cancelToken: queryConfig?.cancelToken,
|
|
146
|
+
responseType: 'json',
|
|
147
|
+
retryStrategy: this.config.retryStrategy,
|
|
148
|
+
headers: this.getHeaders(queryConfig, serviceConfig.headers ? serviceConfig.headers : [])
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
throw this.mapDeliveryError(error);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Http GET response
|
|
158
|
+
* @param url Url of request
|
|
159
|
+
* @param queryConfig Query config configuration
|
|
160
|
+
*/
|
|
161
|
+
protected async getResponseAsync<TRawData>(
|
|
162
|
+
url: string,
|
|
163
|
+
queryConfig?: IQueryConfig,
|
|
164
|
+
serviceConfig?: {
|
|
165
|
+
headers?: IHeader[];
|
|
166
|
+
}
|
|
167
|
+
): Promise<IResponse<TRawData>> {
|
|
168
|
+
if (!queryConfig) {
|
|
169
|
+
queryConfig = {};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!serviceConfig) {
|
|
173
|
+
serviceConfig = {};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
return await this.httpService.getAsync<TRawData>(
|
|
178
|
+
{
|
|
179
|
+
url: url
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
cancelToken: queryConfig?.cancelToken,
|
|
183
|
+
responseType: 'json',
|
|
184
|
+
retryStrategy: this.config.retryStrategy,
|
|
185
|
+
headers: this.getHeaders(queryConfig, serviceConfig.headers ? serviceConfig.headers : [])
|
|
186
|
+
}
|
|
187
|
+
);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
throw this.mapDeliveryError(error);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Gets base URL of the request including the environment Id
|
|
195
|
+
* @param queryConfig Query configuration
|
|
196
|
+
*/
|
|
197
|
+
protected getBaseUrl(queryConfig: IQueryConfig): string {
|
|
198
|
+
return this.getDomain(queryConfig) + '/' + this.config.environmentId;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
protected mapNetworkResponse<TData, TContract>(
|
|
202
|
+
data: TData,
|
|
203
|
+
response: IResponse<any>
|
|
204
|
+
): IDeliveryNetworkResponse<TData, TContract> {
|
|
205
|
+
return {
|
|
206
|
+
data: data,
|
|
207
|
+
response: response,
|
|
208
|
+
hasStaleContent: this.getHasStaleContent(response.headers),
|
|
209
|
+
xContinuationToken: this.getContinuationToken(response.headers)
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Indicates if current query should use preview mode
|
|
215
|
+
* @param queryConfig Query configuration
|
|
216
|
+
*/
|
|
217
|
+
private isPreviewModeEnabled(queryConfig: IQueryConfig): boolean {
|
|
218
|
+
if (queryConfig.usePreviewMode !== undefined) {
|
|
219
|
+
return queryConfig.usePreviewMode;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (!this.config.defaultQueryConfig) {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (this.config.defaultQueryConfig.usePreviewMode === true) {
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private getQueryHeaders(queryConfig: IQueryConfig): IHeader[] {
|
|
234
|
+
if (queryConfig.customHeaders) {
|
|
235
|
+
return queryConfig.customHeaders;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (this.config.defaultQueryConfig?.customHeaders) {
|
|
239
|
+
return this.config.defaultQueryConfig.customHeaders;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return [];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private shouldAddWaitForLoadingNewContentHeader(queryConfig: IQueryConfig): boolean {
|
|
246
|
+
if (queryConfig.waitForLoadingNewContent !== undefined) {
|
|
247
|
+
return queryConfig.waitForLoadingNewContent;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (!this.config.defaultQueryConfig) {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (this.config.defaultQueryConfig.waitForLoadingNewContent === true) {
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Indicates if current query should use secured mode
|
|
263
|
+
* @param queryConfig Query configuration
|
|
264
|
+
*/
|
|
265
|
+
private isSecuredModeEnabled(queryConfig: IQueryConfig): boolean {
|
|
266
|
+
if (queryConfig.useSecuredMode !== undefined) {
|
|
267
|
+
return queryConfig.useSecuredMode;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (!this.config.defaultQueryConfig) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (this.config.defaultQueryConfig.useSecuredMode === true) {
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Gets preview or standard URL based on client and query configuration
|
|
283
|
+
* @param queryConfig Query configuration
|
|
284
|
+
*/
|
|
285
|
+
private getDomain(queryConfig: IQueryConfig): string {
|
|
286
|
+
if (this.isPreviewModeEnabled(queryConfig)) {
|
|
287
|
+
if (!this.config.previewApiKey) {
|
|
288
|
+
throw Error(`Preview API key is not configured.`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// check custom preview url
|
|
292
|
+
if (this.config.proxy && this.config.proxy.basePreviewUrl) {
|
|
293
|
+
return this.config.proxy.basePreviewUrl;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// use default preview url
|
|
297
|
+
return this.defaultPreviewDeliveryApiUrl;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// check custom base url
|
|
301
|
+
if (this.config.proxy && this.config.proxy.baseUrl) {
|
|
302
|
+
return this.config.proxy.baseUrl;
|
|
303
|
+
}
|
|
304
|
+
return this.defaultBaseDeliveryApiUrl;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Gets authorization header. This is used for 'preview' functionality
|
|
309
|
+
*/
|
|
310
|
+
private getAuthorizationHeader(key?: string): IHeader {
|
|
311
|
+
if (!key) {
|
|
312
|
+
throw Error(`Cannot get authorization header because key is invalid`);
|
|
313
|
+
}
|
|
314
|
+
// authorization header required for preview mode
|
|
315
|
+
return {
|
|
316
|
+
header: 'authorization',
|
|
317
|
+
value: `bearer ${key}`
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Header identifying SDK type & version for internal purposes of Kontent.ai
|
|
323
|
+
*/
|
|
324
|
+
private getSdkIdHeader(): IHeader {
|
|
325
|
+
return {
|
|
326
|
+
header: sdkVersionHeader,
|
|
327
|
+
value: `${this.sdkInfo.host};${this.sdkInfo.name};${this.sdkInfo.version}`
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
private mapDeliveryError(error: any): any {
|
|
332
|
+
let axiosError: AxiosError | undefined;
|
|
333
|
+
|
|
334
|
+
if (error.error) {
|
|
335
|
+
axiosError = error.error;
|
|
336
|
+
} else {
|
|
337
|
+
axiosError = error;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (!axiosError || !axiosError.isAxiosError) {
|
|
341
|
+
return error;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const deliveryErrorData = axiosError.response?.data as IDeliveryErrorRaw;
|
|
345
|
+
|
|
346
|
+
if (!deliveryErrorData || !deliveryErrorData.error_code) {
|
|
347
|
+
return error;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return new DeliveryError({
|
|
351
|
+
errorCode: deliveryErrorData.error_code,
|
|
352
|
+
message: deliveryErrorData.message,
|
|
353
|
+
specificCode: deliveryErrorData.specific_code,
|
|
354
|
+
requestId: deliveryErrorData.request_id
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
private getHasStaleContent(headers: IHeader[]): boolean {
|
|
359
|
+
const hasStaleContentHeader = headers.find(
|
|
360
|
+
(m) => m.header.toLowerCase() === staleContentHeaderName.toLowerCase()
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
if (hasStaleContentHeader) {
|
|
364
|
+
if (hasStaleContentHeader.value.toString() === '1') {
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
private getContinuationToken(headers: IHeader[]): string | undefined {
|
|
372
|
+
const header = headers.find((m) => m.header.toLowerCase() === continuationTokenHeaderName.toLowerCase());
|
|
373
|
+
return header ? header.value : undefined;
|
|
374
|
+
}
|
|
375
|
+
}
|