@squiz/dxp-cli-next 5.27.0 → 5.28.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/ApiService.d.ts +2 -2
- package/lib/ApiService.js +4 -4
- package/lib/ApiService.spec.js +28 -20
- package/lib/migration/create/create.js +2 -23
- package/lib/migration/create/create.spec.js +3 -107
- package/lib/migration/index.js +2 -0
- package/lib/migration/list/list.d.ts +3 -0
- package/lib/migration/list/list.js +45 -0
- package/lib/migration/list/list.spec.d.ts +1 -0
- package/lib/migration/list/list.spec.js +138 -0
- package/lib/migration/types/listMigrations.types.d.ts +2 -0
- package/lib/migration/types/listMigrations.types.js +2 -0
- package/lib/migration/utils/listMigrations.d.ts +3 -0
- package/lib/migration/utils/listMigrations.js +41 -0
- package/lib/migration/utils/listMigrations.spec.d.ts +1 -0
- package/lib/migration/utils/listMigrations.spec.js +108 -0
- package/package.json +3 -3
package/lib/ApiService.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AxiosInstance,
|
|
1
|
+
import { AxiosInstance, InternalAxiosRequestConfig, AxiosResponse } from 'axios';
|
|
2
2
|
export declare const COOKIE_KEY = "dxp-sessionid";
|
|
3
3
|
interface ApiServiceParameters {
|
|
4
4
|
validateStatus?: (status: number) => boolean;
|
|
@@ -19,7 +19,7 @@ export declare class ApiService {
|
|
|
19
19
|
* storage and set it on any request. This ensures that post-login, all requests
|
|
20
20
|
* will seamlessly have the authorization required.
|
|
21
21
|
*/
|
|
22
|
-
export declare function requestInterceptor(request:
|
|
22
|
+
export declare function requestInterceptor(request: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig<any>>;
|
|
23
23
|
/**
|
|
24
24
|
* The API Service responseInterceptor will handle the case that a
|
|
25
25
|
* "Set-Cookie" header has been returned from an API request and will
|
package/lib/ApiService.js
CHANGED
|
@@ -52,16 +52,16 @@ function requestInterceptor(request) {
|
|
|
52
52
|
}
|
|
53
53
|
else {
|
|
54
54
|
const maybeSessionCookie = yield (0, ApplicationStore_1.getApplicationFile)(ApplicationStore_1.STORE_FILES.sessionCookie);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
const maybeSessionCookieString = maybeSessionCookie && ((_a = tough_cookie_1.Cookie.fromJSON(maybeSessionCookie)) === null || _a === void 0 ? void 0 : _a.cookieString());
|
|
56
|
+
if (maybeSessionCookieString) {
|
|
57
|
+
additionalHeaders.cookie = maybeSessionCookieString;
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
if (process.env.DXP_JWT) {
|
|
61
61
|
additionalHeaders.authorization = `Bearer ${process.env.DXP_JWT}`;
|
|
62
62
|
}
|
|
63
63
|
if (Object.keys(additionalHeaders).length > 0) {
|
|
64
|
-
request.headers =
|
|
64
|
+
request.headers = request.headers.concat(additionalHeaders);
|
|
65
65
|
}
|
|
66
66
|
return request;
|
|
67
67
|
});
|
package/lib/ApiService.spec.js
CHANGED
|
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const axios_1 = require("axios");
|
|
12
13
|
const tough_cookie_1 = require("tough-cookie");
|
|
13
14
|
const ApiService_1 = require("./ApiService");
|
|
14
15
|
const ApplicationStore_1 = require("./ApplicationStore");
|
|
@@ -26,36 +27,43 @@ describe('ApiService', () => {
|
|
|
26
27
|
}));
|
|
27
28
|
it('loads stored session cookie into request config', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
28
29
|
yield (0, ApplicationStore_1.saveApplicationFile)(ApplicationStore_1.STORE_FILES.sessionCookie, JSON.stringify(VALID_COOKIE.toJSON()));
|
|
29
|
-
const requestConfig = yield (0, ApiService_1.requestInterceptor)({
|
|
30
|
+
const requestConfig = yield (0, ApiService_1.requestInterceptor)({
|
|
31
|
+
headers: new axios_1.AxiosHeaders(),
|
|
32
|
+
});
|
|
30
33
|
expect(requestConfig).toHaveProperty('headers');
|
|
31
34
|
expect(requestConfig.headers).toHaveProperty('cookie');
|
|
32
35
|
expect(tough_cookie_1.Cookie.parse(requestConfig.headers['cookie'])).toHaveProperty('value', 'valid-cookie-value');
|
|
33
36
|
}));
|
|
34
37
|
it('does not use session cookie if none stored', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
35
|
-
const requestConfig = yield (0, ApiService_1.requestInterceptor)({
|
|
36
|
-
|
|
38
|
+
const requestConfig = yield (0, ApiService_1.requestInterceptor)({
|
|
39
|
+
headers: new axios_1.AxiosHeaders(),
|
|
40
|
+
});
|
|
41
|
+
expect(requestConfig).toEqual({
|
|
42
|
+
headers: new axios_1.AxiosHeaders(),
|
|
43
|
+
});
|
|
37
44
|
}));
|
|
38
45
|
it('attaches DXP JWT as authorization header if environment variable defined', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
39
46
|
process.env.DXP_JWT = 'xxx.yyy.zzz';
|
|
40
47
|
const requestConfig = yield (0, ApiService_1.requestInterceptor)({
|
|
41
|
-
headers: {
|
|
48
|
+
headers: new axios_1.AxiosHeaders({
|
|
42
49
|
'x-custom-header': 'header value',
|
|
43
|
-
},
|
|
50
|
+
}),
|
|
44
51
|
});
|
|
45
|
-
expect(requestConfig).
|
|
46
|
-
headers: {
|
|
47
|
-
authorization: 'Bearer xxx.yyy.zzz',
|
|
52
|
+
expect(requestConfig).toMatchObject({
|
|
53
|
+
headers: new axios_1.AxiosHeaders({
|
|
48
54
|
'x-custom-header': 'header value',
|
|
49
|
-
|
|
55
|
+
authorization: 'Bearer xxx.yyy.zzz',
|
|
56
|
+
}),
|
|
50
57
|
});
|
|
51
58
|
}));
|
|
52
59
|
it('uses the SQUIZ_DXP_API_TOKEN variable instead of session if provided', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
53
60
|
process.env.SQUIZ_DXP_API_TOKEN = '123';
|
|
54
61
|
yield (0, ApplicationStore_1.saveApplicationFile)(ApplicationStore_1.STORE_FILES.sessionCookie, JSON.stringify(VALID_COOKIE.toJSON()));
|
|
55
|
-
|
|
56
|
-
|
|
62
|
+
const headers = new axios_1.AxiosHeaders();
|
|
63
|
+
yield expect((0, ApiService_1.requestInterceptor)({ headers })).resolves.toMatchObject({
|
|
64
|
+
headers: new axios_1.AxiosHeaders({
|
|
57
65
|
'x-api-key': '123',
|
|
58
|
-
},
|
|
66
|
+
}),
|
|
59
67
|
});
|
|
60
68
|
}));
|
|
61
69
|
});
|
|
@@ -66,8 +74,8 @@ describe('ApiService', () => {
|
|
|
66
74
|
it('saves the set-cookie for a session into the store', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
67
75
|
const setCookie = [VALID_COOKIE.toString()];
|
|
68
76
|
const response = {
|
|
77
|
+
config: { headers: new axios_1.AxiosHeaders() },
|
|
69
78
|
data: {},
|
|
70
|
-
config: {},
|
|
71
79
|
status: 204,
|
|
72
80
|
statusText: '',
|
|
73
81
|
headers: { 'set-cookie': setCookie },
|
|
@@ -84,7 +92,7 @@ describe('ApiService', () => {
|
|
|
84
92
|
it('does not error if no set-cookie', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
85
93
|
const response = {
|
|
86
94
|
data: {},
|
|
87
|
-
config: {},
|
|
95
|
+
config: { headers: new axios_1.AxiosHeaders() },
|
|
88
96
|
status: 204,
|
|
89
97
|
statusText: '',
|
|
90
98
|
headers: {},
|
|
@@ -97,7 +105,7 @@ describe('ApiService', () => {
|
|
|
97
105
|
yield (0, ApplicationStore_1.saveApplicationFile)(ApplicationStore_1.STORE_FILES.sessionCookie, JSON.stringify(VALID_COOKIE.toJSON()));
|
|
98
106
|
const response = {
|
|
99
107
|
data: {},
|
|
100
|
-
config: {},
|
|
108
|
+
config: { headers: new axios_1.AxiosHeaders() },
|
|
101
109
|
status: 204,
|
|
102
110
|
statusText: '',
|
|
103
111
|
headers: {},
|
|
@@ -110,7 +118,7 @@ describe('ApiService', () => {
|
|
|
110
118
|
yield (0, ApplicationStore_1.saveApplicationFile)(ApplicationStore_1.STORE_FILES.sessionCookie, JSON.stringify(VALID_COOKIE.toJSON()));
|
|
111
119
|
const response = {
|
|
112
120
|
data: { nested: 'rest' },
|
|
113
|
-
config: {},
|
|
121
|
+
config: { headers: new axios_1.AxiosHeaders() },
|
|
114
122
|
status: 204,
|
|
115
123
|
statusText: '',
|
|
116
124
|
headers: {
|
|
@@ -129,7 +137,7 @@ describe('ApiService', () => {
|
|
|
129
137
|
it('errors with InvalidSession on failed session creation if no existing file', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
130
138
|
const response = {
|
|
131
139
|
data: { message: ApiService_1.MESSAGE_FAILED_TO_CREATE_SESSION },
|
|
132
|
-
config: {},
|
|
140
|
+
config: { headers: new axios_1.AxiosHeaders() },
|
|
133
141
|
status: 401,
|
|
134
142
|
statusText: '',
|
|
135
143
|
headers: {},
|
|
@@ -139,7 +147,7 @@ describe('ApiService', () => {
|
|
|
139
147
|
it('errors with InvalidSession on no session provided', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
140
148
|
const response = {
|
|
141
149
|
data: { message: ApiService_1.MESSAGE_INVALID_REQUEST_NO_SESSIONID },
|
|
142
|
-
config: {},
|
|
150
|
+
config: { headers: new axios_1.AxiosHeaders() },
|
|
143
151
|
status: 401,
|
|
144
152
|
statusText: '',
|
|
145
153
|
headers: {},
|
|
@@ -150,7 +158,7 @@ describe('ApiService', () => {
|
|
|
150
158
|
yield (0, ApplicationStore_1.saveApplicationFile)(ApplicationStore_1.STORE_FILES.sessionCookie, JSON.stringify(VALID_COOKIE.toJSON()));
|
|
151
159
|
const response = {
|
|
152
160
|
data: { message: ApiService_1.MESSAGE_FAILED_TO_CREATE_SESSION },
|
|
153
|
-
config: {},
|
|
161
|
+
config: { headers: new axios_1.AxiosHeaders() },
|
|
154
162
|
status: 401,
|
|
155
163
|
statusText: '',
|
|
156
164
|
headers: {},
|
|
@@ -162,7 +170,7 @@ describe('ApiService', () => {
|
|
|
162
170
|
yield (0, ApplicationStore_1.saveApplicationFile)(ApplicationStore_1.STORE_FILES.sessionCookie, JSON.stringify(VALID_COOKIE.toJSON()));
|
|
163
171
|
const response = {
|
|
164
172
|
data: { message: ApiService_1.MESSAGE_FAILED_TO_CREATE_ORG_SESSION },
|
|
165
|
-
config: {},
|
|
173
|
+
config: { headers: new axios_1.AxiosHeaders() },
|
|
166
174
|
status: 401,
|
|
167
175
|
statusText: '',
|
|
168
176
|
headers: {},
|
|
@@ -15,7 +15,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
const commander_1 = require("commander");
|
|
16
16
|
const chalk_1 = __importDefault(require("chalk"));
|
|
17
17
|
const ora_1 = __importDefault(require("ora"));
|
|
18
|
-
const fs_1 = __importDefault(require("fs"));
|
|
19
18
|
const utils_1 = require("../utils");
|
|
20
19
|
const createMigrationCommand = () => {
|
|
21
20
|
const createCommand = new commander_1.Command('create')
|
|
@@ -25,28 +24,16 @@ const createMigrationCommand = () => {
|
|
|
25
24
|
.addOption((0, utils_1.getParamOption)(utils_1.OptionName.PREVIEW_ASSET_ID))
|
|
26
25
|
.addOption((0, utils_1.getParamOption)(utils_1.OptionName.MATRIX_URL))
|
|
27
26
|
.addOption((0, utils_1.getParamOption)(utils_1.OptionName.TENANT, false))
|
|
28
|
-
.argument('<exportPath>', 'Path to the export folder (e.g., ./export)')
|
|
29
27
|
.configureOutput({
|
|
30
28
|
outputError(str, write) {
|
|
31
29
|
write(chalk_1.default.red(str));
|
|
32
30
|
},
|
|
33
31
|
})
|
|
34
|
-
.action((
|
|
32
|
+
.action((options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
35
33
|
yield (0, utils_1.throwErrorIfNotLoggedIn)(createCommand);
|
|
36
|
-
let spinner = (0, ora_1.default)('
|
|
34
|
+
let spinner = (0, ora_1.default)('Creating migration').start();
|
|
37
35
|
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
36
|
// Create migration
|
|
49
|
-
spinner = (0, ora_1.default)('Creating migration').start();
|
|
50
37
|
const response = yield (0, utils_1.createMigration)(options);
|
|
51
38
|
if (!response) {
|
|
52
39
|
throw new Error('Migration creation failed');
|
|
@@ -54,14 +41,6 @@ const createMigrationCommand = () => {
|
|
|
54
41
|
spinner.succeed('Migration created successfully');
|
|
55
42
|
// Upload file to S3
|
|
56
43
|
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
44
|
spinner.succeed(`Successfully created migration: ${JSON.stringify({
|
|
66
45
|
migrationId: response.assetMigration.migrationId,
|
|
67
46
|
assetId: response.assetMigration.assetId,
|
|
@@ -38,17 +38,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
38
38
|
const nock_1 = __importDefault(require("nock"));
|
|
39
39
|
const create_1 = __importDefault(require("./create"));
|
|
40
40
|
const utils = __importStar(require("../utils/common"));
|
|
41
|
-
const ApplicationStore = __importStar(require("../../ApplicationStore"));
|
|
42
|
-
const fs_1 = __importDefault(require("fs"));
|
|
43
41
|
const createMigration = __importStar(require("../utils/createMigration"));
|
|
44
42
|
jest.mock('../utils/common');
|
|
45
43
|
jest.mock('../utils/createMigration');
|
|
46
|
-
jest.mock('../../ApplicationStore');
|
|
47
|
-
jest.mock('fs');
|
|
48
44
|
const mockUtils = utils;
|
|
49
45
|
const mockCreateMigration = createMigration;
|
|
50
|
-
const mockApplicationStore = ApplicationStore;
|
|
51
|
-
const mockFs = fs_1.default;
|
|
52
46
|
describe('createMigrationCommand', () => {
|
|
53
47
|
let logSpy;
|
|
54
48
|
let mockCreateMigrationResponse;
|
|
@@ -57,12 +51,7 @@ describe('createMigrationCommand', () => {
|
|
|
57
51
|
jest.clearAllMocks();
|
|
58
52
|
jest.resetAllMocks();
|
|
59
53
|
logSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
60
|
-
mockApplicationStore.getApplicationFile.mockResolvedValue('session-cookie');
|
|
61
54
|
mockUtils.throwErrorIfNotLoggedIn.mockResolvedValue(undefined);
|
|
62
|
-
mockCreateMigration.validateExportFolder.mockImplementation(() => { });
|
|
63
|
-
mockCreateMigration.createTarFile.mockResolvedValue('/path/to/export_123.tar.gz');
|
|
64
|
-
mockUtils.uploadFileToS3.mockResolvedValue('https://s3.amazonaws.com/uploaded-file');
|
|
65
|
-
mockFs.unlinkSync.mockImplementation(() => { });
|
|
66
55
|
mockCreateMigrationResponse = {
|
|
67
56
|
assetMigration: {
|
|
68
57
|
migrationId: 'migration-123',
|
|
@@ -95,23 +84,14 @@ describe('createMigrationCommand', () => {
|
|
|
95
84
|
'preview-456',
|
|
96
85
|
'--matrix-url',
|
|
97
86
|
'https://matrix.example.com',
|
|
98
|
-
'/path/to/export',
|
|
99
87
|
]);
|
|
100
88
|
expect(mockUtils.throwErrorIfNotLoggedIn).toHaveBeenCalledWith(program);
|
|
101
|
-
expect(mockCreateMigration.validateExportFolder).toHaveBeenCalledWith('/path/to/export');
|
|
102
|
-
expect(mockCreateMigration.createTarFile).toHaveBeenCalledWith('/path/to/export');
|
|
103
89
|
expect(mockCreateMigration.createMigration).toHaveBeenCalledWith({
|
|
104
90
|
assetId: 'asset-123',
|
|
105
91
|
previewAssetId: 'preview-456',
|
|
106
92
|
matrixUrl: 'https://matrix.example.com',
|
|
107
93
|
});
|
|
108
|
-
expect(mockUtils.uploadFileToS3).toHaveBeenCalledWith('https://upload.s3.amazonaws.com', '/path/to/export_123.tar.gz', undefined);
|
|
109
|
-
expect(mockFs.unlinkSync).toHaveBeenCalledWith('/path/to/export_123.tar.gz');
|
|
110
|
-
expect(mockCreateMigration.validateExportFolder).toHaveBeenCalledTimes(1);
|
|
111
|
-
expect(mockCreateMigration.createTarFile).toHaveBeenCalledTimes(1);
|
|
112
94
|
expect(mockCreateMigration.createMigration).toHaveBeenCalledTimes(1);
|
|
113
|
-
expect(mockUtils.uploadFileToS3).toHaveBeenCalledTimes(1);
|
|
114
|
-
expect(mockFs.unlinkSync).toHaveBeenCalledTimes(1);
|
|
115
95
|
expect(mockUtils.handleCommandError).not.toHaveBeenCalled();
|
|
116
96
|
}));
|
|
117
97
|
it('should create migration with tenant option', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -127,7 +107,6 @@ describe('createMigrationCommand', () => {
|
|
|
127
107
|
'https://matrix.example.com',
|
|
128
108
|
'--tenant',
|
|
129
109
|
'test-tenant',
|
|
130
|
-
'/path/to/export',
|
|
131
110
|
]);
|
|
132
111
|
expect(mockCreateMigration.createMigration).toHaveBeenCalledWith({
|
|
133
112
|
assetId: 'asset-123',
|
|
@@ -135,7 +114,6 @@ describe('createMigrationCommand', () => {
|
|
|
135
114
|
matrixUrl: 'https://matrix.example.com',
|
|
136
115
|
tenant: 'test-tenant',
|
|
137
116
|
});
|
|
138
|
-
expect(mockUtils.uploadFileToS3).toHaveBeenCalledWith('https://upload.s3.amazonaws.com', '/path/to/export_123.tar.gz', 'test-tenant');
|
|
139
117
|
}));
|
|
140
118
|
it('should create migration with override URL when environment variable is set', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
141
119
|
const originalEnv = process.env.ENABLE_OVERRIDE_MIGRATION_URL;
|
|
@@ -152,7 +130,6 @@ describe('createMigrationCommand', () => {
|
|
|
152
130
|
'https://matrix.example.com',
|
|
153
131
|
'--overrideUrl',
|
|
154
132
|
'https://custom.migration.url',
|
|
155
|
-
'/path/to/export',
|
|
156
133
|
]);
|
|
157
134
|
expect(mockCreateMigration.createMigration).toHaveBeenCalledWith({
|
|
158
135
|
assetId: 'asset-123',
|
|
@@ -164,46 +141,9 @@ describe('createMigrationCommand', () => {
|
|
|
164
141
|
}));
|
|
165
142
|
});
|
|
166
143
|
describe('error scenarios', () => {
|
|
167
|
-
it('should handle validation error for export folder', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
168
|
-
const validationError = new Error('Export folder does not exist');
|
|
169
|
-
mockCreateMigration.validateExportFolder.mockImplementation(() => {
|
|
170
|
-
throw validationError;
|
|
171
|
-
});
|
|
172
|
-
mockUtils.handleCommandError.mockImplementation(() => { });
|
|
173
|
-
const program = (0, create_1.default)();
|
|
174
|
-
yield program.parseAsync([
|
|
175
|
-
'node',
|
|
176
|
-
'dxp-cli',
|
|
177
|
-
'--asset-id',
|
|
178
|
-
'asset-123',
|
|
179
|
-
'--preview-asset-id',
|
|
180
|
-
'preview-456',
|
|
181
|
-
'--matrix-url',
|
|
182
|
-
'https://matrix.example.com',
|
|
183
|
-
'/invalid/path',
|
|
184
|
-
]);
|
|
185
|
-
expect(mockUtils.handleCommandError).toHaveBeenCalledWith(program, validationError);
|
|
186
|
-
expect(mockCreateMigration.createTarFile).not.toHaveBeenCalled();
|
|
187
|
-
}));
|
|
188
|
-
it('should handle tar file creation failure', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
189
|
-
mockCreateMigration.createTarFile.mockResolvedValue(null);
|
|
190
|
-
const program = (0, create_1.default)();
|
|
191
|
-
yield program.parseAsync([
|
|
192
|
-
'node',
|
|
193
|
-
'dxp-cli',
|
|
194
|
-
'--asset-id',
|
|
195
|
-
'asset-123',
|
|
196
|
-
'--preview-asset-id',
|
|
197
|
-
'preview-456',
|
|
198
|
-
'--matrix-url',
|
|
199
|
-
'https://matrix.example.com',
|
|
200
|
-
'/path/to/export',
|
|
201
|
-
]);
|
|
202
|
-
expect(mockCreateMigration.createMigration).not.toHaveBeenCalled();
|
|
203
|
-
expect(mockUtils.uploadFileToS3).not.toHaveBeenCalled();
|
|
204
|
-
}));
|
|
205
144
|
it('should handle migration creation failure', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
206
145
|
mockCreateMigration.createMigration.mockResolvedValue(null);
|
|
146
|
+
mockUtils.handleCommandError.mockImplementation(() => { });
|
|
207
147
|
const program = (0, create_1.default)();
|
|
208
148
|
yield program.parseAsync([
|
|
209
149
|
'node',
|
|
@@ -214,26 +154,8 @@ describe('createMigrationCommand', () => {
|
|
|
214
154
|
'preview-456',
|
|
215
155
|
'--matrix-url',
|
|
216
156
|
'https://matrix.example.com',
|
|
217
|
-
'/path/to/export',
|
|
218
|
-
]);
|
|
219
|
-
expect(mockUtils.uploadFileToS3).not.toHaveBeenCalled();
|
|
220
|
-
expect(mockFs.unlinkSync).not.toHaveBeenCalled();
|
|
221
|
-
}));
|
|
222
|
-
it('should handle S3 upload failure', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
223
|
-
mockUtils.uploadFileToS3.mockResolvedValue(null);
|
|
224
|
-
const program = (0, create_1.default)();
|
|
225
|
-
yield program.parseAsync([
|
|
226
|
-
'node',
|
|
227
|
-
'dxp-cli',
|
|
228
|
-
'--asset-id',
|
|
229
|
-
'asset-123',
|
|
230
|
-
'--preview-asset-id',
|
|
231
|
-
'preview-456',
|
|
232
|
-
'--matrix-url',
|
|
233
|
-
'https://matrix.example.com',
|
|
234
|
-
'/path/to/export',
|
|
235
157
|
]);
|
|
236
|
-
expect(
|
|
158
|
+
expect(mockUtils.handleCommandError).toHaveBeenCalledWith(program, new Error('Migration creation failed'));
|
|
237
159
|
}));
|
|
238
160
|
it('should handle migration API error', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
239
161
|
const apiError = new Error('Migration API failed');
|
|
@@ -249,7 +171,6 @@ describe('createMigrationCommand', () => {
|
|
|
249
171
|
'preview-456',
|
|
250
172
|
'--matrix-url',
|
|
251
173
|
'https://matrix.example.com',
|
|
252
|
-
'/path/to/export',
|
|
253
174
|
]);
|
|
254
175
|
expect(mockUtils.handleCommandError).toHaveBeenCalledWith(program, apiError);
|
|
255
176
|
}));
|
|
@@ -268,9 +189,8 @@ describe('createMigrationCommand', () => {
|
|
|
268
189
|
'preview-456',
|
|
269
190
|
'--matrix-url',
|
|
270
191
|
'https://matrix.example.com',
|
|
271
|
-
'/path/to/export',
|
|
272
192
|
])).rejects.toThrow('Not logged in');
|
|
273
|
-
expect(mockCreateMigration.
|
|
193
|
+
expect(mockCreateMigration.createMigration).not.toHaveBeenCalled();
|
|
274
194
|
}));
|
|
275
195
|
});
|
|
276
196
|
describe('required options validation', () => {
|
|
@@ -284,7 +204,6 @@ describe('createMigrationCommand', () => {
|
|
|
284
204
|
'preview-456',
|
|
285
205
|
'--matrix-url',
|
|
286
206
|
'https://matrix.example.com',
|
|
287
|
-
'/path/to/export',
|
|
288
207
|
]);
|
|
289
208
|
}).toThrow();
|
|
290
209
|
});
|
|
@@ -298,7 +217,6 @@ describe('createMigrationCommand', () => {
|
|
|
298
217
|
'asset-123',
|
|
299
218
|
'--matrix-url',
|
|
300
219
|
'https://matrix.example.com',
|
|
301
|
-
'/path/to/export',
|
|
302
220
|
]);
|
|
303
221
|
}).toThrow();
|
|
304
222
|
});
|
|
@@ -312,22 +230,6 @@ describe('createMigrationCommand', () => {
|
|
|
312
230
|
'asset-123',
|
|
313
231
|
'--preview-asset-id',
|
|
314
232
|
'preview-456',
|
|
315
|
-
'/path/to/export',
|
|
316
|
-
]);
|
|
317
|
-
}).toThrow();
|
|
318
|
-
});
|
|
319
|
-
it('should require exportPath argument', () => {
|
|
320
|
-
const program = (0, create_1.default)().exitOverride();
|
|
321
|
-
expect(() => {
|
|
322
|
-
program.parse([
|
|
323
|
-
'node',
|
|
324
|
-
'dxp-cli',
|
|
325
|
-
'--asset-id',
|
|
326
|
-
'asset-123',
|
|
327
|
-
'--preview-asset-id',
|
|
328
|
-
'preview-456',
|
|
329
|
-
'--matrix-url',
|
|
330
|
-
'https://matrix.example.com',
|
|
331
233
|
]);
|
|
332
234
|
}).toThrow();
|
|
333
235
|
});
|
|
@@ -351,7 +253,6 @@ describe('createMigrationCommand', () => {
|
|
|
351
253
|
'https://matrix.example.com',
|
|
352
254
|
'--tenant',
|
|
353
255
|
'test-tenant',
|
|
354
|
-
'/path/to/export',
|
|
355
256
|
]);
|
|
356
257
|
const opts = program.opts();
|
|
357
258
|
expect(opts.assetId).toBe('asset-123');
|
|
@@ -372,13 +273,8 @@ describe('createMigrationCommand', () => {
|
|
|
372
273
|
'preview-456',
|
|
373
274
|
'--matrix-url',
|
|
374
275
|
'https://matrix.example.com',
|
|
375
|
-
'/path/to/export',
|
|
376
276
|
]);
|
|
377
|
-
expect(mockCreateMigration.validateExportFolder).toHaveBeenCalledWith('/path/to/export');
|
|
378
|
-
expect(mockCreateMigration.createTarFile).toHaveBeenCalledWith('/path/to/export');
|
|
379
277
|
expect(mockCreateMigration.createMigration).toHaveBeenCalled();
|
|
380
|
-
expect(mockUtils.uploadFileToS3).toHaveBeenCalled();
|
|
381
|
-
expect(mockFs.unlinkSync).toHaveBeenCalled();
|
|
382
278
|
}));
|
|
383
279
|
});
|
|
384
280
|
});
|
package/lib/migration/index.js
CHANGED
|
@@ -9,11 +9,13 @@ const get_1 = __importDefault(require("./get/get"));
|
|
|
9
9
|
const next_1 = __importDefault(require("./next/next"));
|
|
10
10
|
const settings_1 = __importDefault(require("./settings/settings"));
|
|
11
11
|
const revert_1 = __importDefault(require("./revert/revert"));
|
|
12
|
+
const list_1 = __importDefault(require("./list/list"));
|
|
12
13
|
const migrationCommand = new commander_1.Command('migration');
|
|
13
14
|
migrationCommand
|
|
14
15
|
.description('AI Page Migration Service Commands')
|
|
15
16
|
.addCommand((0, create_1.default)())
|
|
16
17
|
.addCommand((0, get_1.default)())
|
|
18
|
+
.addCommand((0, list_1.default)())
|
|
17
19
|
.addCommand((0, next_1.default)())
|
|
18
20
|
.addCommand((0, settings_1.default)())
|
|
19
21
|
.addCommand((0, revert_1.default)());
|
|
@@ -0,0 +1,45 @@
|
|
|
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 listMigrations_1 = require("../utils/listMigrations");
|
|
20
|
+
const listMigrationsCommand = () => {
|
|
21
|
+
const listCommand = new commander_1.Command('list')
|
|
22
|
+
.alias('ls')
|
|
23
|
+
.description('Lists all active migrations for a tenant')
|
|
24
|
+
.addOption((0, utils_1.getParamOption)(utils_1.OptionName.TENANT, false))
|
|
25
|
+
.configureOutput({
|
|
26
|
+
outputError(str, write) {
|
|
27
|
+
write(chalk_1.default.red(str));
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
.action((options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
31
|
+
const spinner = (0, ora_1.default)('Listing migrations').start();
|
|
32
|
+
yield (0, utils_1.throwErrorIfNotLoggedIn)(listCommand);
|
|
33
|
+
try {
|
|
34
|
+
const response = yield (0, listMigrations_1.listMigrations)(options);
|
|
35
|
+
spinner.succeed(`Migrations: ${JSON.stringify(response, null, 2)}`);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
spinner.fail();
|
|
39
|
+
(0, utils_1.handleCommandError)(listCommand, error);
|
|
40
|
+
}
|
|
41
|
+
}));
|
|
42
|
+
(0, utils_1.addOverrideUrlOption)(listCommand);
|
|
43
|
+
return listCommand;
|
|
44
|
+
};
|
|
45
|
+
exports.default = listMigrationsCommand;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -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 __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
+
};
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
const mockListMigrations = jest.fn();
|
|
39
|
+
const nock_1 = __importDefault(require("nock"));
|
|
40
|
+
const list_1 = __importDefault(require("./list"));
|
|
41
|
+
const utils = __importStar(require("../utils/common"));
|
|
42
|
+
jest.mock('../utils/common');
|
|
43
|
+
jest.mock('../utils/listMigrations', () => ({
|
|
44
|
+
listMigrations: mockListMigrations,
|
|
45
|
+
}));
|
|
46
|
+
jest.mock('../../ApplicationStore');
|
|
47
|
+
const mockUtils = utils;
|
|
48
|
+
describe('listMigrationsCommand', () => {
|
|
49
|
+
const program = (0, list_1.default)();
|
|
50
|
+
const migrationsData = [
|
|
51
|
+
{
|
|
52
|
+
migrationId: 'migration-1',
|
|
53
|
+
assetId: 'asset-1',
|
|
54
|
+
stage: 'completed',
|
|
55
|
+
status: 'success',
|
|
56
|
+
created: 100,
|
|
57
|
+
updated: 200,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
migrationId: 'migration-2',
|
|
61
|
+
assetId: 'asset-2',
|
|
62
|
+
stage: 'completed',
|
|
63
|
+
status: 'success',
|
|
64
|
+
created: 100,
|
|
65
|
+
updated: 200,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
migrationId: 'migration-3',
|
|
69
|
+
assetId: 'asset-3',
|
|
70
|
+
stage: 'completed',
|
|
71
|
+
status: 'success',
|
|
72
|
+
created: 100,
|
|
73
|
+
updated: 200,
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
const listArgs = ['node', 'dxp-cli', 'ls', '--tenant', 'test-tenant'];
|
|
77
|
+
beforeAll(() => {
|
|
78
|
+
mockListMigrations.mockResolvedValue(migrationsData);
|
|
79
|
+
mockUtils.throwErrorIfNotLoggedIn.mockReturnThis();
|
|
80
|
+
});
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
nock_1.default.cleanAll();
|
|
83
|
+
jest.clearAllMocks();
|
|
84
|
+
});
|
|
85
|
+
it('should have correct command name and alias', () => {
|
|
86
|
+
expect(program.name()).toBe('list');
|
|
87
|
+
expect(program.aliases()).toStrictEqual(expect.arrayContaining(['ls']));
|
|
88
|
+
});
|
|
89
|
+
it('should list migrations successfully with required options', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
90
|
+
yield program.parseAsync(listArgs);
|
|
91
|
+
expect(mockUtils.throwErrorIfNotLoggedIn).toHaveBeenCalledWith(program);
|
|
92
|
+
expect(mockListMigrations).toHaveBeenCalled();
|
|
93
|
+
expect(mockListMigrations).toHaveBeenCalledTimes(1);
|
|
94
|
+
expect(mockUtils.handleCommandError).not.toHaveBeenCalled();
|
|
95
|
+
}));
|
|
96
|
+
it('should list migrations with tenant option', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
97
|
+
yield program.parseAsync(listArgs);
|
|
98
|
+
expect(mockListMigrations).toHaveBeenCalledWith({
|
|
99
|
+
tenant: 'test-tenant',
|
|
100
|
+
});
|
|
101
|
+
}));
|
|
102
|
+
it('should list migrations with override URL when environment variable is set', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
103
|
+
const originalEnv = process.env.ENABLE_OVERRIDE_MIGRATION_URL;
|
|
104
|
+
process.env.ENABLE_OVERRIDE_MIGRATION_URL = 'true';
|
|
105
|
+
// Recreate to pick up on env var change
|
|
106
|
+
const program = (0, list_1.default)();
|
|
107
|
+
yield program.parseAsync([
|
|
108
|
+
...listArgs,
|
|
109
|
+
'--overrideUrl',
|
|
110
|
+
'https://custom.migration.url',
|
|
111
|
+
]);
|
|
112
|
+
expect(mockListMigrations).toHaveBeenCalledWith({
|
|
113
|
+
tenant: 'test-tenant',
|
|
114
|
+
overrideUrl: 'https://custom.migration.url',
|
|
115
|
+
});
|
|
116
|
+
process.env.ENABLE_OVERRIDE_MIGRATION_URL = originalEnv;
|
|
117
|
+
}));
|
|
118
|
+
it('should not require tenant option set', () => {
|
|
119
|
+
const program = (0, list_1.default)().exitOverride();
|
|
120
|
+
expect(() => {
|
|
121
|
+
program.parse(['node', 'dxp-cli', 'list']);
|
|
122
|
+
}).not.toThrow();
|
|
123
|
+
});
|
|
124
|
+
describe('error scenarios', () => {
|
|
125
|
+
it('should handle listMigrations API error', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
126
|
+
const apiError = new Error('Migration not found');
|
|
127
|
+
mockListMigrations.mockRejectedValue(apiError);
|
|
128
|
+
yield program.parseAsync(listArgs);
|
|
129
|
+
expect(mockUtils.handleCommandError).toHaveBeenCalledWith(program, apiError);
|
|
130
|
+
}));
|
|
131
|
+
it('should handle network error', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
132
|
+
const networkError = new Error('Network connection failed');
|
|
133
|
+
mockListMigrations.mockRejectedValue(networkError);
|
|
134
|
+
yield program.parseAsync(listArgs);
|
|
135
|
+
expect(mockUtils.handleCommandError).toHaveBeenCalledWith(program, networkError);
|
|
136
|
+
}));
|
|
137
|
+
});
|
|
138
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.listMigrations = void 0;
|
|
13
|
+
const ApiService_1 = require("../../ApiService");
|
|
14
|
+
const _1 = require(".");
|
|
15
|
+
function listMigrations(options) {
|
|
16
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
17
|
+
const apiService = new ApiService_1.ApiService({
|
|
18
|
+
validateStatus: _1.validateAxiosStatus,
|
|
19
|
+
});
|
|
20
|
+
const migrationUrl = yield (0, _1.buildMigrationUrl)(options.tenant, options.overrideUrl);
|
|
21
|
+
try {
|
|
22
|
+
const response = yield apiService.client.get(migrationUrl, {
|
|
23
|
+
headers: yield (0, _1.getMigrationHeaders)(options.tenant),
|
|
24
|
+
});
|
|
25
|
+
if (response.status !== 200) {
|
|
26
|
+
throw new Error(`Failed to list migrations: ${response.status}`);
|
|
27
|
+
}
|
|
28
|
+
else if (!(response === null || response === void 0 ? void 0 : response.data)) {
|
|
29
|
+
throw new Error('No data returned from migration service');
|
|
30
|
+
}
|
|
31
|
+
return response.data;
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
if (error instanceof Error) {
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
throw new Error(`Failed to list migrations: ${error}`);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
exports.listMigrations = listMigrations;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const mockClient = { get: jest.fn() };
|
|
13
|
+
const ApplicationConfig_1 = require("../../ApplicationConfig");
|
|
14
|
+
const listMigrations_1 = require("./listMigrations");
|
|
15
|
+
jest.mock('path');
|
|
16
|
+
jest.mock('child_process');
|
|
17
|
+
jest.mock('axios');
|
|
18
|
+
jest.mock('../../ApplicationConfig');
|
|
19
|
+
jest.mock('../../ApiService', () => ({
|
|
20
|
+
ApiService: function () {
|
|
21
|
+
return {
|
|
22
|
+
client: mockClient,
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
}));
|
|
26
|
+
describe('listMigration', () => {
|
|
27
|
+
const options = {
|
|
28
|
+
tenant: 'test-tenant',
|
|
29
|
+
};
|
|
30
|
+
const migrationsData = [
|
|
31
|
+
{
|
|
32
|
+
migrationId: 'migration-1',
|
|
33
|
+
assetId: 'asset-1',
|
|
34
|
+
stage: 'completed',
|
|
35
|
+
status: 'success',
|
|
36
|
+
created: 100,
|
|
37
|
+
updated: 200,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
migrationId: 'migration-2',
|
|
41
|
+
assetId: 'asset-2',
|
|
42
|
+
stage: 'completed',
|
|
43
|
+
status: 'success',
|
|
44
|
+
created: 100,
|
|
45
|
+
updated: 200,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
migrationId: 'migration-3',
|
|
49
|
+
assetId: 'asset-3',
|
|
50
|
+
stage: 'completed',
|
|
51
|
+
status: 'success',
|
|
52
|
+
created: 100,
|
|
53
|
+
updated: 200,
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
const mockFetchApplicationConfig = ApplicationConfig_1.fetchApplicationConfig;
|
|
57
|
+
beforeAll(() => {
|
|
58
|
+
mockClient.get.mockResolvedValue({
|
|
59
|
+
status: 200,
|
|
60
|
+
data: migrationsData,
|
|
61
|
+
});
|
|
62
|
+
mockFetchApplicationConfig.mockResolvedValue({
|
|
63
|
+
tenant: 'test-tenant',
|
|
64
|
+
baseUrl: 'https://example.com',
|
|
65
|
+
region: 'au',
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
it('lists migrations successfully', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
69
|
+
yield expect((0, listMigrations_1.listMigrations)(options)).resolves.toStrictEqual(migrationsData);
|
|
70
|
+
expect(mockClient.get).toHaveBeenCalledWith('https://example.com/__dxp/service/aiapps/migration/migrations', {
|
|
71
|
+
headers: {
|
|
72
|
+
'Content-Type': 'application/json',
|
|
73
|
+
'x-dxp-tenant': 'test-tenant',
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}));
|
|
77
|
+
it('lists migrations with override URL', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
78
|
+
const optionsWithOverride = Object.assign(Object.assign({}, options), { overrideUrl: 'https://custom.migration.url' });
|
|
79
|
+
mockClient.get.mockResolvedValueOnce({
|
|
80
|
+
status: 200,
|
|
81
|
+
data: migrationsData,
|
|
82
|
+
});
|
|
83
|
+
yield expect((0, listMigrations_1.listMigrations)(optionsWithOverride)).resolves.toStrictEqual(migrationsData);
|
|
84
|
+
expect(mockClient.get).toHaveBeenCalledWith('https://custom.migration.url/migrations', expect.any(Object));
|
|
85
|
+
}));
|
|
86
|
+
it('handles non-success status codes', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
87
|
+
mockClient.get.mockResolvedValue({
|
|
88
|
+
status: 400,
|
|
89
|
+
data: [],
|
|
90
|
+
});
|
|
91
|
+
yield expect((0, listMigrations_1.listMigrations)(options)).rejects.toThrow('Failed to list migrations: 400');
|
|
92
|
+
}));
|
|
93
|
+
it('handles missing response data', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
94
|
+
mockClient.get.mockResolvedValue({
|
|
95
|
+
status: 200,
|
|
96
|
+
data: null,
|
|
97
|
+
});
|
|
98
|
+
yield expect((0, listMigrations_1.listMigrations)(options)).rejects.toThrow('No data returned from migration service');
|
|
99
|
+
}));
|
|
100
|
+
it('handles API service errors', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
101
|
+
mockClient.get.mockRejectedValue(new Error('Network error'));
|
|
102
|
+
yield expect((0, listMigrations_1.listMigrations)(options)).rejects.toThrow('Network error');
|
|
103
|
+
}));
|
|
104
|
+
it('handles unknown errors', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
105
|
+
mockClient.get.mockRejectedValue('Unknown error');
|
|
106
|
+
yield expect((0, listMigrations_1.listMigrations)(options)).rejects.toThrow('Failed to list migrations: Unknown error');
|
|
107
|
+
}));
|
|
108
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@squiz/dxp-cli-next",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.28.0-develop.2",
|
|
4
4
|
"repository": {
|
|
5
5
|
"url": "https://gitlab.squiz.net/dxp/dxp-cli-next"
|
|
6
6
|
},
|
|
@@ -44,9 +44,9 @@
|
|
|
44
44
|
"@squiz/component-cli-lib": "1.72.12",
|
|
45
45
|
"@squiz/dx-logger-lib": "^1.65.1",
|
|
46
46
|
"@squiz/dxp-porter-shared": "0.4.0",
|
|
47
|
-
"@squiz/job-runner-lib": "2.
|
|
47
|
+
"@squiz/job-runner-lib": "2.3.0",
|
|
48
48
|
"@squiz/local-component-dev-ui": "0.6.20",
|
|
49
|
-
"axios": "1.
|
|
49
|
+
"axios": "1.11.0",
|
|
50
50
|
"cli-color": "2.0.3",
|
|
51
51
|
"commander": "9.4.0",
|
|
52
52
|
"dotenv": "^16.3.1",
|