@squiz/component-cli-lib 1.2.12 → 1.2.13-alpha.0
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/.env.example +9 -0
- package/.gitlab-ci.yml +2 -3
- package/CHANGELOG.md +11 -419
- package/jest.config.ts +9 -1
- package/jest.integration.config.ts +7 -2
- package/lib/component-dev-folder-structures.integration.spec.js +7 -7
- package/lib/component-dev-folder-structures.integration.spec.js.map +1 -1
- package/lib/component-dev.integration.spec.js +1 -1
- package/lib/component-dev.integration.spec.js.map +1 -1
- package/lib/component-dev.js +16 -9
- package/lib/component-dev.js.map +1 -1
- package/lib/integration-tests/__components__/cmp-format-string/manifest.json +40 -0
- package/lib/integration-tests/helper.d.ts +1 -0
- package/lib/integration-tests/helper.js +47 -1
- package/lib/integration-tests/helper.js.map +1 -1
- package/lib/integration-tests/upload-and-render-component.integration.spec.js +61 -7
- package/lib/integration-tests/upload-and-render-component.integration.spec.js.map +1 -1
- package/lib/upload-component-folder.js +2 -1
- package/lib/upload-component-folder.js.map +1 -1
- package/package.json +12 -11
- package/src/component-dev-folder-structures.integration.spec.ts +13 -7
- package/src/component-dev.integration.spec.ts +1 -1
- package/src/component-dev.ts +28 -16
- package/src/integration-tests/__components__/cmp-format-string/main.js +7 -0
- package/src/integration-tests/__components__/cmp-format-string/manifest.json +41 -0
- package/src/integration-tests/__components__/cmp-static-file-test/main.js +1 -5
- package/src/integration-tests/__components__/invalid-manifest/main.js +0 -4
- package/src/integration-tests/__components__/invalid-upload/main.js +0 -4
- package/src/integration-tests/helper.ts +54 -0
- package/src/integration-tests/upload-and-render-component.integration.spec.ts +68 -7
- package/src/upload-component-folder.ts +2 -1
- package/tsconfig.json +3 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -21,7 +21,7 @@ describe('component-dev', () => {
|
|
|
21
21
|
|
|
22
22
|
it('should fail validation when requesting a function with a missing entry file', async () => {
|
|
23
23
|
const response = await request.get(
|
|
24
|
-
'/r/
|
|
24
|
+
'/r/unit-test-components/test-component/1.0.3/non-existent-entry-file?_componentSet=set&something=not-used',
|
|
25
25
|
);
|
|
26
26
|
|
|
27
27
|
expect(response.body).toEqual({
|
package/src/component-dev.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
ComponentPreviewService,
|
|
3
3
|
ComponentRunnerServiceWithWorkers,
|
|
4
|
+
RenderInputService,
|
|
4
5
|
setupRenderRuntimeServer,
|
|
5
6
|
} from '@squiz/render-runtime-lib';
|
|
6
7
|
import { getLogger, LoggerOptions } from '@squiz/dx-logger-lib';
|
|
7
8
|
import path from 'path';
|
|
8
9
|
import { ComponentFunctionService, ComponentSetServiceForLocalDev, ManifestServiceForDev } from '@squiz/component-lib';
|
|
9
10
|
import open from 'open';
|
|
11
|
+
import { DevelopmentApiKeyService } from '@squiz/dx-common-lib';
|
|
12
|
+
import { JsonValidationService } from '@squiz/dx-json-schema-lib';
|
|
10
13
|
|
|
11
14
|
/**
|
|
12
15
|
* startDevelopmentRender starts a dev-mode render stack for any
|
|
@@ -22,9 +25,10 @@ export function startDevelopmentRender(
|
|
|
22
25
|
options: { port: number; loggingFormat?: LoggerOptions['format']; noBrowser?: boolean },
|
|
23
26
|
) {
|
|
24
27
|
const logger = getLogger({ name: 'component-dev', format: options.loggingFormat || 'human' });
|
|
25
|
-
const rootUrl = `http://localhost:${options.port}`;
|
|
26
28
|
const dataMountPoint = path.resolve(process.cwd(), componentPath);
|
|
27
29
|
|
|
30
|
+
const rootUrl = `http://localhost:${options.port}`;
|
|
31
|
+
|
|
28
32
|
const componentRunnerService = new ComponentRunnerServiceWithWorkers(
|
|
29
33
|
{
|
|
30
34
|
dataMountPoint,
|
|
@@ -33,23 +37,31 @@ export function startDevelopmentRender(
|
|
|
33
37
|
},
|
|
34
38
|
logger,
|
|
35
39
|
);
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
},
|
|
48
|
-
{ rootUrl },
|
|
40
|
+
const jsonValidationService = new JsonValidationService();
|
|
41
|
+
const componentFunctionService = new ComponentFunctionService(rootUrl, jsonValidationService);
|
|
42
|
+
const componentSetService = new ComponentSetServiceForLocalDev(logger);
|
|
43
|
+
const manifestService = new ManifestServiceForDev(dataMountPoint, logger, jsonValidationService);
|
|
44
|
+
const contentItemService = undefined;
|
|
45
|
+
const renderInputService = new RenderInputService(
|
|
46
|
+
componentSetService,
|
|
47
|
+
manifestService,
|
|
48
|
+
componentFunctionService,
|
|
49
|
+
contentItemService,
|
|
50
|
+
rootUrl,
|
|
49
51
|
);
|
|
52
|
+
const webServer = setupRenderRuntimeServer({
|
|
53
|
+
logger,
|
|
54
|
+
componentRunnerService,
|
|
55
|
+
componentFunctionService,
|
|
56
|
+
componentPreviewService: new ComponentPreviewService(),
|
|
57
|
+
componentSetService,
|
|
58
|
+
manifestService,
|
|
59
|
+
renderInputService,
|
|
60
|
+
apiKeyService: new DevelopmentApiKeyService(),
|
|
61
|
+
});
|
|
50
62
|
|
|
51
63
|
const server = webServer.listen(options.port, () => {
|
|
52
|
-
logger.info(`Component development webserver started on port ${
|
|
64
|
+
logger.info(`Component development webserver started on port ${rootUrl}`);
|
|
53
65
|
});
|
|
54
66
|
|
|
55
67
|
if (options?.noBrowser !== true) {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://localhost:3000/schemas/v1.json#",
|
|
3
|
+
|
|
4
|
+
"name": "cmp-format-string",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"mainFunction": "main",
|
|
7
|
+
"displayName": "some-display-name",
|
|
8
|
+
"namespace": "fixtures",
|
|
9
|
+
"description": "some-description",
|
|
10
|
+
"functions": [
|
|
11
|
+
{
|
|
12
|
+
"name": "main",
|
|
13
|
+
"entry": "main.js",
|
|
14
|
+
"input": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"properties": {
|
|
17
|
+
"text": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"format": "multi-line"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"required": ["text"]
|
|
23
|
+
},
|
|
24
|
+
"output": { "responseType": "html" }
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"previews": {
|
|
28
|
+
"test-preview": {
|
|
29
|
+
"functionData": {
|
|
30
|
+
"main": {
|
|
31
|
+
"inputData": {
|
|
32
|
+
"type": "inline",
|
|
33
|
+
"value": {
|
|
34
|
+
"text": "this is a test"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @param {object} input
|
|
3
|
-
* @param {ComponentInfo} info
|
|
4
|
-
*/
|
|
5
1
|
module.exports = async function (input, info) {
|
|
6
2
|
return (
|
|
7
3
|
`<div>Input: ${input.something}</div>` +
|
|
8
|
-
`<div>${info.
|
|
4
|
+
`<div>${info.componentName} ${info.version} ${info.ctx.getStaticResourceUrl('birthday-cake.png')}</div>`
|
|
9
5
|
);
|
|
10
6
|
};
|
|
@@ -8,6 +8,7 @@ import { ComponentSetWebModelForCreate } from '@squiz/component-lib';
|
|
|
8
8
|
import { parseEnvVarForVar } from '@squiz/dx-common-lib';
|
|
9
9
|
import { ContentApi } from '@squiz/component-web-api-lib';
|
|
10
10
|
import { config } from 'dotenv';
|
|
11
|
+
import { execSync } from 'child_process';
|
|
11
12
|
|
|
12
13
|
config();
|
|
13
14
|
|
|
@@ -27,22 +28,75 @@ const configObj: Config = {
|
|
|
27
28
|
ci_buildBranch: parseEnvVarForVar('CI_COMMIT_REF_NAME'),
|
|
28
29
|
};
|
|
29
30
|
|
|
31
|
+
if (!configObj.ci_buildVersion) {
|
|
32
|
+
configObj.ci_buildVersion = execSync('git rev-parse --short HEAD', { encoding: 'utf-8' }).trim();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!configObj.ci_buildBranch) {
|
|
36
|
+
configObj.ci_buildBranch = execSync('git branch --show-current', { encoding: 'utf-8' }).trim();
|
|
37
|
+
}
|
|
38
|
+
|
|
30
39
|
export default configObj;
|
|
31
40
|
|
|
41
|
+
const DXP_SERVICE_NAME = 'dxpComponents';
|
|
42
|
+
const ALL_PERMISSIONS_ROLE = {
|
|
43
|
+
privileges: [
|
|
44
|
+
'COMPONENT_DEPLOY',
|
|
45
|
+
'COMPONENT_DELETE',
|
|
46
|
+
'COMPONENT_READ',
|
|
47
|
+
'COMPONENT_SET_RULES_READ',
|
|
48
|
+
'COMPONENT_SET_RULES_WRITE',
|
|
49
|
+
|
|
50
|
+
'COMPONENT_SET_READ',
|
|
51
|
+
'COMPONENT_SET_WRITE',
|
|
52
|
+
|
|
53
|
+
'COMPONENT_SET_ENVIRONMENT_READ',
|
|
54
|
+
'COMPONENT_SET_ENVIRONMENT_WRITE',
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const validToken = {
|
|
59
|
+
organisationId: 'aa',
|
|
60
|
+
permission: '',
|
|
61
|
+
tenant: 'zz',
|
|
62
|
+
tenantId: 'zz',
|
|
63
|
+
userId: 'zz',
|
|
64
|
+
|
|
65
|
+
service: {
|
|
66
|
+
[DXP_SERVICE_NAME]: {
|
|
67
|
+
privileges: ALL_PERMISSIONS_ROLE.privileges,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const authToken = `Bearer xxx.${Buffer.from(JSON.stringify(validToken)).toString('base64')}.zzz`;
|
|
73
|
+
|
|
32
74
|
export const managementService = axios.create({
|
|
33
75
|
baseURL: configObj.managementServiceUrl + '/v1',
|
|
76
|
+
headers: {
|
|
77
|
+
authorization: authToken,
|
|
78
|
+
},
|
|
34
79
|
});
|
|
35
80
|
|
|
36
81
|
export const managementServiceRoot = axios.create({
|
|
37
82
|
baseURL: configObj.managementServiceUrl,
|
|
83
|
+
headers: {
|
|
84
|
+
authorization: authToken,
|
|
85
|
+
},
|
|
38
86
|
});
|
|
39
87
|
|
|
40
88
|
export const renderService = axios.create({
|
|
41
89
|
baseURL: configObj.renderServiceUrl,
|
|
90
|
+
headers: {
|
|
91
|
+
authorization: authToken,
|
|
92
|
+
},
|
|
42
93
|
});
|
|
43
94
|
|
|
44
95
|
export const contentService = axios.create({
|
|
45
96
|
baseURL: configObj.contentServiceUrl,
|
|
97
|
+
headers: {
|
|
98
|
+
authorization: authToken,
|
|
99
|
+
},
|
|
46
100
|
});
|
|
47
101
|
|
|
48
102
|
export const ci_buildVersion = configObj.ci_buildVersion;
|
|
@@ -74,7 +74,7 @@ describe('uploading a component', () => {
|
|
|
74
74
|
testFilesDir,
|
|
75
75
|
);
|
|
76
76
|
expect(mockConsoleError.mock.calls[0][0]).toEqual(
|
|
77
|
-
color.red('failed validation: /name pattern must match pattern "^[a-zA-Z0-9_\\-]+$"'),
|
|
77
|
+
color.red('failed validation: /name: pattern must match pattern "^[a-zA-Z0-9_\\-]+$"'),
|
|
78
78
|
);
|
|
79
79
|
});
|
|
80
80
|
|
|
@@ -97,6 +97,65 @@ describe('uploading a component', () => {
|
|
|
97
97
|
});
|
|
98
98
|
});
|
|
99
99
|
|
|
100
|
+
describe('Deploy a basic component having a input with multiline format', () => {
|
|
101
|
+
beforeAll(async () => {
|
|
102
|
+
await deleteComponentSet(webPath);
|
|
103
|
+
await deleteContentItem(contentItemId);
|
|
104
|
+
for (const componentName of getTestComponents()) {
|
|
105
|
+
try {
|
|
106
|
+
await managementService.delete(`/component/${componentName}`);
|
|
107
|
+
} catch {
|
|
108
|
+
// no op
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
beforeEach(() => {
|
|
114
|
+
console.error = mockConsoleError;
|
|
115
|
+
console.log = mockConsoleLog;
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
afterEach(() => {
|
|
119
|
+
console.error = orgConsoleError;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('Should upload the component and return a valid url to preview', async () => {
|
|
123
|
+
const componentPath = path.join(__dirname, '/__components__/cmp-format-string');
|
|
124
|
+
await uploadComponentFolder(managementServiceRoot, configObj.managementServiceUrl, componentPath, testFilesDir);
|
|
125
|
+
const uploadedComponent = '<a href="/r/fixtures/cmp-format-string/1.0.0?_previewKey=test-preview">1.0.0</a>';
|
|
126
|
+
const get = await supertest(configObj.renderServiceUrl).get('/');
|
|
127
|
+
expect(get.status).toEqual(200);
|
|
128
|
+
expect((get as any)?.res?.text).toContain(uploadedComponent);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('Should render a component with multi-line format string input', async () => {
|
|
132
|
+
const componentSet: ComponentSetWebModelForCreate = {
|
|
133
|
+
webPath,
|
|
134
|
+
displayName: 'some-display-name',
|
|
135
|
+
description: 'Set description',
|
|
136
|
+
headers: {},
|
|
137
|
+
environmentVariables: {},
|
|
138
|
+
components: {
|
|
139
|
+
'fixtures/cmp-format-string': [{ environmentVariables: {}, version: '1.0.0' }],
|
|
140
|
+
},
|
|
141
|
+
componentVersionRules: {},
|
|
142
|
+
};
|
|
143
|
+
await addComponentSet(componentSet);
|
|
144
|
+
await addContentItem({
|
|
145
|
+
id: contentItemId,
|
|
146
|
+
schemaName: 'fixtures/cmp-format-string/1.0.0/main',
|
|
147
|
+
content: {
|
|
148
|
+
text: 'from-content-item-service',
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
const response = await renderService.get(
|
|
152
|
+
`/r/fixtures/cmp-format-string/1.0.0/?_contentItemId=${contentItemId}&_componentSet=${webPath}`,
|
|
153
|
+
);
|
|
154
|
+
expect(response.status).toEqual(200);
|
|
155
|
+
expect(response.data).toEqual(`<div>Input: from-content-item-service</div>`);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
100
159
|
describe('Deploy basic component having a static file', () => {
|
|
101
160
|
// component to deploy for this test
|
|
102
161
|
const componentPath = path.join(__dirname, '/__components__/cmp-static-file-test');
|
|
@@ -131,7 +190,7 @@ describe('uploading a component', () => {
|
|
|
131
190
|
testFilesDir,
|
|
132
191
|
);
|
|
133
192
|
|
|
134
|
-
const uploadedComponent = '<a href="/
|
|
193
|
+
const uploadedComponent = '<a href="/r/smoke-test-components/cmp-static-file-test/1.0.0">1.0.0</a>';
|
|
135
194
|
const get = await supertest(configObj.renderServiceUrl).get('/');
|
|
136
195
|
expect(get.status).toEqual(200);
|
|
137
196
|
expect((get as any)?.res?.text).toContain(uploadedComponent);
|
|
@@ -172,14 +231,14 @@ describe('uploading a component', () => {
|
|
|
172
231
|
await addComponentSet(componentSet);
|
|
173
232
|
|
|
174
233
|
const response = await renderService.get(
|
|
175
|
-
`/r
|
|
234
|
+
`/r/smoke-test-components/cmp-static-file-test/1.0.0/?_componentSet=${webPath}&something=hello`,
|
|
176
235
|
);
|
|
177
236
|
expect(response.status).toEqual(200);
|
|
178
237
|
expect(response.data).toEqual(
|
|
179
238
|
[
|
|
180
239
|
'<div>Input: hello</div>',
|
|
181
240
|
'<div>smoke-test-components/cmp-static-file-test 1.0.0 ',
|
|
182
|
-
`${configObj.renderServiceUrl}/s
|
|
241
|
+
`${configObj.renderServiceUrl}/s/smoke-test-components/cmp-static-file-test/1.0.0/birthday-cake.png?_componentSet=${webPath}</div>`,
|
|
183
242
|
].join(''),
|
|
184
243
|
);
|
|
185
244
|
});
|
|
@@ -207,17 +266,19 @@ describe('uploading a component', () => {
|
|
|
207
266
|
},
|
|
208
267
|
});
|
|
209
268
|
|
|
210
|
-
console.log(
|
|
269
|
+
console.log(
|
|
270
|
+
`/r/smoke-test-components/cmp-static-file-test/1.0.0/?_componentSet=${webPath}&_contentItemId=${contentItemId}`,
|
|
271
|
+
);
|
|
211
272
|
|
|
212
273
|
const response = await renderService.get(
|
|
213
|
-
`/r
|
|
274
|
+
`/r/smoke-test-components/cmp-static-file-test/1.0.0/?_componentSet=${webPath}&_contentItemId=${contentItemId}`,
|
|
214
275
|
);
|
|
215
276
|
expect(response.status).toEqual(200);
|
|
216
277
|
expect(response.data).toEqual(
|
|
217
278
|
[
|
|
218
279
|
'<div>Input: from-content-item-service</div>',
|
|
219
280
|
'<div>smoke-test-components/cmp-static-file-test 1.0.0 ',
|
|
220
|
-
`${configObj.renderServiceUrl}/s
|
|
281
|
+
`${configObj.renderServiceUrl}/s/smoke-test-components/cmp-static-file-test/1.0.0/birthday-cake.png?_componentSet=${webPath}</div>`,
|
|
221
282
|
].join(''),
|
|
222
283
|
);
|
|
223
284
|
});
|
|
@@ -7,6 +7,7 @@ import path from 'path';
|
|
|
7
7
|
import { AxiosResponse, AxiosError, AxiosInstance } from 'axios';
|
|
8
8
|
import color from 'cli-color';
|
|
9
9
|
import { getLogger, Logger } from '@squiz/dx-logger-lib';
|
|
10
|
+
import { JsonValidationService } from '@squiz/dx-json-schema-lib';
|
|
10
11
|
|
|
11
12
|
export const logger: Logger = getLogger({ name: 'upload-component', format: 'human' });
|
|
12
13
|
|
|
@@ -57,7 +58,7 @@ export async function uploadComponentFolder(
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
async function preUploadChecks(apiClient: AxiosInstance, managementURL: string, folderPath: string) {
|
|
60
|
-
const service = new ManifestServiceForDev(folderPath, logger);
|
|
61
|
+
const service = new ManifestServiceForDev(folderPath, logger, new JsonValidationService());
|
|
61
62
|
const manifestPath = path.join(folderPath, `manifest.json`);
|
|
62
63
|
|
|
63
64
|
const result = await service.readManifest(manifestPath);
|
package/tsconfig.json
CHANGED
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
|
|
12
12
|
"references": [
|
|
13
13
|
{ "path": "../virus-scanner-lib" },
|
|
14
|
-
{ "path": "../component-lib" },
|
|
15
14
|
{ "path": "../render-runtime-lib" },
|
|
16
|
-
{ "path": "../
|
|
15
|
+
{ "path": "../component-lib" },
|
|
16
|
+
{ "path": "../dx-logger-lib" },
|
|
17
|
+
{ "path": "../dx-json-schema-lib" }
|
|
17
18
|
],
|
|
18
19
|
|
|
19
20
|
"include": ["src/**/*.ts", "src/**/*.json"],
|