@squiz/component-cli-lib 1.2.1-alpha.100
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/.gitlab-ci.yml +113 -0
- package/CHANGELOG.md +690 -0
- package/jest.config.ts +16 -0
- package/jest.integration.config.ts +17 -0
- package/lib/component-dev.d.ts +13 -0
- package/lib/component-dev.js +40 -0
- package/lib/component-dev.js.map +1 -0
- package/lib/component-dev.spec.d.ts +1 -0
- package/lib/component-dev.spec.js +91 -0
- package/lib/component-dev.spec.js.map +1 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +8 -0
- package/lib/index.js.map +1 -0
- package/lib/integration-tests/__components__/big-package/manifest.json +32 -0
- package/lib/integration-tests/__components__/cmp-static-file-test/manifest.json +36 -0
- package/lib/integration-tests/__components__/invalid-manifest/manifest.json +24 -0
- package/lib/integration-tests/helper.d.ts +18 -0
- package/lib/integration-tests/helper.js +70 -0
- package/lib/integration-tests/helper.js.map +1 -0
- package/lib/integration-tests/service-deployment.spec.d.ts +1 -0
- package/lib/integration-tests/service-deployment.spec.js +16 -0
- package/lib/integration-tests/service-deployment.spec.js.map +1 -0
- package/lib/integration-tests/test-setup.d.ts +0 -0
- package/lib/integration-tests/test-setup.js +3 -0
- package/lib/integration-tests/test-setup.js.map +1 -0
- package/lib/integration-tests/upload-and-render-component.spec.d.ts +1 -0
- package/lib/integration-tests/upload-and-render-component.spec.js +135 -0
- package/lib/integration-tests/upload-and-render-component.spec.js.map +1 -0
- package/lib/upload-component-folder.d.ts +3 -0
- package/lib/upload-component-folder.js +127 -0
- package/lib/upload-component-folder.js.map +1 -0
- package/package.json +41 -0
- package/src/component-dev.spec.ts +111 -0
- package/src/component-dev.ts +34 -0
- package/src/index.ts +2 -0
- package/src/integration-tests/__components__/big-package/manifest.json +33 -0
- package/src/integration-tests/__components__/big-package/render-json.js +5 -0
- package/src/integration-tests/__components__/cmp-static-file-test/main.js +10 -0
- package/src/integration-tests/__components__/cmp-static-file-test/manifest.json +40 -0
- package/src/integration-tests/__components__/cmp-static-file-test/public/static-library-file.js +1 -0
- package/src/integration-tests/__components__/invalid-manifest/main.js +7 -0
- package/src/integration-tests/__components__/invalid-manifest/manifest.json +26 -0
- package/src/integration-tests/__components__/invalid-upload/main.js +7 -0
- package/src/integration-tests/helper.ts +74 -0
- package/src/integration-tests/service-deployment.spec.ts +21 -0
- package/src/integration-tests/test-setup.ts +1 -0
- package/src/integration-tests/upload-and-render-component.spec.ts +137 -0
- package/src/upload-component-folder.ts +136 -0
- package/tsconfig.json +21 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.uploadComponentFolder = exports.logger = void 0;
|
|
7
|
+
const virus_scanner_lib_1 = require("@squiz/virus-scanner-lib");
|
|
8
|
+
const component_lib_1 = require("@squiz/component-lib");
|
|
9
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const axios_1 = __importDefault(require("axios"));
|
|
12
|
+
const cli_color_1 = __importDefault(require("cli-color"));
|
|
13
|
+
const dx_logger_lib_1 = require("@squiz/dx-logger-lib");
|
|
14
|
+
exports.logger = (0, dx_logger_lib_1.getLogger)({ name: 'upload-component', format: 'human' });
|
|
15
|
+
async function uploadComponentFolder(folderPath, componentServiceManagementUrl, componentRenderServiceUrl) {
|
|
16
|
+
const tmpDir = path_1.default.resolve(await promises_1.default.mkdtemp('cmp-upload'));
|
|
17
|
+
try {
|
|
18
|
+
const axiosInstance = axios_1.default.create({
|
|
19
|
+
baseURL: componentServiceManagementUrl,
|
|
20
|
+
headers: {
|
|
21
|
+
'content-type': 'application/json',
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
await preUploadChecks(folderPath, componentRenderServiceUrl);
|
|
25
|
+
exports.logger.info('Initial scanning');
|
|
26
|
+
const zip = await (0, component_lib_1.zipDirectory)(folderPath, tmpDir);
|
|
27
|
+
const initialUpload = await handleResponse(axiosInstance.post('upload-component'));
|
|
28
|
+
exports.logger.info(`deployment id: ${initialUpload.id} status: transferring`);
|
|
29
|
+
await (0, virus_scanner_lib_1.uploadFile)(initialUpload, zip);
|
|
30
|
+
await watchAndWaitForUploadAndScanComplete(initialUpload.id, axiosInstance);
|
|
31
|
+
exports.logger.info(`deployment id: ${initialUpload.id} status: deploying component folder`);
|
|
32
|
+
const result = await handleResponse(axiosInstance.post(`upload-component/next/${initialUpload.id}`));
|
|
33
|
+
await promises_1.default.rm(tmpDir, { force: true, recursive: true });
|
|
34
|
+
if (result.status === 'successful') {
|
|
35
|
+
exports.logger.info(`deployment id: ${initialUpload.id} status: ${cli_color_1.default.green('success')}`);
|
|
36
|
+
exports.logger.info(`uploaded location: ${result.accessLink}`);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
exports.logger.error('failed for an unknown reason', cli_color_1.default.red(result));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
await promises_1.default.rm(tmpDir, { force: true, recursive: true });
|
|
44
|
+
if (e instanceof Error) {
|
|
45
|
+
exports.logger.error(cli_color_1.default.red(e.message));
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
throw e;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.uploadComponentFolder = uploadComponentFolder;
|
|
53
|
+
async function preUploadChecks(folderPath, renderService) {
|
|
54
|
+
const result = await (0, component_lib_1.loadManifest)(path_1.default.join(folderPath, `manifest.json`));
|
|
55
|
+
if (await checkIfVersionExists(result, renderService)) {
|
|
56
|
+
throw new Error(`Cannot upload component version, ${result.name} ${result.version} already exists`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function checkIfVersionExists(inputManifest, renderService) {
|
|
60
|
+
const axiosInstance = axios_1.default.create({
|
|
61
|
+
baseURL: renderService,
|
|
62
|
+
});
|
|
63
|
+
try {
|
|
64
|
+
const response = await axiosInstance.get(`d/${inputManifest.name}/${inputManifest.version}/manifest.json`);
|
|
65
|
+
if (response.status === 200) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
throw new Error(`Unexpected response code ${response.status}`);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (isAxiosError(error)) {
|
|
72
|
+
const { response } = error;
|
|
73
|
+
if ((response === null || response === void 0 ? void 0 : response.status) === 404) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function watchAndWaitForUploadAndScanComplete(id, axiosInstance) {
|
|
81
|
+
const poll = () => handleResponse(axiosInstance.get('upload-component/status/' + id));
|
|
82
|
+
return new Promise((resolve, reject) => {
|
|
83
|
+
const recurse = () => poll().then(async (req) => {
|
|
84
|
+
if (req.status == 'Success') {
|
|
85
|
+
resolve();
|
|
86
|
+
}
|
|
87
|
+
else if (req.status == 'Flagged') {
|
|
88
|
+
reject(new Error('upload has been flagged as a virus'));
|
|
89
|
+
}
|
|
90
|
+
else if (req.status == 'Error') {
|
|
91
|
+
reject(new Error('there has been an error'));
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
setTimeout(async () => {
|
|
95
|
+
await recurse();
|
|
96
|
+
}, 1000);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
recurse();
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
async function handleResponse(axiosInstance) {
|
|
103
|
+
try {
|
|
104
|
+
const response = await axiosInstance;
|
|
105
|
+
return response.data;
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
throw handleError(error);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function handleError(error) {
|
|
112
|
+
const { response } = error;
|
|
113
|
+
if (!response || !isAxiosError(error)) {
|
|
114
|
+
return error;
|
|
115
|
+
}
|
|
116
|
+
const message = response.data.message;
|
|
117
|
+
if (message) {
|
|
118
|
+
return new Error(`Unexpected response code ${response.status}. message: ${message}`);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
return new Error(`Unexpected response code ${response.status}.`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function isAxiosError(error) {
|
|
125
|
+
return error && error.isAxiosError === true;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=upload-component-folder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-component-folder.js","sourceRoot":"","sources":["../src/upload-component-folder.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@squiz/component-cli-lib",
|
|
3
|
+
"version": "1.2.1-alpha.100",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node ./lib/index.js",
|
|
8
|
+
"compile": "tsc",
|
|
9
|
+
"lint": "eslint ./src --ext .ts",
|
|
10
|
+
"test": "jest -c jest.config.ts",
|
|
11
|
+
"test:integration": "jest -c jest.integration.config.ts",
|
|
12
|
+
"clean": "rimraf \".tsbuildinfo\" \"./lib\""
|
|
13
|
+
},
|
|
14
|
+
"author": "",
|
|
15
|
+
"license": "ISC",
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/cli-color": "2.0.2",
|
|
18
|
+
"@types/express": "4.17.13",
|
|
19
|
+
"@types/jest": "28.1.6",
|
|
20
|
+
"@types/node": "17.0.27",
|
|
21
|
+
"@types/supertest": "2.0.12",
|
|
22
|
+
"dotenv": "16.0.1",
|
|
23
|
+
"eslint": "8.22.0",
|
|
24
|
+
"jest": "28.1.3",
|
|
25
|
+
"ts-jest": "28.0.7",
|
|
26
|
+
"ts-loader": "9.3.1",
|
|
27
|
+
"ts-node": "10.9.1",
|
|
28
|
+
"typescript": "4.7.4"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@squiz/component-lib": "^1.2.1-alpha.100",
|
|
32
|
+
"@squiz/dx-logger-lib": "^1.2.1-alpha.100",
|
|
33
|
+
"@squiz/render-runtime-lib": "^1.2.1-alpha.100",
|
|
34
|
+
"@squiz/virus-scanner-lib": "^1.2.1-alpha.100",
|
|
35
|
+
"archiver": "5.3.1",
|
|
36
|
+
"axios": "0.27.2",
|
|
37
|
+
"cli-color": "^2.0.2",
|
|
38
|
+
"supertest": "^6.2.3"
|
|
39
|
+
},
|
|
40
|
+
"gitHead": "909ab48128b72579f5f1390b7f045a3a41958795"
|
|
41
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fsp from 'fs/promises';
|
|
3
|
+
|
|
4
|
+
import { routeTests, TestHelpers } from '@squiz/render-runtime-lib';
|
|
5
|
+
import { startDevelopmentRender } from './component-dev';
|
|
6
|
+
import supertest from 'supertest';
|
|
7
|
+
|
|
8
|
+
jest.setTimeout(20_000);
|
|
9
|
+
|
|
10
|
+
describe('component-dev', () => {
|
|
11
|
+
describe('production style folder structure', () => {
|
|
12
|
+
let doStopStack: Awaited<ReturnType<typeof startDevelopmentRender>>;
|
|
13
|
+
|
|
14
|
+
beforeAll(async () => {
|
|
15
|
+
doStopStack = await startDevelopmentRender(path.join(__dirname, '../../../test-components'), { port: 0 });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterAll(async () => {
|
|
19
|
+
await doStopStack();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should find the component', async () => {
|
|
23
|
+
const testServer = TestHelpers.getTestServer();
|
|
24
|
+
const response = await testServer.get(`/r/set/test-component/1.0.0?something=hello`);
|
|
25
|
+
expect(response.text).toEqual('<h1>hello</h1>');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('local developer style folders', () => {
|
|
30
|
+
let doStopStack: Awaited<ReturnType<typeof startDevelopmentRender>>;
|
|
31
|
+
let version: string;
|
|
32
|
+
let componentName: string;
|
|
33
|
+
let fixtureDirectory: string;
|
|
34
|
+
|
|
35
|
+
beforeAll(async () => {
|
|
36
|
+
const {
|
|
37
|
+
fixtureDirectory: ceratedFixtureDirectory,
|
|
38
|
+
version: createdVersion,
|
|
39
|
+
componentName: createdName,
|
|
40
|
+
} = await TestHelpers.ComponentFixture.setupFullComponentDirectory('<h1>Hello World</h1>');
|
|
41
|
+
|
|
42
|
+
version = createdVersion;
|
|
43
|
+
fixtureDirectory = ceratedFixtureDirectory;
|
|
44
|
+
componentName = createdName;
|
|
45
|
+
|
|
46
|
+
doStopStack = await startDevelopmentRender(fixtureDirectory, { port: 0 });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
afterAll(async () => {
|
|
50
|
+
await doStopStack();
|
|
51
|
+
await fsp.rm(path.join(fixtureDirectory, componentName), { force: true, recursive: true });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should handle recursing folders to find the component', async () => {
|
|
55
|
+
const testServer = TestHelpers.getTestServer();
|
|
56
|
+
const response = await testServer.get(`/r/set/${componentName}/${version}/`);
|
|
57
|
+
expect(response.text).toEqual('<h1>Hello World</h1>');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should handle serving static files from single component directory', async () => {
|
|
61
|
+
const testServer = TestHelpers.getTestServer();
|
|
62
|
+
const response = await testServer.get(`/s/${componentName}/${version}/static.txt`);
|
|
63
|
+
expect(response.text).toEqual('hello');
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe('accessing local dev routes', () => {
|
|
68
|
+
let stopServer: Awaited<ReturnType<typeof startDevelopmentRender>>;
|
|
69
|
+
const port = 3006;
|
|
70
|
+
const url = `http://localhost:${port}`;
|
|
71
|
+
const request = supertest(url);
|
|
72
|
+
|
|
73
|
+
beforeAll(async () => {
|
|
74
|
+
const compDir = path.join(__dirname, '../', '../../test-components');
|
|
75
|
+
|
|
76
|
+
stopServer = await startDevelopmentRender(compDir, {
|
|
77
|
+
port: port,
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
afterAll(async () => {
|
|
82
|
+
await stopServer();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('definition routes', () => {
|
|
86
|
+
routeTests.definition(url, url);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('static routes', () => {
|
|
90
|
+
routeTests.static(url);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('render routes', () => {
|
|
94
|
+
routeTests.render(url, url);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe('GET /r/set/test-component/1.0.1', () => {
|
|
98
|
+
//
|
|
99
|
+
// this scenario is different to the 'production' tests
|
|
100
|
+
// this is should 500 in prod and 404 locally
|
|
101
|
+
it('should fail validation if trying to render a component with an invalid schema', async () => {
|
|
102
|
+
const response = await request.get('/r/set/test-component/1.0.1?something=not-used');
|
|
103
|
+
|
|
104
|
+
expect(response.statusCode).toEqual(404);
|
|
105
|
+
expect(response.body).toEqual({
|
|
106
|
+
message: 'manifest could not be found',
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { startRenderStack, stopRenderStack } from '@squiz/render-runtime-lib';
|
|
2
|
+
import { getLogger } from '@squiz/dx-logger-lib';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* startDevelopmentRender starts a dev-mode render stack for any
|
|
7
|
+
* local directory. This should start an express server on a random unused port
|
|
8
|
+
* which can be accessed for viewing the component in development.
|
|
9
|
+
*
|
|
10
|
+
* @param {string} componentPath - The directory with the component to be rendered
|
|
11
|
+
* @param {object} options - Additional configuration for the dev stack
|
|
12
|
+
* @returns a function to stop the render stack
|
|
13
|
+
*/
|
|
14
|
+
export async function startDevelopmentRender(componentPath: string, options: { port: number; previewFile?: string }) {
|
|
15
|
+
const logger = getLogger({ name: 'component-dev', format: 'human' });
|
|
16
|
+
await startRenderStack({
|
|
17
|
+
logger: logger,
|
|
18
|
+
webserver: {
|
|
19
|
+
port: options.port,
|
|
20
|
+
rootUrl: `http://localhost:${options.port}`,
|
|
21
|
+
shouldRunMigrations: false,
|
|
22
|
+
},
|
|
23
|
+
componentRunner: {
|
|
24
|
+
// Considering the component path will be passed in from CLI
|
|
25
|
+
// We need to generate the path from the calling directory
|
|
26
|
+
dataMountPoint: path.resolve(process.cwd(), componentPath),
|
|
27
|
+
localDevMode: true,
|
|
28
|
+
previewFile: options.previewFile,
|
|
29
|
+
workerTimeout: 20_000,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return () => stopRenderStack();
|
|
34
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://localhost:3000/schemas/v1.json#",
|
|
3
|
+
|
|
4
|
+
"name": "big-package",
|
|
5
|
+
"version": "1.0.2",
|
|
6
|
+
"main-function": "render-json",
|
|
7
|
+
"functions": [
|
|
8
|
+
{
|
|
9
|
+
"name": "render-json",
|
|
10
|
+
"entry": "render-json.js",
|
|
11
|
+
"input": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"properties": {
|
|
14
|
+
"something": {
|
|
15
|
+
"type": "string"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"required": ["something"]
|
|
19
|
+
},
|
|
20
|
+
"output": {
|
|
21
|
+
"response-type": "json",
|
|
22
|
+
"definition": {
|
|
23
|
+
"properties": {
|
|
24
|
+
"my-prop": {
|
|
25
|
+
"type": "string"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"required": ["my-prop"]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {object} input
|
|
3
|
+
* @param {ComponentInfo} info
|
|
4
|
+
*/
|
|
5
|
+
module.exports = async function (input, info) {
|
|
6
|
+
return (
|
|
7
|
+
`<div>Input: ${input.something}</div>` +
|
|
8
|
+
`<div>${info.ctx.componentName} ${info.ctx.version} ${info.ctx.getStaticResourceUrl('birthday-cake.png')}</div>`
|
|
9
|
+
);
|
|
10
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://localhost:3000/schemas/v1.json#",
|
|
3
|
+
|
|
4
|
+
"name": "cmp-static-file-test",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"main-function": "main",
|
|
7
|
+
|
|
8
|
+
"functions": [
|
|
9
|
+
{
|
|
10
|
+
"entry": "main.js",
|
|
11
|
+
"name": "main",
|
|
12
|
+
"input": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"properties": {
|
|
15
|
+
"something": {
|
|
16
|
+
"type": "string"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"required": ["something"]
|
|
20
|
+
},
|
|
21
|
+
"output": {
|
|
22
|
+
"response-type": "html",
|
|
23
|
+
|
|
24
|
+
"static-files": [
|
|
25
|
+
{
|
|
26
|
+
"location": "header",
|
|
27
|
+
"file": {
|
|
28
|
+
"filepath": "static-library-file.js",
|
|
29
|
+
"type": "js"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
|
|
37
|
+
"static-files": {
|
|
38
|
+
"location-root": "public"
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/integration-tests/__components__/cmp-static-file-test/public/static-library-file.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const name = 'foo';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://localhost:3000/schemas/v1.json#",
|
|
3
|
+
|
|
4
|
+
"name": "invalid-manifes@t",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"main-function": "main",
|
|
7
|
+
|
|
8
|
+
"functions": [
|
|
9
|
+
{
|
|
10
|
+
"entry": "main.js",
|
|
11
|
+
"name": "main",
|
|
12
|
+
"input": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"properties": {
|
|
15
|
+
"something": {
|
|
16
|
+
"type": "string"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"required": ["something"]
|
|
20
|
+
},
|
|
21
|
+
"output": {
|
|
22
|
+
"response-type": "html"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
import fsp from 'fs/promises';
|
|
6
|
+
import { randomBytes } from 'crypto';
|
|
7
|
+
import { ComponentSet, parseEnvVarForVar } from '@squiz/component-lib';
|
|
8
|
+
import { config } from 'dotenv';
|
|
9
|
+
|
|
10
|
+
config();
|
|
11
|
+
|
|
12
|
+
interface Config {
|
|
13
|
+
managementServiceUrl: string;
|
|
14
|
+
renderServiceUrl: string;
|
|
15
|
+
ci_buildVersion: string;
|
|
16
|
+
ci_buildBranch: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const configObj: Config = {
|
|
20
|
+
managementServiceUrl: parseEnvVarForVar('COMPONENT_MANAGEMENT_SERVICE_URL').replace(/\/+$/, ''),
|
|
21
|
+
renderServiceUrl: parseEnvVarForVar('COMPONENT_RENDER_SERVICE_URL').replace(/\/+$/, ''),
|
|
22
|
+
ci_buildVersion: parseEnvVarForVar('CI_COMMIT_SHORT_SHA'),
|
|
23
|
+
ci_buildBranch: parseEnvVarForVar('CI_COMMIT_REF_NAME'),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default configObj;
|
|
27
|
+
|
|
28
|
+
export const managementService = axios.create({
|
|
29
|
+
baseURL: configObj.managementServiceUrl,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const renderService = axios.create({
|
|
33
|
+
baseURL: configObj.renderServiceUrl,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export const ci_buildVersion = configObj.ci_buildVersion;
|
|
37
|
+
export const ci_buildBranch = configObj.ci_buildBranch;
|
|
38
|
+
|
|
39
|
+
export function getTestComponents(): string[] {
|
|
40
|
+
const componets = [];
|
|
41
|
+
const componentsDir = path.join(__dirname, '/__components__/');
|
|
42
|
+
const files = fs.readdirSync(componentsDir);
|
|
43
|
+
for (const file of files) {
|
|
44
|
+
const stat = fs.statSync(path.join(componentsDir, file));
|
|
45
|
+
if (stat.isDirectory() && file.substring(0, 4) === 'cmp-') {
|
|
46
|
+
componets.push(file);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return componets;
|
|
50
|
+
}
|
|
51
|
+
export async function createFile(filePath: string, sizeInMB: number) {
|
|
52
|
+
const content = randomBytes(sizeInMB * 1000000);
|
|
53
|
+
await fsp.writeFile(`${filePath}`, content);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function removeFile(filePath: string) {
|
|
57
|
+
fsp.unlink(filePath);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function deleteComponentSet(webPath: string) {
|
|
61
|
+
try {
|
|
62
|
+
await managementService.delete(`/component-set/${webPath}`);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
// no ops
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function addComponentSet(componentSet: ComponentSet) {
|
|
69
|
+
try {
|
|
70
|
+
await managementService.post(`/component-set`, componentSet);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
//no ops
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { renderService, managementService, ci_buildVersion, ci_buildBranch } from './helper';
|
|
2
|
+
|
|
3
|
+
interface HealthInfo {
|
|
4
|
+
status: string;
|
|
5
|
+
buildVersion: string;
|
|
6
|
+
buildBranch: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
describe('Verify latest services deployments', () => {
|
|
10
|
+
it('Should have latest Management API service', async () => {
|
|
11
|
+
const response: HealthInfo = (await managementService.get('/health')).data;
|
|
12
|
+
expect(response.buildVersion).toBe(ci_buildVersion);
|
|
13
|
+
expect(response.buildBranch).toBe(ci_buildBranch);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('Should have latest Render Runtime service', async () => {
|
|
17
|
+
const response: HealthInfo = (await renderService.get('/health')).data;
|
|
18
|
+
expect(response.buildVersion).toBe(ci_buildVersion);
|
|
19
|
+
expect(response.buildBranch).toBe(ci_buildBranch);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
jest.setTimeout(60_000);
|