@squiz/dxp-cli-next 5.23.1 → 5.24.0-develop.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/__tests__/integration/main.spec.js +8 -0
- package/lib/datastore/blueprint/list/list.spec.js +1 -0
- package/lib/datastore/bundle/bundle.spec.js +1 -0
- package/lib/dxp.js +4 -0
- package/lib/job-runner/job/listJobs/listJobs.spec.js +1 -0
- package/lib/job-runner/job/uploadJob/uploadJob.spec.js +1 -0
- package/lib/job-runner/jobContext/listJobContexts.spec.js +1 -0
- package/lib/job-runner/jobExecution/listJobExecutions/listJobExecutions.spec.js +1 -0
- package/lib/job-runner/jobExecution/terminateJob/terminateJob.spec.js +1 -0
- package/lib/page/index.d.ts +3 -0
- package/lib/page/index.js +12 -0
- package/lib/page/layouts/deploy/deploy.d.ts +5 -0
- package/lib/page/layouts/deploy/deploy.js +108 -0
- package/lib/page/layouts/deploy/deploy.spec.d.ts +1 -0
- package/lib/page/layouts/deploy/deploy.spec.js +162 -0
- package/lib/page/layouts/layouts.d.ts +3 -0
- package/lib/page/layouts/layouts.js +15 -0
- package/lib/page/layouts/layouts.spec.d.ts +1 -0
- package/lib/page/layouts/layouts.spec.js +22 -0
- package/lib/page/utils/definitions.d.ts +264 -0
- package/lib/page/utils/definitions.js +138 -0
- package/lib/page/utils/definitions.spec.d.ts +1 -0
- package/lib/page/utils/definitions.spec.js +290 -0
- package/package.json +2 -1
|
@@ -36,4 +36,12 @@ describe('dxp', () => {
|
|
|
36
36
|
'porter Porter Service Commands',
|
|
37
37
|
]);
|
|
38
38
|
});
|
|
39
|
+
it('should show "page" command if ENABLE_PAGE_LAYOUTS="true"', () => {
|
|
40
|
+
var _a;
|
|
41
|
+
process.env.ENABLE_PAGE_LAYOUTS = 'true';
|
|
42
|
+
const { stdout } = (0, helpers_1.runCLI)(process.cwd(), ['--help']);
|
|
43
|
+
const commandsText = (_a = stdout
|
|
44
|
+
.match(/Commands\:(.*)/is)) === null || _a === void 0 ? void 0 : _a[1].split('\n').map(a => a.trim()).filter(a => !!a);
|
|
45
|
+
expect(commandsText).toContain('page Page Content Service Commands');
|
|
46
|
+
});
|
|
39
47
|
});
|
package/lib/dxp.js
CHANGED
|
@@ -17,6 +17,7 @@ const job_runner_1 = __importDefault(require("./job-runner"));
|
|
|
17
17
|
const datastore_1 = __importDefault(require("./datastore"));
|
|
18
18
|
const cdp_1 = __importDefault(require("./cdp"));
|
|
19
19
|
const porter_1 = __importDefault(require("./porter"));
|
|
20
|
+
const page_1 = __importDefault(require("./page"));
|
|
20
21
|
const program = new commander_1.default.Command();
|
|
21
22
|
const packageJson = require('../package.json');
|
|
22
23
|
const version = packageJson.version;
|
|
@@ -32,6 +33,9 @@ program
|
|
|
32
33
|
if (process.env.ENABLE_PORTER === 'true') {
|
|
33
34
|
program.addCommand(porter_1.default);
|
|
34
35
|
}
|
|
36
|
+
if (process.env.ENABLE_PAGE_LAYOUTS === 'true') {
|
|
37
|
+
program.addCommand(page_1.default);
|
|
38
|
+
}
|
|
35
39
|
program
|
|
36
40
|
.action(() => {
|
|
37
41
|
program.help();
|
|
@@ -21,6 +21,7 @@ describe('listJobs', () => {
|
|
|
21
21
|
.get('/job/latest?&page%5Bsize%5D=5')
|
|
22
22
|
.reply(200, [{ name: 'simple-job', type: 'job', version: '1.0.0' }]);
|
|
23
23
|
const program = (0, listJobs_1.default)();
|
|
24
|
+
jest.spyOn(program, 'error').mockImplementation();
|
|
24
25
|
yield program.parseAsync([
|
|
25
26
|
'node',
|
|
26
27
|
'dxp-cli',
|
|
@@ -18,6 +18,7 @@ describe('upload job', () => {
|
|
|
18
18
|
it('correctly displays the console logs for uploading the job', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
19
19
|
process.env.ENABLE_OVERRIDE_JOB_RUNNER_URL = 'true';
|
|
20
20
|
const program = (0, uploadJob_1.default)();
|
|
21
|
+
jest.spyOn(program, 'error').mockImplementation();
|
|
21
22
|
yield program.parseAsync([
|
|
22
23
|
'node',
|
|
23
24
|
'dxp-cli',
|
|
@@ -0,0 +1,12 @@
|
|
|
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
|
+
const commander_1 = require("commander");
|
|
7
|
+
const layouts_1 = __importDefault(require("./layouts/layouts"));
|
|
8
|
+
const pageCommand = new commander_1.Command('page');
|
|
9
|
+
pageCommand
|
|
10
|
+
.description('Page Content Service Commands')
|
|
11
|
+
.addCommand((0, layouts_1.default)());
|
|
12
|
+
exports.default = pageCommand;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.logger = void 0;
|
|
16
|
+
const commander_1 = require("commander");
|
|
17
|
+
const cli_color_1 = __importDefault(require("cli-color"));
|
|
18
|
+
const ApiService_1 = require("../../../ApiService");
|
|
19
|
+
const ApplicationConfig_1 = require("../../../ApplicationConfig");
|
|
20
|
+
const constants_1 = require("../../../constants");
|
|
21
|
+
const definitions_1 = require("../../utils/definitions");
|
|
22
|
+
const dx_logger_lib_1 = require("@squiz/dx-logger-lib");
|
|
23
|
+
exports.logger = (0, dx_logger_lib_1.getLogger)({
|
|
24
|
+
name: 'upload-layout',
|
|
25
|
+
format: 'human',
|
|
26
|
+
});
|
|
27
|
+
const createDeployCommand = () => {
|
|
28
|
+
const deployCommand = new commander_1.Command()
|
|
29
|
+
.name('deploy')
|
|
30
|
+
.addOption(new commander_1.Option('--config <string>', 'File path to the page layout config file').default('./page-layout.yaml'))
|
|
31
|
+
.addOption(new commander_1.Option('-cu, --content-service-url <string>', 'Override the content service url from login')
|
|
32
|
+
.env('CONTENT_SERVICE_URL')
|
|
33
|
+
.hideHelp(true))
|
|
34
|
+
.addOption(new commander_1.Option('--dxp-base-url <baseURL>', 'DXP Console URL').env('DXP_BASE_URL'))
|
|
35
|
+
.addOption(new commander_1.Option('-t, --tenant <string>', 'Tenant ID to deploy to. If not provided will use configured tenant from login').env('SQUIZ_DXP_TENANT_ID'))
|
|
36
|
+
.addOption(new commander_1.Option('--dry-run', 'Run all pre-deployment processes without deploying').default(false))
|
|
37
|
+
.action((options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
38
|
+
var _a, _b, _c, _d;
|
|
39
|
+
if (options.contentServiceUrl) {
|
|
40
|
+
console.log(`NOTICE: CONTENT_SERVICE_URL is set and will deploy to ${options.contentServiceUrl}`);
|
|
41
|
+
}
|
|
42
|
+
const maybeConfig = yield maybeGetApplicationConfig();
|
|
43
|
+
const baseUrl = (_b = (_a = options.dxpBaseUrl) !== null && _a !== void 0 ? _a : maybeConfig === null || maybeConfig === void 0 ? void 0 : maybeConfig.baseUrl) !== null && _b !== void 0 ? _b : constants_1.PRODUCTION_URL;
|
|
44
|
+
const apiService = new ApiService_1.ApiService({
|
|
45
|
+
baseUrl,
|
|
46
|
+
tenantId: (_c = options.tenant) !== null && _c !== void 0 ? _c : maybeConfig === null || maybeConfig === void 0 ? void 0 : maybeConfig.tenant,
|
|
47
|
+
});
|
|
48
|
+
const contentServiceUrl = (_d = options.contentServiceUrl) !== null && _d !== void 0 ? _d : new URL('/__dxp/service/components-content', baseUrl).toString();
|
|
49
|
+
const layoutFile = options.config;
|
|
50
|
+
exports.logger.info(`Loading layout data from the file ${layoutFile}`);
|
|
51
|
+
try {
|
|
52
|
+
const layout = yield (0, definitions_1.loadLayoutDefinition)(layoutFile);
|
|
53
|
+
if (layout !== undefined) {
|
|
54
|
+
const response = yield uploadLayout(apiService.client, layout, contentServiceUrl, options.dryRun);
|
|
55
|
+
if (!options.dryRun) {
|
|
56
|
+
exports.logger.info(`Layout "${layout.name}" version ${response.data.version} deployed successfully.`);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
exports.logger.info(`Layout "${layout.name}" dry run successful.`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
if (!!process.env.DEBUG && error.stack) {
|
|
65
|
+
deployCommand.error(error.stack);
|
|
66
|
+
}
|
|
67
|
+
if (error instanceof ApiService_1.InvalidLoginSessionError) {
|
|
68
|
+
deployCommand.error(cli_color_1.default.red('ERROR:', 'Login session expired. Please login again.'));
|
|
69
|
+
}
|
|
70
|
+
if (error instanceof ApiService_1.InvalidTenantError) {
|
|
71
|
+
deployCommand.error(cli_color_1.default.red('ERROR:', 'Cannot deploy to specified tenant'));
|
|
72
|
+
}
|
|
73
|
+
if (error.message) {
|
|
74
|
+
deployCommand.error(cli_color_1.default.red('ERROR:', error.message));
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
deployCommand.error(cli_color_1.default.red('ERROR:', 'An unknown error occurred'));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}));
|
|
81
|
+
return deployCommand;
|
|
82
|
+
};
|
|
83
|
+
exports.default = createDeployCommand;
|
|
84
|
+
function uploadLayout(client, layout, contentServiceUrl, dryRun) {
|
|
85
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
86
|
+
try {
|
|
87
|
+
const queryParam = dryRun ? '?_dryRun=true' : '';
|
|
88
|
+
const response = yield client.post('/page-layout' + queryParam, layout, {
|
|
89
|
+
baseURL: contentServiceUrl,
|
|
90
|
+
});
|
|
91
|
+
if (response.status === 200) {
|
|
92
|
+
return response;
|
|
93
|
+
}
|
|
94
|
+
throw new Error(response.data.message);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function maybeGetApplicationConfig() {
|
|
102
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
103
|
+
try {
|
|
104
|
+
return yield (0, ApplicationConfig_1.fetchApplicationConfig)();
|
|
105
|
+
}
|
|
106
|
+
catch (_a) { }
|
|
107
|
+
});
|
|
108
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
jest.mock('../../utils/definitions');
|
|
16
|
+
jest.mock('@squiz/dx-logger-lib', () => ({
|
|
17
|
+
getLogger: () => {
|
|
18
|
+
return {
|
|
19
|
+
info: mockLoggerInfoFn,
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
}));
|
|
23
|
+
const mockLoggerInfoFn = jest.fn();
|
|
24
|
+
const nock_1 = __importDefault(require("nock"));
|
|
25
|
+
const deploy_1 = __importDefault(require("./deploy"));
|
|
26
|
+
const definitions_1 = require("../../utils/definitions");
|
|
27
|
+
function createMockArgs(opts) {
|
|
28
|
+
const args = ['node', 'dxp-cli', 'deploy'];
|
|
29
|
+
if (opts.config)
|
|
30
|
+
args.push('--config', opts.config);
|
|
31
|
+
if (opts.contentServiceUrl)
|
|
32
|
+
args.push('--content-service-url', opts.contentServiceUrl);
|
|
33
|
+
if (opts.tenant)
|
|
34
|
+
args.push('--tenant', opts.tenant);
|
|
35
|
+
if (opts.dxpBaseUrl)
|
|
36
|
+
args.push('--dxp-base-url', opts.dxpBaseUrl);
|
|
37
|
+
if (opts.dryRun)
|
|
38
|
+
args.push('--dry-run');
|
|
39
|
+
return args;
|
|
40
|
+
}
|
|
41
|
+
describe('deployCommand', () => {
|
|
42
|
+
let logSpy;
|
|
43
|
+
let errorSpy;
|
|
44
|
+
beforeAll(() => {
|
|
45
|
+
process.env.SQUIZ_DXP_TENANT_ID = 'myTenant';
|
|
46
|
+
});
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
logSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
49
|
+
if (!nock_1.default.isActive()) {
|
|
50
|
+
nock_1.default.activate();
|
|
51
|
+
}
|
|
52
|
+
nock_1.default.cleanAll();
|
|
53
|
+
});
|
|
54
|
+
afterEach(() => {
|
|
55
|
+
jest.clearAllMocks();
|
|
56
|
+
});
|
|
57
|
+
it('correctly handles command option defaults', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
58
|
+
const program = (0, deploy_1.default)();
|
|
59
|
+
yield program.parseAsync(createMockArgs({}));
|
|
60
|
+
const opts = program.opts();
|
|
61
|
+
expect(opts.config).toEqual('./page-layout.yaml');
|
|
62
|
+
expect(opts.tenant).toEqual('myTenant');
|
|
63
|
+
expect(opts.dryRun).toEqual(false);
|
|
64
|
+
}));
|
|
65
|
+
it('correctly handles command arguments', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
66
|
+
const config = './src/__tests__/layout.yaml';
|
|
67
|
+
const contentServiceUrl = 'http://localhost:9999';
|
|
68
|
+
const tenant = 'myTenant';
|
|
69
|
+
const dxpBaseUrl = 'http://dxp-base-url.com';
|
|
70
|
+
const dryRun = true;
|
|
71
|
+
const program = (0, deploy_1.default)();
|
|
72
|
+
yield program.parseAsync(createMockArgs({
|
|
73
|
+
config,
|
|
74
|
+
contentServiceUrl,
|
|
75
|
+
tenant,
|
|
76
|
+
dxpBaseUrl,
|
|
77
|
+
dryRun,
|
|
78
|
+
}));
|
|
79
|
+
const opts = program.opts();
|
|
80
|
+
expect(opts.config).toEqual(config);
|
|
81
|
+
expect(opts.contentServiceUrl).toEqual(contentServiceUrl);
|
|
82
|
+
expect(opts.tenant).toEqual(tenant);
|
|
83
|
+
expect(opts.dxpBaseUrl).toEqual(dxpBaseUrl);
|
|
84
|
+
expect(opts.dryRun).toEqual(true);
|
|
85
|
+
}));
|
|
86
|
+
it('logs a notice when contentServiceUrl is set', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
87
|
+
const contentServiceUrl = 'http://localhost:9999';
|
|
88
|
+
const program = (0, deploy_1.default)();
|
|
89
|
+
yield program.parseAsync(createMockArgs({ contentServiceUrl }));
|
|
90
|
+
expect(logSpy).toHaveBeenCalledWith(`NOTICE: CONTENT_SERVICE_URL is set and will deploy to ${contentServiceUrl}`);
|
|
91
|
+
}));
|
|
92
|
+
it('deploys a layout successfully', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
93
|
+
const file = './src/__tests__/layouts/page-layout.yaml';
|
|
94
|
+
const dxpBaseUrl = 'http://dxp-base-url.com';
|
|
95
|
+
const mockLayout = { name: 'Test Layout' };
|
|
96
|
+
const mockResponse = { name: 'Test Layout', version: '12345' };
|
|
97
|
+
definitions_1.loadLayoutDefinition.mockResolvedValue(mockLayout);
|
|
98
|
+
(0, nock_1.default)(dxpBaseUrl + '/__dxp/service/components-content')
|
|
99
|
+
.post('/page-layout', mockLayout)
|
|
100
|
+
.reply(200, mockResponse);
|
|
101
|
+
const program = (0, deploy_1.default)();
|
|
102
|
+
yield program.parseAsync(createMockArgs({ config: file, dxpBaseUrl }));
|
|
103
|
+
expect(mockLoggerInfoFn).toHaveBeenNthCalledWith(1, `Loading layout data from the file ${file}`);
|
|
104
|
+
expect(mockLoggerInfoFn).toHaveBeenNthCalledWith(2, 'Layout "Test Layout" version 12345 deployed successfully.');
|
|
105
|
+
}));
|
|
106
|
+
it('deploys a layout with dry-run option', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
107
|
+
const file = './src/__tests__/layout.yaml';
|
|
108
|
+
const dxpBaseUrl = 'http://dxp-base-url.com';
|
|
109
|
+
const dryRun = true;
|
|
110
|
+
const mockLayout = { name: 'Test Layout' };
|
|
111
|
+
const mockResponse = { name: 'Test Layout', version: '12345' };
|
|
112
|
+
definitions_1.loadLayoutDefinition.mockResolvedValue(mockLayout);
|
|
113
|
+
(0, nock_1.default)(dxpBaseUrl + '/__dxp/service/components-content')
|
|
114
|
+
.post('/page-layout', mockLayout)
|
|
115
|
+
.query({ _dryRun: 'true' })
|
|
116
|
+
.reply(200, mockResponse);
|
|
117
|
+
const program = (0, deploy_1.default)();
|
|
118
|
+
yield program.parseAsync(createMockArgs({ config: file, dxpBaseUrl, dryRun }));
|
|
119
|
+
expect(mockLoggerInfoFn).toHaveBeenNthCalledWith(1, `Loading layout data from the file ${file}`);
|
|
120
|
+
expect(mockLoggerInfoFn).toHaveBeenNthCalledWith(2, 'Layout "Test Layout" dry run successful.');
|
|
121
|
+
}));
|
|
122
|
+
it('handles InvalidLoginSessionError', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
123
|
+
const dxpBaseUrl = 'http://dxp-base-url.com';
|
|
124
|
+
(0, nock_1.default)(dxpBaseUrl + '/__dxp/service/components-content')
|
|
125
|
+
.post('/page-layout')
|
|
126
|
+
.reply(401, { message: 'Invalid request: no session' });
|
|
127
|
+
const program = (0, deploy_1.default)();
|
|
128
|
+
errorSpy = jest.spyOn(program, 'error').mockImplementation();
|
|
129
|
+
yield program.parseAsync(createMockArgs({ dxpBaseUrl }));
|
|
130
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('Login session expired. Please login again.'));
|
|
131
|
+
}));
|
|
132
|
+
it('handles InvalidTenantError', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
133
|
+
const dxpBaseUrl = 'http://dxp-base-url.com';
|
|
134
|
+
(0, nock_1.default)(dxpBaseUrl + '/__dxp/service/components-content')
|
|
135
|
+
.post('/page-layout')
|
|
136
|
+
.reply(400, { message: 'Cannot deploy to specified tenant' });
|
|
137
|
+
const program = (0, deploy_1.default)();
|
|
138
|
+
errorSpy = jest.spyOn(program, 'error').mockImplementation();
|
|
139
|
+
yield program.parseAsync(createMockArgs({ dxpBaseUrl }));
|
|
140
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('Cannot deploy to specified tenant'));
|
|
141
|
+
}));
|
|
142
|
+
it('handles errors with message', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
143
|
+
const dxpBaseUrl = 'http://dxp-base-url.com';
|
|
144
|
+
(0, nock_1.default)(dxpBaseUrl + '/__dxp/service/components-content')
|
|
145
|
+
.post('/page-layout')
|
|
146
|
+
.reply(400, { message: 'Invalid layout data!' });
|
|
147
|
+
const program = (0, deploy_1.default)();
|
|
148
|
+
errorSpy = jest.spyOn(program, 'error').mockImplementation();
|
|
149
|
+
yield program.parseAsync(createMockArgs({ dxpBaseUrl }));
|
|
150
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('Invalid layout data!'));
|
|
151
|
+
}));
|
|
152
|
+
it('handles unknown errors', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
153
|
+
const contentServiceUrl = 'http://localhost:9999';
|
|
154
|
+
(0, nock_1.default)(contentServiceUrl)
|
|
155
|
+
.post('/page-layout')
|
|
156
|
+
.reply(500, { data: 'Internal server error' });
|
|
157
|
+
const program = (0, deploy_1.default)();
|
|
158
|
+
errorSpy = jest.spyOn(program, 'error').mockImplementation();
|
|
159
|
+
yield program.parseAsync(createMockArgs({ contentServiceUrl }));
|
|
160
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('An unknown error occurred'));
|
|
161
|
+
}));
|
|
162
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
const commander_1 = require("commander");
|
|
7
|
+
const deploy_1 = __importDefault(require("./deploy/deploy"));
|
|
8
|
+
const createLayoutsCommand = () => {
|
|
9
|
+
const layoutsCommand = new commander_1.Command('layouts');
|
|
10
|
+
layoutsCommand
|
|
11
|
+
.description('Page Contents Layouts Commands')
|
|
12
|
+
.addCommand((0, deploy_1.default)());
|
|
13
|
+
return layoutsCommand;
|
|
14
|
+
};
|
|
15
|
+
exports.default = createLayoutsCommand;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
const commander_1 = require("commander");
|
|
7
|
+
const layouts_1 = __importDefault(require("./layouts"));
|
|
8
|
+
const deploy_1 = __importDefault(require("./deploy/deploy"));
|
|
9
|
+
jest.mock('./deploy/deploy');
|
|
10
|
+
deploy_1.default.mockImplementation(() => {
|
|
11
|
+
return new commander_1.Command('mock-deploy');
|
|
12
|
+
});
|
|
13
|
+
describe('createLayoutsCommand', () => {
|
|
14
|
+
it('should create a layouts command with deploy subcommand', () => {
|
|
15
|
+
const layoutsCommand = (0, layouts_1.default)();
|
|
16
|
+
expect(layoutsCommand).toBeInstanceOf(commander_1.Command);
|
|
17
|
+
expect(layoutsCommand.name()).toBe('layouts');
|
|
18
|
+
expect(layoutsCommand.description()).toBe('Page Contents Layouts Commands');
|
|
19
|
+
expect(deploy_1.default).toHaveBeenCalled();
|
|
20
|
+
expect(layoutsCommand.commands.map(cmd => cmd.name())).toContain('mock-deploy');
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare function loadLayoutDefinition(layoutFile: string): Promise<LayoutDefinition>;
|
|
3
|
+
export declare const BaseLayoutDefinition: z.ZodObject<{
|
|
4
|
+
/**
|
|
5
|
+
* User defined identifier for a Page Layout
|
|
6
|
+
*/
|
|
7
|
+
name: z.ZodString;
|
|
8
|
+
/**
|
|
9
|
+
* Name displayed in user interfaces
|
|
10
|
+
*/
|
|
11
|
+
displayName: z.ZodString;
|
|
12
|
+
/**
|
|
13
|
+
* Description to provide extra information on the layout
|
|
14
|
+
*/
|
|
15
|
+
description: z.ZodString;
|
|
16
|
+
/**
|
|
17
|
+
* Zone input structure configuration
|
|
18
|
+
*
|
|
19
|
+
* Declares the content structure that will be the input to the
|
|
20
|
+
* Handlebars template
|
|
21
|
+
*/
|
|
22
|
+
zones: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
23
|
+
displayName: z.ZodString;
|
|
24
|
+
description: z.ZodString;
|
|
25
|
+
minNodes: z.ZodNumber;
|
|
26
|
+
maxNodes: z.ZodOptional<z.ZodNumber>;
|
|
27
|
+
}, "strip", z.ZodTypeAny, {
|
|
28
|
+
description: string;
|
|
29
|
+
displayName: string;
|
|
30
|
+
minNodes: number;
|
|
31
|
+
maxNodes?: number | undefined;
|
|
32
|
+
}, {
|
|
33
|
+
description: string;
|
|
34
|
+
displayName: string;
|
|
35
|
+
minNodes: number;
|
|
36
|
+
maxNodes?: number | undefined;
|
|
37
|
+
}>>;
|
|
38
|
+
/**
|
|
39
|
+
* Layout options
|
|
40
|
+
*
|
|
41
|
+
* Declares any configurable options that can be provided by a Content Editor
|
|
42
|
+
*/
|
|
43
|
+
options: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
44
|
+
displayName: z.ZodString;
|
|
45
|
+
description: z.ZodString;
|
|
46
|
+
values: z.ZodArray<z.ZodString, "many">;
|
|
47
|
+
}, "strip", z.ZodTypeAny, {
|
|
48
|
+
values: string[];
|
|
49
|
+
description: string;
|
|
50
|
+
displayName: string;
|
|
51
|
+
}, {
|
|
52
|
+
values: string[];
|
|
53
|
+
description: string;
|
|
54
|
+
displayName: string;
|
|
55
|
+
}>>;
|
|
56
|
+
}, "strip", z.ZodTypeAny, {
|
|
57
|
+
name: string;
|
|
58
|
+
options: Record<string, {
|
|
59
|
+
values: string[];
|
|
60
|
+
description: string;
|
|
61
|
+
displayName: string;
|
|
62
|
+
}>;
|
|
63
|
+
description: string;
|
|
64
|
+
displayName: string;
|
|
65
|
+
zones: Record<string, {
|
|
66
|
+
description: string;
|
|
67
|
+
displayName: string;
|
|
68
|
+
minNodes: number;
|
|
69
|
+
maxNodes?: number | undefined;
|
|
70
|
+
}>;
|
|
71
|
+
}, {
|
|
72
|
+
name: string;
|
|
73
|
+
options: Record<string, {
|
|
74
|
+
values: string[];
|
|
75
|
+
description: string;
|
|
76
|
+
displayName: string;
|
|
77
|
+
}>;
|
|
78
|
+
description: string;
|
|
79
|
+
displayName: string;
|
|
80
|
+
zones: Record<string, {
|
|
81
|
+
description: string;
|
|
82
|
+
displayName: string;
|
|
83
|
+
minNodes: number;
|
|
84
|
+
maxNodes?: number | undefined;
|
|
85
|
+
}>;
|
|
86
|
+
}>;
|
|
87
|
+
export declare const InputLayoutDefinition: z.ZodObject<z.objectUtil.extendShape<{
|
|
88
|
+
/**
|
|
89
|
+
* User defined identifier for a Page Layout
|
|
90
|
+
*/
|
|
91
|
+
name: z.ZodString;
|
|
92
|
+
/**
|
|
93
|
+
* Name displayed in user interfaces
|
|
94
|
+
*/
|
|
95
|
+
displayName: z.ZodString;
|
|
96
|
+
/**
|
|
97
|
+
* Description to provide extra information on the layout
|
|
98
|
+
*/
|
|
99
|
+
description: z.ZodString;
|
|
100
|
+
/**
|
|
101
|
+
* Zone input structure configuration
|
|
102
|
+
*
|
|
103
|
+
* Declares the content structure that will be the input to the
|
|
104
|
+
* Handlebars template
|
|
105
|
+
*/
|
|
106
|
+
zones: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
107
|
+
displayName: z.ZodString;
|
|
108
|
+
description: z.ZodString;
|
|
109
|
+
minNodes: z.ZodNumber;
|
|
110
|
+
maxNodes: z.ZodOptional<z.ZodNumber>;
|
|
111
|
+
}, "strip", z.ZodTypeAny, {
|
|
112
|
+
description: string;
|
|
113
|
+
displayName: string;
|
|
114
|
+
minNodes: number;
|
|
115
|
+
maxNodes?: number | undefined;
|
|
116
|
+
}, {
|
|
117
|
+
description: string;
|
|
118
|
+
displayName: string;
|
|
119
|
+
minNodes: number;
|
|
120
|
+
maxNodes?: number | undefined;
|
|
121
|
+
}>>;
|
|
122
|
+
/**
|
|
123
|
+
* Layout options
|
|
124
|
+
*
|
|
125
|
+
* Declares any configurable options that can be provided by a Content Editor
|
|
126
|
+
*/
|
|
127
|
+
options: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
128
|
+
displayName: z.ZodString;
|
|
129
|
+
description: z.ZodString;
|
|
130
|
+
values: z.ZodArray<z.ZodString, "many">;
|
|
131
|
+
}, "strip", z.ZodTypeAny, {
|
|
132
|
+
values: string[];
|
|
133
|
+
description: string;
|
|
134
|
+
displayName: string;
|
|
135
|
+
}, {
|
|
136
|
+
values: string[];
|
|
137
|
+
description: string;
|
|
138
|
+
displayName: string;
|
|
139
|
+
}>>;
|
|
140
|
+
}, {
|
|
141
|
+
entry: z.ZodString;
|
|
142
|
+
}>, "strip", z.ZodTypeAny, {
|
|
143
|
+
name: string;
|
|
144
|
+
options: Record<string, {
|
|
145
|
+
values: string[];
|
|
146
|
+
description: string;
|
|
147
|
+
displayName: string;
|
|
148
|
+
}>;
|
|
149
|
+
entry: string;
|
|
150
|
+
description: string;
|
|
151
|
+
displayName: string;
|
|
152
|
+
zones: Record<string, {
|
|
153
|
+
description: string;
|
|
154
|
+
displayName: string;
|
|
155
|
+
minNodes: number;
|
|
156
|
+
maxNodes?: number | undefined;
|
|
157
|
+
}>;
|
|
158
|
+
}, {
|
|
159
|
+
name: string;
|
|
160
|
+
options: Record<string, {
|
|
161
|
+
values: string[];
|
|
162
|
+
description: string;
|
|
163
|
+
displayName: string;
|
|
164
|
+
}>;
|
|
165
|
+
entry: string;
|
|
166
|
+
description: string;
|
|
167
|
+
displayName: string;
|
|
168
|
+
zones: Record<string, {
|
|
169
|
+
description: string;
|
|
170
|
+
displayName: string;
|
|
171
|
+
minNodes: number;
|
|
172
|
+
maxNodes?: number | undefined;
|
|
173
|
+
}>;
|
|
174
|
+
}>;
|
|
175
|
+
export declare const LayoutDefinition: z.ZodObject<z.objectUtil.extendShape<{
|
|
176
|
+
/**
|
|
177
|
+
* User defined identifier for a Page Layout
|
|
178
|
+
*/
|
|
179
|
+
name: z.ZodString;
|
|
180
|
+
/**
|
|
181
|
+
* Name displayed in user interfaces
|
|
182
|
+
*/
|
|
183
|
+
displayName: z.ZodString;
|
|
184
|
+
/**
|
|
185
|
+
* Description to provide extra information on the layout
|
|
186
|
+
*/
|
|
187
|
+
description: z.ZodString;
|
|
188
|
+
/**
|
|
189
|
+
* Zone input structure configuration
|
|
190
|
+
*
|
|
191
|
+
* Declares the content structure that will be the input to the
|
|
192
|
+
* Handlebars template
|
|
193
|
+
*/
|
|
194
|
+
zones: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
195
|
+
displayName: z.ZodString;
|
|
196
|
+
description: z.ZodString;
|
|
197
|
+
minNodes: z.ZodNumber;
|
|
198
|
+
maxNodes: z.ZodOptional<z.ZodNumber>;
|
|
199
|
+
}, "strip", z.ZodTypeAny, {
|
|
200
|
+
description: string;
|
|
201
|
+
displayName: string;
|
|
202
|
+
minNodes: number;
|
|
203
|
+
maxNodes?: number | undefined;
|
|
204
|
+
}, {
|
|
205
|
+
description: string;
|
|
206
|
+
displayName: string;
|
|
207
|
+
minNodes: number;
|
|
208
|
+
maxNodes?: number | undefined;
|
|
209
|
+
}>>;
|
|
210
|
+
/**
|
|
211
|
+
* Layout options
|
|
212
|
+
*
|
|
213
|
+
* Declares any configurable options that can be provided by a Content Editor
|
|
214
|
+
*/
|
|
215
|
+
options: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
216
|
+
displayName: z.ZodString;
|
|
217
|
+
description: z.ZodString;
|
|
218
|
+
values: z.ZodArray<z.ZodString, "many">;
|
|
219
|
+
}, "strip", z.ZodTypeAny, {
|
|
220
|
+
values: string[];
|
|
221
|
+
description: string;
|
|
222
|
+
displayName: string;
|
|
223
|
+
}, {
|
|
224
|
+
values: string[];
|
|
225
|
+
description: string;
|
|
226
|
+
displayName: string;
|
|
227
|
+
}>>;
|
|
228
|
+
}, {
|
|
229
|
+
template: z.ZodString;
|
|
230
|
+
}>, "strip", z.ZodTypeAny, {
|
|
231
|
+
name: string;
|
|
232
|
+
options: Record<string, {
|
|
233
|
+
values: string[];
|
|
234
|
+
description: string;
|
|
235
|
+
displayName: string;
|
|
236
|
+
}>;
|
|
237
|
+
description: string;
|
|
238
|
+
displayName: string;
|
|
239
|
+
zones: Record<string, {
|
|
240
|
+
description: string;
|
|
241
|
+
displayName: string;
|
|
242
|
+
minNodes: number;
|
|
243
|
+
maxNodes?: number | undefined;
|
|
244
|
+
}>;
|
|
245
|
+
template: string;
|
|
246
|
+
}, {
|
|
247
|
+
name: string;
|
|
248
|
+
options: Record<string, {
|
|
249
|
+
values: string[];
|
|
250
|
+
description: string;
|
|
251
|
+
displayName: string;
|
|
252
|
+
}>;
|
|
253
|
+
description: string;
|
|
254
|
+
displayName: string;
|
|
255
|
+
zones: Record<string, {
|
|
256
|
+
description: string;
|
|
257
|
+
displayName: string;
|
|
258
|
+
minNodes: number;
|
|
259
|
+
maxNodes?: number | undefined;
|
|
260
|
+
}>;
|
|
261
|
+
template: string;
|
|
262
|
+
}>;
|
|
263
|
+
export declare type InputLayoutDefinition = z.infer<typeof InputLayoutDefinition>;
|
|
264
|
+
export declare type LayoutDefinition = z.infer<typeof LayoutDefinition>;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
35
|
+
var t = {};
|
|
36
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
37
|
+
t[p] = s[p];
|
|
38
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
39
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
40
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
41
|
+
t[p[i]] = s[p[i]];
|
|
42
|
+
}
|
|
43
|
+
return t;
|
|
44
|
+
};
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.LayoutDefinition = exports.InputLayoutDefinition = exports.BaseLayoutDefinition = exports.loadLayoutDefinition = void 0;
|
|
47
|
+
const fs = __importStar(require("node:fs/promises"));
|
|
48
|
+
const path = __importStar(require("node:path"));
|
|
49
|
+
const zod_1 = require("zod");
|
|
50
|
+
const yaml_1 = require("yaml");
|
|
51
|
+
function loadLayoutDefinition(layoutFile) {
|
|
52
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
53
|
+
try {
|
|
54
|
+
const _a = yield loadLayoutFromFile(layoutFile), { entry } = _a, layout = __rest(_a, ["entry"]);
|
|
55
|
+
const template = yield loadTemplate(path.dirname(layoutFile), entry);
|
|
56
|
+
const layoutDefinition = Object.assign(Object.assign({}, layout), { template });
|
|
57
|
+
exports.LayoutDefinition.parse(layoutDefinition);
|
|
58
|
+
return layoutDefinition;
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
throw Error(`Failed loading layout definition: ${e.message}`);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
exports.loadLayoutDefinition = loadLayoutDefinition;
|
|
66
|
+
function loadLayoutFromFile(layoutFile) {
|
|
67
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
68
|
+
const fileType = layoutFile.split('.').pop();
|
|
69
|
+
if (!fileType || !['yaml', 'json'].includes(fileType)) {
|
|
70
|
+
throw Error('Layout file must have a valid extension: [yaml|json]');
|
|
71
|
+
}
|
|
72
|
+
const content = yield fs.readFile(layoutFile, {
|
|
73
|
+
encoding: 'utf-8',
|
|
74
|
+
});
|
|
75
|
+
try {
|
|
76
|
+
const layout = (0, yaml_1.parse)(content);
|
|
77
|
+
exports.InputLayoutDefinition.parse(layout);
|
|
78
|
+
return layout;
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
throw Error(`Failed to parse ${layoutFile}: ${e.message}`);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function loadTemplate(layoutDirectory, templateFile) {
|
|
86
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
87
|
+
try {
|
|
88
|
+
return yield fs.readFile(path.resolve(layoutDirectory, templateFile), {
|
|
89
|
+
encoding: 'utf-8',
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
catch (e) {
|
|
93
|
+
throw Error(`Failed loading template file "${templateFile}": ${e.message}`);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
exports.BaseLayoutDefinition = zod_1.z.object({
|
|
98
|
+
/**
|
|
99
|
+
* User defined identifier for a Page Layout
|
|
100
|
+
*/
|
|
101
|
+
name: zod_1.z.string().regex(/^[a-z][a-z0-9-_]*[a-z0-9]$/),
|
|
102
|
+
/**
|
|
103
|
+
* Name displayed in user interfaces
|
|
104
|
+
*/
|
|
105
|
+
displayName: zod_1.z.string(),
|
|
106
|
+
/**
|
|
107
|
+
* Description to provide extra information on the layout
|
|
108
|
+
*/
|
|
109
|
+
description: zod_1.z.string(),
|
|
110
|
+
/**
|
|
111
|
+
* Zone input structure configuration
|
|
112
|
+
*
|
|
113
|
+
* Declares the content structure that will be the input to the
|
|
114
|
+
* Handlebars template
|
|
115
|
+
*/
|
|
116
|
+
zones: zod_1.z.record(zod_1.z.string(), zod_1.z.object({
|
|
117
|
+
displayName: zod_1.z.string(),
|
|
118
|
+
description: zod_1.z.string(),
|
|
119
|
+
minNodes: zod_1.z.number().min(0),
|
|
120
|
+
maxNodes: zod_1.z.number().min(1).optional(),
|
|
121
|
+
})),
|
|
122
|
+
/**
|
|
123
|
+
* Layout options
|
|
124
|
+
*
|
|
125
|
+
* Declares any configurable options that can be provided by a Content Editor
|
|
126
|
+
*/
|
|
127
|
+
options: zod_1.z.record(zod_1.z.string(), zod_1.z.object({
|
|
128
|
+
displayName: zod_1.z.string(),
|
|
129
|
+
description: zod_1.z.string(),
|
|
130
|
+
values: zod_1.z.array(zod_1.z.string()),
|
|
131
|
+
})),
|
|
132
|
+
});
|
|
133
|
+
exports.InputLayoutDefinition = exports.BaseLayoutDefinition.extend({
|
|
134
|
+
entry: zod_1.z.string(),
|
|
135
|
+
});
|
|
136
|
+
exports.LayoutDefinition = exports.BaseLayoutDefinition.extend({
|
|
137
|
+
template: zod_1.z.string(),
|
|
138
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
const fs = __importStar(require("node:fs/promises"));
|
|
36
|
+
const definitions_1 = require("./definitions");
|
|
37
|
+
jest.mock('node:fs/promises');
|
|
38
|
+
describe('loadLayoutDefinition', () => {
|
|
39
|
+
const paintLayoutFileYaml = './some-dir/page-layout.yaml';
|
|
40
|
+
const paintLayoutFileJson = './some-dir/page-layout.json';
|
|
41
|
+
const yamlContent = `
|
|
42
|
+
name: test-layout
|
|
43
|
+
displayName: Test Layout
|
|
44
|
+
description: A test layout
|
|
45
|
+
zones:
|
|
46
|
+
main:
|
|
47
|
+
displayName: Main Zone
|
|
48
|
+
description: Main content area
|
|
49
|
+
minNodes: 1
|
|
50
|
+
options:
|
|
51
|
+
color:
|
|
52
|
+
displayName: Color
|
|
53
|
+
description: Color options
|
|
54
|
+
values: ['red', 'blue']
|
|
55
|
+
entry: template.hbs
|
|
56
|
+
`;
|
|
57
|
+
// missing "name" field
|
|
58
|
+
const yamlContentInvalidLayout = `
|
|
59
|
+
displayName: Test Layout
|
|
60
|
+
description: A test layout
|
|
61
|
+
zones:
|
|
62
|
+
main:
|
|
63
|
+
displayName: Main Zone
|
|
64
|
+
description: Main content area
|
|
65
|
+
minNodes: 1
|
|
66
|
+
options:
|
|
67
|
+
color:
|
|
68
|
+
displayName: Color
|
|
69
|
+
description: Color options
|
|
70
|
+
values: ['red', 'blue']
|
|
71
|
+
entry: template.hbs
|
|
72
|
+
`;
|
|
73
|
+
const jsonContent = JSON.stringify({
|
|
74
|
+
name: 'test-layout',
|
|
75
|
+
displayName: 'Test Layout',
|
|
76
|
+
description: 'A test layout',
|
|
77
|
+
zones: {
|
|
78
|
+
main: {
|
|
79
|
+
displayName: 'Main Zone',
|
|
80
|
+
description: 'Main content area',
|
|
81
|
+
minNodes: 1,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
options: {
|
|
85
|
+
color: {
|
|
86
|
+
displayName: 'Color',
|
|
87
|
+
description: 'Color options',
|
|
88
|
+
values: ['red', 'blue'],
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
entry: 'template.hbs',
|
|
92
|
+
});
|
|
93
|
+
// missing "name" field
|
|
94
|
+
const jsonContentInvalidLayout = JSON.stringify({
|
|
95
|
+
displayName: 'Test Layout',
|
|
96
|
+
description: 'A test layout',
|
|
97
|
+
zones: {
|
|
98
|
+
main: {
|
|
99
|
+
displayName: 'Main Zone',
|
|
100
|
+
description: 'Main content area',
|
|
101
|
+
minNodes: 1,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
options: {
|
|
105
|
+
color: {
|
|
106
|
+
displayName: 'Color',
|
|
107
|
+
description: 'Color options',
|
|
108
|
+
values: ['red', 'blue'],
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
entry: 'template.hbs',
|
|
112
|
+
});
|
|
113
|
+
const templateContent = '<div>{{content}}</div>';
|
|
114
|
+
beforeEach(() => {
|
|
115
|
+
jest.resetAllMocks();
|
|
116
|
+
});
|
|
117
|
+
it('should load layout definition from YAML file', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
118
|
+
fs.readFile.mockImplementation((filePath) => {
|
|
119
|
+
if (filePath.endsWith('page-layout.yaml')) {
|
|
120
|
+
return yamlContent;
|
|
121
|
+
}
|
|
122
|
+
if (filePath.endsWith('template.hbs')) {
|
|
123
|
+
return templateContent;
|
|
124
|
+
}
|
|
125
|
+
throw new Error('File not found');
|
|
126
|
+
});
|
|
127
|
+
const layoutDefinition = yield (0, definitions_1.loadLayoutDefinition)(paintLayoutFileYaml);
|
|
128
|
+
expect(layoutDefinition).toEqual({
|
|
129
|
+
name: 'test-layout',
|
|
130
|
+
displayName: 'Test Layout',
|
|
131
|
+
description: 'A test layout',
|
|
132
|
+
zones: {
|
|
133
|
+
main: {
|
|
134
|
+
displayName: 'Main Zone',
|
|
135
|
+
description: 'Main content area',
|
|
136
|
+
minNodes: 1,
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
options: {
|
|
140
|
+
color: {
|
|
141
|
+
displayName: 'Color',
|
|
142
|
+
description: 'Color options',
|
|
143
|
+
values: ['red', 'blue'],
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
template: templateContent,
|
|
147
|
+
});
|
|
148
|
+
}));
|
|
149
|
+
it('should load layout definition from JSON file', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
150
|
+
fs.readFile.mockImplementation((filePath) => {
|
|
151
|
+
if (filePath.endsWith('page-layout.yaml')) {
|
|
152
|
+
throw { code: 'ENOENT' };
|
|
153
|
+
}
|
|
154
|
+
if (filePath.endsWith('page-layout.json')) {
|
|
155
|
+
return jsonContent;
|
|
156
|
+
}
|
|
157
|
+
if (filePath.endsWith('template.hbs')) {
|
|
158
|
+
return templateContent;
|
|
159
|
+
}
|
|
160
|
+
throw new Error('File not found');
|
|
161
|
+
});
|
|
162
|
+
const layoutDefinition = yield (0, definitions_1.loadLayoutDefinition)(paintLayoutFileJson);
|
|
163
|
+
expect(layoutDefinition).toEqual({
|
|
164
|
+
name: 'test-layout',
|
|
165
|
+
displayName: 'Test Layout',
|
|
166
|
+
description: 'A test layout',
|
|
167
|
+
zones: {
|
|
168
|
+
main: {
|
|
169
|
+
displayName: 'Main Zone',
|
|
170
|
+
description: 'Main content area',
|
|
171
|
+
minNodes: 1,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
options: {
|
|
175
|
+
color: {
|
|
176
|
+
displayName: 'Color',
|
|
177
|
+
description: 'Color options',
|
|
178
|
+
values: ['red', 'blue'],
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
template: templateContent,
|
|
182
|
+
});
|
|
183
|
+
}));
|
|
184
|
+
it('should throw validation error if invalid data for YAML', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
185
|
+
fs.readFile.mockImplementation((filePath) => {
|
|
186
|
+
if (filePath.endsWith('page-layout.yaml')) {
|
|
187
|
+
return yamlContentInvalidLayout;
|
|
188
|
+
}
|
|
189
|
+
if (filePath.endsWith('template.hbs')) {
|
|
190
|
+
return templateContent;
|
|
191
|
+
}
|
|
192
|
+
throw new Error('File not found');
|
|
193
|
+
});
|
|
194
|
+
yield expect((0, definitions_1.loadLayoutDefinition)(paintLayoutFileYaml)).rejects
|
|
195
|
+
.toThrowErrorMatchingInlineSnapshot(`
|
|
196
|
+
"Failed loading layout definition: Failed to parse ./some-dir/page-layout.yaml: [
|
|
197
|
+
{
|
|
198
|
+
"code": "invalid_type",
|
|
199
|
+
"expected": "string",
|
|
200
|
+
"received": "undefined",
|
|
201
|
+
"path": [
|
|
202
|
+
"name"
|
|
203
|
+
],
|
|
204
|
+
"message": "Required"
|
|
205
|
+
}
|
|
206
|
+
]"
|
|
207
|
+
`);
|
|
208
|
+
}));
|
|
209
|
+
it('should throw validation error if invalid data for JSON', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
210
|
+
fs.readFile.mockImplementation((filePath) => {
|
|
211
|
+
if (filePath.endsWith('page-layout.json')) {
|
|
212
|
+
return jsonContentInvalidLayout;
|
|
213
|
+
}
|
|
214
|
+
if (filePath.endsWith('template.hbs')) {
|
|
215
|
+
return templateContent;
|
|
216
|
+
}
|
|
217
|
+
throw new Error('File not found');
|
|
218
|
+
});
|
|
219
|
+
yield expect((0, definitions_1.loadLayoutDefinition)(paintLayoutFileJson)).rejects
|
|
220
|
+
.toThrowErrorMatchingInlineSnapshot(`
|
|
221
|
+
"Failed loading layout definition: Failed to parse ./some-dir/page-layout.json: [
|
|
222
|
+
{
|
|
223
|
+
"code": "invalid_type",
|
|
224
|
+
"expected": "string",
|
|
225
|
+
"received": "undefined",
|
|
226
|
+
"path": [
|
|
227
|
+
"name"
|
|
228
|
+
],
|
|
229
|
+
"message": "Required"
|
|
230
|
+
}
|
|
231
|
+
]"
|
|
232
|
+
`);
|
|
233
|
+
}));
|
|
234
|
+
it('should throw an error if YAML layout file has invalid YAML', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
235
|
+
fs.readFile.mockImplementation((filePath) => {
|
|
236
|
+
if (filePath.endsWith('page-layout.yaml')) {
|
|
237
|
+
return 'name: invalid yaml, entry: template.hbs';
|
|
238
|
+
}
|
|
239
|
+
if (filePath.endsWith('page-layout.json')) {
|
|
240
|
+
return jsonContent;
|
|
241
|
+
}
|
|
242
|
+
if (filePath.endsWith('template.hbs')) {
|
|
243
|
+
return templateContent;
|
|
244
|
+
}
|
|
245
|
+
throw new Error('File not found');
|
|
246
|
+
});
|
|
247
|
+
yield expect((0, definitions_1.loadLayoutDefinition)(paintLayoutFileYaml)).rejects
|
|
248
|
+
.toThrowErrorMatchingInlineSnapshot(`
|
|
249
|
+
"Failed loading layout definition: Failed to parse ./some-dir/page-layout.yaml: Nested mappings are not allowed in compact mappings at line 1, column 7:
|
|
250
|
+
|
|
251
|
+
name: invalid yaml, entry: template.hbs
|
|
252
|
+
^
|
|
253
|
+
"
|
|
254
|
+
`);
|
|
255
|
+
}));
|
|
256
|
+
it('should throw an error if JSON layout file has invalid JSON', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
257
|
+
fs.readFile.mockImplementation((filePath) => {
|
|
258
|
+
if (filePath.endsWith('page-layout.yaml')) {
|
|
259
|
+
throw { code: 'ENOENT' };
|
|
260
|
+
}
|
|
261
|
+
if (filePath.endsWith('page-layout.json')) {
|
|
262
|
+
return '{invalid: json';
|
|
263
|
+
}
|
|
264
|
+
if (filePath.endsWith('template.hbs')) {
|
|
265
|
+
return templateContent;
|
|
266
|
+
}
|
|
267
|
+
throw new Error('File not found');
|
|
268
|
+
});
|
|
269
|
+
yield expect((0, definitions_1.loadLayoutDefinition)(paintLayoutFileJson)).rejects
|
|
270
|
+
.toThrowErrorMatchingInlineSnapshot(`
|
|
271
|
+
"Failed loading layout definition: Failed to parse ./some-dir/page-layout.json: Flow map must end with a } at line 1, column 15:
|
|
272
|
+
|
|
273
|
+
{invalid: json
|
|
274
|
+
^
|
|
275
|
+
"
|
|
276
|
+
`);
|
|
277
|
+
}));
|
|
278
|
+
it('should throw an error if layout file do not have valid extension', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
279
|
+
yield expect((0, definitions_1.loadLayoutDefinition)('/foo/paint-layout.html')).rejects.toThrow('Layout file must have a valid extension: [yaml|json]');
|
|
280
|
+
}));
|
|
281
|
+
it('should throw an error if template file is not found', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
282
|
+
fs.readFile.mockImplementation((filePath) => {
|
|
283
|
+
if (filePath.endsWith('page-layout.yaml')) {
|
|
284
|
+
return yamlContent;
|
|
285
|
+
}
|
|
286
|
+
throw Error('File not found');
|
|
287
|
+
});
|
|
288
|
+
yield expect((0, definitions_1.loadLayoutDefinition)(paintLayoutFileYaml)).rejects.toThrow('Failed loading layout definition: Failed loading template file "template.hbs": File not found');
|
|
289
|
+
}));
|
|
290
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@squiz/dxp-cli-next",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.24.0-develop.1",
|
|
4
4
|
"repository": {
|
|
5
5
|
"url": "https://gitlab.squiz.net/dxp/dxp-cli-next"
|
|
6
6
|
},
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"@squiz/component-cli-lib": "1.72.6",
|
|
45
45
|
"@squiz/dxp-porter-shared": "0.4.0",
|
|
46
46
|
"@squiz/local-component-dev-ui": "0.6.18",
|
|
47
|
+
"@squiz/dx-logger-lib": "^1.65.1",
|
|
47
48
|
"axios": "1.1.3",
|
|
48
49
|
"cli-color": "2.0.3",
|
|
49
50
|
"commander": "9.4.0",
|