@dotcms/client 0.0.1-alpha.8 → 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} +121 -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 -148
- 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.
|
|
@@ -80,6 +81,7 @@ type PageApiOptions = {
|
|
|
80
81
|
*/
|
|
81
82
|
depth?: number;
|
|
82
83
|
};
|
|
84
|
+
|
|
83
85
|
type NavApiOptions = {
|
|
84
86
|
/**
|
|
85
87
|
* The root path to begin traversing the folder tree.
|
|
@@ -115,6 +117,17 @@ type NavApiOptions = {
|
|
|
115
117
|
*/
|
|
116
118
|
languageId?: number;
|
|
117
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
|
+
|
|
118
131
|
/**
|
|
119
132
|
* `DotCmsClient` is a TypeScript class that provides methods to interact with the DotCMS REST API.
|
|
120
133
|
* It requires a configuration object on instantiation, which includes the DotCMS URL, site ID, and authentication token.
|
|
@@ -130,11 +143,37 @@ type NavApiOptions = {
|
|
|
130
143
|
* @method nav.get(options: NavApiOptions = { depth: 0, path: '/', languageId: 1 }): Promise<unknown> - Retrieves information about the dotCMS file and folder tree.
|
|
131
144
|
*
|
|
132
145
|
*/
|
|
133
|
-
export
|
|
134
|
-
private config;
|
|
135
|
-
private requestOptions
|
|
136
|
-
|
|
137
|
-
|
|
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 = {
|
|
138
177
|
/**
|
|
139
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.
|
|
140
179
|
* It takes a `PageApiOptions` object as a parameter and returns a Promise that resolves to the response from the DotCMS API.
|
|
@@ -148,9 +187,41 @@ export declare class DotCmsClient {
|
|
|
148
187
|
* @returns {Promise<unknown>} - A Promise that resolves to the response from the DotCMS API.
|
|
149
188
|
* @throws {Error} - Throws an error if the options are not valid.
|
|
150
189
|
*/
|
|
151
|
-
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
|
+
}
|
|
152
222
|
};
|
|
153
|
-
|
|
223
|
+
|
|
224
|
+
nav = {
|
|
154
225
|
/**
|
|
155
226
|
* `nav.get` is an asynchronous method of the `DotCmsClient` class that retrieves information about the dotCMS file and folder tree.
|
|
156
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.
|
|
@@ -162,11 +233,47 @@ export declare class DotCmsClient {
|
|
|
162
233
|
* @returns {Promise<unknown>} - A Promise that resolves to the response from the DotCMS API.
|
|
163
234
|
* @throws {Error} - Throws an error if the options are not valid.
|
|
164
235
|
*/
|
|
165
|
-
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
|
+
}
|
|
166
262
|
};
|
|
167
|
-
|
|
168
|
-
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
|
+
}
|
|
169
275
|
}
|
|
276
|
+
|
|
170
277
|
/**
|
|
171
278
|
* `dotcmsClient` is an object that provides a method to initialize the DotCMS SDK client.
|
|
172
279
|
* It has a single method `init` which takes a configuration object and returns an instance of the `DotCmsClient` class.
|
|
@@ -175,7 +282,7 @@ export declare class DotCmsClient {
|
|
|
175
282
|
*
|
|
176
283
|
* @method init(config: ClientConfig): DotCmsClient - Initializes the SDK client.
|
|
177
284
|
*/
|
|
178
|
-
export
|
|
285
|
+
export const dotcmsClient = {
|
|
179
286
|
/**
|
|
180
287
|
* `init` is a method of the `dotcmsClient` object that initializes the SDK client.
|
|
181
288
|
* It takes a configuration object as a parameter and returns an instance of the `DotCmsClient` class.
|
|
@@ -184,6 +291,7 @@ export declare const dotcmsClient: {
|
|
|
184
291
|
* @param {ClientConfig} config - The configuration object for the DotCMS client.
|
|
185
292
|
* @returns {DotCmsClient} - An instance of the {@link DotCmsClient} class.
|
|
186
293
|
*/
|
|
187
|
-
init: (config: ClientConfig) =>
|
|
294
|
+
init: (config: ClientConfig): DotCmsClient => {
|
|
295
|
+
return new DotCmsClient(config);
|
|
296
|
+
}
|
|
188
297
|
};
|
|
189
|
-
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
|
+
});
|