@squiz/dxp-cli-next 5.25.3 → 5.26.0-develop.2
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 +4 -0
- package/lib/dxp.js +4 -0
- package/lib/migration/create/createMigration.d.ts +3 -0
- package/lib/migration/create/createMigration.js +82 -0
- package/lib/migration/create/createMigration.spec.d.ts +1 -0
- package/lib/migration/create/createMigration.spec.js +381 -0
- package/lib/migration/get/getMigration.d.ts +3 -0
- package/lib/migration/get/getMigration.js +47 -0
- package/lib/migration/get/getMigration.spec.d.ts +1 -0
- package/lib/migration/get/getMigration.spec.js +278 -0
- package/lib/migration/index.d.ts +3 -0
- package/lib/migration/index.js +14 -0
- package/lib/migration/types.d.ts +37 -0
- package/lib/migration/types.js +2 -0
- package/lib/migration/utils.d.ts +12 -0
- package/lib/migration/utils.js +225 -0
- package/lib/migration/utils.spec.d.ts +1 -0
- package/lib/migration/utils.spec.js +543 -0
- package/package.json +1 -1
|
@@ -17,6 +17,8 @@ describe('dxp', () => {
|
|
|
17
17
|
'job-runner Job Runner Service Commands',
|
|
18
18
|
'datastore Datastore Service Commands',
|
|
19
19
|
'cdp Customer Data Platform Service Commands',
|
|
20
|
+
// TODO: Migration is hidden behind feature flag.
|
|
21
|
+
// 'migration AI Page Migration Service Commands',
|
|
20
22
|
// TODO: Porter is hidden behind feature flag.
|
|
21
23
|
// 'porter Porter Service Commands',
|
|
22
24
|
]);
|
|
@@ -33,6 +35,8 @@ describe('dxp', () => {
|
|
|
33
35
|
'job-runner Job Runner Service Commands',
|
|
34
36
|
'datastore Datastore Service Commands',
|
|
35
37
|
'cdp Customer Data Platform Service Commands',
|
|
38
|
+
// TODO: Migration is hidden behind feature flag.
|
|
39
|
+
// 'migration AI Page Migration Service Commands',
|
|
36
40
|
'porter Porter Service Commands',
|
|
37
41
|
]);
|
|
38
42
|
});
|
package/lib/dxp.js
CHANGED
|
@@ -18,6 +18,7 @@ const datastore_1 = __importDefault(require("./datastore"));
|
|
|
18
18
|
const cdp_1 = __importDefault(require("./cdp"));
|
|
19
19
|
const porter_1 = __importDefault(require("./porter"));
|
|
20
20
|
const page_1 = __importDefault(require("./page"));
|
|
21
|
+
const migration_1 = __importDefault(require("./migration"));
|
|
21
22
|
const program = new commander_1.default.Command();
|
|
22
23
|
const packageJson = require('../package.json');
|
|
23
24
|
const version = packageJson.version;
|
|
@@ -36,6 +37,9 @@ if (process.env.ENABLE_PORTER === 'true') {
|
|
|
36
37
|
if (process.env.ENABLE_PAGE_LAYOUTS === 'true') {
|
|
37
38
|
program.addCommand(page_1.default);
|
|
38
39
|
}
|
|
40
|
+
if (process.env.ENABLE_MIGRATION === 'true') {
|
|
41
|
+
program.addCommand(migration_1.default);
|
|
42
|
+
}
|
|
39
43
|
program
|
|
40
44
|
.action(() => {
|
|
41
45
|
program.help();
|
|
@@ -0,0 +1,82 @@
|
|
|
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
|
+
const commander_1 = require("commander");
|
|
16
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
17
|
+
const ora_1 = __importDefault(require("ora"));
|
|
18
|
+
const fs_1 = __importDefault(require("fs"));
|
|
19
|
+
const utils_1 = require("../utils");
|
|
20
|
+
const createMigrationCommand = () => {
|
|
21
|
+
const createCommand = new commander_1.Command('create')
|
|
22
|
+
.name('create')
|
|
23
|
+
.description('Create a new migration using the AI Page migration service')
|
|
24
|
+
.addOption(new commander_1.Option('--asset-id <string>', 'The ID of the asset to be migrated').makeOptionMandatory())
|
|
25
|
+
.addOption(new commander_1.Option('--preview-asset-id <string>', 'The ID of the folder that will be used as the parent of the migrated asset generated during the Preview stage').makeOptionMandatory())
|
|
26
|
+
.addOption(new commander_1.Option('--matrix-url <string>', 'Matrix URL for the migration').makeOptionMandatory())
|
|
27
|
+
.addOption(new commander_1.Option('-t, --tenant <string>', 'Tenant ID to run against. If not provided will use configured tenant from login'))
|
|
28
|
+
.argument('<exportPath>', 'Path to the export folder (e.g., ./export)')
|
|
29
|
+
.configureOutput({
|
|
30
|
+
outputError(str, write) {
|
|
31
|
+
write(chalk_1.default.red(str));
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
.action((exportPath, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
35
|
+
yield (0, utils_1.throwErrorIfNotLoggedIn)(createCommand);
|
|
36
|
+
let spinner = (0, ora_1.default)('Validating export folder structure').start();
|
|
37
|
+
try {
|
|
38
|
+
// Validate export folder structure
|
|
39
|
+
(0, utils_1.validateExportFolder)(exportPath);
|
|
40
|
+
spinner.succeed('Export folder structure validated');
|
|
41
|
+
// Create tar file
|
|
42
|
+
spinner = (0, ora_1.default)('Creating tar file from export folder').start();
|
|
43
|
+
const tarFilePath = yield (0, utils_1.createTarFile)(exportPath);
|
|
44
|
+
if (!tarFilePath) {
|
|
45
|
+
throw new Error('Tar file creation failed');
|
|
46
|
+
}
|
|
47
|
+
spinner.succeed(`Tar file created: ${tarFilePath}`);
|
|
48
|
+
// Create migration
|
|
49
|
+
spinner = (0, ora_1.default)('Creating migration').start();
|
|
50
|
+
const response = yield (0, utils_1.createMigration)(options);
|
|
51
|
+
if (!response) {
|
|
52
|
+
throw new Error('Migration creation failed');
|
|
53
|
+
}
|
|
54
|
+
spinner.succeed('Migration created successfully');
|
|
55
|
+
// Upload file to S3
|
|
56
|
+
spinner = (0, ora_1.default)('Uploading file to S3').start();
|
|
57
|
+
const uploadUrl = response.uploadUrl;
|
|
58
|
+
const s3Url = yield (0, utils_1.uploadFileToS3)(uploadUrl, tarFilePath, options.tenant);
|
|
59
|
+
if (!s3Url) {
|
|
60
|
+
throw new Error('File upload failed');
|
|
61
|
+
}
|
|
62
|
+
spinner.succeed('File uploaded to S3');
|
|
63
|
+
// Clean up tar file
|
|
64
|
+
fs_1.default.unlinkSync(tarFilePath);
|
|
65
|
+
spinner.succeed(`Successfully created migration: ${JSON.stringify({
|
|
66
|
+
migrationId: response.assetMigration.migrationId,
|
|
67
|
+
assetId: response.assetMigration.assetId,
|
|
68
|
+
stage: response.assetMigration.stage,
|
|
69
|
+
status: response.assetMigration.status,
|
|
70
|
+
})}`);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
spinner.fail();
|
|
74
|
+
(0, utils_1.handleCommandError)(createCommand, error);
|
|
75
|
+
}
|
|
76
|
+
}));
|
|
77
|
+
if (process.env.ENABLE_OVERRIDE_MIGRATION_URL === 'true') {
|
|
78
|
+
createCommand.addOption(new commander_1.Option('-ou, --overrideUrl <string>', 'Developer option to override the entire migration url with a custom value'));
|
|
79
|
+
}
|
|
80
|
+
return createCommand;
|
|
81
|
+
};
|
|
82
|
+
exports.default = createMigrationCommand;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,381 @@
|
|
|
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 __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
+
};
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
const nock_1 = __importDefault(require("nock"));
|
|
39
|
+
const createMigration_1 = __importDefault(require("./createMigration"));
|
|
40
|
+
const utils = __importStar(require("../utils"));
|
|
41
|
+
const ApplicationStore = __importStar(require("../../ApplicationStore"));
|
|
42
|
+
const fs_1 = __importDefault(require("fs"));
|
|
43
|
+
jest.mock('../utils');
|
|
44
|
+
jest.mock('../../ApplicationStore');
|
|
45
|
+
jest.mock('fs');
|
|
46
|
+
const mockUtils = utils;
|
|
47
|
+
const mockApplicationStore = ApplicationStore;
|
|
48
|
+
const mockFs = fs_1.default;
|
|
49
|
+
describe('createMigrationCommand', () => {
|
|
50
|
+
let logSpy;
|
|
51
|
+
let mockCreateMigrationResponse;
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
nock_1.default.cleanAll();
|
|
54
|
+
jest.clearAllMocks();
|
|
55
|
+
jest.resetAllMocks();
|
|
56
|
+
logSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
57
|
+
mockApplicationStore.getApplicationFile.mockResolvedValue('session-cookie');
|
|
58
|
+
mockUtils.throwErrorIfNotLoggedIn.mockResolvedValue(undefined);
|
|
59
|
+
mockUtils.validateExportFolder.mockImplementation(() => { });
|
|
60
|
+
mockUtils.createTarFile.mockResolvedValue('/path/to/export_123.tar.gz');
|
|
61
|
+
mockUtils.uploadFileToS3.mockResolvedValue('https://s3.amazonaws.com/uploaded-file');
|
|
62
|
+
mockFs.unlinkSync.mockImplementation(() => { });
|
|
63
|
+
mockCreateMigrationResponse = {
|
|
64
|
+
assetMigration: {
|
|
65
|
+
migrationId: 'migration-123',
|
|
66
|
+
assetId: 'asset-456',
|
|
67
|
+
stage: 'pending',
|
|
68
|
+
status: 'created',
|
|
69
|
+
xmlFilePath: '/path/to/xml',
|
|
70
|
+
matrixUrl: 'https://matrix.example.com',
|
|
71
|
+
previewAssetId: 'preview-789',
|
|
72
|
+
created: 1234567890,
|
|
73
|
+
updated: 1234567890,
|
|
74
|
+
migrationIdAssetId: 'migration-123-asset-456',
|
|
75
|
+
},
|
|
76
|
+
uploadUrl: 'https://upload.s3.amazonaws.com',
|
|
77
|
+
};
|
|
78
|
+
mockUtils.createMigration.mockResolvedValue(mockCreateMigrationResponse);
|
|
79
|
+
});
|
|
80
|
+
afterEach(() => {
|
|
81
|
+
logSpy.mockRestore();
|
|
82
|
+
});
|
|
83
|
+
describe('successful migration creation', () => {
|
|
84
|
+
it('should create migration successfully with all required options', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
85
|
+
const program = (0, createMigration_1.default)();
|
|
86
|
+
yield program.parseAsync([
|
|
87
|
+
'node',
|
|
88
|
+
'dxp-cli',
|
|
89
|
+
'--asset-id',
|
|
90
|
+
'asset-123',
|
|
91
|
+
'--preview-asset-id',
|
|
92
|
+
'preview-456',
|
|
93
|
+
'--matrix-url',
|
|
94
|
+
'https://matrix.example.com',
|
|
95
|
+
'/path/to/export',
|
|
96
|
+
]);
|
|
97
|
+
expect(mockUtils.throwErrorIfNotLoggedIn).toHaveBeenCalledWith(program);
|
|
98
|
+
expect(mockUtils.validateExportFolder).toHaveBeenCalledWith('/path/to/export');
|
|
99
|
+
expect(mockUtils.createTarFile).toHaveBeenCalledWith('/path/to/export');
|
|
100
|
+
expect(mockUtils.createMigration).toHaveBeenCalledWith({
|
|
101
|
+
assetId: 'asset-123',
|
|
102
|
+
previewAssetId: 'preview-456',
|
|
103
|
+
matrixUrl: 'https://matrix.example.com',
|
|
104
|
+
});
|
|
105
|
+
expect(mockUtils.uploadFileToS3).toHaveBeenCalledWith('https://upload.s3.amazonaws.com', '/path/to/export_123.tar.gz', undefined);
|
|
106
|
+
expect(mockFs.unlinkSync).toHaveBeenCalledWith('/path/to/export_123.tar.gz');
|
|
107
|
+
expect(mockUtils.validateExportFolder).toHaveBeenCalledTimes(1);
|
|
108
|
+
expect(mockUtils.createTarFile).toHaveBeenCalledTimes(1);
|
|
109
|
+
expect(mockUtils.createMigration).toHaveBeenCalledTimes(1);
|
|
110
|
+
expect(mockUtils.uploadFileToS3).toHaveBeenCalledTimes(1);
|
|
111
|
+
expect(mockFs.unlinkSync).toHaveBeenCalledTimes(1);
|
|
112
|
+
expect(mockUtils.handleCommandError).not.toHaveBeenCalled();
|
|
113
|
+
}));
|
|
114
|
+
it('should create migration with tenant option', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
115
|
+
const program = (0, createMigration_1.default)();
|
|
116
|
+
yield program.parseAsync([
|
|
117
|
+
'node',
|
|
118
|
+
'dxp-cli',
|
|
119
|
+
'--asset-id',
|
|
120
|
+
'asset-123',
|
|
121
|
+
'--preview-asset-id',
|
|
122
|
+
'preview-456',
|
|
123
|
+
'--matrix-url',
|
|
124
|
+
'https://matrix.example.com',
|
|
125
|
+
'--tenant',
|
|
126
|
+
'test-tenant',
|
|
127
|
+
'/path/to/export',
|
|
128
|
+
]);
|
|
129
|
+
expect(mockUtils.createMigration).toHaveBeenCalledWith({
|
|
130
|
+
assetId: 'asset-123',
|
|
131
|
+
previewAssetId: 'preview-456',
|
|
132
|
+
matrixUrl: 'https://matrix.example.com',
|
|
133
|
+
tenant: 'test-tenant',
|
|
134
|
+
});
|
|
135
|
+
expect(mockUtils.uploadFileToS3).toHaveBeenCalledWith('https://upload.s3.amazonaws.com', '/path/to/export_123.tar.gz', 'test-tenant');
|
|
136
|
+
}));
|
|
137
|
+
it('should create migration with override URL when environment variable is set', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
138
|
+
const originalEnv = process.env.ENABLE_OVERRIDE_MIGRATION_URL;
|
|
139
|
+
process.env.ENABLE_OVERRIDE_MIGRATION_URL = 'true';
|
|
140
|
+
const program = (0, createMigration_1.default)();
|
|
141
|
+
yield program.parseAsync([
|
|
142
|
+
'node',
|
|
143
|
+
'dxp-cli',
|
|
144
|
+
'--asset-id',
|
|
145
|
+
'asset-123',
|
|
146
|
+
'--preview-asset-id',
|
|
147
|
+
'preview-456',
|
|
148
|
+
'--matrix-url',
|
|
149
|
+
'https://matrix.example.com',
|
|
150
|
+
'--overrideUrl',
|
|
151
|
+
'https://custom.migration.url',
|
|
152
|
+
'/path/to/export',
|
|
153
|
+
]);
|
|
154
|
+
expect(mockUtils.createMigration).toHaveBeenCalledWith({
|
|
155
|
+
assetId: 'asset-123',
|
|
156
|
+
previewAssetId: 'preview-456',
|
|
157
|
+
matrixUrl: 'https://matrix.example.com',
|
|
158
|
+
overrideUrl: 'https://custom.migration.url',
|
|
159
|
+
});
|
|
160
|
+
process.env.ENABLE_OVERRIDE_MIGRATION_URL = originalEnv;
|
|
161
|
+
}));
|
|
162
|
+
});
|
|
163
|
+
describe('error scenarios', () => {
|
|
164
|
+
it('should handle validation error for export folder', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
165
|
+
const validationError = new Error('Export folder does not exist');
|
|
166
|
+
mockUtils.validateExportFolder.mockImplementation(() => {
|
|
167
|
+
throw validationError;
|
|
168
|
+
});
|
|
169
|
+
mockUtils.handleCommandError.mockImplementation(() => { });
|
|
170
|
+
const program = (0, createMigration_1.default)();
|
|
171
|
+
yield program.parseAsync([
|
|
172
|
+
'node',
|
|
173
|
+
'dxp-cli',
|
|
174
|
+
'--asset-id',
|
|
175
|
+
'asset-123',
|
|
176
|
+
'--preview-asset-id',
|
|
177
|
+
'preview-456',
|
|
178
|
+
'--matrix-url',
|
|
179
|
+
'https://matrix.example.com',
|
|
180
|
+
'/invalid/path',
|
|
181
|
+
]);
|
|
182
|
+
expect(mockUtils.handleCommandError).toHaveBeenCalledWith(program, validationError);
|
|
183
|
+
expect(mockUtils.createTarFile).not.toHaveBeenCalled();
|
|
184
|
+
}));
|
|
185
|
+
it('should handle tar file creation failure', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
186
|
+
mockUtils.createTarFile.mockResolvedValue(null);
|
|
187
|
+
const program = (0, createMigration_1.default)();
|
|
188
|
+
yield program.parseAsync([
|
|
189
|
+
'node',
|
|
190
|
+
'dxp-cli',
|
|
191
|
+
'--asset-id',
|
|
192
|
+
'asset-123',
|
|
193
|
+
'--preview-asset-id',
|
|
194
|
+
'preview-456',
|
|
195
|
+
'--matrix-url',
|
|
196
|
+
'https://matrix.example.com',
|
|
197
|
+
'/path/to/export',
|
|
198
|
+
]);
|
|
199
|
+
expect(mockUtils.createMigration).not.toHaveBeenCalled();
|
|
200
|
+
expect(mockUtils.uploadFileToS3).not.toHaveBeenCalled();
|
|
201
|
+
}));
|
|
202
|
+
it('should handle migration creation failure', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
203
|
+
mockUtils.createMigration.mockResolvedValue(null);
|
|
204
|
+
const program = (0, createMigration_1.default)();
|
|
205
|
+
yield program.parseAsync([
|
|
206
|
+
'node',
|
|
207
|
+
'dxp-cli',
|
|
208
|
+
'--asset-id',
|
|
209
|
+
'asset-123',
|
|
210
|
+
'--preview-asset-id',
|
|
211
|
+
'preview-456',
|
|
212
|
+
'--matrix-url',
|
|
213
|
+
'https://matrix.example.com',
|
|
214
|
+
'/path/to/export',
|
|
215
|
+
]);
|
|
216
|
+
expect(mockUtils.uploadFileToS3).not.toHaveBeenCalled();
|
|
217
|
+
expect(mockFs.unlinkSync).not.toHaveBeenCalled();
|
|
218
|
+
}));
|
|
219
|
+
it('should handle S3 upload failure', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
220
|
+
mockUtils.uploadFileToS3.mockResolvedValue(null);
|
|
221
|
+
const program = (0, createMigration_1.default)();
|
|
222
|
+
yield program.parseAsync([
|
|
223
|
+
'node',
|
|
224
|
+
'dxp-cli',
|
|
225
|
+
'--asset-id',
|
|
226
|
+
'asset-123',
|
|
227
|
+
'--preview-asset-id',
|
|
228
|
+
'preview-456',
|
|
229
|
+
'--matrix-url',
|
|
230
|
+
'https://matrix.example.com',
|
|
231
|
+
'/path/to/export',
|
|
232
|
+
]);
|
|
233
|
+
expect(mockFs.unlinkSync).not.toHaveBeenCalled();
|
|
234
|
+
}));
|
|
235
|
+
it('should handle migration API error', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
236
|
+
const apiError = new Error('Migration API failed');
|
|
237
|
+
mockUtils.createMigration.mockRejectedValue(apiError);
|
|
238
|
+
mockUtils.handleCommandError.mockImplementation(() => { });
|
|
239
|
+
const program = (0, createMigration_1.default)();
|
|
240
|
+
yield program.parseAsync([
|
|
241
|
+
'node',
|
|
242
|
+
'dxp-cli',
|
|
243
|
+
'--asset-id',
|
|
244
|
+
'asset-123',
|
|
245
|
+
'--preview-asset-id',
|
|
246
|
+
'preview-456',
|
|
247
|
+
'--matrix-url',
|
|
248
|
+
'https://matrix.example.com',
|
|
249
|
+
'/path/to/export',
|
|
250
|
+
]);
|
|
251
|
+
expect(mockUtils.handleCommandError).toHaveBeenCalledWith(program, apiError);
|
|
252
|
+
}));
|
|
253
|
+
it('should handle not being logged in', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
254
|
+
const loginError = new Error('Not logged in');
|
|
255
|
+
mockUtils.throwErrorIfNotLoggedIn.mockImplementation(() => {
|
|
256
|
+
throw loginError;
|
|
257
|
+
});
|
|
258
|
+
const program = (0, createMigration_1.default)();
|
|
259
|
+
yield expect(program.parseAsync([
|
|
260
|
+
'node',
|
|
261
|
+
'dxp-cli',
|
|
262
|
+
'--asset-id',
|
|
263
|
+
'asset-123',
|
|
264
|
+
'--preview-asset-id',
|
|
265
|
+
'preview-456',
|
|
266
|
+
'--matrix-url',
|
|
267
|
+
'https://matrix.example.com',
|
|
268
|
+
'/path/to/export',
|
|
269
|
+
])).rejects.toThrow('Not logged in');
|
|
270
|
+
expect(mockUtils.validateExportFolder).not.toHaveBeenCalled();
|
|
271
|
+
}));
|
|
272
|
+
});
|
|
273
|
+
describe('required options validation', () => {
|
|
274
|
+
it('should require assetid option', () => {
|
|
275
|
+
const program = (0, createMigration_1.default)().exitOverride();
|
|
276
|
+
expect(() => {
|
|
277
|
+
program.parse([
|
|
278
|
+
'node',
|
|
279
|
+
'dxp-cli',
|
|
280
|
+
'--preview-asset-id',
|
|
281
|
+
'preview-456',
|
|
282
|
+
'--matrix-url',
|
|
283
|
+
'https://matrix.example.com',
|
|
284
|
+
'/path/to/export',
|
|
285
|
+
]);
|
|
286
|
+
}).toThrow();
|
|
287
|
+
});
|
|
288
|
+
it('should require previewAssetid option', () => {
|
|
289
|
+
const program = (0, createMigration_1.default)().exitOverride();
|
|
290
|
+
expect(() => {
|
|
291
|
+
program.parse([
|
|
292
|
+
'node',
|
|
293
|
+
'dxp-cli',
|
|
294
|
+
'--asset-id',
|
|
295
|
+
'asset-123',
|
|
296
|
+
'--matrix-url',
|
|
297
|
+
'https://matrix.example.com',
|
|
298
|
+
'/path/to/export',
|
|
299
|
+
]);
|
|
300
|
+
}).toThrow();
|
|
301
|
+
});
|
|
302
|
+
it('should require matrixUrl option', () => {
|
|
303
|
+
const program = (0, createMigration_1.default)().exitOverride();
|
|
304
|
+
expect(() => {
|
|
305
|
+
program.parse([
|
|
306
|
+
'node',
|
|
307
|
+
'dxp-cli',
|
|
308
|
+
'--asset-id',
|
|
309
|
+
'asset-123',
|
|
310
|
+
'--preview-asset-id',
|
|
311
|
+
'preview-456',
|
|
312
|
+
'/path/to/export',
|
|
313
|
+
]);
|
|
314
|
+
}).toThrow();
|
|
315
|
+
});
|
|
316
|
+
it('should require exportPath argument', () => {
|
|
317
|
+
const program = (0, createMigration_1.default)().exitOverride();
|
|
318
|
+
expect(() => {
|
|
319
|
+
program.parse([
|
|
320
|
+
'node',
|
|
321
|
+
'dxp-cli',
|
|
322
|
+
'--asset-id',
|
|
323
|
+
'asset-123',
|
|
324
|
+
'--preview-asset-id',
|
|
325
|
+
'preview-456',
|
|
326
|
+
'--matrix-url',
|
|
327
|
+
'https://matrix.example.com',
|
|
328
|
+
]);
|
|
329
|
+
}).toThrow();
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
describe('command configuration', () => {
|
|
333
|
+
it('should have correct command name and description', () => {
|
|
334
|
+
const program = (0, createMigration_1.default)();
|
|
335
|
+
expect(program.name()).toBe('create');
|
|
336
|
+
expect(program.description()).toBe('Create a new migration using the AI Page migration service');
|
|
337
|
+
});
|
|
338
|
+
it('should parse options correctly', () => {
|
|
339
|
+
const program = (0, createMigration_1.default)();
|
|
340
|
+
program.parse([
|
|
341
|
+
'node',
|
|
342
|
+
'dxp-cli',
|
|
343
|
+
'--asset-id',
|
|
344
|
+
'asset-123',
|
|
345
|
+
'--preview-asset-id',
|
|
346
|
+
'preview-456',
|
|
347
|
+
'--matrix-url',
|
|
348
|
+
'https://matrix.example.com',
|
|
349
|
+
'--tenant',
|
|
350
|
+
'test-tenant',
|
|
351
|
+
'/path/to/export',
|
|
352
|
+
]);
|
|
353
|
+
const opts = program.opts();
|
|
354
|
+
expect(opts.assetId).toBe('asset-123');
|
|
355
|
+
expect(opts.previewAssetId).toBe('preview-456');
|
|
356
|
+
expect(opts.matrixUrl).toBe('https://matrix.example.com');
|
|
357
|
+
expect(opts.tenant).toBe('test-tenant');
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
describe('spinner and output behavior', () => {
|
|
361
|
+
it('should display appropriate spinner messages during execution', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
362
|
+
const program = (0, createMigration_1.default)();
|
|
363
|
+
yield program.parseAsync([
|
|
364
|
+
'node',
|
|
365
|
+
'dxp-cli',
|
|
366
|
+
'--asset-id',
|
|
367
|
+
'asset-123',
|
|
368
|
+
'--preview-asset-id',
|
|
369
|
+
'preview-456',
|
|
370
|
+
'--matrix-url',
|
|
371
|
+
'https://matrix.example.com',
|
|
372
|
+
'/path/to/export',
|
|
373
|
+
]);
|
|
374
|
+
expect(mockUtils.validateExportFolder).toHaveBeenCalledWith('/path/to/export');
|
|
375
|
+
expect(mockUtils.createTarFile).toHaveBeenCalledWith('/path/to/export');
|
|
376
|
+
expect(mockUtils.createMigration).toHaveBeenCalled();
|
|
377
|
+
expect(mockUtils.uploadFileToS3).toHaveBeenCalled();
|
|
378
|
+
expect(mockFs.unlinkSync).toHaveBeenCalled();
|
|
379
|
+
}));
|
|
380
|
+
});
|
|
381
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
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
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
16
|
+
const commander_1 = require("commander");
|
|
17
|
+
const utils_1 = require("../utils");
|
|
18
|
+
const ora_1 = __importDefault(require("ora"));
|
|
19
|
+
const getMigrationCommand = () => {
|
|
20
|
+
const getCommand = new commander_1.Command('get')
|
|
21
|
+
.name('get')
|
|
22
|
+
.description('Get a migration using the AI Page migration service')
|
|
23
|
+
.addOption(new commander_1.Option('--migration-id <string>', 'The ID of the migration to get').makeOptionMandatory())
|
|
24
|
+
.addOption(new commander_1.Option('--asset-id <string>', 'The ID of the asset to get the migration for'))
|
|
25
|
+
.addOption(new commander_1.Option('-t, --tenant <string>', 'Tenant ID to run against. If not provided will use configured tenant from login'))
|
|
26
|
+
.configureOutput({
|
|
27
|
+
outputError(str, write) {
|
|
28
|
+
write(chalk_1.default.red(str));
|
|
29
|
+
},
|
|
30
|
+
})
|
|
31
|
+
.action((options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
32
|
+
const spinner = (0, ora_1.default)('Getting migration').start();
|
|
33
|
+
yield (0, utils_1.throwErrorIfNotLoggedIn)(getCommand);
|
|
34
|
+
try {
|
|
35
|
+
const response = yield (0, utils_1.getMigration)(options);
|
|
36
|
+
spinner.succeed(`Migration details: ${JSON.stringify(response, null, 2)}`);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
(0, utils_1.handleCommandError)(getCommand, error);
|
|
40
|
+
}
|
|
41
|
+
}));
|
|
42
|
+
if (process.env.ENABLE_OVERRIDE_MIGRATION_URL === 'true') {
|
|
43
|
+
getCommand.addOption(new commander_1.Option('-ou, --overrideUrl <string>', 'Developer option to override the entire migration url with a custom value'));
|
|
44
|
+
}
|
|
45
|
+
return getCommand;
|
|
46
|
+
};
|
|
47
|
+
exports.default = getMigrationCommand;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|