@kontent-ai/core-sdk 12.0.0-preview.8 → 12.0.0-preview.9
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/README.md +4 -3
- package/dist/http/http.adapter.js +5 -8
- package/dist/http/http.adapter.js.map +1 -1
- package/dist/http/http.models.d.ts +28 -10
- package/dist/http/http.service.js +238 -198
- package/dist/http/http.service.js.map +1 -1
- package/dist/models/core.models.d.ts +9 -17
- package/dist/models/error.models.d.ts +11 -12
- package/dist/models/error.models.js +6 -5
- package/dist/models/error.models.js.map +1 -1
- package/dist/models/json.models.d.ts +2 -2
- package/dist/models/utility.models.d.ts +8 -0
- package/dist/public_api.d.ts +5 -5
- package/dist/public_api.js +2 -2
- package/dist/public_api.js.map +1 -1
- package/dist/sdk/sdk-models.d.ts +15 -16
- package/dist/sdk/sdk-queries.d.ts +32 -15
- package/dist/sdk/sdk-queries.js +116 -36
- package/dist/sdk/sdk-queries.js.map +1 -1
- package/dist/sdk-info.js +1 -1
- package/dist/testkit/testkit.utils.d.ts +2 -1
- package/dist/testkit/testkit.utils.js +24 -11
- package/dist/testkit/testkit.utils.js.map +1 -1
- package/dist/utils/core.utils.d.ts +1 -0
- package/dist/utils/core.utils.js +15 -0
- package/dist/utils/core.utils.js.map +1 -1
- package/dist/utils/error.utils.d.ts +10 -5
- package/dist/utils/error.utils.js +19 -4
- package/dist/utils/error.utils.js.map +1 -1
- package/dist/utils/header.utils.d.ts +1 -0
- package/dist/utils/header.utils.js +6 -0
- package/dist/utils/header.utils.js.map +1 -1
- package/dist/utils/retry.utils.d.ts +4 -5
- package/dist/utils/retry.utils.js +33 -30
- package/dist/utils/retry.utils.js.map +1 -1
- package/lib/http/http.adapter.ts +5 -10
- package/lib/http/http.models.ts +36 -12
- package/lib/http/http.service.ts +356 -256
- package/lib/models/core.models.ts +9 -19
- package/lib/models/error.models.ts +30 -18
- package/lib/models/json.models.ts +2 -2
- package/lib/models/utility.models.ts +10 -0
- package/lib/public_api.ts +13 -4
- package/lib/sdk/sdk-models.ts +17 -17
- package/lib/sdk/sdk-queries.ts +234 -68
- package/lib/testkit/testkit.utils.ts +30 -13
- package/lib/utils/core.utils.ts +21 -0
- package/lib/utils/error.utils.ts +31 -10
- package/lib/utils/header.utils.ts +9 -0
- package/lib/utils/retry.utils.ts +51 -50
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -25,13 +25,13 @@ These implementations are designed to work out-of-the-box but are also fully cus
|
|
|
25
25
|
The `HttpService` comes with several built-in capabilities, such as:
|
|
26
26
|
|
|
27
27
|
- **Retry policies**
|
|
28
|
-
- **Request validation**
|
|
28
|
+
- **Request parsing and validation** (URL parsing, body serialization)
|
|
29
29
|
- **Automatic header and tracking management**
|
|
30
30
|
- **Kontent.ai-specific error extraction**
|
|
31
31
|
|
|
32
32
|
To customize these behaviors entirely, you can replace the `HttpService` with your own implementation.
|
|
33
33
|
|
|
34
|
-
However, if your goal is to retain the core features (e.g., retry policies, request
|
|
34
|
+
However, if your goal is to retain the core features (e.g., retry policies, request parsing) and only swap out the underlying HTTP client, you can do so by supplying a custom `HttpAdapter` to the `getDefaultHttpService` method.
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
@@ -40,7 +40,7 @@ However, if your goal is to retain the core features (e.g., retry policies, requ
|
|
|
40
40
|
Below is an example demonstrating how to provide your own HTTP client by implementing a custom `HttpAdapter`:
|
|
41
41
|
|
|
42
42
|
```typescript
|
|
43
|
-
const httpService =
|
|
43
|
+
const httpService = getDefaultHttpService({
|
|
44
44
|
adapter: {
|
|
45
45
|
requestAsync: async (options) => {
|
|
46
46
|
// Execute the request using your custom HTTP client
|
|
@@ -49,6 +49,7 @@ const httpService = await getDefaultHttpService({
|
|
|
49
49
|
responseHeaders: <arrayOfHeaders>,
|
|
50
50
|
status: <statusCode>,
|
|
51
51
|
statusText: <statusText>,
|
|
52
|
+
url: options.url,
|
|
52
53
|
toJsonAsync: async () => <responseInJson>,
|
|
53
54
|
toBlobAsync: async () => <responseInBlob>,
|
|
54
55
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { toFetchHeaders, toSdkHeaders } from "../utils/header.utils.js";
|
|
1
|
+
import { isApplicationJsonResponseType, toFetchHeaders, toSdkHeaders } from "../utils/header.utils.js";
|
|
2
2
|
export function getDefaultHttpAdapter() {
|
|
3
3
|
return {
|
|
4
4
|
requestAsync: async (options) => {
|
|
@@ -7,19 +7,16 @@ export function getDefaultHttpAdapter() {
|
|
|
7
7
|
headers: toFetchHeaders(options.requestHeaders ?? []),
|
|
8
8
|
body: options.body,
|
|
9
9
|
});
|
|
10
|
-
const
|
|
10
|
+
const sdkHeaders = toSdkHeaders(response.headers);
|
|
11
11
|
return {
|
|
12
12
|
isValidResponse: response.ok,
|
|
13
|
-
responseHeaders:
|
|
13
|
+
responseHeaders: sdkHeaders,
|
|
14
14
|
status: response.status,
|
|
15
15
|
statusText: response.statusText,
|
|
16
|
+
url: options.url,
|
|
16
17
|
toBlobAsync: async () => await response.blob(),
|
|
17
18
|
toJsonAsync: async () => {
|
|
18
|
-
|
|
19
|
-
.find((m) => m.name.toLowerCase() === "Content-Type".toLowerCase())
|
|
20
|
-
?.value.toLowerCase();
|
|
21
|
-
if (contentTypeResponseHeader?.includes("application/json")) {
|
|
22
|
-
// Includes instead of equal due to the fact that the header value can be 'application/json; charset=utf-8' or similar
|
|
19
|
+
if (isApplicationJsonResponseType(sdkHeaders)) {
|
|
23
20
|
return (await response.json());
|
|
24
21
|
}
|
|
25
22
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.adapter.js","sourceRoot":"","sources":["../../lib/http/http.adapter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"http.adapter.js","sourceRoot":"","sources":["../../lib/http/http.adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,6BAA6B,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGvG,MAAM,UAAU,qBAAqB;IACpC,OAAO;QACN,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YAC/B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE;gBACzC,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,cAAc,IAAI,EAAE,CAAC;gBACrD,IAAI,EAAE,OAAO,CAAC,IAAI;aAClB,CAAC,CAAC;YAEH,MAAM,UAAU,GAAG,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAElD,OAAO;gBACN,eAAe,EAAE,QAAQ,CAAC,EAAE;gBAC5B,eAAe,EAAE,UAAU;gBAC3B,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;gBAC/B,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE;gBAC9C,WAAW,EAAE,KAAK,IAAI,EAAE;oBACvB,IAAI,6BAA6B,CAAC,UAAU,CAAC,EAAE,CAAC;wBAC/C,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAc,CAAC;oBAC7C,CAAC;oBAED,OAAO,IAAI,CAAC;gBACb,CAAC;aACD,CAAC;QACH,CAAC;KACD,CAAC;AACH,CAAC"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { Header, HttpMethod,
|
|
2
|
-
import type {
|
|
3
|
-
import type { JsonValue } from "../models/json.models.js";
|
|
1
|
+
import type { Header, HttpMethod, RetryStrategyOptions } from "../models/core.models.js";
|
|
2
|
+
import type { KontentSdkError } from "../models/error.models.js";
|
|
3
|
+
import type { JsonObject, JsonValue } from "../models/json.models.js";
|
|
4
|
+
import type { LiteralUnionNumber } from "../models/utility.models.js";
|
|
5
|
+
import type { QueryResponse } from "../sdk/sdk-models.js";
|
|
4
6
|
type Success<TData> = {
|
|
5
7
|
readonly success: true;
|
|
6
8
|
readonly response: TData;
|
|
@@ -9,7 +11,7 @@ type Success<TData> = {
|
|
|
9
11
|
type Failure = {
|
|
10
12
|
readonly success: false;
|
|
11
13
|
readonly response?: never;
|
|
12
|
-
readonly error:
|
|
14
|
+
readonly error: KontentSdkError;
|
|
13
15
|
};
|
|
14
16
|
export type HttpResult<TData> = Success<TData> | Failure;
|
|
15
17
|
/**
|
|
@@ -31,17 +33,20 @@ export type DefaultHttpServiceConfig = {
|
|
|
31
33
|
*/
|
|
32
34
|
readonly adapter?: HttpAdapter;
|
|
33
35
|
};
|
|
34
|
-
export type
|
|
36
|
+
export type ResponseData = JsonValue | Blob;
|
|
37
|
+
export type RequestBody = JsonObject | Blob | null;
|
|
38
|
+
export type BodyData = RequestBody;
|
|
39
|
+
export type HttpResponse<TResponseData extends ResponseData, TRequestBody extends RequestBody> = HttpResult<{
|
|
35
40
|
readonly data: TResponseData;
|
|
36
|
-
readonly body:
|
|
41
|
+
readonly body: TRequestBody;
|
|
37
42
|
readonly method: HttpMethod;
|
|
38
43
|
readonly requestHeaders: readonly Header[];
|
|
39
44
|
readonly adapterResponse: Omit<AdapterResponse, "toJsonAsync" | "toBlobAsync">;
|
|
40
45
|
}>;
|
|
41
|
-
export type ExecuteRequestOptions<
|
|
46
|
+
export type ExecuteRequestOptions<TRequestBody extends RequestBody> = {
|
|
42
47
|
readonly url: string;
|
|
43
48
|
readonly method: HttpMethod;
|
|
44
|
-
readonly body:
|
|
49
|
+
readonly body: TRequestBody;
|
|
45
50
|
readonly requestHeaders?: readonly Header[];
|
|
46
51
|
};
|
|
47
52
|
export type UploadFileRequestOptions = Omit<ExecuteRequestOptions<Blob>, "method"> & {
|
|
@@ -62,7 +67,7 @@ export type HttpService = {
|
|
|
62
67
|
/**
|
|
63
68
|
* Executes request with the given method and body.
|
|
64
69
|
*/
|
|
65
|
-
requestAsync<TResponseData extends JsonValue,
|
|
70
|
+
requestAsync<TResponseData extends JsonValue, TRequestBody extends RequestBody>(opts: ExecuteRequestOptions<TRequestBody>): Promise<HttpResponse<TResponseData, TRequestBody>>;
|
|
66
71
|
/**
|
|
67
72
|
* Downloads a file from the given URL as a blob.
|
|
68
73
|
*/
|
|
@@ -79,13 +84,26 @@ export type AdapterResponse<TStatusCode extends HttpServiceStatus = HttpServiceS
|
|
|
79
84
|
readonly responseHeaders: readonly Header[];
|
|
80
85
|
readonly status: TStatusCode;
|
|
81
86
|
readonly statusText: string;
|
|
87
|
+
readonly url: string;
|
|
82
88
|
};
|
|
89
|
+
export type AdapterRequestBody = string | Blob | null;
|
|
83
90
|
export type AdapterRequestOptions = {
|
|
84
91
|
readonly url: string;
|
|
85
92
|
readonly method: HttpMethod;
|
|
86
|
-
readonly body:
|
|
93
|
+
readonly body: AdapterRequestBody;
|
|
87
94
|
readonly requestHeaders?: readonly Header[];
|
|
88
95
|
};
|
|
96
|
+
export type GetNextPageData<TResponseData extends JsonValue, TMeta> = (response: QueryResponse<TResponseData, TMeta>) => {
|
|
97
|
+
readonly continuationToken?: string;
|
|
98
|
+
readonly nextPageUrl?: string;
|
|
99
|
+
};
|
|
100
|
+
export type PaginationConfig = {
|
|
101
|
+
readonly maxPagesCount?: number;
|
|
102
|
+
};
|
|
103
|
+
export type Pagination<TResponseData extends JsonValue, TMeta> = {
|
|
104
|
+
readonly getNextPageData: GetNextPageData<TResponseData, TMeta>;
|
|
105
|
+
readonly config?: PaginationConfig;
|
|
106
|
+
};
|
|
89
107
|
/**
|
|
90
108
|
* Defines the adapter responsible solely for executing HTTP requests.
|
|
91
109
|
*
|
|
@@ -1,183 +1,16 @@
|
|
|
1
|
+
import { match, P } from "ts-pattern";
|
|
1
2
|
import { sdkInfo } from "../sdk-info.js";
|
|
2
|
-
import { isNotUndefined } from "../utils/core.utils.js";
|
|
3
|
-
import { createSdkError, getErrorMessage } from "../utils/error.utils.js";
|
|
4
|
-
import { getSdkIdHeader } from "../utils/header.utils.js";
|
|
3
|
+
import { isBlob, isNotUndefined } from "../utils/core.utils.js";
|
|
4
|
+
import { createSdkError, getErrorMessage, isKontentErrorResponseData } from "../utils/error.utils.js";
|
|
5
|
+
import { getSdkIdHeader, isApplicationJsonResponseType } from "../utils/header.utils.js";
|
|
5
6
|
import { runWithRetryAsync, toRequiredRetryStrategyOptions } from "../utils/retry.utils.js";
|
|
6
7
|
import { tryCatch, tryCatchAsync } from "../utils/try.utils.js";
|
|
7
8
|
import { getDefaultHttpAdapter } from "./http.adapter.js";
|
|
8
9
|
export function getDefaultHttpService(config) {
|
|
9
|
-
const withUnknownErrorHandlingAsync = async ({ url, funcAsync, }) => {
|
|
10
|
-
const { success, data, error } = await tryCatchAsync(funcAsync);
|
|
11
|
-
if (success) {
|
|
12
|
-
return data;
|
|
13
|
-
}
|
|
14
|
-
return {
|
|
15
|
-
success: false,
|
|
16
|
-
error: createSdkError({
|
|
17
|
-
message: "Unknown error. See the error object for more details.",
|
|
18
|
-
url: url,
|
|
19
|
-
reason: "unknown",
|
|
20
|
-
originalError: error,
|
|
21
|
-
}),
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
const resolveRequestAsync = async ({ options, resolveDataAsync, }) => {
|
|
25
|
-
return await withUnknownErrorHandlingAsync({
|
|
26
|
-
url: options.url,
|
|
27
|
-
funcAsync: async () => {
|
|
28
|
-
const adapter = config?.adapter ?? getDefaultHttpAdapter();
|
|
29
|
-
const getCombinedRequestHeaders = () => {
|
|
30
|
-
return getRequestHeaders([...(config?.requestHeaders ?? []), ...(options.requestHeaders ?? [])], options.body);
|
|
31
|
-
};
|
|
32
|
-
const getRequestBody = () => {
|
|
33
|
-
if (options.body === null) {
|
|
34
|
-
return {
|
|
35
|
-
success: true,
|
|
36
|
-
data: null,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
if (options.body instanceof Blob) {
|
|
40
|
-
return {
|
|
41
|
-
success: true,
|
|
42
|
-
data: options.body,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
const { success, data: parsedBody, error } = tryCatch(() => JSON.stringify(options.body));
|
|
46
|
-
if (!success) {
|
|
47
|
-
return {
|
|
48
|
-
success: false,
|
|
49
|
-
error: createSdkError({
|
|
50
|
-
message: "Failed to stringify body of request.",
|
|
51
|
-
url: options.url,
|
|
52
|
-
reason: "invalidBody",
|
|
53
|
-
originalError: error,
|
|
54
|
-
}),
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
return {
|
|
58
|
-
success: true,
|
|
59
|
-
data: parsedBody,
|
|
60
|
-
};
|
|
61
|
-
};
|
|
62
|
-
const getUrl = () => {
|
|
63
|
-
const { success, data: parsedUrl, error } = tryCatch(() => new URL(options.url));
|
|
64
|
-
if (!success) {
|
|
65
|
-
return {
|
|
66
|
-
success: false,
|
|
67
|
-
error: createSdkError({
|
|
68
|
-
message: `Failed to parse url '${options.url}'.`,
|
|
69
|
-
url: options.url,
|
|
70
|
-
reason: "invalidUrl",
|
|
71
|
-
originalError: error,
|
|
72
|
-
}),
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
return {
|
|
76
|
-
success: true,
|
|
77
|
-
data: parsedUrl,
|
|
78
|
-
};
|
|
79
|
-
};
|
|
80
|
-
const requestHeaders = getCombinedRequestHeaders();
|
|
81
|
-
const retryStrategyOptions = toRequiredRetryStrategyOptions(config?.retryStrategy);
|
|
82
|
-
const withRetryAsync = async (funcAsync) => {
|
|
83
|
-
return await runWithRetryAsync({
|
|
84
|
-
url: options.url,
|
|
85
|
-
retryStrategyOptions,
|
|
86
|
-
retryAttempt: 0,
|
|
87
|
-
requestHeaders,
|
|
88
|
-
method: options.method,
|
|
89
|
-
funcAsync: async () => {
|
|
90
|
-
return await funcAsync();
|
|
91
|
-
},
|
|
92
|
-
});
|
|
93
|
-
};
|
|
94
|
-
const { success: urlParsedSuccess, data: parsedUrl, error: urlError } = getUrl();
|
|
95
|
-
if (!urlParsedSuccess) {
|
|
96
|
-
return {
|
|
97
|
-
success: false,
|
|
98
|
-
error: urlError,
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
const { success: requestBodyParsedSuccess, data: requestBody, error: requestBodyError } = getRequestBody();
|
|
102
|
-
if (!requestBodyParsedSuccess) {
|
|
103
|
-
return {
|
|
104
|
-
success: false,
|
|
105
|
-
error: requestBodyError,
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
const getResponseAsync = async () => {
|
|
109
|
-
return await adapter.requestAsync({
|
|
110
|
-
url: parsedUrl.toString(),
|
|
111
|
-
method: options.method,
|
|
112
|
-
requestHeaders,
|
|
113
|
-
body: requestBody,
|
|
114
|
-
});
|
|
115
|
-
};
|
|
116
|
-
const getErrorDetailsForInvalidResponseAsync = async (response) => {
|
|
117
|
-
const sharedErrorData = {
|
|
118
|
-
message: getErrorMessage({
|
|
119
|
-
url: options.url,
|
|
120
|
-
adapterResponse: response,
|
|
121
|
-
method: options.method,
|
|
122
|
-
}),
|
|
123
|
-
url: options.url,
|
|
124
|
-
};
|
|
125
|
-
if (response.status === 404) {
|
|
126
|
-
const error404 = {
|
|
127
|
-
...sharedErrorData,
|
|
128
|
-
reason: "notFound",
|
|
129
|
-
isValidResponse: response.isValidResponse,
|
|
130
|
-
responseHeaders: response.responseHeaders,
|
|
131
|
-
status: 404,
|
|
132
|
-
statusText: response.statusText,
|
|
133
|
-
kontentErrorResponse: await getKontentErrorDataAsync(response),
|
|
134
|
-
};
|
|
135
|
-
return error404;
|
|
136
|
-
}
|
|
137
|
-
const error = {
|
|
138
|
-
...sharedErrorData,
|
|
139
|
-
reason: "invalidResponse",
|
|
140
|
-
isValidResponse: response.isValidResponse,
|
|
141
|
-
responseHeaders: response.responseHeaders,
|
|
142
|
-
status: response.status,
|
|
143
|
-
statusText: response.statusText,
|
|
144
|
-
kontentErrorResponse: await getKontentErrorDataAsync(response),
|
|
145
|
-
};
|
|
146
|
-
return error;
|
|
147
|
-
};
|
|
148
|
-
const getErrorForInvalidResponseAsync = async (response) => {
|
|
149
|
-
return createSdkError(await getErrorDetailsForInvalidResponseAsync(response));
|
|
150
|
-
};
|
|
151
|
-
const resolveResponseAsync = async (response) => {
|
|
152
|
-
if (!response.isValidResponse) {
|
|
153
|
-
return {
|
|
154
|
-
success: false,
|
|
155
|
-
error: await getErrorForInvalidResponseAsync(response),
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
return {
|
|
159
|
-
success: true,
|
|
160
|
-
response: {
|
|
161
|
-
data: await resolveDataAsync(response),
|
|
162
|
-
body: options.body,
|
|
163
|
-
method: options.method,
|
|
164
|
-
adapterResponse: {
|
|
165
|
-
isValidResponse: response.isValidResponse,
|
|
166
|
-
responseHeaders: response.responseHeaders,
|
|
167
|
-
status: response.status,
|
|
168
|
-
statusText: response.statusText,
|
|
169
|
-
},
|
|
170
|
-
requestHeaders: requestHeaders,
|
|
171
|
-
},
|
|
172
|
-
};
|
|
173
|
-
};
|
|
174
|
-
return await withRetryAsync(async () => await resolveResponseAsync(await getResponseAsync()));
|
|
175
|
-
},
|
|
176
|
-
});
|
|
177
|
-
};
|
|
178
10
|
return {
|
|
179
11
|
requestAsync: async (options) => {
|
|
180
12
|
return await resolveRequestAsync({
|
|
13
|
+
config,
|
|
181
14
|
options,
|
|
182
15
|
resolveDataAsync: async (response) => {
|
|
183
16
|
return (await response.toJsonAsync());
|
|
@@ -186,6 +19,7 @@ export function getDefaultHttpService(config) {
|
|
|
186
19
|
},
|
|
187
20
|
downloadFileAsync: async (options) => {
|
|
188
21
|
return await resolveRequestAsync({
|
|
22
|
+
config,
|
|
189
23
|
options: {
|
|
190
24
|
...options,
|
|
191
25
|
method: "GET",
|
|
@@ -198,6 +32,7 @@ export function getDefaultHttpService(config) {
|
|
|
198
32
|
},
|
|
199
33
|
uploadFileAsync: async (options) => {
|
|
200
34
|
return await resolveRequestAsync({
|
|
35
|
+
config,
|
|
201
36
|
options,
|
|
202
37
|
resolveDataAsync: async (response) => {
|
|
203
38
|
return (await response.toJsonAsync());
|
|
@@ -206,32 +41,224 @@ export function getDefaultHttpService(config) {
|
|
|
206
41
|
},
|
|
207
42
|
};
|
|
208
43
|
}
|
|
209
|
-
async function
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
44
|
+
async function resolveRequestAsync({ options, resolveDataAsync, config, }) {
|
|
45
|
+
return await withUnknownErrorHandlingAsync({
|
|
46
|
+
url: options.url,
|
|
47
|
+
funcAsync: async () => {
|
|
48
|
+
const { success: parseSuccess, data: parsedRequest, error: parseError } = parseAndValidateRequest(options);
|
|
49
|
+
if (!parseSuccess) {
|
|
50
|
+
return {
|
|
51
|
+
success: false,
|
|
52
|
+
error: parseError,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const requestHeaders = buildRequestHeaders({
|
|
56
|
+
configHeaders: config?.requestHeaders,
|
|
57
|
+
optionHeaders: options.requestHeaders,
|
|
58
|
+
body: options.body,
|
|
59
|
+
});
|
|
60
|
+
return await withRetryAsync({
|
|
61
|
+
funcAsync: async () => {
|
|
62
|
+
const adapterResponse = await executeWithAdapter({
|
|
63
|
+
adapter: config?.adapter ?? getDefaultHttpAdapter(),
|
|
64
|
+
parsedUrl: parsedRequest.parsedUrl,
|
|
65
|
+
method: options.method,
|
|
66
|
+
requestHeaders,
|
|
67
|
+
parsedBody: parsedRequest.parsedBody,
|
|
68
|
+
});
|
|
69
|
+
return await resolveResponseAsync({
|
|
70
|
+
method: options.method,
|
|
71
|
+
requestBody: options.body,
|
|
72
|
+
requestHeaders,
|
|
73
|
+
response: adapterResponse,
|
|
74
|
+
resolveDataAsync,
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
url: options.url,
|
|
78
|
+
retryStrategyOptions: toRequiredRetryStrategyOptions(config?.retryStrategy),
|
|
79
|
+
requestHeaders,
|
|
80
|
+
method: options.method,
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
async function withUnknownErrorHandlingAsync({ url, funcAsync, }) {
|
|
86
|
+
const { success, data, error } = await tryCatchAsync(funcAsync);
|
|
87
|
+
if (success) {
|
|
88
|
+
return data;
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
success: false,
|
|
92
|
+
error: createSdkError({
|
|
93
|
+
message: "Unknown error. See the error object for more details.",
|
|
94
|
+
url: url,
|
|
95
|
+
reason: "unknown",
|
|
96
|
+
originalError: error,
|
|
97
|
+
}),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
async function resolveResponseAsync({ response, resolveDataAsync, method, requestHeaders, requestBody, }) {
|
|
101
|
+
if (!response.isValidResponse) {
|
|
102
|
+
return {
|
|
103
|
+
success: false,
|
|
104
|
+
error: await getErrorForInvalidResponseAsync(response, method),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
success: true,
|
|
109
|
+
response: {
|
|
110
|
+
data: await resolveDataAsync(response),
|
|
111
|
+
body: requestBody,
|
|
112
|
+
method: method,
|
|
113
|
+
adapterResponse: {
|
|
114
|
+
url: response.url,
|
|
115
|
+
isValidResponse: response.isValidResponse,
|
|
116
|
+
responseHeaders: response.responseHeaders,
|
|
117
|
+
status: response.status,
|
|
118
|
+
statusText: response.statusText,
|
|
119
|
+
},
|
|
120
|
+
requestHeaders: requestHeaders,
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
async function executeWithAdapter({ adapter, parsedUrl, method, requestHeaders, parsedBody, }) {
|
|
125
|
+
return await adapter.requestAsync({
|
|
126
|
+
url: parsedUrl.toString(),
|
|
127
|
+
method,
|
|
128
|
+
requestHeaders,
|
|
129
|
+
body: parsedBody,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
async function withRetryAsync({ funcAsync, url, retryStrategyOptions, requestHeaders, method, }) {
|
|
133
|
+
return await runWithRetryAsync({
|
|
134
|
+
url: url,
|
|
135
|
+
retryStrategyOptions,
|
|
136
|
+
retryAttempt: 0,
|
|
137
|
+
requestHeaders,
|
|
138
|
+
method: method,
|
|
139
|
+
funcAsync: async () => {
|
|
140
|
+
return await funcAsync();
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
async function getErrorForInvalidResponseAsync(response, method) {
|
|
145
|
+
return createSdkError(await getErrorDetailsForInvalidResponseAsync(response, method));
|
|
146
|
+
}
|
|
147
|
+
async function getErrorDetailsForInvalidResponseAsync(response, method) {
|
|
148
|
+
const sharedErrorData = {
|
|
149
|
+
message: getErrorMessage({
|
|
150
|
+
url: response.url,
|
|
151
|
+
adapterResponse: response,
|
|
152
|
+
method: method,
|
|
153
|
+
}),
|
|
154
|
+
url: response.url,
|
|
155
|
+
};
|
|
156
|
+
return await match(response)
|
|
157
|
+
.returnType()
|
|
158
|
+
.with({ status: P.union(401, 404) }, async (m) => ({
|
|
159
|
+
...sharedErrorData,
|
|
160
|
+
reason: m.status === 401 ? "unauthorized" : "notFound",
|
|
161
|
+
isValidResponse: m.isValidResponse,
|
|
162
|
+
responseHeaders: m.responseHeaders,
|
|
163
|
+
status: m.status,
|
|
164
|
+
statusText: m.statusText,
|
|
165
|
+
kontentErrorResponse: await getKontentErrorDataAsync(m),
|
|
166
|
+
}))
|
|
167
|
+
.otherwise(async () => ({
|
|
168
|
+
...sharedErrorData,
|
|
169
|
+
reason: "invalidResponse",
|
|
170
|
+
isValidResponse: response.isValidResponse,
|
|
171
|
+
responseHeaders: response.responseHeaders,
|
|
172
|
+
status: response.status,
|
|
173
|
+
statusText: response.statusText,
|
|
174
|
+
kontentErrorResponse: await getKontentErrorDataAsync(response),
|
|
175
|
+
}));
|
|
176
|
+
}
|
|
177
|
+
function parseRequestBody({ requestBody, url, }) {
|
|
178
|
+
return match(requestBody)
|
|
179
|
+
.returnType()
|
|
180
|
+
.with(P.nullish, () => ({
|
|
181
|
+
success: true,
|
|
182
|
+
data: null,
|
|
183
|
+
}))
|
|
184
|
+
.when(isBlob, (blob) => ({
|
|
185
|
+
success: true,
|
|
186
|
+
data: blob,
|
|
187
|
+
}))
|
|
188
|
+
.otherwise((m) => {
|
|
189
|
+
const { success: isParseSuccess, data: parsedBody, error: parseError } = tryCatch(() => JSON.stringify(m));
|
|
190
|
+
if (!isParseSuccess) {
|
|
191
|
+
return {
|
|
192
|
+
success: false,
|
|
193
|
+
error: createSdkError({
|
|
194
|
+
message: "Failed to stringify body of the request.",
|
|
195
|
+
url: url,
|
|
196
|
+
reason: "invalidBody",
|
|
197
|
+
originalError: parseError,
|
|
198
|
+
}),
|
|
199
|
+
};
|
|
218
200
|
}
|
|
219
201
|
return {
|
|
220
|
-
|
|
221
|
-
|
|
202
|
+
success: true,
|
|
203
|
+
data: parsedBody,
|
|
222
204
|
};
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
async function getKontentErrorDataAsync(response) {
|
|
208
|
+
if (isApplicationJsonResponseType(response.responseHeaders)) {
|
|
209
|
+
const json = await response.toJsonAsync();
|
|
210
|
+
if (isKontentErrorResponseData(json)) {
|
|
211
|
+
return json;
|
|
212
|
+
}
|
|
223
213
|
}
|
|
224
214
|
return undefined;
|
|
225
215
|
}
|
|
226
|
-
function
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
216
|
+
function parseAndValidateRequest(options) {
|
|
217
|
+
const { success: urlParsedSuccess, data: parsedUrl, error: urlError } = parseUrl(options.url);
|
|
218
|
+
if (!urlParsedSuccess) {
|
|
219
|
+
return {
|
|
220
|
+
success: false,
|
|
221
|
+
error: urlError,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
const { success: requestBodyParsedSuccess, data: parsedRequestBody, error: requestBodyError, } = parseRequestBody({ requestBody: options.body, url: options.url });
|
|
225
|
+
if (!requestBodyParsedSuccess) {
|
|
226
|
+
return {
|
|
227
|
+
success: false,
|
|
228
|
+
error: requestBodyError,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
success: true,
|
|
233
|
+
data: {
|
|
234
|
+
parsedUrl,
|
|
235
|
+
parsedBody: parsedRequestBody,
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function parseUrl(url) {
|
|
240
|
+
const { success, data: parsedUrl, error } = tryCatch(() => new URL(url));
|
|
241
|
+
if (!success) {
|
|
242
|
+
return {
|
|
243
|
+
success: false,
|
|
244
|
+
error: createSdkError({
|
|
245
|
+
message: `Failed to parse url '${url}'.`,
|
|
246
|
+
url: url,
|
|
247
|
+
reason: "invalidUrl",
|
|
248
|
+
originalError: error,
|
|
249
|
+
}),
|
|
234
250
|
};
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
success: true,
|
|
254
|
+
data: parsedUrl,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function buildRequestHeaders({ configHeaders, optionHeaders, body, }) {
|
|
258
|
+
const combinedHeaders = [...(configHeaders ?? []), ...(optionHeaders ?? [])];
|
|
259
|
+
const existingContentTypeHeader = getExistingContentTypeHeader(combinedHeaders);
|
|
260
|
+
const existingSdkVersionHeader = getExistingSdkVersionHeader(combinedHeaders);
|
|
261
|
+
const contentTypeHeader = existingContentTypeHeader ? undefined : createDefaultContentTypeHeader(body);
|
|
235
262
|
const sdkVersionHeader = existingSdkVersionHeader
|
|
236
263
|
? undefined
|
|
237
264
|
: getSdkIdHeader({
|
|
@@ -239,12 +266,25 @@ function getRequestHeaders(headers, body) {
|
|
|
239
266
|
name: sdkInfo.name,
|
|
240
267
|
version: sdkInfo.version,
|
|
241
268
|
});
|
|
242
|
-
const contentLengthHeader = body instanceof Blob
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
:
|
|
248
|
-
|
|
269
|
+
const contentLengthHeader = body instanceof Blob ? createDefaultContentLengthHeader(body) : undefined;
|
|
270
|
+
return [...combinedHeaders, contentTypeHeader, contentLengthHeader, sdkVersionHeader].filter(isNotUndefined);
|
|
271
|
+
}
|
|
272
|
+
function createDefaultContentTypeHeader(body) {
|
|
273
|
+
return {
|
|
274
|
+
name: "Content-Type",
|
|
275
|
+
value: body instanceof Blob ? body.type : "application/json",
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function createDefaultContentLengthHeader(body) {
|
|
279
|
+
return {
|
|
280
|
+
name: "Content-Length",
|
|
281
|
+
value: body.size.toString(),
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function getExistingContentTypeHeader(headers) {
|
|
285
|
+
return headers.find((header) => header.name.toLowerCase() === "Content-Type".toLowerCase());
|
|
286
|
+
}
|
|
287
|
+
function getExistingSdkVersionHeader(headers) {
|
|
288
|
+
return headers.find((header) => header.name.toLowerCase() === "X-KC-SDKID".toLowerCase());
|
|
249
289
|
}
|
|
250
290
|
//# sourceMappingURL=http.service.js.map
|