@squiz/component-cli-lib 1.69.1 → 1.70.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.js +63 -63
- package/lib/index.js.map +2 -2
- package/package.json +10 -7
- package/.env.example +0 -11
- package/.gitlab-ci.yml +0 -141
- package/CHANGELOG.md +0 -664
- package/build.js +0 -28
- package/jest.config.ts +0 -28
- package/jest.integration.config.ts +0 -27
- package/job-runner-lib.tgz +0 -0
- package/lib/job-runner-lib.tgz +0 -0
- package/localStart.ts +0 -11
- package/src/component-dev-folder-structures.integration.spec.ts +0 -91
- package/src/component-dev.integration.spec.ts +0 -71
- package/src/component-dev.ts +0 -102
- package/src/component-edge-dev.ts +0 -75
- package/src/component-init.spec.ts +0 -258
- package/src/component-init.ts +0 -81
- package/src/index.ts +0 -5
- package/src/integration-tests/__components__/big-package/manifest.json +0 -36
- package/src/integration-tests/__components__/big-package/render-json.js +0 -5
- package/src/integration-tests/__components__/cmp-format-string/main.js +0 -7
- package/src/integration-tests/__components__/cmp-format-string/manifest.json +0 -41
- package/src/integration-tests/__components__/cmp-no-api-key/main.js +0 -25
- package/src/integration-tests/__components__/cmp-no-api-key/manifest.json +0 -41
- package/src/integration-tests/__components__/cmp-property-order/main.js +0 -6
- package/src/integration-tests/__components__/cmp-property-order/manifest.json +0 -60
- package/src/integration-tests/__components__/cmp-static-file-test/main.js +0 -6
- package/src/integration-tests/__components__/cmp-static-file-test/manifest.json +0 -42
- package/src/integration-tests/__components__/cmp-static-file-test/public/static-library-file.js +0 -1
- package/src/integration-tests/__components__/invalid-manifest/main.js +0 -3
- package/src/integration-tests/__components__/invalid-manifest/manifest.json +0 -29
- package/src/integration-tests/__components__/invalid-upload/main.js +0 -3
- package/src/integration-tests/__components__/matrix-asset-uri/main.js +0 -7
- package/src/integration-tests/__components__/matrix-asset-uri/manifest.json +0 -42
- package/src/integration-tests/__components__/test-page-render/main.js +0 -3
- package/src/integration-tests/__components__/test-page-render/manifest.json +0 -46
- package/src/integration-tests/__components__/test-page-render/public/static-library-script.js +0 -1
- package/src/integration-tests/__components__/test-page-render/public/static-library-styles.css +0 -3
- package/src/integration-tests/__jobs__/invalid-manifest/main.js +0 -3
- package/src/integration-tests/__jobs__/invalid-manifest/manifest.json +0 -28
- package/src/integration-tests/__jobs__/invalid-upload/main.js +0 -3
- package/src/integration-tests/__jobs__/simple-job/main.js +0 -3
- package/src/integration-tests/__jobs__/simple-job/manifest.json +0 -25
- package/src/integration-tests/helper.ts +0 -227
- package/src/integration-tests/service-deployment.integration.spec.ts +0 -74
- package/src/integration-tests/test-setup.ts +0 -1
- package/src/upload-component-folder.ts +0 -130
- package/src/upload-job.ts +0 -101
- package/src/utils.spec.ts +0 -370
- package/src/utils.ts +0 -149
- package/templates/advanced/build.js +0 -43
- package/templates/advanced/main.js +0 -3
- package/templates/advanced/manifest.json +0 -60
- package/templates/advanced/package-lock.json +0 -916
- package/templates/advanced/package.json +0 -11
- package/templates/advanced/previews/example.data.json +0 -1
- package/templates/advanced/previews/preview.html +0 -9
- package/templates/advanced/static/default.js +0 -1
- package/templates/advanced/static/default.scss +0 -1
- package/templates/basic/main.js +0 -3
- package/templates/basic/manifest.json +0 -62
- package/templates/basic/previews/example.data.json +0 -1
- package/templates/basic/previews/preview.html +0 -9
- package/templates/basic/static/default.css +0 -1
- package/templates/basic/static/default.js +0 -1
- package/tsconfig.json +0 -26
- package/tsconfig.tsbuildinfo +0 -1
package/src/upload-job.ts
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { uploadFile } from '@squiz/virus-scanner-lib';
|
|
2
|
-
|
|
3
|
-
import { zipDirectory } from '@squiz/dx-common-lib';
|
|
4
|
-
import { ManifestServiceForDev, JobManifestV1Model } from '@squiz/job-runner-lib';
|
|
5
|
-
import fsp from 'fs/promises';
|
|
6
|
-
import path from 'path';
|
|
7
|
-
import { AxiosInstance } from 'axios';
|
|
8
|
-
import color from 'cli-color';
|
|
9
|
-
import { getLogger, Logger } from '@squiz/dx-logger-lib';
|
|
10
|
-
import { watchAndWaitForUploadAndScanComplete, handleResponse, checkIfVersionExists, isAxiosResponse } from './utils';
|
|
11
|
-
|
|
12
|
-
export const logger: Logger = getLogger({ name: 'upload-job', format: 'human' });
|
|
13
|
-
|
|
14
|
-
export async function uploadJobFolder(
|
|
15
|
-
apiClient: AxiosInstance,
|
|
16
|
-
folderPath: string,
|
|
17
|
-
baseTempDir: string = '',
|
|
18
|
-
): Promise<void> {
|
|
19
|
-
const tmpDir = await fsp.mkdtemp(path.resolve(baseTempDir, 'job-upload'));
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
const manifest = await getAndValidateManifest(apiClient, folderPath);
|
|
23
|
-
|
|
24
|
-
const initialUpload = await initializeUpload(apiClient, folderPath, tmpDir);
|
|
25
|
-
|
|
26
|
-
await uploadFile(initialUpload.upload, initialUpload.zip);
|
|
27
|
-
|
|
28
|
-
await watchAndWaitForUploadAndScanComplete(apiClient, '/upload-job/status/', initialUpload.upload.id);
|
|
29
|
-
|
|
30
|
-
logger.info(`deployment id: ${initialUpload.upload.id} status: deploying job folder`);
|
|
31
|
-
|
|
32
|
-
const result = await pollForResult(apiClient, initialUpload.upload.id, manifest.getModel());
|
|
33
|
-
|
|
34
|
-
await cleanupTmpDir(tmpDir);
|
|
35
|
-
|
|
36
|
-
if (result.status === 'successful') {
|
|
37
|
-
logger.info(`deployment id: ${initialUpload.upload.id} status: ${color.green('success')}`);
|
|
38
|
-
} else {
|
|
39
|
-
const message = result?.message ?? 'unknown';
|
|
40
|
-
logger.error(`failed due an unexpected reason: ${message}`);
|
|
41
|
-
await cleanupTmpDir(tmpDir);
|
|
42
|
-
}
|
|
43
|
-
} catch (e) {
|
|
44
|
-
await cleanupTmpDir(tmpDir);
|
|
45
|
-
throw e;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async function initializeUpload(apiClient: AxiosInstance, folderPath: string, tmpDir: string) {
|
|
50
|
-
logger.info('Initial scanning');
|
|
51
|
-
const zip = await zipDirectory(folderPath, tmpDir);
|
|
52
|
-
const initialUpload = { zip: zip, upload: await handleResponse<any>(apiClient.post('/upload-job', {})) };
|
|
53
|
-
logger.info(`deployment id: ${initialUpload.upload.id} status: transferring`);
|
|
54
|
-
return initialUpload;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async function getAndValidateManifest(apiClient: AxiosInstance, folderPath: string) {
|
|
58
|
-
const manifestService = new ManifestServiceForDev(folderPath, logger);
|
|
59
|
-
const manifestPath = path.join(folderPath, `manifest.json`);
|
|
60
|
-
|
|
61
|
-
const manifest = await manifestService.readJobManifest(manifestPath);
|
|
62
|
-
|
|
63
|
-
if (await checkIfVersionExists(apiClient, `/job/${manifest.getName()}/${manifest.getVersion()}`)) {
|
|
64
|
-
throw new Error(`Cannot upload job version, ${manifest.getName()} ${manifest.getVersion()} already exists`);
|
|
65
|
-
}
|
|
66
|
-
return manifest;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async function pollForResult(apiClient: AxiosInstance, uploadId: string, manifest: JobManifestV1Model) {
|
|
70
|
-
let result = await handleResponse<any>(apiClient.post(`/upload-job/next`, { id: uploadId, manifest: manifest }));
|
|
71
|
-
|
|
72
|
-
if (!isAxiosResponse(result)) {
|
|
73
|
-
let retriesAllowed = 12;
|
|
74
|
-
while (retriesAllowed > 0) {
|
|
75
|
-
logger.info(
|
|
76
|
-
`deployment id: ${uploadId} status: ${color.yellow(
|
|
77
|
-
`unknown, retrying to check the upload status ${retriesAllowed} more times`,
|
|
78
|
-
)}`,
|
|
79
|
-
);
|
|
80
|
-
if (await checkIfVersionExists(apiClient, `/job/${manifest.name}/${manifest.version}`)) {
|
|
81
|
-
result = {
|
|
82
|
-
status: 'successful',
|
|
83
|
-
};
|
|
84
|
-
break;
|
|
85
|
-
}
|
|
86
|
-
await new Promise((r) => setTimeout(r, 10000));
|
|
87
|
-
retriesAllowed--;
|
|
88
|
-
}
|
|
89
|
-
if (retriesAllowed === 0) {
|
|
90
|
-
logger.error(`deployment id: ${uploadId} status: ${color.red('failed to check the upload status')}`);
|
|
91
|
-
throw new Error(result.toString());
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return result;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
async function cleanupTmpDir(tmpDir: string) {
|
|
98
|
-
await fsp.rm(tmpDir, { force: true, recursive: true }).catch((error: any) => {
|
|
99
|
-
logger.error('failed to remove temp directory', error);
|
|
100
|
-
});
|
|
101
|
-
}
|
package/src/utils.spec.ts
DELETED
|
@@ -1,370 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
watchAndWaitForUploadAndScanComplete,
|
|
3
|
-
checkIfVersionExists,
|
|
4
|
-
handleResponse,
|
|
5
|
-
handleError,
|
|
6
|
-
isAxiosError,
|
|
7
|
-
isAxiosResponse,
|
|
8
|
-
isUserInOrganization,
|
|
9
|
-
assertComponentTypeAllowed,
|
|
10
|
-
} from './utils';
|
|
11
|
-
import axios, { AxiosResponse, AxiosError } from 'axios';
|
|
12
|
-
|
|
13
|
-
jest.mock('child_process', () => {
|
|
14
|
-
return {
|
|
15
|
-
execSync: (command: string) => {
|
|
16
|
-
if (command === 'npm whoami') {
|
|
17
|
-
return 'testuser\n';
|
|
18
|
-
}
|
|
19
|
-
if (command === 'npm org ls testuser foo') {
|
|
20
|
-
return 'user role';
|
|
21
|
-
}
|
|
22
|
-
if (command === 'npm org ls testuser bar') {
|
|
23
|
-
return 'No teams in the organization\n';
|
|
24
|
-
}
|
|
25
|
-
if (command === 'npm org ls testuser not_logged') {
|
|
26
|
-
throw new Error('Not logged in');
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
beforeEach(() => {
|
|
33
|
-
jest.resetAllMocks();
|
|
34
|
-
|
|
35
|
-
// Reset our process.env.NODE_ENV variable
|
|
36
|
-
process.env.NODE_ENV = 'test';
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
const jobService = axios.create({
|
|
40
|
-
baseURL: 'some_url',
|
|
41
|
-
headers: {
|
|
42
|
-
authorization: 'someToken',
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
describe('cli lib utils watchAndWaitForUploadAndScanComplete', () => {
|
|
47
|
-
it('should pass if virus scan is successful', async () => {
|
|
48
|
-
jest
|
|
49
|
-
.spyOn(jobService as any, `get`)
|
|
50
|
-
.mockImplementation(() => Promise.resolve({ data: { status: 'Success' }, status: 200 } as AxiosResponse));
|
|
51
|
-
|
|
52
|
-
const res = await watchAndWaitForUploadAndScanComplete(jobService, '/upload-job/status/', '123');
|
|
53
|
-
expect(res).toBeUndefined();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('should throw specific error if virus scan is flagged', async () => {
|
|
57
|
-
jest
|
|
58
|
-
.spyOn(jobService as any, `get`)
|
|
59
|
-
.mockImplementation(() => Promise.resolve({ data: { status: 'Flagged' }, status: 200 } as AxiosResponse));
|
|
60
|
-
|
|
61
|
-
await expect(async () => {
|
|
62
|
-
await watchAndWaitForUploadAndScanComplete(jobService, '/upload-job/status/', '123');
|
|
63
|
-
}).rejects.toThrow('upload has been flagged as a virus');
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('should throw generic error if virus scan is Error', async () => {
|
|
67
|
-
jest
|
|
68
|
-
.spyOn(jobService as any, `get`)
|
|
69
|
-
.mockImplementation(() => Promise.resolve({ data: { status: 'Error' }, status: 200 } as AxiosResponse));
|
|
70
|
-
|
|
71
|
-
await expect(async () => {
|
|
72
|
-
await watchAndWaitForUploadAndScanComplete(jobService, '/upload-job/status/', '123');
|
|
73
|
-
}).rejects.toThrow('there has been an error');
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should poll until a result is received', async () => {
|
|
77
|
-
jest
|
|
78
|
-
.spyOn(jobService as any, `get`)
|
|
79
|
-
.mockImplementationOnce(() => Promise.resolve({ data: { status: 'Scanning' }, status: 200 } as AxiosResponse))
|
|
80
|
-
.mockImplementationOnce(() => Promise.resolve({ data: { status: 'Scanning' }, status: 200 } as AxiosResponse))
|
|
81
|
-
.mockImplementationOnce(() => Promise.resolve({ data: { status: 'Scanning' }, status: 200 } as AxiosResponse))
|
|
82
|
-
.mockImplementationOnce(() => Promise.resolve({ data: { status: 'Scanning' }, status: 200 } as AxiosResponse))
|
|
83
|
-
.mockImplementationOnce(() => Promise.resolve({ data: { status: 'Success' }, status: 200 } as AxiosResponse));
|
|
84
|
-
|
|
85
|
-
const res = await watchAndWaitForUploadAndScanComplete(jobService, '/upload-job/status/', '123');
|
|
86
|
-
expect(res).toBeUndefined();
|
|
87
|
-
expect((jobService as any).get.mock.calls).toHaveLength(5);
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
describe('cli lib utils checkIfVersionExists', () => {
|
|
92
|
-
it('should return true if version exists', async () => {
|
|
93
|
-
jest.spyOn(jobService as any, `get`).mockImplementation(() => Promise.resolve({ status: 200 } as AxiosResponse));
|
|
94
|
-
|
|
95
|
-
const res = await checkIfVersionExists(jobService, '/job/testJob/1.0.0');
|
|
96
|
-
expect(res).toBe(true);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('should return false if version does not exist', async () => {
|
|
100
|
-
jest.spyOn(jobService as any, `get`).mockImplementation(() => Promise.resolve({ status: 404 } as AxiosResponse));
|
|
101
|
-
|
|
102
|
-
const res = await checkIfVersionExists(jobService, '/job/testJob/5.0.0');
|
|
103
|
-
expect(res).toBe(false);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('should throw an error if status is not 404 or 200', async () => {
|
|
107
|
-
jest
|
|
108
|
-
.spyOn(jobService as any, `get`)
|
|
109
|
-
.mockImplementation(() =>
|
|
110
|
-
Promise.resolve({ status: 500, data: { message: 'something bad happened' } } as AxiosResponse),
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
await expect(async () => {
|
|
114
|
-
await checkIfVersionExists(jobService, '/job/testJob/5.0.0');
|
|
115
|
-
}).rejects.toThrow('Unexpected response code 500. something bad happened');
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
describe('cli lib utils handleResponse', () => {
|
|
120
|
-
it('should return response data for a successful request', async () => {
|
|
121
|
-
jest.spyOn(jobService as any, `get`).mockImplementation(
|
|
122
|
-
() =>
|
|
123
|
-
new Promise((resolve) => {
|
|
124
|
-
resolve({ status: 200, data: { status: 'Successful' } } as AxiosResponse);
|
|
125
|
-
}),
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
const response = jobService.get('/upload-job/status/123');
|
|
129
|
-
const res = await handleResponse(response);
|
|
130
|
-
expect(res).toStrictEqual({ status: 'Successful' });
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it('should throw an error if error occurs in the request', async () => {
|
|
134
|
-
jest.spyOn(jobService as any, `get`).mockImplementation(
|
|
135
|
-
() =>
|
|
136
|
-
new Promise((resolve, reject) => {
|
|
137
|
-
reject(new Error('something really bad happened'));
|
|
138
|
-
}),
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
const response = jobService.get('/upload-job/status/123');
|
|
142
|
-
await expect(async () => {
|
|
143
|
-
await handleResponse(response);
|
|
144
|
-
}).rejects.toThrow('something really bad happened');
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('should throw an error if response is not 200 status', async () => {
|
|
148
|
-
jest.spyOn(jobService as any, `get`).mockImplementation(
|
|
149
|
-
() =>
|
|
150
|
-
new Promise((resolve) => {
|
|
151
|
-
resolve({ status: 400, data: { message: 'some bad request' } } as AxiosResponse);
|
|
152
|
-
}),
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
const response = jobService.get('/upload-job/status/123');
|
|
156
|
-
await expect(async () => {
|
|
157
|
-
await handleResponse(response);
|
|
158
|
-
}).rejects.toThrow('some bad request');
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
describe('cli lib utils handleError', () => {
|
|
163
|
-
it('should throw an error with message if not an AxiosError', async () => {
|
|
164
|
-
const error = new Error('oh no');
|
|
165
|
-
const res = handleError(error);
|
|
166
|
-
|
|
167
|
-
expect(res.message).toBe('oh no');
|
|
168
|
-
});
|
|
169
|
-
it('should throw an error with generic message if not an AxiosError', async () => {
|
|
170
|
-
const error = new Error();
|
|
171
|
-
const res = handleError(error);
|
|
172
|
-
|
|
173
|
-
expect(res.message).toBe('An error has occurred');
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it('should throw an error with additional fields if an AxiosError without message', async () => {
|
|
177
|
-
const error = new AxiosError('i am an axios error', '500', undefined, undefined, {
|
|
178
|
-
status: 500,
|
|
179
|
-
data: { status: 'fail' },
|
|
180
|
-
} as AxiosResponse);
|
|
181
|
-
const res = handleError(error);
|
|
182
|
-
|
|
183
|
-
expect(res.message).toBe('i am an axios error (code: 500)');
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it('should throw an error with additional fields if an AxiosError with message', async () => {
|
|
187
|
-
const error = new AxiosError('i am an axios error', '500', undefined, undefined, {
|
|
188
|
-
status: 500,
|
|
189
|
-
data: { message: 'i have failed' },
|
|
190
|
-
} as AxiosResponse);
|
|
191
|
-
const res = handleError(error);
|
|
192
|
-
|
|
193
|
-
expect(res.message).toBe('Unexpected response code 500. i have failed');
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
describe('cli lib utils isAxiosError', () => {
|
|
198
|
-
it('should return false if not an AxiosError', async () => {
|
|
199
|
-
const error = new Error('oh no');
|
|
200
|
-
const res = isAxiosError(error);
|
|
201
|
-
|
|
202
|
-
expect(res).toBe(false);
|
|
203
|
-
});
|
|
204
|
-
it('should return true if an AxiosError', async () => {
|
|
205
|
-
const error = new AxiosError('i am an axios error', '500', undefined, undefined, {
|
|
206
|
-
status: 500,
|
|
207
|
-
data: { message: 'i have failed' },
|
|
208
|
-
} as AxiosResponse);
|
|
209
|
-
const res = isAxiosError(error);
|
|
210
|
-
|
|
211
|
-
expect(res).toBe(true);
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
describe('cli lib utils isAxiosResponse', () => {
|
|
216
|
-
it('should return true if an AxiosResponse', async () => {
|
|
217
|
-
const resp = {
|
|
218
|
-
status: 200,
|
|
219
|
-
data: { message: 'i have succeeded' },
|
|
220
|
-
} as AxiosResponse;
|
|
221
|
-
const res = isAxiosResponse(resp);
|
|
222
|
-
|
|
223
|
-
expect(res).toBe(true);
|
|
224
|
-
});
|
|
225
|
-
it('should return false if not an AxiosResponse', async () => {
|
|
226
|
-
const resp = {
|
|
227
|
-
code: 200,
|
|
228
|
-
data: { message: 'i am not an axios response' },
|
|
229
|
-
};
|
|
230
|
-
const res = isAxiosResponse(resp);
|
|
231
|
-
|
|
232
|
-
expect(res).toBe(false);
|
|
233
|
-
});
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
describe('isUserInFooOrganization', () => {
|
|
237
|
-
it('should return true if the user is logged in and belongs to the "foo" organization', () => {
|
|
238
|
-
const result = isUserInOrganization('foo');
|
|
239
|
-
expect(result).toBe(true);
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it('should return false if the user is logged in but does not belong to the "bar" organization', () => {
|
|
243
|
-
const result = isUserInOrganization('bar');
|
|
244
|
-
expect(result).toBe(false);
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
it('should return false if the user is not logged in', () => {
|
|
248
|
-
const result = isUserInOrganization('not_logged');
|
|
249
|
-
expect(result).toBe(false);
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
describe('cli lib utils assertComponentTypeAllowed', () => {
|
|
254
|
-
it('"edge" component only - should NOT throw error if uploading "edge" component', async () => {
|
|
255
|
-
const response = {
|
|
256
|
-
data: {
|
|
257
|
-
edgeComponentsOnly: true,
|
|
258
|
-
},
|
|
259
|
-
} as AxiosResponse;
|
|
260
|
-
const spy = jest.spyOn(jobService as any, `get`);
|
|
261
|
-
spy.mockImplementation(() => Promise.resolve(response));
|
|
262
|
-
|
|
263
|
-
await assertComponentTypeAllowed('edge', jobService, 'foo', 'some-url');
|
|
264
|
-
expect(spy).toHaveBeenCalledTimes(0);
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
it('"edge" component only - should throw error if uploading "server" component', async () => {
|
|
268
|
-
const response = {
|
|
269
|
-
data: {
|
|
270
|
-
edgeComponentsOnly: true,
|
|
271
|
-
},
|
|
272
|
-
} as AxiosResponse;
|
|
273
|
-
const spy = jest.spyOn(jobService as any, `get`);
|
|
274
|
-
spy.mockImplementation(() => Promise.resolve(response));
|
|
275
|
-
|
|
276
|
-
await expect(assertComponentTypeAllowed('server', jobService, 'foo', 'some-url')).rejects.toThrow(
|
|
277
|
-
`Cannot upload "server" type component. ` +
|
|
278
|
-
`Tenant component service is configured to allow "edge" type components only.`,
|
|
279
|
-
);
|
|
280
|
-
expect(spy).toHaveBeenCalledTimes(1);
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
it('"edge" component only - should throw error if uploading "undefined" component', async () => {
|
|
284
|
-
const response = {
|
|
285
|
-
data: {
|
|
286
|
-
edgeComponentsOnly: true,
|
|
287
|
-
},
|
|
288
|
-
} as AxiosResponse;
|
|
289
|
-
const spy = jest.spyOn(jobService as any, `get`);
|
|
290
|
-
spy.mockImplementation(() => Promise.resolve(response));
|
|
291
|
-
|
|
292
|
-
await expect(assertComponentTypeAllowed(undefined, jobService, 'foo', 'some-url')).rejects.toThrow(
|
|
293
|
-
`Cannot upload "server" type component. ` +
|
|
294
|
-
`Tenant component service is configured to allow "edge" type components only.`,
|
|
295
|
-
);
|
|
296
|
-
expect(spy).toHaveBeenCalledTimes(1);
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
it('no restriction set - should NOT throw error uploading "edge" component', async () => {
|
|
300
|
-
const response = {
|
|
301
|
-
data: {
|
|
302
|
-
edgeComponentsOnly: false,
|
|
303
|
-
},
|
|
304
|
-
} as AxiosResponse;
|
|
305
|
-
const spy = jest.spyOn(jobService as any, `get`);
|
|
306
|
-
spy.mockImplementation(() => Promise.resolve(response));
|
|
307
|
-
|
|
308
|
-
await assertComponentTypeAllowed('edge', jobService, 'foo', 'some-url');
|
|
309
|
-
expect(spy).toHaveBeenCalledTimes(0);
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
it('no restriction set - should NOT throw error uploading "server" component', async () => {
|
|
313
|
-
const response = {
|
|
314
|
-
data: {
|
|
315
|
-
edgeComponentsOnly: false,
|
|
316
|
-
},
|
|
317
|
-
} as AxiosResponse;
|
|
318
|
-
const spy = jest.spyOn(jobService as any, `get`);
|
|
319
|
-
spy.mockImplementation(() => Promise.resolve(response));
|
|
320
|
-
|
|
321
|
-
assertComponentTypeAllowed('server', jobService, 'foo', 'some-url');
|
|
322
|
-
expect(spy).toHaveBeenCalledTimes(1);
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
it('no restriction set - should NOT throw error uploading "undefined" component', async () => {
|
|
326
|
-
const response = {
|
|
327
|
-
data: {
|
|
328
|
-
edgeComponentsOnly: false,
|
|
329
|
-
},
|
|
330
|
-
} as AxiosResponse;
|
|
331
|
-
const spy = jest.spyOn(jobService as any, `get`);
|
|
332
|
-
spy.mockImplementation(() => Promise.resolve(response));
|
|
333
|
-
|
|
334
|
-
assertComponentTypeAllowed(undefined, jobService, 'foo', 'some-url');
|
|
335
|
-
expect(spy).toHaveBeenCalledTimes(1);
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
it('settings undefined - should NOT throw error uploading "edge" component', async () => {
|
|
339
|
-
const response = {
|
|
340
|
-
data: {},
|
|
341
|
-
} as AxiosResponse;
|
|
342
|
-
const spy = jest.spyOn(jobService as any, `get`);
|
|
343
|
-
spy.mockImplementation(() => Promise.resolve(response));
|
|
344
|
-
|
|
345
|
-
await assertComponentTypeAllowed('edge', jobService, 'foo', 'some-url');
|
|
346
|
-
expect(spy).toHaveBeenCalledTimes(0);
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
it('settings undefined - should NOT throw error uploading "server" component', async () => {
|
|
350
|
-
const response = {
|
|
351
|
-
data: {},
|
|
352
|
-
} as AxiosResponse;
|
|
353
|
-
const spy = jest.spyOn(jobService as any, `get`);
|
|
354
|
-
spy.mockImplementation(() => Promise.resolve(response));
|
|
355
|
-
|
|
356
|
-
assertComponentTypeAllowed('server', jobService, 'foo', 'some-url');
|
|
357
|
-
expect(spy).toHaveBeenCalledTimes(1);
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
it('settings undefined - should NOT throw error uploading "undefined" component', async () => {
|
|
361
|
-
const response = {
|
|
362
|
-
data: {},
|
|
363
|
-
} as AxiosResponse;
|
|
364
|
-
const spy = jest.spyOn(jobService as any, `get`);
|
|
365
|
-
spy.mockImplementation(() => Promise.resolve(response));
|
|
366
|
-
|
|
367
|
-
assertComponentTypeAllowed(undefined, jobService, 'foo', 'some-url');
|
|
368
|
-
expect(spy).toHaveBeenCalledTimes(1);
|
|
369
|
-
});
|
|
370
|
-
});
|
package/src/utils.ts
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import { AxiosResponse, AxiosError, AxiosInstance } from 'axios';
|
|
2
|
-
import { ScanStatus } from '@squiz/virus-scanner-lib';
|
|
3
|
-
import { execSync } from 'child_process';
|
|
4
|
-
import { ComponentType } from '@squiz/component-lib';
|
|
5
|
-
|
|
6
|
-
export async function watchAndWaitForUploadAndScanComplete(
|
|
7
|
-
apiClient: AxiosInstance,
|
|
8
|
-
endpoint: string,
|
|
9
|
-
id: string,
|
|
10
|
-
managementURL?: string,
|
|
11
|
-
): Promise<void> {
|
|
12
|
-
let poll: () => Promise<ScanStatus>;
|
|
13
|
-
if (managementURL) {
|
|
14
|
-
poll = () => handleResponse<ScanStatus>(apiClient.get(endpoint + id, { baseURL: managementURL }));
|
|
15
|
-
} else {
|
|
16
|
-
poll = () => handleResponse<ScanStatus>(apiClient.get(endpoint + id));
|
|
17
|
-
}
|
|
18
|
-
return new Promise((resolve, reject) => {
|
|
19
|
-
const recurse = () =>
|
|
20
|
-
poll().then(async (req) => {
|
|
21
|
-
if (req.status == 'Success') {
|
|
22
|
-
resolve();
|
|
23
|
-
} else if (req.status == 'Flagged') {
|
|
24
|
-
reject(new Error('upload has been flagged as a virus'));
|
|
25
|
-
} else if (req.status == 'Error') {
|
|
26
|
-
reject(new Error('there has been an error'));
|
|
27
|
-
} else {
|
|
28
|
-
setTimeout(async () => {
|
|
29
|
-
await recurse();
|
|
30
|
-
}, 1000);
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
recurse();
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export async function checkIfVersionExists(apiClient: AxiosInstance, endpoint: string, managementURL?: string) {
|
|
39
|
-
try {
|
|
40
|
-
const response = await apiClient.get(endpoint, {
|
|
41
|
-
validateStatus: null,
|
|
42
|
-
baseURL: managementURL,
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
if (response.status === 200) {
|
|
46
|
-
return true;
|
|
47
|
-
} else if (response.status === 404) {
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const error = new AxiosError(
|
|
52
|
-
response?.data?.message || response.statusText,
|
|
53
|
-
response.status.toString(),
|
|
54
|
-
undefined,
|
|
55
|
-
undefined,
|
|
56
|
-
response,
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
throw handleError(error);
|
|
60
|
-
} catch (error) {
|
|
61
|
-
throw handleError(error);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export async function handleResponse<T>(axiosInstance: Promise<AxiosResponse<T>>): Promise<T> {
|
|
66
|
-
try {
|
|
67
|
-
const response = await axiosInstance;
|
|
68
|
-
if (response.status.toString().startsWith('2')) {
|
|
69
|
-
return response.data;
|
|
70
|
-
}
|
|
71
|
-
throw handleError(response.data);
|
|
72
|
-
} catch (error) {
|
|
73
|
-
throw handleError(error);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function handleError(error: any): Error {
|
|
78
|
-
const { response } = error;
|
|
79
|
-
const errorMessage = error.message;
|
|
80
|
-
const newError = new Error(errorMessage || 'An error has occurred');
|
|
81
|
-
|
|
82
|
-
if (isAxiosError(error)) {
|
|
83
|
-
const errorCode = response?.status ?? 'unknown';
|
|
84
|
-
if (response?.data?.message) {
|
|
85
|
-
newError.message = `Unexpected response code ${errorCode}. ${response.data.message}`.trim();
|
|
86
|
-
} else {
|
|
87
|
-
newError.message = `${errorMessage} (code: ${errorCode})`;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
newError.stack = error.stack;
|
|
92
|
-
|
|
93
|
-
return newError;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export function isAxiosError(error: any): error is AxiosError {
|
|
97
|
-
return error && error.isAxiosError === true;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function isAxiosResponse(response: unknown): response is AxiosResponse {
|
|
101
|
-
return response instanceof Object && Object.prototype.hasOwnProperty.call(response, 'status');
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Execute a shell command and return the trimmed result as a string.
|
|
106
|
-
* @param {string} command - The shell command to execute.
|
|
107
|
-
* @returns {string} The trimmed result of the shell command.
|
|
108
|
-
*/
|
|
109
|
-
function executeShellCommand(command: string): string {
|
|
110
|
-
try {
|
|
111
|
-
return execSync(command, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();
|
|
112
|
-
} catch (error) {
|
|
113
|
-
return '';
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export function isUserInOrganization(organisationName: string = 'squiz'): boolean {
|
|
118
|
-
const npmUsername = executeShellCommand('npm whoami');
|
|
119
|
-
if (!npmUsername) {
|
|
120
|
-
return false;
|
|
121
|
-
}
|
|
122
|
-
const orgListProcess = executeShellCommand(`npm org ls ${npmUsername} ${organisationName}`);
|
|
123
|
-
const isMember = orgListProcess.includes('user');
|
|
124
|
-
|
|
125
|
-
return isMember;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export async function assertComponentTypeAllowed(
|
|
129
|
-
componentType: unknown,
|
|
130
|
-
apiClient: AxiosInstance,
|
|
131
|
-
endpoint: string,
|
|
132
|
-
managementURL: string,
|
|
133
|
-
) {
|
|
134
|
-
if ((componentType as ComponentType) !== 'edge') {
|
|
135
|
-
try {
|
|
136
|
-
const response = await apiClient.get(endpoint, {
|
|
137
|
-
baseURL: managementURL,
|
|
138
|
-
});
|
|
139
|
-
if (response.data.edgeComponentsOnly) {
|
|
140
|
-
throw Error(
|
|
141
|
-
`Cannot upload "server" type component. ` +
|
|
142
|
-
`Tenant component service is configured to allow "edge" type components only.`,
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
} catch (error) {
|
|
146
|
-
throw handleError(error);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
// Build process
|
|
2
|
-
const esbuild = require('esbuild');
|
|
3
|
-
const { sassPlugin } = require('esbuild-sass-plugin');
|
|
4
|
-
const postcss = require('postcss');
|
|
5
|
-
const autoprefixer = require('autoprefixer');
|
|
6
|
-
|
|
7
|
-
const esbuildOptions = {
|
|
8
|
-
outdir: 'dist',
|
|
9
|
-
bundle: true,
|
|
10
|
-
minify: false,
|
|
11
|
-
treeShaking: true,
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
// Server build
|
|
15
|
-
esbuild
|
|
16
|
-
.build({
|
|
17
|
-
...esbuildOptions,
|
|
18
|
-
platform: 'node',
|
|
19
|
-
entryPoints: ['./main.js'],
|
|
20
|
-
format: 'cjs',
|
|
21
|
-
target: 'node16',
|
|
22
|
-
})
|
|
23
|
-
.catch(() => process.exit(1));
|
|
24
|
-
|
|
25
|
-
// Client Build
|
|
26
|
-
esbuild
|
|
27
|
-
.build({
|
|
28
|
-
...esbuildOptions,
|
|
29
|
-
platform: 'browser',
|
|
30
|
-
entryPoints: ['./static/default.js', './static/default.scss'],
|
|
31
|
-
format: 'esm',
|
|
32
|
-
target: 'es2020',
|
|
33
|
-
plugins: [
|
|
34
|
-
sassPlugin({
|
|
35
|
-
async transform(source) {
|
|
36
|
-
const { css } = await postcss([autoprefixer]).process(source);
|
|
37
|
-
return css;
|
|
38
|
-
},
|
|
39
|
-
}),
|
|
40
|
-
],
|
|
41
|
-
})
|
|
42
|
-
.then(() => console.log('⚡ Styles & Scripts Compiled! ⚡ '))
|
|
43
|
-
.catch(() => process.exit(1));
|