@dotcms/client 1.0.0 → 1.0.1
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/.eslintrc.json +18 -0
- package/CLAUDE.md +253 -0
- package/MIGRATION.md +329 -0
- package/README.md +250 -434
- package/jest.config.ts +15 -0
- package/package.json +8 -25
- package/project.json +73 -0
- package/src/lib/client/client.spec.ts +147 -0
- package/src/lib/client/client.ts +125 -0
- package/src/lib/client/content/builders/collection/collection.spec.ts +514 -0
- package/src/lib/client/content/builders/collection/{collection.d.ts → collection.ts} +210 -19
- package/src/lib/client/content/builders/query/lucene-syntax/{Equals.d.ts → Equals.ts} +45 -11
- package/src/lib/client/content/builders/query/lucene-syntax/{Field.d.ts → Field.ts} +13 -5
- package/src/lib/client/content/builders/query/lucene-syntax/{NotOperand.d.ts → NotOperand.ts} +13 -5
- package/src/lib/client/content/builders/query/lucene-syntax/{Operand.d.ts → Operand.ts} +21 -7
- package/src/lib/client/content/builders/query/query.spec.ts +159 -0
- package/src/lib/client/content/builders/query/{query.d.ts → query.ts} +16 -5
- package/src/lib/client/content/builders/query/utils/{index.d.ts → index.ts} +49 -12
- package/src/lib/client/content/{content-api.d.ts → content-api.ts} +14 -4
- package/src/lib/client/content/shared/{const.d.ts → const.ts} +5 -3
- package/src/lib/client/content/shared/{types.d.ts → types.ts} +18 -2
- package/src/lib/client/content/shared/{utils.d.ts → utils.ts} +9 -1
- package/src/lib/client/models/{index.d.ts → index.ts} +8 -1
- package/src/lib/client/navigation/navigation-api.spec.ts +167 -0
- package/src/lib/client/navigation/navigation-api.ts +62 -0
- package/src/lib/client/page/page-api.spec.ts +359 -0
- package/src/lib/client/page/page-api.ts +197 -0
- package/src/lib/client/page/utils.ts +291 -0
- package/src/lib/utils/graphql/transforms.spec.ts +250 -0
- package/src/lib/utils/graphql/transforms.ts +128 -0
- package/tsconfig.json +22 -0
- package/tsconfig.lib.json +13 -0
- package/tsconfig.spec.json +9 -0
- package/index.cjs.d.ts +0 -1
- package/index.cjs.default.js +0 -1
- package/index.cjs.js +0 -1591
- package/index.cjs.mjs +0 -2
- package/index.esm.d.ts +0 -1
- package/index.esm.js +0 -1589
- package/internal.cjs.d.ts +0 -1
- package/internal.cjs.default.js +0 -1
- package/internal.cjs.js +0 -85
- package/internal.cjs.mjs +0 -2
- package/internal.esm.d.ts +0 -1
- package/internal.esm.js +0 -83
- package/src/lib/client/client.d.ts +0 -56
- package/src/lib/client/navigation/navigation-api.d.ts +0 -14
- package/src/lib/client/page/page-api.d.ts +0 -95
- package/src/lib/client/page/utils.d.ts +0 -41
- package/src/lib/utils/graphql/transforms.d.ts +0 -13
- /package/src/{index.d.ts → index.ts} +0 -0
- /package/src/{internal.d.ts → internal.ts} +0 -0
- /package/src/lib/client/content/builders/query/lucene-syntax/{index.d.ts → index.ts} +0 -0
- /package/src/lib/utils/{index.d.ts → index.ts} +0 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { consola } from 'consola';
|
|
2
|
+
|
|
3
|
+
import { ErrorMessages } from '../models';
|
|
4
|
+
|
|
5
|
+
const DEFAULT_PAGE_CONTENTLETS_CONTENT = `
|
|
6
|
+
publishDate
|
|
7
|
+
inode
|
|
8
|
+
identifier
|
|
9
|
+
archived
|
|
10
|
+
urlMap
|
|
11
|
+
urlMap
|
|
12
|
+
locked
|
|
13
|
+
contentType
|
|
14
|
+
creationDate
|
|
15
|
+
modDate
|
|
16
|
+
title
|
|
17
|
+
baseType
|
|
18
|
+
working
|
|
19
|
+
live
|
|
20
|
+
publishUser {
|
|
21
|
+
firstName
|
|
22
|
+
lastName
|
|
23
|
+
}
|
|
24
|
+
owner {
|
|
25
|
+
lastName
|
|
26
|
+
}
|
|
27
|
+
conLanguage {
|
|
28
|
+
language
|
|
29
|
+
languageCode
|
|
30
|
+
}
|
|
31
|
+
modUser {
|
|
32
|
+
firstName
|
|
33
|
+
lastName
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Builds a GraphQL query for retrieving page content from DotCMS.
|
|
39
|
+
*
|
|
40
|
+
* @param {string} pageQuery - Custom fragment fields to include in the ClientPage fragment
|
|
41
|
+
* @param {string} additionalQueries - Additional GraphQL queries to include in the main query
|
|
42
|
+
* @returns {string} Complete GraphQL query string for page content
|
|
43
|
+
*/
|
|
44
|
+
export const buildPageQuery = ({
|
|
45
|
+
page,
|
|
46
|
+
fragments,
|
|
47
|
+
additionalQueries
|
|
48
|
+
}: {
|
|
49
|
+
page?: string;
|
|
50
|
+
fragments?: string[];
|
|
51
|
+
additionalQueries?: string;
|
|
52
|
+
}) => {
|
|
53
|
+
if (!page) {
|
|
54
|
+
consola.warn(
|
|
55
|
+
"[DotCMS Client]: No page query was found, so we're loading all content using _map. This might slow things down. For better performance, we recommend adding a specific query in the page attribute."
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return `
|
|
60
|
+
fragment DotCMSPage on DotPage {
|
|
61
|
+
publishDate
|
|
62
|
+
type
|
|
63
|
+
httpsRequired
|
|
64
|
+
inode
|
|
65
|
+
path
|
|
66
|
+
identifier
|
|
67
|
+
hasTitleImage
|
|
68
|
+
sortOrder
|
|
69
|
+
extension
|
|
70
|
+
canRead
|
|
71
|
+
pageURI
|
|
72
|
+
canEdit
|
|
73
|
+
archived
|
|
74
|
+
friendlyName
|
|
75
|
+
workingInode
|
|
76
|
+
url
|
|
77
|
+
pageURI
|
|
78
|
+
hasLiveVersion
|
|
79
|
+
deleted
|
|
80
|
+
pageUrl
|
|
81
|
+
shortyWorking
|
|
82
|
+
mimeType
|
|
83
|
+
locked
|
|
84
|
+
stInode
|
|
85
|
+
contentType
|
|
86
|
+
creationDate
|
|
87
|
+
liveInode
|
|
88
|
+
name
|
|
89
|
+
shortyLive
|
|
90
|
+
modDate
|
|
91
|
+
title
|
|
92
|
+
baseType
|
|
93
|
+
working
|
|
94
|
+
canLock
|
|
95
|
+
live
|
|
96
|
+
isContentlet
|
|
97
|
+
statusIcons
|
|
98
|
+
canEdit
|
|
99
|
+
canLock
|
|
100
|
+
canRead
|
|
101
|
+
canEdit
|
|
102
|
+
canLock
|
|
103
|
+
canRead
|
|
104
|
+
runningExperimentId
|
|
105
|
+
urlContentMap {
|
|
106
|
+
_map
|
|
107
|
+
}
|
|
108
|
+
host {
|
|
109
|
+
identifier
|
|
110
|
+
hostName
|
|
111
|
+
googleMap
|
|
112
|
+
archived
|
|
113
|
+
contentType
|
|
114
|
+
}
|
|
115
|
+
vanityUrl {
|
|
116
|
+
action
|
|
117
|
+
forwardTo
|
|
118
|
+
uri
|
|
119
|
+
}
|
|
120
|
+
conLanguage {
|
|
121
|
+
id
|
|
122
|
+
language
|
|
123
|
+
languageCode
|
|
124
|
+
}
|
|
125
|
+
template {
|
|
126
|
+
drawed
|
|
127
|
+
anonymous
|
|
128
|
+
theme
|
|
129
|
+
identifier
|
|
130
|
+
}
|
|
131
|
+
containers {
|
|
132
|
+
path
|
|
133
|
+
identifier
|
|
134
|
+
maxContentlets
|
|
135
|
+
containerStructures {
|
|
136
|
+
id
|
|
137
|
+
code
|
|
138
|
+
structureId
|
|
139
|
+
containerId
|
|
140
|
+
contentTypeVar
|
|
141
|
+
containerInode
|
|
142
|
+
}
|
|
143
|
+
containerContentlets {
|
|
144
|
+
uuid
|
|
145
|
+
contentlets {
|
|
146
|
+
${page ? DEFAULT_PAGE_CONTENTLETS_CONTENT : '_map'}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
layout {
|
|
151
|
+
header
|
|
152
|
+
footer
|
|
153
|
+
body {
|
|
154
|
+
rows {
|
|
155
|
+
styleClass
|
|
156
|
+
columns {
|
|
157
|
+
leftOffset
|
|
158
|
+
styleClass
|
|
159
|
+
width
|
|
160
|
+
left
|
|
161
|
+
containers {
|
|
162
|
+
identifier
|
|
163
|
+
uuid
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
viewAs {
|
|
170
|
+
visitor {
|
|
171
|
+
persona {
|
|
172
|
+
modDate
|
|
173
|
+
inode
|
|
174
|
+
name
|
|
175
|
+
identifier
|
|
176
|
+
keyTag
|
|
177
|
+
photo {
|
|
178
|
+
versionPath
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
persona {
|
|
183
|
+
modDate
|
|
184
|
+
inode
|
|
185
|
+
name
|
|
186
|
+
identifier
|
|
187
|
+
keyTag
|
|
188
|
+
photo {
|
|
189
|
+
versionPath
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
language {
|
|
193
|
+
id
|
|
194
|
+
languageCode
|
|
195
|
+
countryCode
|
|
196
|
+
language
|
|
197
|
+
country
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
${page ? ` fragment ClientPage on DotPage { ${page} } ` : ''}
|
|
203
|
+
|
|
204
|
+
${fragments ? fragments.join('\n\n') : ''}
|
|
205
|
+
|
|
206
|
+
query PageContent($url: String!, $languageId: String, $mode: String, $personaId: String, $fireRules: Boolean, $publishDate: String, $siteId: String, $variantName: String) {
|
|
207
|
+
page: page(url: $url, languageId: $languageId, pageMode: $mode, persona: $personaId, fireRules: $fireRules, publishDate: $publishDate, site: $siteId, variantName: $variantName) {
|
|
208
|
+
...DotCMSPage
|
|
209
|
+
${page ? '...ClientPage' : ''}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
${additionalQueries}
|
|
213
|
+
}
|
|
214
|
+
`;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Converts a record of query strings into a single GraphQL query string.
|
|
219
|
+
*
|
|
220
|
+
* @param {Record<string, string>} queryData - Object containing named query strings
|
|
221
|
+
* @returns {string} Combined query string or empty string if no queryData provided
|
|
222
|
+
*/
|
|
223
|
+
export function buildQuery(queryData: Record<string, string>): string {
|
|
224
|
+
if (!queryData) return '';
|
|
225
|
+
|
|
226
|
+
return Object.entries(queryData)
|
|
227
|
+
.map(([key, query]) => `${key}: ${query}`)
|
|
228
|
+
.join(' ');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Filters response data to include only specified keys.
|
|
233
|
+
*
|
|
234
|
+
* @param {Record<string, string>} responseData - Original response data object
|
|
235
|
+
* @param {string[]} keys - Array of keys to extract from the response data
|
|
236
|
+
* @returns {Record<string, string>} New object containing only the specified keys
|
|
237
|
+
*/
|
|
238
|
+
export function mapResponseData(
|
|
239
|
+
responseData: Record<string, string>,
|
|
240
|
+
keys: string[]
|
|
241
|
+
): Record<string, string> {
|
|
242
|
+
return keys.reduce(
|
|
243
|
+
(accumulator, key) => {
|
|
244
|
+
if (responseData[key] !== undefined) {
|
|
245
|
+
accumulator[key] = responseData[key];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return accumulator;
|
|
249
|
+
},
|
|
250
|
+
{} as Record<string, string>
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Executes a GraphQL query against the DotCMS API.
|
|
256
|
+
*
|
|
257
|
+
* @param {Object} options - Options for the fetch request
|
|
258
|
+
* @param {string} options.body - GraphQL query string
|
|
259
|
+
* @param {Record<string, string>} options.headers - HTTP headers for the request
|
|
260
|
+
* @returns {Promise<any>} Parsed JSON response from the GraphQL API
|
|
261
|
+
* @throws {Error} If the HTTP response is not successful
|
|
262
|
+
*/
|
|
263
|
+
export async function fetchGraphQL({
|
|
264
|
+
baseURL,
|
|
265
|
+
body,
|
|
266
|
+
headers
|
|
267
|
+
}: {
|
|
268
|
+
baseURL: string;
|
|
269
|
+
body: string;
|
|
270
|
+
headers: Record<string, string>;
|
|
271
|
+
}) {
|
|
272
|
+
const url = new URL(baseURL);
|
|
273
|
+
url.pathname = '/api/v1/graphql';
|
|
274
|
+
|
|
275
|
+
const response = await fetch(url.toString(), {
|
|
276
|
+
method: 'POST',
|
|
277
|
+
body,
|
|
278
|
+
headers
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
if (!response.ok) {
|
|
282
|
+
const error = {
|
|
283
|
+
status: response.status,
|
|
284
|
+
message: ErrorMessages[response.status] || response.statusText
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
throw error;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return await response.json();
|
|
291
|
+
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { DotCMSGraphQLPageResponse } from '@dotcms/types';
|
|
2
|
+
|
|
3
|
+
import { graphqlToPageEntity } from './transforms';
|
|
4
|
+
|
|
5
|
+
const GRAPHQL_RESPONSE_MOCK = {
|
|
6
|
+
page: {
|
|
7
|
+
title: 'test2',
|
|
8
|
+
url: '/test2',
|
|
9
|
+
seodescription: null,
|
|
10
|
+
containers: [
|
|
11
|
+
{
|
|
12
|
+
path: '//demo.dotcms.com/application/containers/default/',
|
|
13
|
+
identifier: '69b3d24d-7e80-4be6-b04a-d352d16493ee',
|
|
14
|
+
containerStructures: [
|
|
15
|
+
{
|
|
16
|
+
id: '77ec7720-bad8-431c-a0a3-6443fe87af73'
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: '1e5372c4-9fff-4793-ad8a-a52558d0d395'
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: '461b6fad-04a8-49ba-b75b-6634799bc289'
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: '57b4a9de-4765-4c57-8a05-2a0a317ec546'
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: '342a83bf-040a-45a2-a4bc-defb0f1bf4eb'
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: '91a955a0-5c97-4fc3-91dd-f894e2f3dc54'
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: '71ded7c9deb5b05452824bb42b9aa9d5'
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: '86a06a2e-ba40-43d4-a3ad-3a870d43bfc1'
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: '7673e601-26d4-4dbc-b6ad-7028599df656'
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: '313498a4-5b57-4d9e-8d32-ef2d30f9e867'
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
containerContentlets: [
|
|
47
|
+
{
|
|
48
|
+
uuid: 'uuid-1562770692396',
|
|
49
|
+
contentlets: [
|
|
50
|
+
{
|
|
51
|
+
identifier: 'd9444fa005a10d555318d8b014e474d4'
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
host: {
|
|
59
|
+
hostName: 'demo.dotcms.com'
|
|
60
|
+
},
|
|
61
|
+
layout: {
|
|
62
|
+
title: 'default-template'
|
|
63
|
+
},
|
|
64
|
+
template: {
|
|
65
|
+
identifier: '31f4c794-c769-4929-9d5d-7c383408c65c'
|
|
66
|
+
},
|
|
67
|
+
urlContentMap: {
|
|
68
|
+
_map: {
|
|
69
|
+
identifier: '31f4c794-c769-4929-9d5d-7c383408c74d'
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
viewAs: {
|
|
73
|
+
mode: 'LIVE'
|
|
74
|
+
},
|
|
75
|
+
runningExperimentId: '123',
|
|
76
|
+
vanityUrl: {
|
|
77
|
+
action: 200,
|
|
78
|
+
uri: '/test2',
|
|
79
|
+
forwardTo: '/test2'
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const MOCK_PAGE_ENTITY = {
|
|
85
|
+
containers: {
|
|
86
|
+
'//demo.dotcms.com/application/containers/default/': {
|
|
87
|
+
container: {
|
|
88
|
+
identifier: '69b3d24d-7e80-4be6-b04a-d352d16493ee',
|
|
89
|
+
path: '//demo.dotcms.com/application/containers/default/'
|
|
90
|
+
},
|
|
91
|
+
containerStructures: [
|
|
92
|
+
{
|
|
93
|
+
id: '77ec7720-bad8-431c-a0a3-6443fe87af73'
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: '1e5372c4-9fff-4793-ad8a-a52558d0d395'
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: '461b6fad-04a8-49ba-b75b-6634799bc289'
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: '57b4a9de-4765-4c57-8a05-2a0a317ec546'
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: '342a83bf-040a-45a2-a4bc-defb0f1bf4eb'
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: '91a955a0-5c97-4fc3-91dd-f894e2f3dc54'
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
id: '71ded7c9deb5b05452824bb42b9aa9d5'
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: '86a06a2e-ba40-43d4-a3ad-3a870d43bfc1'
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: '7673e601-26d4-4dbc-b6ad-7028599df656'
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: '313498a4-5b57-4d9e-8d32-ef2d30f9e867'
|
|
121
|
+
}
|
|
122
|
+
],
|
|
123
|
+
contentlets: {
|
|
124
|
+
'uuid-1562770692396': [
|
|
125
|
+
{
|
|
126
|
+
identifier: 'd9444fa005a10d555318d8b014e474d4'
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
layout: {
|
|
133
|
+
title: 'default-template'
|
|
134
|
+
},
|
|
135
|
+
page: {
|
|
136
|
+
seodescription: null,
|
|
137
|
+
title: 'test2',
|
|
138
|
+
url: '/test2'
|
|
139
|
+
},
|
|
140
|
+
urlContentMap: {
|
|
141
|
+
identifier: '31f4c794-c769-4929-9d5d-7c383408c74d'
|
|
142
|
+
},
|
|
143
|
+
template: {
|
|
144
|
+
identifier: '31f4c794-c769-4929-9d5d-7c383408c65c'
|
|
145
|
+
},
|
|
146
|
+
viewAs: {
|
|
147
|
+
mode: 'LIVE'
|
|
148
|
+
},
|
|
149
|
+
site: {
|
|
150
|
+
hostName: 'demo.dotcms.com'
|
|
151
|
+
},
|
|
152
|
+
vanityUrl: {
|
|
153
|
+
action: 200,
|
|
154
|
+
uri: '/test2',
|
|
155
|
+
forwardTo: '/test2'
|
|
156
|
+
},
|
|
157
|
+
runningExperimentId: '123'
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
describe('GraphQL Parser', () => {
|
|
161
|
+
it('should return the correct page entity', () => {
|
|
162
|
+
const pageEntity = graphqlToPageEntity(
|
|
163
|
+
GRAPHQL_RESPONSE_MOCK as unknown as DotCMSGraphQLPageResponse
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
expect(pageEntity).toEqual(MOCK_PAGE_ENTITY);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should transform _map properties correctly', () => {
|
|
170
|
+
const graphqlResponse = {
|
|
171
|
+
page: {
|
|
172
|
+
title: 'map-test',
|
|
173
|
+
url: '/map-test',
|
|
174
|
+
_map: {
|
|
175
|
+
customField: 'custom value'
|
|
176
|
+
},
|
|
177
|
+
urlContentMap: {
|
|
178
|
+
_map: {
|
|
179
|
+
mapField: 'map value',
|
|
180
|
+
identifier: 'test-id',
|
|
181
|
+
someNestedField: {
|
|
182
|
+
nestedField: 'nested value'
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
containers: [
|
|
187
|
+
{
|
|
188
|
+
path: '//test/container/',
|
|
189
|
+
identifier: 'test-container-id',
|
|
190
|
+
containerContentlets: [
|
|
191
|
+
{
|
|
192
|
+
uuid: 'test-uuid',
|
|
193
|
+
contentlets: [
|
|
194
|
+
{
|
|
195
|
+
_map: {
|
|
196
|
+
identifier: 'test-contentlet-id',
|
|
197
|
+
contentletField: 'contentlet value'
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
}
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const expectedResult = {
|
|
209
|
+
page: {
|
|
210
|
+
title: 'map-test',
|
|
211
|
+
url: '/map-test',
|
|
212
|
+
customField: 'custom value'
|
|
213
|
+
},
|
|
214
|
+
urlContentMap: {
|
|
215
|
+
identifier: 'test-id',
|
|
216
|
+
mapField: 'map value',
|
|
217
|
+
someNestedField: {
|
|
218
|
+
nestedField: 'nested value'
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
containers: {
|
|
222
|
+
'//test/container/': {
|
|
223
|
+
container: {
|
|
224
|
+
path: '//test/container/',
|
|
225
|
+
identifier: 'test-container-id'
|
|
226
|
+
},
|
|
227
|
+
containerStructures: undefined,
|
|
228
|
+
contentlets: {
|
|
229
|
+
'test-uuid': [
|
|
230
|
+
{
|
|
231
|
+
identifier: 'test-contentlet-id',
|
|
232
|
+
contentletField: 'contentlet value'
|
|
233
|
+
}
|
|
234
|
+
]
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const pageEntity = graphqlToPageEntity(
|
|
241
|
+
graphqlResponse as unknown as DotCMSGraphQLPageResponse
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
expect(pageEntity?.page).toEqual(expectedResult.page);
|
|
245
|
+
expect(pageEntity?.urlContentMap).toEqual(expectedResult.urlContentMap);
|
|
246
|
+
expect(pageEntity?.containers['//test/container/'].contentlets['test-uuid'][0]).toEqual(
|
|
247
|
+
expectedResult.containers['//test/container/'].contentlets['test-uuid'][0]
|
|
248
|
+
);
|
|
249
|
+
});
|
|
250
|
+
});
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
DotCMSBasicContentlet,
|
|
5
|
+
DotCMSGraphQLPageContainer,
|
|
6
|
+
DotCMSGraphQLPageResponse,
|
|
7
|
+
DotCMSPageAssetContainers,
|
|
8
|
+
DotCMSPageContainerContentlets,
|
|
9
|
+
DotCMSPage,
|
|
10
|
+
DotCMSPageAsset,
|
|
11
|
+
DotCMSContainer
|
|
12
|
+
} from '@dotcms/types';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Transforms a GraphQL Page response to a Page Entity.
|
|
16
|
+
*
|
|
17
|
+
* @param {GraphQLPageResponse} graphQLPageResponse - The GraphQL Page response object.
|
|
18
|
+
* @returns {object|null} The transformed Page Entity or null if the page is not present.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const pageEntity = graphqlToPageEntity(graphQLPageResponse);
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export const graphqlToPageEntity = (
|
|
26
|
+
graphQLPageResponse: DotCMSGraphQLPageResponse
|
|
27
|
+
): DotCMSPageAsset | null => {
|
|
28
|
+
const { page } = graphQLPageResponse;
|
|
29
|
+
|
|
30
|
+
// If there is no page, return null
|
|
31
|
+
if (!page) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const {
|
|
36
|
+
layout,
|
|
37
|
+
template,
|
|
38
|
+
containers,
|
|
39
|
+
urlContentMap,
|
|
40
|
+
viewAs,
|
|
41
|
+
host,
|
|
42
|
+
vanityUrl,
|
|
43
|
+
runningExperimentId,
|
|
44
|
+
_map,
|
|
45
|
+
...pageAsset
|
|
46
|
+
} = page;
|
|
47
|
+
const data = (_map || {}) as Record<string, unknown>;
|
|
48
|
+
|
|
49
|
+
const typedPageAsset = pageAsset as unknown as DotCMSPage;
|
|
50
|
+
|
|
51
|
+
// To prevent type errors, we cast the urlContentMap to an object
|
|
52
|
+
const urlContentMapObject = urlContentMap;
|
|
53
|
+
|
|
54
|
+
// Extract the _map data from the urlContentMap object
|
|
55
|
+
const urlContentMapData = urlContentMapObject?.['_map'];
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
layout,
|
|
59
|
+
template,
|
|
60
|
+
viewAs,
|
|
61
|
+
vanityUrl,
|
|
62
|
+
runningExperimentId,
|
|
63
|
+
site: host,
|
|
64
|
+
urlContentMap: urlContentMapData,
|
|
65
|
+
containers: parseContainers(containers as []),
|
|
66
|
+
page: {
|
|
67
|
+
...data,
|
|
68
|
+
...typedPageAsset
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Parses the containers from the GraphQL response.
|
|
75
|
+
*
|
|
76
|
+
* @param {DotCMSGraphQLPageContainer[]} [containers=[]] - The containers array from the GraphQL response.
|
|
77
|
+
* @returns {DotCMSPageAssetContainers} The parsed containers.
|
|
78
|
+
*/
|
|
79
|
+
const parseContainers = (
|
|
80
|
+
containers: DotCMSGraphQLPageContainer[] = []
|
|
81
|
+
): DotCMSPageAssetContainers => {
|
|
82
|
+
return containers.reduce(
|
|
83
|
+
(acc: DotCMSPageAssetContainers, container: DotCMSGraphQLPageContainer) => {
|
|
84
|
+
const { path, identifier, containerStructures, containerContentlets, ...rest } =
|
|
85
|
+
container;
|
|
86
|
+
|
|
87
|
+
const key = (path || identifier) as string;
|
|
88
|
+
|
|
89
|
+
acc[key] = {
|
|
90
|
+
containerStructures,
|
|
91
|
+
container: {
|
|
92
|
+
path,
|
|
93
|
+
identifier,
|
|
94
|
+
...rest
|
|
95
|
+
} as DotCMSContainer,
|
|
96
|
+
contentlets: parseContentletsToUuidMap(containerContentlets as [])
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
return acc;
|
|
100
|
+
},
|
|
101
|
+
{}
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Parses the contentlets from the GraphQL response.
|
|
107
|
+
*
|
|
108
|
+
* @param {Array<Record<string, unknown>>} containerContentlets - The contentlets array from the GraphQL response.
|
|
109
|
+
* @returns {Record<string, Array<Record<string, unknown>>>} The parsed contentlets mapped by UUID.
|
|
110
|
+
*/
|
|
111
|
+
const parseContentletsToUuidMap = (containerContentlets: DotCMSPageContainerContentlets[] = []) => {
|
|
112
|
+
return containerContentlets.reduce(
|
|
113
|
+
(acc, containerContentlet) => {
|
|
114
|
+
const { uuid, contentlets } = containerContentlet;
|
|
115
|
+
|
|
116
|
+
// TODO: This is a temporary solution, we need to find a better way to handle this.
|
|
117
|
+
acc[uuid] = contentlets.map(({ _map = {}, ...rest }) => {
|
|
118
|
+
return {
|
|
119
|
+
...(_map as Record<string, unknown>),
|
|
120
|
+
...rest
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return acc;
|
|
125
|
+
},
|
|
126
|
+
{} as Record<string, DotCMSBasicContentlet[]>
|
|
127
|
+
);
|
|
128
|
+
};
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"forceConsistentCasingInFileNames": true,
|
|
6
|
+
"strict": true,
|
|
7
|
+
"noImplicitOverride": true,
|
|
8
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
9
|
+
"noImplicitReturns": true,
|
|
10
|
+
"noFallthroughCasesInSwitch": true
|
|
11
|
+
},
|
|
12
|
+
"files": [],
|
|
13
|
+
"include": [],
|
|
14
|
+
"references": [
|
|
15
|
+
{
|
|
16
|
+
"path": "./tsconfig.lib.json"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"path": "./tsconfig.spec.json"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "../../../dist/out-tsc",
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"types": [""],
|
|
7
|
+
"target": "es2020",
|
|
8
|
+
"module": "es2020",
|
|
9
|
+
"moduleResolution": "node"
|
|
10
|
+
},
|
|
11
|
+
"include": ["src/**/*.ts"],
|
|
12
|
+
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
|
|
13
|
+
}
|