@dotcms/client 1.0.6-next.3 → 1.0.6-next.4
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 +279 -8
- package/index.cjs.js +214 -88
- package/index.esm.js +214 -88
- package/internal.cjs.js +3 -4
- package/internal.esm.js +3 -4
- package/package.json +1 -1
- package/src/lib/client/adapters/fetch-http-client.d.ts +66 -0
- package/src/lib/client/client.d.ts +1 -0
- package/src/lib/client/content/builders/collection/collection.d.ts +8 -7
- package/src/lib/client/content/content-api.d.ts +4 -3
- package/src/lib/client/content/shared/types.d.ts +6 -16
- package/src/lib/client/navigation/navigation-api.d.ts +6 -4
- package/src/lib/client/page/page-api.d.ts +12 -4
- package/src/lib/client/page/utils.d.ts +11 -8
- package/src/lib/utils/graphql/transforms.d.ts +2 -2
package/index.cjs.js
CHANGED
|
@@ -1,8 +1,117 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var consola = require('consola');
|
|
4
|
+
var types = require('@dotcms/types');
|
|
4
5
|
var internal = require('./internal.cjs.js');
|
|
5
6
|
|
|
7
|
+
/**
|
|
8
|
+
* HTTP client implementation using the Fetch API.
|
|
9
|
+
*
|
|
10
|
+
* Extends BaseHttpClient to provide a standard interface for making HTTP requests.
|
|
11
|
+
* This implementation uses the native Fetch API and handles:
|
|
12
|
+
* - JSON and non-JSON response parsing
|
|
13
|
+
* - HTTP error response parsing and conversion to DotHttpError
|
|
14
|
+
* - Network error handling and wrapping
|
|
15
|
+
* - Content-Type detection for proper response handling
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const client = new FetchHttpClient();
|
|
20
|
+
*
|
|
21
|
+
* // JSON request
|
|
22
|
+
* const data = await client.request<MyType>('/api/data', {
|
|
23
|
+
* method: 'GET',
|
|
24
|
+
* headers: { 'Authorization': 'Bearer token' }
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* // Non-JSON request (e.g., file download)
|
|
28
|
+
* const response = await client.request<Response>('/api/file.pdf', {
|
|
29
|
+
* method: 'GET'
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
class FetchHttpClient extends types.BaseHttpClient {
|
|
34
|
+
/**
|
|
35
|
+
* Sends an HTTP request using the Fetch API.
|
|
36
|
+
*
|
|
37
|
+
* Implements the abstract request method from BaseHttpClient using the native Fetch API.
|
|
38
|
+
* Automatically handles response parsing based on Content-Type headers and converts
|
|
39
|
+
* HTTP errors to standardized DotHttpError instances.
|
|
40
|
+
*
|
|
41
|
+
* @template T - The expected response type. For JSON responses, T should be the parsed object type.
|
|
42
|
+
* For non-JSON responses, T should be Response or the expected response type.
|
|
43
|
+
* @param url - The URL to send the request to.
|
|
44
|
+
* @param options - Optional fetch options including method, headers, body, etc.
|
|
45
|
+
* @returns Promise that resolves with the parsed response data or the Response object for non-JSON.
|
|
46
|
+
* @throws {DotHttpError} - Throws DotHttpError for HTTP errors (4xx/5xx status codes).
|
|
47
|
+
* @throws {DotHttpError} - Throws DotHttpError for network errors (connection issues, timeouts).
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* // JSON API request
|
|
52
|
+
* const user = await client.request<User>('/api/users/123', {
|
|
53
|
+
* method: 'GET',
|
|
54
|
+
* headers: { 'Accept': 'application/json' }
|
|
55
|
+
* });
|
|
56
|
+
*
|
|
57
|
+
* // POST request with JSON body
|
|
58
|
+
* const result = await client.request<CreateResult>('/api/users', {
|
|
59
|
+
* method: 'POST',
|
|
60
|
+
* headers: { 'Content-Type': 'application/json' },
|
|
61
|
+
* body: JSON.stringify({ name: 'John', email: 'john@example.com' })
|
|
62
|
+
* });
|
|
63
|
+
*
|
|
64
|
+
* // File download (non-JSON response)
|
|
65
|
+
* const response = await client.request<Response>('/api/files/document.pdf', {
|
|
66
|
+
* method: 'GET'
|
|
67
|
+
* });
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
async request(url, options) {
|
|
71
|
+
try {
|
|
72
|
+
// Use native fetch API - no additional configuration needed
|
|
73
|
+
const response = await fetch(url, options);
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
// Parse response body for error context
|
|
76
|
+
let errorBody;
|
|
77
|
+
try {
|
|
78
|
+
const contentType = response.headers.get('content-type');
|
|
79
|
+
if (contentType?.includes('application/json')) {
|
|
80
|
+
errorBody = await response.json();
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
errorBody = await response.text();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
errorBody = response.statusText;
|
|
88
|
+
}
|
|
89
|
+
// Convert headers to plain object
|
|
90
|
+
const headers = {};
|
|
91
|
+
response.headers.forEach((value, key) => {
|
|
92
|
+
headers[key] = value;
|
|
93
|
+
});
|
|
94
|
+
throw this.createHttpError(response.status, response.statusText, headers, errorBody);
|
|
95
|
+
}
|
|
96
|
+
// Handle different response types
|
|
97
|
+
const contentType = response.headers.get('content-type');
|
|
98
|
+
if (contentType?.includes('application/json')) {
|
|
99
|
+
return response.json();
|
|
100
|
+
}
|
|
101
|
+
// For non-JSON responses, return the response object
|
|
102
|
+
// Sub-clients can handle specific response types as needed
|
|
103
|
+
return response;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
// Handle network errors (fetch throws TypeError for network issues)
|
|
107
|
+
if (error instanceof TypeError) {
|
|
108
|
+
throw this.createNetworkError(error);
|
|
109
|
+
}
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
6
115
|
/******************************************************************************
|
|
7
116
|
Copyright (c) Microsoft Corporation.
|
|
8
117
|
|
|
@@ -637,7 +746,7 @@ class QueryBuilder {
|
|
|
637
746
|
}
|
|
638
747
|
_QueryBuilder_query = new WeakMap();
|
|
639
748
|
|
|
640
|
-
var _CollectionBuilder_page, _CollectionBuilder_limit, _CollectionBuilder_depth, _CollectionBuilder_render, _CollectionBuilder_sortBy, _CollectionBuilder_contentType, _CollectionBuilder_defaultQuery, _CollectionBuilder_query, _CollectionBuilder_rawQuery, _CollectionBuilder_languageId, _CollectionBuilder_draft, _CollectionBuilder_requestOptions, _CollectionBuilder_config;
|
|
749
|
+
var _CollectionBuilder_page, _CollectionBuilder_limit, _CollectionBuilder_depth, _CollectionBuilder_render, _CollectionBuilder_sortBy, _CollectionBuilder_contentType, _CollectionBuilder_defaultQuery, _CollectionBuilder_query, _CollectionBuilder_rawQuery, _CollectionBuilder_languageId, _CollectionBuilder_draft, _CollectionBuilder_requestOptions, _CollectionBuilder_httpClient, _CollectionBuilder_config;
|
|
641
750
|
/**
|
|
642
751
|
* Creates a Builder to filter and fetch content from the content API for a specific content type.
|
|
643
752
|
*
|
|
@@ -651,9 +760,10 @@ class CollectionBuilder {
|
|
|
651
760
|
* @param {ClientOptions} requestOptions Options for the client request.
|
|
652
761
|
* @param {DotCMSClientConfig} config The client configuration.
|
|
653
762
|
* @param {string} contentType The content type to fetch.
|
|
763
|
+
* @param {DotHttpClient} httpClient HTTP client for making requests.
|
|
654
764
|
* @memberof CollectionBuilder
|
|
655
765
|
*/
|
|
656
|
-
constructor(requestOptions, config, contentType) {
|
|
766
|
+
constructor(requestOptions, config, contentType, httpClient) {
|
|
657
767
|
_CollectionBuilder_page.set(this, 1);
|
|
658
768
|
_CollectionBuilder_limit.set(this, 10);
|
|
659
769
|
_CollectionBuilder_depth.set(this, 0);
|
|
@@ -666,10 +776,13 @@ class CollectionBuilder {
|
|
|
666
776
|
_CollectionBuilder_languageId.set(this, 1);
|
|
667
777
|
_CollectionBuilder_draft.set(this, false);
|
|
668
778
|
_CollectionBuilder_requestOptions.set(this, void 0);
|
|
779
|
+
_CollectionBuilder_httpClient.set(this, void 0);
|
|
669
780
|
_CollectionBuilder_config.set(this, void 0);
|
|
670
781
|
__classPrivateFieldSet(this, _CollectionBuilder_requestOptions, requestOptions, "f");
|
|
671
782
|
__classPrivateFieldSet(this, _CollectionBuilder_config, config, "f");
|
|
672
783
|
__classPrivateFieldSet(this, _CollectionBuilder_contentType, contentType, "f");
|
|
784
|
+
__classPrivateFieldSet(this, _CollectionBuilder_httpClient, httpClient, "f");
|
|
785
|
+
__classPrivateFieldSet(this, _CollectionBuilder_config, config, "f");
|
|
673
786
|
// Build the default query with the contentType field
|
|
674
787
|
__classPrivateFieldSet(this, _CollectionBuilder_defaultQuery, new QueryBuilder().field('contentType').equals(__classPrivateFieldGet(this, _CollectionBuilder_contentType, "f")), "f");
|
|
675
788
|
}
|
|
@@ -896,26 +1009,36 @@ class CollectionBuilder {
|
|
|
896
1009
|
*
|
|
897
1010
|
* @param {OnFullfilled} [onfulfilled] A callback that is called when the fetch is successful.
|
|
898
1011
|
* @param {OnRejected} [onrejected] A callback that is called when the fetch fails.
|
|
899
|
-
* @return {Promise<GetCollectionResponse<T> |
|
|
1012
|
+
* @return {Promise<GetCollectionResponse<T> | DotErrorContent>} A promise that resolves to the content or rejects with an error.
|
|
900
1013
|
* @memberof CollectionBuilder
|
|
901
1014
|
*/
|
|
902
1015
|
then(onfulfilled, onrejected) {
|
|
903
|
-
return this.fetch().then(
|
|
904
|
-
const
|
|
905
|
-
if (
|
|
906
|
-
const
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1016
|
+
return this.fetch().then((data) => {
|
|
1017
|
+
const formattedResponse = this.formatResponse(data);
|
|
1018
|
+
if (typeof onfulfilled === 'function') {
|
|
1019
|
+
const result = onfulfilled(formattedResponse);
|
|
1020
|
+
// Ensure we always return a value, fallback to formattedResponse if callback returns undefined
|
|
1021
|
+
return result ?? formattedResponse;
|
|
1022
|
+
}
|
|
1023
|
+
return formattedResponse;
|
|
1024
|
+
}, (error) => {
|
|
1025
|
+
// Wrap error in DotCMSContentError
|
|
1026
|
+
let contentError;
|
|
1027
|
+
if (error instanceof types.DotHttpError) {
|
|
1028
|
+
contentError = new types.DotErrorContent(`Content API failed for '${__classPrivateFieldGet(this, _CollectionBuilder_contentType, "f")}' (fetch): ${error.message}`, __classPrivateFieldGet(this, _CollectionBuilder_contentType, "f"), 'fetch', error, this.getFinalQuery());
|
|
911
1029
|
}
|
|
912
1030
|
else {
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
...data
|
|
916
|
-
};
|
|
1031
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
1032
|
+
contentError = new types.DotErrorContent(`Content API failed for '${__classPrivateFieldGet(this, _CollectionBuilder_contentType, "f")}' (fetch): ${errorMessage}`, __classPrivateFieldGet(this, _CollectionBuilder_contentType, "f"), 'fetch', undefined, this.getFinalQuery());
|
|
917
1033
|
}
|
|
918
|
-
|
|
1034
|
+
if (typeof onrejected === 'function') {
|
|
1035
|
+
const result = onrejected(contentError);
|
|
1036
|
+
// Ensure we always return a value, fallback to original error if callback returns undefined
|
|
1037
|
+
return result ?? contentError;
|
|
1038
|
+
}
|
|
1039
|
+
// Throw the wrapped error to trigger .catch()
|
|
1040
|
+
throw contentError;
|
|
1041
|
+
});
|
|
919
1042
|
}
|
|
920
1043
|
/**
|
|
921
1044
|
* Formats the response to the desired format.
|
|
@@ -945,14 +1068,15 @@ class CollectionBuilder {
|
|
|
945
1068
|
* Calls the content API to fetch the content.
|
|
946
1069
|
*
|
|
947
1070
|
* @private
|
|
948
|
-
* @return {Promise<
|
|
1071
|
+
* @return {Promise<GetCollectionRawResponse<T>>} The fetch response data.
|
|
1072
|
+
* @throws {DotHttpError} When the HTTP request fails.
|
|
949
1073
|
* @memberof CollectionBuilder
|
|
950
1074
|
*/
|
|
951
1075
|
fetch() {
|
|
952
1076
|
const finalQuery = this.getFinalQuery();
|
|
953
1077
|
const sanitizedQuery = sanitizeQueryForContentType(finalQuery, __classPrivateFieldGet(this, _CollectionBuilder_contentType, "f"));
|
|
954
1078
|
const query = __classPrivateFieldGet(this, _CollectionBuilder_rawQuery, "f") ? `${sanitizedQuery} ${__classPrivateFieldGet(this, _CollectionBuilder_rawQuery, "f")}` : sanitizedQuery;
|
|
955
|
-
return
|
|
1079
|
+
return __classPrivateFieldGet(this, _CollectionBuilder_httpClient, "f").request(this.url, {
|
|
956
1080
|
...__classPrivateFieldGet(this, _CollectionBuilder_requestOptions, "f"),
|
|
957
1081
|
method: 'POST',
|
|
958
1082
|
headers: {
|
|
@@ -1024,9 +1148,9 @@ class CollectionBuilder {
|
|
|
1024
1148
|
return baseQuery;
|
|
1025
1149
|
}
|
|
1026
1150
|
}
|
|
1027
|
-
_CollectionBuilder_page = new WeakMap(), _CollectionBuilder_limit = new WeakMap(), _CollectionBuilder_depth = new WeakMap(), _CollectionBuilder_render = new WeakMap(), _CollectionBuilder_sortBy = new WeakMap(), _CollectionBuilder_contentType = new WeakMap(), _CollectionBuilder_defaultQuery = new WeakMap(), _CollectionBuilder_query = new WeakMap(), _CollectionBuilder_rawQuery = new WeakMap(), _CollectionBuilder_languageId = new WeakMap(), _CollectionBuilder_draft = new WeakMap(), _CollectionBuilder_requestOptions = new WeakMap(), _CollectionBuilder_config = new WeakMap();
|
|
1151
|
+
_CollectionBuilder_page = new WeakMap(), _CollectionBuilder_limit = new WeakMap(), _CollectionBuilder_depth = new WeakMap(), _CollectionBuilder_render = new WeakMap(), _CollectionBuilder_sortBy = new WeakMap(), _CollectionBuilder_contentType = new WeakMap(), _CollectionBuilder_defaultQuery = new WeakMap(), _CollectionBuilder_query = new WeakMap(), _CollectionBuilder_rawQuery = new WeakMap(), _CollectionBuilder_languageId = new WeakMap(), _CollectionBuilder_draft = new WeakMap(), _CollectionBuilder_requestOptions = new WeakMap(), _CollectionBuilder_httpClient = new WeakMap(), _CollectionBuilder_config = new WeakMap();
|
|
1028
1152
|
|
|
1029
|
-
var _Content_requestOptions, _Content_config;
|
|
1153
|
+
var _Content_requestOptions, _Content_httpClient, _Content_config;
|
|
1030
1154
|
/**
|
|
1031
1155
|
* Creates a builder to filter and fetch a collection of content items.
|
|
1032
1156
|
* @param contentType - The content type to retrieve.
|
|
@@ -1079,14 +1203,17 @@ var _Content_requestOptions, _Content_config;
|
|
|
1079
1203
|
class Content {
|
|
1080
1204
|
/**
|
|
1081
1205
|
* Creates an instance of Content.
|
|
1082
|
-
* @param {
|
|
1206
|
+
* @param {DotRequestOptions} requestOptions - The options for the client request.
|
|
1083
1207
|
* @param {string} serverUrl - The server URL.
|
|
1208
|
+
* @param {DotHttpClient} httpClient - HTTP client for making requests.
|
|
1084
1209
|
*/
|
|
1085
|
-
constructor(config, requestOptions) {
|
|
1210
|
+
constructor(config, requestOptions, httpClient) {
|
|
1086
1211
|
_Content_requestOptions.set(this, void 0);
|
|
1212
|
+
_Content_httpClient.set(this, void 0);
|
|
1087
1213
|
_Content_config.set(this, void 0);
|
|
1088
1214
|
__classPrivateFieldSet(this, _Content_requestOptions, requestOptions, "f");
|
|
1089
1215
|
__classPrivateFieldSet(this, _Content_config, config, "f");
|
|
1216
|
+
__classPrivateFieldSet(this, _Content_httpClient, httpClient, "f");
|
|
1090
1217
|
}
|
|
1091
1218
|
/**
|
|
1092
1219
|
* Takes a content type and returns a builder to filter and fetch the collection.
|
|
@@ -1154,35 +1281,44 @@ class Content {
|
|
|
1154
1281
|
*
|
|
1155
1282
|
*/
|
|
1156
1283
|
getCollection(contentType) {
|
|
1157
|
-
return new CollectionBuilder(__classPrivateFieldGet(this, _Content_requestOptions, "f"), __classPrivateFieldGet(this, _Content_config, "f"), contentType);
|
|
1284
|
+
return new CollectionBuilder(__classPrivateFieldGet(this, _Content_requestOptions, "f"), __classPrivateFieldGet(this, _Content_config, "f"), contentType, __classPrivateFieldGet(this, _Content_httpClient, "f"));
|
|
1158
1285
|
}
|
|
1159
1286
|
}
|
|
1160
|
-
_Content_requestOptions = new WeakMap(), _Content_config = new WeakMap();
|
|
1287
|
+
_Content_requestOptions = new WeakMap(), _Content_httpClient = new WeakMap(), _Content_config = new WeakMap();
|
|
1161
1288
|
|
|
1162
1289
|
class NavigationClient {
|
|
1163
|
-
constructor(config, requestOptions) {
|
|
1290
|
+
constructor(config, requestOptions, httpClient) {
|
|
1164
1291
|
this.requestOptions = requestOptions;
|
|
1165
1292
|
this.BASE_URL = `${config?.dotcmsUrl}/api/v1/nav`;
|
|
1293
|
+
this.httpClient = httpClient;
|
|
1166
1294
|
}
|
|
1167
1295
|
/**
|
|
1168
1296
|
* Retrieves information about the dotCMS file and folder tree.
|
|
1169
|
-
* @param {
|
|
1297
|
+
* @param {string} path - The path to retrieve navigation for.
|
|
1298
|
+
* @param {DotCMSNavigationRequestParams} params - The options for the Navigation API call.
|
|
1170
1299
|
* @returns {Promise<DotCMSNavigationItem[]>} - A Promise that resolves to the response from the DotCMS API.
|
|
1171
|
-
* @throws {
|
|
1300
|
+
* @throws {DotErrorNavigation} - Throws a navigation-specific error if the request fails.
|
|
1172
1301
|
*/
|
|
1173
1302
|
async get(path, params) {
|
|
1174
1303
|
if (!path) {
|
|
1175
|
-
throw new
|
|
1304
|
+
throw new types.DotErrorNavigation("The 'path' parameter is required for the Navigation API", path);
|
|
1176
1305
|
}
|
|
1177
1306
|
const navParams = params ? this.mapToBackendParams(params) : {};
|
|
1178
1307
|
const urlParams = new URLSearchParams(navParams).toString();
|
|
1179
1308
|
const parsedPath = path.replace(/^\/+/, '/').replace(/\/+$/, '/');
|
|
1180
1309
|
const url = `${this.BASE_URL}${parsedPath}${urlParams ? `?${urlParams}` : ''}`;
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1310
|
+
try {
|
|
1311
|
+
const response = await this.httpClient.request(url, this.requestOptions);
|
|
1312
|
+
return response.entity;
|
|
1313
|
+
}
|
|
1314
|
+
catch (error) {
|
|
1315
|
+
// Handle DotHttpError instances from httpClient.request
|
|
1316
|
+
if (error instanceof types.DotHttpError) {
|
|
1317
|
+
throw new types.DotErrorNavigation(`Navigation API failed for path '${parsedPath}': ${error.message}`, parsedPath, error);
|
|
1318
|
+
}
|
|
1319
|
+
// Handle other errors (validation, network, etc.)
|
|
1320
|
+
throw new types.DotErrorNavigation(`Navigation API failed for path '${parsedPath}': ${error instanceof Error ? error.message : 'Unknown error'}`, parsedPath);
|
|
1184
1321
|
}
|
|
1185
|
-
return response.json().then((data) => data.entity);
|
|
1186
1322
|
}
|
|
1187
1323
|
mapToBackendParams(params) {
|
|
1188
1324
|
const backendParams = {};
|
|
@@ -1196,26 +1332,6 @@ class NavigationClient {
|
|
|
1196
1332
|
}
|
|
1197
1333
|
}
|
|
1198
1334
|
|
|
1199
|
-
/**
|
|
1200
|
-
* A record of HTTP status codes and their corresponding error messages.
|
|
1201
|
-
*
|
|
1202
|
-
* @type {Record<number, string>}
|
|
1203
|
-
* @property {string} 401 - Unauthorized. Check the token and try again.
|
|
1204
|
-
* @property {string} 403 - Forbidden. Check the permissions and try again.
|
|
1205
|
-
* @property {string} 404 - Not Found. Check the URL and try again.
|
|
1206
|
-
* @property {string} 500 - Internal Server Error. Try again later.
|
|
1207
|
-
* @property {string} 502 - Bad Gateway. Try again later.
|
|
1208
|
-
* @property {string} 503 - Service Unavailable. Try again later.
|
|
1209
|
-
*/
|
|
1210
|
-
const ErrorMessages = {
|
|
1211
|
-
401: 'Unauthorized. Check the token and try again.',
|
|
1212
|
-
403: 'Forbidden. Check the permissions and try again.',
|
|
1213
|
-
404: 'Not Found. Check the URL and try again.',
|
|
1214
|
-
500: 'Internal Server Error. Try again later.',
|
|
1215
|
-
502: 'Bad Gateway. Try again later.',
|
|
1216
|
-
503: 'Service Unavailable. Try again later.'
|
|
1217
|
-
};
|
|
1218
|
-
|
|
1219
1335
|
const DEFAULT_PAGE_CONTENTLETS_CONTENT = `
|
|
1220
1336
|
publishDate
|
|
1221
1337
|
inode
|
|
@@ -1431,11 +1547,14 @@ function buildQuery(queryData) {
|
|
|
1431
1547
|
/**
|
|
1432
1548
|
* Filters response data to include only specified keys.
|
|
1433
1549
|
*
|
|
1434
|
-
* @param {Record<string,
|
|
1550
|
+
* @param {Record<string, unknown> | undefined} responseData - Original response data object
|
|
1435
1551
|
* @param {string[]} keys - Array of keys to extract from the response data
|
|
1436
|
-
* @returns {Record<string,
|
|
1552
|
+
* @returns {Record<string, unknown> | undefined} New object containing only the specified keys
|
|
1437
1553
|
*/
|
|
1438
|
-
function
|
|
1554
|
+
function mapContentResponse(responseData, keys) {
|
|
1555
|
+
if (!responseData) {
|
|
1556
|
+
return undefined;
|
|
1557
|
+
}
|
|
1439
1558
|
return keys.reduce((accumulator, key) => {
|
|
1440
1559
|
if (responseData[key] !== undefined) {
|
|
1441
1560
|
accumulator[key] = responseData[key];
|
|
@@ -1449,25 +1568,19 @@ function mapResponseData(responseData, keys) {
|
|
|
1449
1568
|
* @param {Object} options - Options for the fetch request
|
|
1450
1569
|
* @param {string} options.body - GraphQL query string
|
|
1451
1570
|
* @param {Record<string, string>} options.headers - HTTP headers for the request
|
|
1452
|
-
* @
|
|
1453
|
-
* @
|
|
1571
|
+
* @param {DotHttpClient} options.httpClient - HTTP client for making requests
|
|
1572
|
+
* @returns {Promise<DotGraphQLApiResponse>} Parsed JSON response from the GraphQL API
|
|
1573
|
+
* @throws {DotHttpError} If the HTTP request fails (non-2xx status or network error)
|
|
1454
1574
|
*/
|
|
1455
|
-
async function fetchGraphQL({ baseURL, body, headers }) {
|
|
1575
|
+
async function fetchGraphQL({ baseURL, body, headers, httpClient }) {
|
|
1456
1576
|
const url = new URL(baseURL);
|
|
1457
1577
|
url.pathname = '/api/v1/graphql';
|
|
1458
|
-
|
|
1578
|
+
// httpClient.request throws DotHttpError on failure, so we just return the response directly
|
|
1579
|
+
return await httpClient.request(url.toString(), {
|
|
1459
1580
|
method: 'POST',
|
|
1460
1581
|
body,
|
|
1461
1582
|
headers
|
|
1462
1583
|
});
|
|
1463
|
-
if (!response.ok) {
|
|
1464
|
-
const error = {
|
|
1465
|
-
status: response.status,
|
|
1466
|
-
message: ErrorMessages[response.status] || response.statusText
|
|
1467
|
-
};
|
|
1468
|
-
throw error;
|
|
1469
|
-
}
|
|
1470
|
-
return await response.json();
|
|
1471
1584
|
}
|
|
1472
1585
|
|
|
1473
1586
|
/**
|
|
@@ -1479,7 +1592,8 @@ class PageClient {
|
|
|
1479
1592
|
* Creates a new PageClient instance.
|
|
1480
1593
|
*
|
|
1481
1594
|
* @param {DotCMSClientConfig} config - Configuration options for the DotCMS client
|
|
1482
|
-
* @param {
|
|
1595
|
+
* @param {DotRequestOptions} requestOptions - Options for fetch requests including authorization headers
|
|
1596
|
+
* @param {DotHttpClient} httpClient - HTTP client for making requests
|
|
1483
1597
|
* @example
|
|
1484
1598
|
* ```typescript
|
|
1485
1599
|
* const pageClient = new PageClient(
|
|
@@ -1492,14 +1606,16 @@ class PageClient {
|
|
|
1492
1606
|
* headers: {
|
|
1493
1607
|
* Authorization: 'Bearer your-auth-token'
|
|
1494
1608
|
* }
|
|
1495
|
-
* }
|
|
1609
|
+
* },
|
|
1610
|
+
* httpClient
|
|
1496
1611
|
* );
|
|
1497
1612
|
* ```
|
|
1498
1613
|
*/
|
|
1499
|
-
constructor(config, requestOptions) {
|
|
1614
|
+
constructor(config, requestOptions, httpClient) {
|
|
1500
1615
|
this.requestOptions = requestOptions;
|
|
1501
1616
|
this.siteId = config.siteId || '';
|
|
1502
1617
|
this.dotcmsUrl = config.dotcmsUrl;
|
|
1618
|
+
this.httpClient = httpClient;
|
|
1503
1619
|
}
|
|
1504
1620
|
/**
|
|
1505
1621
|
* Retrieves a page from DotCMS using GraphQL.
|
|
@@ -1508,6 +1624,7 @@ class PageClient {
|
|
|
1508
1624
|
* @param {DotCMSPageRequestParams} [options] - Options for the request
|
|
1509
1625
|
* @template T - The type of the page and content, defaults to DotCMSBasicPage and Record<string, unknown> | unknown
|
|
1510
1626
|
* @returns {Promise<DotCMSComposedPageResponse<T>>} A Promise that resolves to the page data
|
|
1627
|
+
* @throws {DotErrorPage} - Throws a page-specific error if the request fails or page is not found
|
|
1511
1628
|
*
|
|
1512
1629
|
* @example Using GraphQL
|
|
1513
1630
|
* ```typescript
|
|
@@ -1575,21 +1692,26 @@ class PageClient {
|
|
|
1575
1692
|
const requestHeaders = this.requestOptions.headers;
|
|
1576
1693
|
const requestBody = JSON.stringify({ query: completeQuery, variables: requestVariables });
|
|
1577
1694
|
try {
|
|
1578
|
-
const
|
|
1695
|
+
const response = await fetchGraphQL({
|
|
1579
1696
|
baseURL: this.dotcmsUrl,
|
|
1580
1697
|
body: requestBody,
|
|
1581
|
-
headers: requestHeaders
|
|
1698
|
+
headers: requestHeaders,
|
|
1699
|
+
httpClient: this.httpClient
|
|
1582
1700
|
});
|
|
1583
|
-
|
|
1584
|
-
|
|
1701
|
+
// The GQL endpoint can return errors and data, we need to handle both
|
|
1702
|
+
if (response.errors) {
|
|
1703
|
+
response.errors.forEach((error) => {
|
|
1585
1704
|
consola.consola.error('[DotCMS GraphQL Error]: ', error.message);
|
|
1586
1705
|
});
|
|
1587
1706
|
}
|
|
1588
|
-
const pageResponse = internal.graphqlToPageEntity(data);
|
|
1707
|
+
const pageResponse = internal.graphqlToPageEntity(response.data.page);
|
|
1589
1708
|
if (!pageResponse) {
|
|
1590
|
-
throw new
|
|
1709
|
+
throw new types.DotErrorPage(`Page ${url} not found. Check the page URL and permissions.`, undefined, {
|
|
1710
|
+
query: completeQuery,
|
|
1711
|
+
variables: requestVariables
|
|
1712
|
+
});
|
|
1591
1713
|
}
|
|
1592
|
-
const contentResponse =
|
|
1714
|
+
const contentResponse = mapContentResponse(response.data, Object.keys(content));
|
|
1593
1715
|
return {
|
|
1594
1716
|
pageAsset: pageResponse,
|
|
1595
1717
|
content: contentResponse,
|
|
@@ -1600,15 +1722,18 @@ class PageClient {
|
|
|
1600
1722
|
};
|
|
1601
1723
|
}
|
|
1602
1724
|
catch (error) {
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
graphql: {
|
|
1725
|
+
// Handle DotHttpError instances from httpClient.request
|
|
1726
|
+
if (error instanceof types.DotHttpError) {
|
|
1727
|
+
throw new types.DotErrorPage(`Page request failed for URL '${url}': ${error.message}`, error, {
|
|
1607
1728
|
query: completeQuery,
|
|
1608
1729
|
variables: requestVariables
|
|
1609
|
-
}
|
|
1610
|
-
}
|
|
1611
|
-
|
|
1730
|
+
});
|
|
1731
|
+
}
|
|
1732
|
+
// Handle other errors (GraphQL errors, validation errors, etc.)
|
|
1733
|
+
throw new types.DotErrorPage(`Page request failed for URL '${url}': ${error instanceof Error ? error.message : 'Unknown error'}`, undefined, {
|
|
1734
|
+
query: completeQuery,
|
|
1735
|
+
variables: requestVariables
|
|
1736
|
+
});
|
|
1612
1737
|
}
|
|
1613
1738
|
}
|
|
1614
1739
|
}
|
|
@@ -1649,11 +1774,12 @@ class DotCMSClient {
|
|
|
1649
1774
|
*/
|
|
1650
1775
|
constructor(config = defaultConfig) {
|
|
1651
1776
|
this.config = config;
|
|
1777
|
+
this.httpClient = config.httpClient || new FetchHttpClient();
|
|
1652
1778
|
this.requestOptions = this.createAuthenticatedRequestOptions(this.config);
|
|
1653
|
-
// Initialize clients
|
|
1654
|
-
this.page = new PageClient(this.config, this.requestOptions);
|
|
1655
|
-
this.nav = new NavigationClient(this.config, this.requestOptions);
|
|
1656
|
-
this.content = new Content(this.config, this.requestOptions);
|
|
1779
|
+
// Initialize clients with httpClient
|
|
1780
|
+
this.page = new PageClient(this.config, this.requestOptions, this.httpClient);
|
|
1781
|
+
this.nav = new NavigationClient(this.config, this.requestOptions, this.httpClient);
|
|
1782
|
+
this.content = new Content(this.config, this.requestOptions, this.httpClient);
|
|
1657
1783
|
}
|
|
1658
1784
|
/**
|
|
1659
1785
|
* Creates request options with authentication headers.
|