@dotcms/client 0.0.1-alpha.7 → 0.0.1-alpha.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/.eslintrc.json +18 -0
- package/jest.config.ts +15 -0
- package/package.json +24 -23
- package/project.json +63 -0
- package/src/index.ts +4 -0
- package/src/lib/client/sdk-js-client.spec.ts +258 -0
- package/src/lib/{sdk-js-client.d.ts → client/sdk-js-client.ts} +127 -13
- package/src/lib/editor/listeners/listeners.spec.ts +55 -0
- package/src/lib/editor/listeners/listeners.ts +200 -0
- package/src/lib/{postMessageToEditor.d.ts → editor/models/client.model.ts} +15 -10
- package/src/lib/editor/models/editor.model.ts +17 -0
- package/src/lib/editor/models/listeners.model.ts +46 -0
- package/src/lib/editor/sdk-editor-vtl.ts +24 -0
- package/src/lib/editor/sdk-editor.spec.ts +95 -0
- package/src/lib/editor/sdk-editor.ts +70 -0
- package/src/lib/editor/utils/editor.utils.spec.ts +164 -0
- package/src/lib/editor/utils/editor.utils.ts +151 -0
- package/tsconfig.json +22 -0
- package/tsconfig.lib.json +10 -0
- package/tsconfig.spec.json +9 -0
- package/src/index.d.ts +0 -2
- package/src/index.js +0 -3
- package/src/index.js.map +0 -1
- package/src/lib/postMessageToEditor.js +0 -42
- package/src/lib/postMessageToEditor.js.map +0 -1
- package/src/lib/sdk-js-client.js +0 -145
- package/src/lib/sdk-js-client.js.map +0 -1
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": ["../../../.eslintrc.base.json"],
|
|
3
|
+
"ignorePatterns": ["!**/*"],
|
|
4
|
+
"overrides": [
|
|
5
|
+
{
|
|
6
|
+
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
|
7
|
+
"rules": {}
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"files": ["*.ts", "*.tsx"],
|
|
11
|
+
"rules": {}
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"files": ["*.js", "*.jsx"],
|
|
15
|
+
"rules": {}
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
package/jest.config.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
export default {
|
|
3
|
+
displayName: 'sdk-client',
|
|
4
|
+
preset: '../../../jest.preset.js',
|
|
5
|
+
globals: {
|
|
6
|
+
'ts-jest': {
|
|
7
|
+
tsconfig: '<rootDir>/tsconfig.spec.json'
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
transform: {
|
|
11
|
+
'^.+\\.[tj]s$': 'ts-jest'
|
|
12
|
+
},
|
|
13
|
+
moduleFileExtensions: ['ts', 'js', 'html'],
|
|
14
|
+
coverageDirectory: '../../../coverage/libs/sdk/client'
|
|
15
|
+
};
|
package/package.json
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
2
|
+
"name": "@dotcms/client",
|
|
3
|
+
"version": "0.0.1-alpha.9",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Official JavaScript library for interacting with DotCMS REST APIs.",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/dotCMS/core.git#master"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "nx run sdk-client:build:js; cd ../../../../dotCMS/src/main/webapp/html/js/editor-js; rm -rf src package.json *.esm.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"dotCMS",
|
|
15
|
+
"CMS",
|
|
16
|
+
"Content Management",
|
|
17
|
+
"API Client",
|
|
18
|
+
"REST API"
|
|
19
|
+
],
|
|
20
|
+
"author": "dotcms <dev@dotcms.com>",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/dotCMS/core/issues"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/dotCMS/core/tree/master/core-web/libs/sdk/client/README.md"
|
|
25
26
|
}
|
package/project.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sdk-client",
|
|
3
|
+
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
+
"sourceRoot": "libs/sdk/client/src",
|
|
5
|
+
"projectType": "library",
|
|
6
|
+
"targets": {
|
|
7
|
+
"build": {
|
|
8
|
+
"executor": "@nrwl/js:tsc",
|
|
9
|
+
"outputs": ["{options.outputPath}"],
|
|
10
|
+
"options": {
|
|
11
|
+
"outputPath": "dist/libs/sdk/client",
|
|
12
|
+
"main": "libs/sdk/client/src/index.ts",
|
|
13
|
+
"tsConfig": "libs/sdk/client/tsconfig.lib.json",
|
|
14
|
+
"assets": ["libs/sdk/client/*.md"]
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"build:js": {
|
|
18
|
+
"executor": "@nrwl/rollup:rollup",
|
|
19
|
+
"outputs": ["{options.outputPath}"],
|
|
20
|
+
"options": {
|
|
21
|
+
"outputPath": "../dotCMS/src/main/webapp/html/js/editor-js",
|
|
22
|
+
"outputFileName": "sdk-editor",
|
|
23
|
+
"format": ["esm"],
|
|
24
|
+
"tsConfig": "libs/sdk/client/tsconfig.lib.json",
|
|
25
|
+
"project": "libs/sdk/client/package.json",
|
|
26
|
+
"entryFile": "libs/sdk/client/src/lib/editor/sdk-editor-vtl.ts",
|
|
27
|
+
"external": ["react/jsx-runtime"],
|
|
28
|
+
"rollupConfig": "@nrwl/react/plugins/bundle-rollup",
|
|
29
|
+
"compiler": "swc",
|
|
30
|
+
"extractCss": false
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"publish": {
|
|
34
|
+
"executor": "nx:run-commands",
|
|
35
|
+
"options": {
|
|
36
|
+
"command": "node tools/scripts/publish.mjs sdk-js-client {args.ver} {args.tag}"
|
|
37
|
+
},
|
|
38
|
+
"dependsOn": ["build"]
|
|
39
|
+
},
|
|
40
|
+
"lint": {
|
|
41
|
+
"executor": "@nrwl/linter:eslint",
|
|
42
|
+
"outputs": ["{options.outputFile}"],
|
|
43
|
+
"options": {
|
|
44
|
+
"lintFilePatterns": ["libs/sdk/client/**/*.ts"]
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"test": {
|
|
48
|
+
"executor": "@nrwl/jest:jest",
|
|
49
|
+
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
|
50
|
+
"options": {
|
|
51
|
+
"jestConfig": "libs/sdk/client/jest.config.ts",
|
|
52
|
+
"passWithNoTests": true
|
|
53
|
+
},
|
|
54
|
+
"configurations": {
|
|
55
|
+
"ci": {
|
|
56
|
+
"ci": true,
|
|
57
|
+
"codeCoverage": true
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"tags": []
|
|
63
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/// <reference types="jest" />
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
import { DotCmsClient, dotcmsClient } from './sdk-js-client';
|
|
4
|
+
global.fetch = jest.fn();
|
|
5
|
+
|
|
6
|
+
// Utility function to mock fetch responses
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
|
+
const mockFetchResponse = (body: any, ok = true, status = 200) => {
|
|
9
|
+
(fetch as jest.Mock).mockImplementationOnce(() =>
|
|
10
|
+
Promise.resolve({
|
|
11
|
+
ok,
|
|
12
|
+
status,
|
|
13
|
+
json: () => Promise.resolve(body)
|
|
14
|
+
})
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
describe('DotCmsClient', () => {
|
|
19
|
+
describe('with full configuration', () => {
|
|
20
|
+
let client: DotCmsClient;
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
(fetch as jest.Mock).mockClear();
|
|
24
|
+
|
|
25
|
+
client = dotcmsClient.init({
|
|
26
|
+
dotcmsUrl: 'http://localhost',
|
|
27
|
+
siteId: '123456',
|
|
28
|
+
authToken: 'ABC'
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('init', () => {
|
|
33
|
+
it('should initialize with valid configuration', () => {
|
|
34
|
+
const config = {
|
|
35
|
+
dotcmsUrl: 'https://example.com',
|
|
36
|
+
siteId: '123456',
|
|
37
|
+
authToken: 'ABC'
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const client = dotcmsClient.init(config);
|
|
41
|
+
expect(client).toBeDefined();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should throw error on missing dotcmsUrl', () => {
|
|
45
|
+
const config = {
|
|
46
|
+
dotcmsUrl: '',
|
|
47
|
+
siteId: '123456',
|
|
48
|
+
authToken: 'ABC'
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
expect(() => {
|
|
52
|
+
dotcmsClient.init(config);
|
|
53
|
+
}).toThrow("Invalid configuration - 'dotcmsUrl' is required");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should throw error if dotcmsUrl is not a valid URL', () => {
|
|
57
|
+
const config = {
|
|
58
|
+
dotcmsUrl: '//example.com',
|
|
59
|
+
siteId: '123456',
|
|
60
|
+
authToken: 'ABC'
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
expect(() => {
|
|
64
|
+
dotcmsClient.init(config);
|
|
65
|
+
}).toThrow("Invalid configuration - 'dotcmsUrl' must be a valid URL");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should throw error on missing authToken', () => {
|
|
69
|
+
const config = {
|
|
70
|
+
dotcmsUrl: 'https://example.com',
|
|
71
|
+
siteId: '123456',
|
|
72
|
+
authToken: ''
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
expect(() => {
|
|
76
|
+
dotcmsClient.init(config);
|
|
77
|
+
}).toThrow("Invalid configuration - 'authToken' is required");
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('page.get', () => {
|
|
82
|
+
it('should fetch page data successfully', async () => {
|
|
83
|
+
const mockResponse = { content: 'Page data' };
|
|
84
|
+
mockFetchResponse(mockResponse);
|
|
85
|
+
|
|
86
|
+
const data = await client.page.get({ path: '/home' });
|
|
87
|
+
|
|
88
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
89
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
90
|
+
'http://localhost/api/v1/page/json/home?host_id=123456',
|
|
91
|
+
{
|
|
92
|
+
headers: { Authorization: 'Bearer ABC' }
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
expect(data).toEqual(mockResponse);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should throw an error if the path is not provided', async () => {
|
|
99
|
+
await expect(client.page.get({} as any)).rejects.toThrowError(
|
|
100
|
+
`The 'path' parameter is required for the Page API`
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should get the page for specified persona', () => {
|
|
105
|
+
const mockResponse = { content: 'Page data' };
|
|
106
|
+
mockFetchResponse(mockResponse);
|
|
107
|
+
|
|
108
|
+
client.page.get({ path: '/home', personaId: 'doe123' });
|
|
109
|
+
|
|
110
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
111
|
+
'http://localhost/api/v1/page/json/home?com.dotmarketing.persona.id=doe123&host_id=123456',
|
|
112
|
+
{
|
|
113
|
+
headers: { Authorization: 'Bearer ABC' }
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should get the page for specified siteId', () => {
|
|
119
|
+
const mockResponse = { content: 'Page data' };
|
|
120
|
+
mockFetchResponse(mockResponse);
|
|
121
|
+
|
|
122
|
+
client.page.get({ path: '/home', siteId: 'host-123' });
|
|
123
|
+
|
|
124
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
125
|
+
'http://localhost/api/v1/page/json/home?host_id=host-123',
|
|
126
|
+
{
|
|
127
|
+
headers: { Authorization: 'Bearer ABC' }
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should get the page for specified language', () => {
|
|
133
|
+
const mockResponse = { content: 'Page data' };
|
|
134
|
+
mockFetchResponse(mockResponse);
|
|
135
|
+
|
|
136
|
+
client.page.get({ path: '/home', language_id: 99 });
|
|
137
|
+
|
|
138
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
139
|
+
'http://localhost/api/v1/page/json/home?language_id=99&host_id=123456',
|
|
140
|
+
{
|
|
141
|
+
headers: { Authorization: 'Bearer ABC' }
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
describe('nav.get', () => {
|
|
148
|
+
it('should fetch navigation data successfully', async () => {
|
|
149
|
+
const mockResponse = { nav: 'Navigation data' };
|
|
150
|
+
mockFetchResponse(mockResponse);
|
|
151
|
+
|
|
152
|
+
const data = await client.nav.get({ path: '/', depth: 1 });
|
|
153
|
+
|
|
154
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
155
|
+
expect(fetch).toHaveBeenCalledWith('http://localhost/api/v1/nav/?depth=1', {
|
|
156
|
+
headers: { Authorization: 'Bearer ABC' }
|
|
157
|
+
});
|
|
158
|
+
expect(data).toEqual(mockResponse);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should correctly handle the root path', async () => {
|
|
162
|
+
const mockResponse = { nav: 'Root nav data' };
|
|
163
|
+
mockFetchResponse(mockResponse);
|
|
164
|
+
|
|
165
|
+
const data = await client.nav.get({ path: '/' });
|
|
166
|
+
|
|
167
|
+
expect(fetch).toHaveBeenCalledWith('http://localhost/api/v1/nav/', {
|
|
168
|
+
headers: { Authorization: 'Bearer ABC' }
|
|
169
|
+
});
|
|
170
|
+
expect(data).toEqual(mockResponse);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('should throw an error if the path is not provided', async () => {
|
|
174
|
+
await expect(client.nav.get({} as any)).rejects.toThrowError(
|
|
175
|
+
`The 'path' parameter is required for the Nav API`
|
|
176
|
+
);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
describe('with minimal configuration', () => {
|
|
182
|
+
let client: DotCmsClient;
|
|
183
|
+
|
|
184
|
+
beforeEach(() => {
|
|
185
|
+
(fetch as jest.Mock).mockClear();
|
|
186
|
+
|
|
187
|
+
client = dotcmsClient.init({
|
|
188
|
+
dotcmsUrl: 'http://localhost',
|
|
189
|
+
authToken: 'ABC'
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should get the page without siteId', () => {
|
|
194
|
+
const mockResponse = { content: 'Page data' };
|
|
195
|
+
mockFetchResponse(mockResponse);
|
|
196
|
+
|
|
197
|
+
client.page.get({ path: '/home' });
|
|
198
|
+
|
|
199
|
+
expect(fetch).toHaveBeenCalledWith('http://localhost/api/v1/page/json/home', {
|
|
200
|
+
headers: { Authorization: 'Bearer ABC' }
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe('with requestOptions', () => {
|
|
206
|
+
let client: DotCmsClient;
|
|
207
|
+
|
|
208
|
+
beforeEach(() => {
|
|
209
|
+
(fetch as jest.Mock).mockClear();
|
|
210
|
+
|
|
211
|
+
client = dotcmsClient.init({
|
|
212
|
+
dotcmsUrl: 'http://localhost',
|
|
213
|
+
siteId: '123456',
|
|
214
|
+
authToken: 'ABC',
|
|
215
|
+
requestOptions: {
|
|
216
|
+
headers: {
|
|
217
|
+
'X-My-Header': 'my-value'
|
|
218
|
+
},
|
|
219
|
+
cache: 'no-cache'
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('should fetch page data with extra headers and cache', async () => {
|
|
225
|
+
const mockResponse = { content: 'Page data' };
|
|
226
|
+
mockFetchResponse(mockResponse);
|
|
227
|
+
|
|
228
|
+
const data = await client.page.get({ path: '/home' });
|
|
229
|
+
|
|
230
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
231
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
232
|
+
'http://localhost/api/v1/page/json/home?host_id=123456',
|
|
233
|
+
{
|
|
234
|
+
headers: { Authorization: 'Bearer ABC', 'X-My-Header': 'my-value' },
|
|
235
|
+
cache: 'no-cache'
|
|
236
|
+
}
|
|
237
|
+
);
|
|
238
|
+
expect(data).toEqual(mockResponse);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should fetch bav data with extra headers and cache', async () => {
|
|
242
|
+
const mockResponse = { content: 'Page data' };
|
|
243
|
+
mockFetchResponse(mockResponse);
|
|
244
|
+
|
|
245
|
+
const data = await client.nav.get();
|
|
246
|
+
|
|
247
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
248
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
249
|
+
'http://localhost/api/v1/nav/?depth=0&languageId=1',
|
|
250
|
+
{
|
|
251
|
+
headers: { Authorization: 'Bearer ABC', 'X-My-Header': 'my-value' },
|
|
252
|
+
cache: 'no-cache'
|
|
253
|
+
}
|
|
254
|
+
);
|
|
255
|
+
expect(data).toEqual(mockResponse);
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
});
|
|
@@ -36,6 +36,7 @@ export interface ClientConfig {
|
|
|
36
36
|
*/
|
|
37
37
|
requestOptions?: Omit<RequestInit, 'body' | 'method'>;
|
|
38
38
|
}
|
|
39
|
+
|
|
39
40
|
type PageApiOptions = {
|
|
40
41
|
/**
|
|
41
42
|
* The path of the page you want to retrieve.
|
|
@@ -49,6 +50,12 @@ type PageApiOptions = {
|
|
|
49
50
|
* @type {number}
|
|
50
51
|
*/
|
|
51
52
|
siteId?: string;
|
|
53
|
+
/**
|
|
54
|
+
* The mode of the page you want to retrieve. If not provided will use the default mode of the site.
|
|
55
|
+
*
|
|
56
|
+
* @type {number}
|
|
57
|
+
*/
|
|
58
|
+
mode?: string;
|
|
52
59
|
/**
|
|
53
60
|
* The language id of the page you want to retrieve. If not provided will use the default language of the site.
|
|
54
61
|
*
|
|
@@ -74,6 +81,7 @@ type PageApiOptions = {
|
|
|
74
81
|
*/
|
|
75
82
|
depth?: number;
|
|
76
83
|
};
|
|
84
|
+
|
|
77
85
|
type NavApiOptions = {
|
|
78
86
|
/**
|
|
79
87
|
* The root path to begin traversing the folder tree.
|
|
@@ -109,6 +117,17 @@ type NavApiOptions = {
|
|
|
109
117
|
*/
|
|
110
118
|
languageId?: number;
|
|
111
119
|
};
|
|
120
|
+
|
|
121
|
+
function isValidUrl(url: string): boolean {
|
|
122
|
+
try {
|
|
123
|
+
new URL(url);
|
|
124
|
+
|
|
125
|
+
return true;
|
|
126
|
+
} catch (error) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
112
131
|
/**
|
|
113
132
|
* `DotCmsClient` is a TypeScript class that provides methods to interact with the DotCMS REST API.
|
|
114
133
|
* It requires a configuration object on instantiation, which includes the DotCMS URL, site ID, and authentication token.
|
|
@@ -124,11 +143,37 @@ type NavApiOptions = {
|
|
|
124
143
|
* @method nav.get(options: NavApiOptions = { depth: 0, path: '/', languageId: 1 }): Promise<unknown> - Retrieves information about the dotCMS file and folder tree.
|
|
125
144
|
*
|
|
126
145
|
*/
|
|
127
|
-
export
|
|
128
|
-
private config;
|
|
129
|
-
private requestOptions
|
|
130
|
-
|
|
131
|
-
|
|
146
|
+
export class DotCmsClient {
|
|
147
|
+
private config: ClientConfig;
|
|
148
|
+
private requestOptions!: Omit<RequestInit, 'body' | 'method'>;
|
|
149
|
+
|
|
150
|
+
constructor(
|
|
151
|
+
config: ClientConfig = { dotcmsUrl: '', authToken: '', requestOptions: {}, siteId: '' }
|
|
152
|
+
) {
|
|
153
|
+
if (!config.dotcmsUrl) {
|
|
154
|
+
throw new Error("Invalid configuration - 'dotcmsUrl' is required");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (!isValidUrl(config.dotcmsUrl)) {
|
|
158
|
+
throw new Error("Invalid configuration - 'dotcmsUrl' must be a valid URL");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!config.authToken) {
|
|
162
|
+
throw new Error("Invalid configuration - 'authToken' is required");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
this.config = config;
|
|
166
|
+
|
|
167
|
+
this.requestOptions = {
|
|
168
|
+
...this.config.requestOptions,
|
|
169
|
+
headers: {
|
|
170
|
+
Authorization: `Bearer ${this.config.authToken}`,
|
|
171
|
+
...this.config.requestOptions?.headers
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
page = {
|
|
132
177
|
/**
|
|
133
178
|
* `page.get` is an asynchronous method of the `DotCmsClient` class that retrieves all the elements of any Page in your dotCMS system in JSON format.
|
|
134
179
|
* It takes a `PageApiOptions` object as a parameter and returns a Promise that resolves to the response from the DotCMS API.
|
|
@@ -142,9 +187,41 @@ export declare class DotCmsClient {
|
|
|
142
187
|
* @returns {Promise<unknown>} - A Promise that resolves to the response from the DotCMS API.
|
|
143
188
|
* @throws {Error} - Throws an error if the options are not valid.
|
|
144
189
|
*/
|
|
145
|
-
get: (options: PageApiOptions)
|
|
190
|
+
get: async (options: PageApiOptions): Promise<unknown> => {
|
|
191
|
+
this.validatePageOptions(options);
|
|
192
|
+
|
|
193
|
+
const queryParamsObj: Record<string, string> = {};
|
|
194
|
+
for (const [key, value] of Object.entries(options)) {
|
|
195
|
+
if (value === undefined || key === 'path' || key === 'siteId') continue;
|
|
196
|
+
|
|
197
|
+
if (key === 'personaId') {
|
|
198
|
+
queryParamsObj['com.dotmarketing.persona.id'] = String(value);
|
|
199
|
+
} else if (key === 'mode' && value) {
|
|
200
|
+
queryParamsObj['mode'] = String(value);
|
|
201
|
+
} else {
|
|
202
|
+
queryParamsObj[key] = String(value);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const queryHostId = options.siteId ?? this.config.siteId ?? '';
|
|
207
|
+
|
|
208
|
+
if (queryHostId) {
|
|
209
|
+
queryParamsObj['host_id'] = queryHostId;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const queryParams = new URLSearchParams(queryParamsObj).toString();
|
|
213
|
+
|
|
214
|
+
const formattedPath = options.path.startsWith('/') ? options.path : `/${options.path}`;
|
|
215
|
+
const url = `${this.config.dotcmsUrl}/api/v1/page/json${formattedPath}${
|
|
216
|
+
queryParams ? `?${queryParams}` : ''
|
|
217
|
+
}`;
|
|
218
|
+
const response = await fetch(url, this.requestOptions);
|
|
219
|
+
|
|
220
|
+
return response.json();
|
|
221
|
+
}
|
|
146
222
|
};
|
|
147
|
-
|
|
223
|
+
|
|
224
|
+
nav = {
|
|
148
225
|
/**
|
|
149
226
|
* `nav.get` is an asynchronous method of the `DotCmsClient` class that retrieves information about the dotCMS file and folder tree.
|
|
150
227
|
* It takes a `NavApiOptions` object as a parameter (with default values) and returns a Promise that resolves to the response from the DotCMS API.
|
|
@@ -156,11 +233,47 @@ export declare class DotCmsClient {
|
|
|
156
233
|
* @returns {Promise<unknown>} - A Promise that resolves to the response from the DotCMS API.
|
|
157
234
|
* @throws {Error} - Throws an error if the options are not valid.
|
|
158
235
|
*/
|
|
159
|
-
get: (
|
|
236
|
+
get: async (
|
|
237
|
+
options: NavApiOptions = { depth: 0, path: '/', languageId: 1 }
|
|
238
|
+
): Promise<unknown> => {
|
|
239
|
+
this.validateNavOptions(options);
|
|
240
|
+
|
|
241
|
+
// Extract the 'path' from the options and prepare the rest as query parameters
|
|
242
|
+
const { path, ...queryParamsOptions } = options;
|
|
243
|
+
const queryParamsObj: Record<string, string> = {};
|
|
244
|
+
Object.entries(queryParamsOptions).forEach(([key, value]) => {
|
|
245
|
+
if (value !== undefined) {
|
|
246
|
+
queryParamsObj[key] = String(value);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const queryParams = new URLSearchParams(queryParamsObj).toString();
|
|
251
|
+
|
|
252
|
+
// Format the URL correctly depending on the 'path' value
|
|
253
|
+
const formattedPath = path === '/' ? '/' : `/${path}`;
|
|
254
|
+
const url = `${this.config.dotcmsUrl}/api/v1/nav${formattedPath}${
|
|
255
|
+
queryParams ? `?${queryParams}` : ''
|
|
256
|
+
}`;
|
|
257
|
+
|
|
258
|
+
const response = await fetch(url, this.requestOptions);
|
|
259
|
+
|
|
260
|
+
return response.json();
|
|
261
|
+
}
|
|
160
262
|
};
|
|
161
|
-
|
|
162
|
-
private
|
|
263
|
+
|
|
264
|
+
private validatePageOptions(options: PageApiOptions): void {
|
|
265
|
+
if (!options.path) {
|
|
266
|
+
throw new Error("The 'path' parameter is required for the Page API");
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
private validateNavOptions(options: NavApiOptions): void {
|
|
271
|
+
if (!options.path) {
|
|
272
|
+
throw new Error("The 'path' parameter is required for the Nav API");
|
|
273
|
+
}
|
|
274
|
+
}
|
|
163
275
|
}
|
|
276
|
+
|
|
164
277
|
/**
|
|
165
278
|
* `dotcmsClient` is an object that provides a method to initialize the DotCMS SDK client.
|
|
166
279
|
* It has a single method `init` which takes a configuration object and returns an instance of the `DotCmsClient` class.
|
|
@@ -169,7 +282,7 @@ export declare class DotCmsClient {
|
|
|
169
282
|
*
|
|
170
283
|
* @method init(config: ClientConfig): DotCmsClient - Initializes the SDK client.
|
|
171
284
|
*/
|
|
172
|
-
export
|
|
285
|
+
export const dotcmsClient = {
|
|
173
286
|
/**
|
|
174
287
|
* `init` is a method of the `dotcmsClient` object that initializes the SDK client.
|
|
175
288
|
* It takes a configuration object as a parameter and returns an instance of the `DotCmsClient` class.
|
|
@@ -178,6 +291,7 @@ export declare const dotcmsClient: {
|
|
|
178
291
|
* @param {ClientConfig} config - The configuration object for the DotCMS client.
|
|
179
292
|
* @returns {DotCmsClient} - An instance of the {@link DotCmsClient} class.
|
|
180
293
|
*/
|
|
181
|
-
init: (config: ClientConfig) =>
|
|
294
|
+
init: (config: ClientConfig): DotCmsClient => {
|
|
295
|
+
return new DotCmsClient(config);
|
|
296
|
+
}
|
|
182
297
|
};
|
|
183
|
-
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
listenContentChange,
|
|
3
|
+
listenEditorMessages,
|
|
4
|
+
listenHoveredContentlet,
|
|
5
|
+
pingEditor,
|
|
6
|
+
scrollHandler
|
|
7
|
+
} from './listeners';
|
|
8
|
+
|
|
9
|
+
import { CUSTOMER_ACTIONS, postMessageToEditor } from '../models/client.model';
|
|
10
|
+
|
|
11
|
+
jest.mock('../models/client.model', () => ({
|
|
12
|
+
postMessageToEditor: jest.fn(),
|
|
13
|
+
CUSTOMER_ACTIONS: {
|
|
14
|
+
NAVIGATION_UPDATE: 'set-url',
|
|
15
|
+
SET_BOUNDS: 'set-bounds',
|
|
16
|
+
SET_CONTENTLET: 'set-contentlet',
|
|
17
|
+
IFRAME_SCROLL: 'scroll',
|
|
18
|
+
PING_EDITOR: 'ping-editor',
|
|
19
|
+
CONTENT_CHANGE: 'content-change',
|
|
20
|
+
NOOP: 'noop'
|
|
21
|
+
}
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
describe('listeners', () => {
|
|
25
|
+
it('should listen editor messages', () => {
|
|
26
|
+
const addEventListenerSpy = jest.spyOn(window, 'addEventListener');
|
|
27
|
+
listenEditorMessages();
|
|
28
|
+
expect(addEventListenerSpy).toHaveBeenCalledWith('message', expect.any(Function));
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should listen to hovered contentlet', () => {
|
|
32
|
+
const addEventListenerSpy = jest.spyOn(document, 'addEventListener');
|
|
33
|
+
listenHoveredContentlet();
|
|
34
|
+
expect(addEventListenerSpy).toHaveBeenCalledWith('pointermove', expect.any(Function));
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should handle scroll', () => {
|
|
38
|
+
const addEventListenerSpy = jest.spyOn(window, 'addEventListener');
|
|
39
|
+
scrollHandler();
|
|
40
|
+
expect(addEventListenerSpy).toHaveBeenCalledWith('scroll', expect.any(Function));
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should listen to content change', () => {
|
|
44
|
+
const observeSpy = jest.spyOn(MutationObserver.prototype, 'observe');
|
|
45
|
+
listenContentChange();
|
|
46
|
+
expect(observeSpy).toHaveBeenCalledWith(document, { childList: true, subtree: true });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should send ping to editor', () => {
|
|
50
|
+
pingEditor();
|
|
51
|
+
expect(postMessageToEditor).toHaveBeenCalledWith({
|
|
52
|
+
action: CUSTOMER_ACTIONS.PING_EDITOR
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|