@squiz/dxp-cli-next 5.26.0-develop.4 → 5.26.0-develop.5
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/migration/create/{createMigration.js → create.js} +5 -5
- package/lib/migration/create/{createMigration.spec.js → create.spec.js} +44 -41
- package/lib/migration/get/{getMigration.js → get.js} +4 -6
- package/lib/migration/get/{getMigration.spec.js → get.spec.js} +39 -30
- package/lib/migration/index.js +9 -7
- package/lib/migration/next/next.d.ts +3 -0
- package/lib/migration/next/next.js +46 -0
- package/lib/migration/next/next.spec.js +186 -0
- package/lib/migration/revert/revert.js +5 -6
- package/lib/migration/revert/revert.spec.js +10 -7
- package/lib/migration/settings/{setMigrationSettings.js → settings.js} +7 -8
- package/lib/migration/settings/{setMigrationSettings.spec.js → settings.spec.js} +34 -26
- package/lib/migration/types/common.types.d.ts +19 -0
- package/lib/migration/types/createMigration.types.d.ts +7 -0
- package/lib/migration/types/createMigration.types.js +2 -0
- package/lib/migration/types/getMigration.types.d.ts +5 -0
- package/lib/migration/types/getMigration.types.js +2 -0
- package/lib/migration/types/index.d.ts +6 -0
- package/lib/migration/types/index.js +22 -0
- package/lib/migration/types/nextStage.types.d.ts +6 -0
- package/lib/migration/types/nextStage.types.js +2 -0
- package/lib/migration/types/revert.types.d.ts +6 -0
- package/lib/migration/types/revert.types.js +2 -0
- package/lib/migration/types/settings.types.d.ts +10 -0
- package/lib/migration/types/settings.types.js +2 -0
- package/lib/migration/utils/common.d.ts +32 -0
- package/lib/migration/utils/common.js +129 -0
- package/lib/migration/utils/common.spec.d.ts +1 -0
- package/lib/migration/utils/common.spec.js +196 -0
- package/lib/migration/utils/createMigration.d.ts +9 -0
- package/lib/migration/utils/createMigration.js +122 -0
- package/lib/migration/utils/createMigration.spec.d.ts +1 -0
- package/lib/migration/utils/createMigration.spec.js +276 -0
- package/lib/migration/utils/getMigration.d.ts +2 -0
- package/lib/migration/utils/getMigration.js +41 -0
- package/lib/migration/utils/getMigration.spec.d.ts +1 -0
- package/lib/migration/utils/getMigration.spec.js +156 -0
- package/lib/migration/utils/index.d.ts +7 -0
- package/lib/migration/utils/index.js +23 -0
- package/lib/migration/utils/nextStage.d.ts +2 -0
- package/lib/migration/utils/nextStage.js +44 -0
- package/lib/migration/utils/nextStage.spec.d.ts +1 -0
- package/lib/migration/utils/nextStage.spec.js +151 -0
- package/lib/migration/utils/options.d.ts +12 -0
- package/lib/migration/utils/options.js +82 -0
- package/lib/migration/utils/options.spec.d.ts +1 -0
- package/lib/migration/utils/options.spec.js +115 -0
- package/lib/migration/utils/revertMigration.d.ts +2 -0
- package/lib/migration/utils/revertMigration.js +38 -0
- package/lib/migration/utils/revertMigration.spec.d.ts +1 -0
- package/lib/migration/utils/revertMigration.spec.js +95 -0
- package/lib/migration/utils/setMigrationSettings.d.ts +2 -0
- package/lib/migration/utils/setMigrationSettings.js +53 -0
- package/lib/migration/utils/setMigrationSettings.spec.d.ts +1 -0
- package/lib/migration/utils/setMigrationSettings.spec.js +156 -0
- package/package.json +1 -1
- package/lib/migration/types.d.ts +0 -54
- package/lib/migration/utils.d.ts +0 -15
- package/lib/migration/utils.js +0 -298
- package/lib/migration/utils.spec.js +0 -617
- /package/lib/migration/create/{createMigration.d.ts → create.d.ts} +0 -0
- /package/lib/migration/create/{createMigration.spec.d.ts → create.spec.d.ts} +0 -0
- /package/lib/migration/get/{getMigration.d.ts → get.d.ts} +0 -0
- /package/lib/migration/get/{getMigration.spec.d.ts → get.spec.d.ts} +0 -0
- /package/lib/migration/{settings/setMigrationSettings.spec.d.ts → next/next.spec.d.ts} +0 -0
- /package/lib/migration/settings/{setMigrationSettings.d.ts → settings.d.ts} +0 -0
- /package/lib/migration/{utils.spec.d.ts → settings/settings.spec.d.ts} +0 -0
- /package/lib/migration/{types.js → types/common.types.js} +0 -0
|
@@ -0,0 +1,129 @@
|
|
|
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.redactKey = exports.uploadFileToS3 = exports.getMigrationHeaders = exports.validateAxiosStatus = exports.buildMigrationUrl = exports.throwErrorIfNotLoggedIn = exports.handleCommandError = void 0;
|
|
16
|
+
const fs_1 = __importDefault(require("fs"));
|
|
17
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
18
|
+
const ApplicationConfig_1 = require("../../ApplicationConfig");
|
|
19
|
+
const axios_1 = __importDefault(require("axios"));
|
|
20
|
+
const ApplicationStore_1 = require("../../ApplicationStore");
|
|
21
|
+
/**
|
|
22
|
+
* The purpose of this file is to provide utilities in common
|
|
23
|
+
* such as functions that can be reused across the migration service.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Handles errors from the migration service.
|
|
27
|
+
* @param command - The command that is being executed.
|
|
28
|
+
* @param error - The error that occurred.
|
|
29
|
+
*/
|
|
30
|
+
function handleCommandError(command, error) {
|
|
31
|
+
var _a, _b, _c, _d;
|
|
32
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
33
|
+
let message = `${error.message}`;
|
|
34
|
+
if ((_b = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.message) {
|
|
35
|
+
message += `: ${error.response.data.message}`;
|
|
36
|
+
}
|
|
37
|
+
if ((_d = (_c = error.response) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d.details) {
|
|
38
|
+
message += ` - ${error.response.data.details}`;
|
|
39
|
+
}
|
|
40
|
+
command.error(chalk_1.default.red(message));
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
if (!!process.env.DEBUG && error.stack) {
|
|
44
|
+
command.error(error.stack);
|
|
45
|
+
}
|
|
46
|
+
if (error.message) {
|
|
47
|
+
command.error(chalk_1.default.red(error.message));
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
command.error(chalk_1.default.red('An unknown error occurred'));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.handleCommandError = handleCommandError;
|
|
55
|
+
/**
|
|
56
|
+
* Throws an error if the user is not logged in.
|
|
57
|
+
* @param command - The command that is being executed.
|
|
58
|
+
*/
|
|
59
|
+
function throwErrorIfNotLoggedIn(command) {
|
|
60
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
61
|
+
if (!(yield (0, ApplicationStore_1.getApplicationFile)(ApplicationStore_1.STORE_FILES.sessionCookie))) {
|
|
62
|
+
command.error(chalk_1.default.red('You must login to interact with the migration service. See `dxp-next auth login`'));
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
exports.throwErrorIfNotLoggedIn = throwErrorIfNotLoggedIn;
|
|
67
|
+
/**
|
|
68
|
+
* Builds the migration URL.
|
|
69
|
+
* @param tenantID - The tenant ID.
|
|
70
|
+
* @param overrideUrl - The override URL.
|
|
71
|
+
* @returns The migration URL.
|
|
72
|
+
*/
|
|
73
|
+
function buildMigrationUrl(tenantID, overrideUrl, baseDomain = 'migrations') {
|
|
74
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
75
|
+
if (!overrideUrl) {
|
|
76
|
+
const existingConfig = yield (0, ApplicationConfig_1.fetchApplicationConfig)(tenantID);
|
|
77
|
+
return `${existingConfig.baseUrl}/__dxp/service/aiapps/migration/${baseDomain}`;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
return `${overrideUrl}/${baseDomain}`;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
exports.buildMigrationUrl = buildMigrationUrl;
|
|
85
|
+
/**
|
|
86
|
+
* Validates the status of the axios request.
|
|
87
|
+
* @param status - The status of the axios request.
|
|
88
|
+
* @returns True if the status is less than 400, false otherwise.
|
|
89
|
+
*/
|
|
90
|
+
function validateAxiosStatus(status) {
|
|
91
|
+
return status < 400;
|
|
92
|
+
}
|
|
93
|
+
exports.validateAxiosStatus = validateAxiosStatus;
|
|
94
|
+
function getMigrationHeaders(tenantID, isJson = true) {
|
|
95
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
96
|
+
const existingConfig = yield (0, ApplicationConfig_1.fetchApplicationConfig)(tenantID);
|
|
97
|
+
return isJson
|
|
98
|
+
? {
|
|
99
|
+
'Content-Type': 'application/json',
|
|
100
|
+
'x-dxp-tenant': existingConfig.tenant,
|
|
101
|
+
}
|
|
102
|
+
: {
|
|
103
|
+
'x-dxp-tenant': existingConfig.tenant,
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
exports.getMigrationHeaders = getMigrationHeaders;
|
|
108
|
+
function uploadFileToS3(uploadUrl, filePath, tenantID) {
|
|
109
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
110
|
+
const fileBuffer = fs_1.default.readFileSync(filePath);
|
|
111
|
+
const response = yield fetch(uploadUrl, {
|
|
112
|
+
method: 'PUT',
|
|
113
|
+
body: fileBuffer,
|
|
114
|
+
headers: yield getMigrationHeaders(tenantID, false),
|
|
115
|
+
});
|
|
116
|
+
return response.url;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
exports.uploadFileToS3 = uploadFileToS3;
|
|
120
|
+
function redactKey(key, visibleChars = 3) {
|
|
121
|
+
if (!key)
|
|
122
|
+
return '';
|
|
123
|
+
const len = key.length;
|
|
124
|
+
if (len <= visibleChars)
|
|
125
|
+
return '*'.repeat(len);
|
|
126
|
+
const masked = '*'.repeat(len - visibleChars);
|
|
127
|
+
return masked + key.slice(-visibleChars);
|
|
128
|
+
}
|
|
129
|
+
exports.redactKey = redactKey;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,196 @@
|
|
|
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 fs_1 = __importDefault(require("fs"));
|
|
16
|
+
const axios_1 = __importDefault(require("axios"));
|
|
17
|
+
const ApplicationConfig_1 = require("../../ApplicationConfig");
|
|
18
|
+
const ApplicationStore_1 = require("../../ApplicationStore");
|
|
19
|
+
const _1 = require(".");
|
|
20
|
+
// Mock all external dependencies
|
|
21
|
+
jest.mock('fs');
|
|
22
|
+
jest.mock('path');
|
|
23
|
+
jest.mock('child_process');
|
|
24
|
+
jest.mock('axios');
|
|
25
|
+
jest.mock('../../ApplicationConfig');
|
|
26
|
+
jest.mock('../../ApplicationStore');
|
|
27
|
+
jest.mock('../../ApiService');
|
|
28
|
+
// Mock global fetch
|
|
29
|
+
const mockFetch = jest.fn();
|
|
30
|
+
global.fetch = mockFetch;
|
|
31
|
+
const mockFs = fs_1.default;
|
|
32
|
+
const mockAxios = axios_1.default;
|
|
33
|
+
const mockFetchApplicationConfig = ApplicationConfig_1.fetchApplicationConfig;
|
|
34
|
+
const mockGetApplicationFile = ApplicationStore_1.getApplicationFile;
|
|
35
|
+
describe('Migration Utils', () => {
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
jest.clearAllMocks();
|
|
38
|
+
jest.resetAllMocks();
|
|
39
|
+
});
|
|
40
|
+
describe('handleCommandError', () => {
|
|
41
|
+
let mockCommand;
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
mockCommand = {
|
|
44
|
+
error: jest.fn(),
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
it('handles axios errors with response data message', () => {
|
|
48
|
+
const axiosError = {
|
|
49
|
+
message: 'Network Error',
|
|
50
|
+
response: {
|
|
51
|
+
data: {
|
|
52
|
+
message: 'Server Error',
|
|
53
|
+
details: 'Additional details',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
mockAxios.isAxiosError.mockReturnValue(true);
|
|
58
|
+
(0, _1.handleCommandError)(mockCommand, axiosError);
|
|
59
|
+
expect(mockCommand.error).toHaveBeenCalledWith(expect.stringContaining('Network Error: Server Error - Additional details'));
|
|
60
|
+
});
|
|
61
|
+
it('handles axios errors without response data', () => {
|
|
62
|
+
const axiosError = {
|
|
63
|
+
message: 'Network Error',
|
|
64
|
+
};
|
|
65
|
+
mockAxios.isAxiosError.mockReturnValue(true);
|
|
66
|
+
(0, _1.handleCommandError)(mockCommand, axiosError);
|
|
67
|
+
expect(mockCommand.error).toHaveBeenCalledWith(expect.stringContaining('Network Error'));
|
|
68
|
+
});
|
|
69
|
+
it('handles regular errors with message', () => {
|
|
70
|
+
const error = new Error('Regular error');
|
|
71
|
+
mockAxios.isAxiosError.mockReturnValue(false);
|
|
72
|
+
(0, _1.handleCommandError)(mockCommand, error);
|
|
73
|
+
expect(mockCommand.error).toHaveBeenCalledWith(expect.stringContaining('Regular error'));
|
|
74
|
+
});
|
|
75
|
+
it('handles errors without message', () => {
|
|
76
|
+
const error = new Error();
|
|
77
|
+
error.message = '';
|
|
78
|
+
mockAxios.isAxiosError.mockReturnValue(false);
|
|
79
|
+
(0, _1.handleCommandError)(mockCommand, error);
|
|
80
|
+
expect(mockCommand.error).toHaveBeenCalledWith(expect.stringContaining('An unknown error occurred'));
|
|
81
|
+
});
|
|
82
|
+
it('shows stack trace in debug mode', () => {
|
|
83
|
+
const originalDebug = process.env.DEBUG;
|
|
84
|
+
process.env.DEBUG = 'true';
|
|
85
|
+
const error = new Error('Test error');
|
|
86
|
+
error.stack = 'Error stack trace';
|
|
87
|
+
mockAxios.isAxiosError.mockReturnValue(false);
|
|
88
|
+
(0, _1.handleCommandError)(mockCommand, error);
|
|
89
|
+
expect(mockCommand.error).toHaveBeenCalledWith('Error stack trace');
|
|
90
|
+
process.env.DEBUG = originalDebug;
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
describe('throwErrorIfNotLoggedIn', () => {
|
|
94
|
+
let mockCommand;
|
|
95
|
+
beforeEach(() => {
|
|
96
|
+
mockCommand = {
|
|
97
|
+
error: jest.fn(),
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
it('throws error when not logged in', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
101
|
+
mockGetApplicationFile.mockResolvedValue(undefined);
|
|
102
|
+
yield (0, _1.throwErrorIfNotLoggedIn)(mockCommand);
|
|
103
|
+
expect(mockCommand.error).toHaveBeenCalledWith(expect.stringContaining('You must login to interact with the migration service'));
|
|
104
|
+
}));
|
|
105
|
+
it('does not throw error when logged in', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
106
|
+
mockGetApplicationFile.mockResolvedValue('session-cookie');
|
|
107
|
+
yield (0, _1.throwErrorIfNotLoggedIn)(mockCommand);
|
|
108
|
+
expect(mockCommand.error).not.toHaveBeenCalled();
|
|
109
|
+
}));
|
|
110
|
+
});
|
|
111
|
+
describe('buildMigrationUrl', () => {
|
|
112
|
+
it('builds url without override', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
113
|
+
const mockConfig = {
|
|
114
|
+
baseUrl: 'https://example.com',
|
|
115
|
+
tenant: 'test-tenant',
|
|
116
|
+
region: 'au',
|
|
117
|
+
};
|
|
118
|
+
mockFetchApplicationConfig.mockResolvedValue(mockConfig);
|
|
119
|
+
const result = yield (0, _1.buildMigrationUrl)('tenant-id');
|
|
120
|
+
expect(result).toBe('https://example.com/__dxp/service/aiapps/migration/migrations');
|
|
121
|
+
expect(mockFetchApplicationConfig).toHaveBeenCalledWith('tenant-id');
|
|
122
|
+
}));
|
|
123
|
+
it('returns override url when provided', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
124
|
+
const overrideUrl = 'https://override.com';
|
|
125
|
+
const result = yield (0, _1.buildMigrationUrl)('tenant-id', overrideUrl);
|
|
126
|
+
expect(result).toBe(overrideUrl + '/migrations');
|
|
127
|
+
expect(mockFetchApplicationConfig).not.toHaveBeenCalled();
|
|
128
|
+
}));
|
|
129
|
+
});
|
|
130
|
+
describe('validateAxiosStatus', () => {
|
|
131
|
+
it('returns true for successful status codes', () => {
|
|
132
|
+
expect((0, _1.validateAxiosStatus)(200)).toBe(true);
|
|
133
|
+
expect((0, _1.validateAxiosStatus)(201)).toBe(true);
|
|
134
|
+
expect((0, _1.validateAxiosStatus)(399)).toBe(true);
|
|
135
|
+
});
|
|
136
|
+
it('returns false for error status codes', () => {
|
|
137
|
+
expect((0, _1.validateAxiosStatus)(400)).toBe(false);
|
|
138
|
+
expect((0, _1.validateAxiosStatus)(404)).toBe(false);
|
|
139
|
+
expect((0, _1.validateAxiosStatus)(500)).toBe(false);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
describe('getMigrationHeaders', () => {
|
|
143
|
+
it('returns headers with tenant from config', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
144
|
+
const mockConfig = {
|
|
145
|
+
tenant: 'test-tenant',
|
|
146
|
+
baseUrl: 'https://example.com',
|
|
147
|
+
region: 'au',
|
|
148
|
+
};
|
|
149
|
+
mockFetchApplicationConfig.mockResolvedValue(mockConfig);
|
|
150
|
+
const result = yield (0, _1.getMigrationHeaders)('tenant-id');
|
|
151
|
+
expect(result).toEqual({
|
|
152
|
+
'Content-Type': 'application/json',
|
|
153
|
+
'x-dxp-tenant': 'test-tenant',
|
|
154
|
+
});
|
|
155
|
+
expect(mockFetchApplicationConfig).toHaveBeenCalledWith('tenant-id');
|
|
156
|
+
}));
|
|
157
|
+
});
|
|
158
|
+
describe('uploadFileToS3', () => {
|
|
159
|
+
beforeEach(() => {
|
|
160
|
+
mockFs.readFileSync.mockReturnValue(Buffer.from('file content'));
|
|
161
|
+
mockFetchApplicationConfig.mockResolvedValue({
|
|
162
|
+
tenant: 'test-tenant',
|
|
163
|
+
baseUrl: 'https://example.com',
|
|
164
|
+
region: 'au',
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
it('uploads file successfully', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
168
|
+
const mockResponse = {
|
|
169
|
+
url: 'https://s3.amazonaws.com/uploaded-file',
|
|
170
|
+
};
|
|
171
|
+
mockFetch.mockResolvedValue(mockResponse);
|
|
172
|
+
const result = yield (0, _1.uploadFileToS3)('https://upload.url', '/path/to/file.tar.gz', 'tenant-id');
|
|
173
|
+
expect(result).toBe('https://s3.amazonaws.com/uploaded-file');
|
|
174
|
+
expect(mockFetch).toHaveBeenCalledWith('https://upload.url', {
|
|
175
|
+
method: 'PUT',
|
|
176
|
+
body: Buffer.from('file content'),
|
|
177
|
+
headers: {
|
|
178
|
+
'x-dxp-tenant': 'test-tenant',
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
}));
|
|
182
|
+
it('handles file read error', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
183
|
+
mockFs.readFileSync.mockImplementation(() => {
|
|
184
|
+
throw new Error('File not found');
|
|
185
|
+
});
|
|
186
|
+
yield expect((0, _1.uploadFileToS3)('https://upload.url', '/nonexistent/file.tar.gz')).rejects.toThrow('File not found');
|
|
187
|
+
}));
|
|
188
|
+
});
|
|
189
|
+
describe('redactKey', () => {
|
|
190
|
+
it('redacts key correctly', () => {
|
|
191
|
+
expect((0, _1.redactKey)('1234567890', 3)).toBe('*******890');
|
|
192
|
+
expect((0, _1.redactKey)('1234567890', 1)).toBe('*********0');
|
|
193
|
+
expect((0, _1.redactKey)('1234567890', 10)).toBe('**********');
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CreateMigrationOptions, CreateMigrationApiResponse } from '../types';
|
|
2
|
+
export declare function createMigration(options: CreateMigrationOptions): Promise<CreateMigrationApiResponse>;
|
|
3
|
+
export declare function validateExportFolder(exportPath: string): void;
|
|
4
|
+
/**
|
|
5
|
+
* Creates a tar file from the export path.
|
|
6
|
+
* @param exportPath - The path to the export folder.
|
|
7
|
+
* @returns The path to the tar file.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createTarFile(exportPath: string): Promise<string>;
|
|
@@ -0,0 +1,122 @@
|
|
|
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.createTarFile = exports.validateExportFolder = exports.createMigration = void 0;
|
|
16
|
+
const fs_1 = __importDefault(require("fs"));
|
|
17
|
+
const path_1 = __importDefault(require("path"));
|
|
18
|
+
const ApiService_1 = require("../../ApiService");
|
|
19
|
+
const _1 = require(".");
|
|
20
|
+
const child_process_1 = require("child_process");
|
|
21
|
+
function createMigration(options) {
|
|
22
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
const apiService = new ApiService_1.ApiService({
|
|
24
|
+
validateStatus: _1.validateAxiosStatus,
|
|
25
|
+
});
|
|
26
|
+
const migrationUrl = yield (0, _1.buildMigrationUrl)(options.tenant, options.overrideUrl);
|
|
27
|
+
try {
|
|
28
|
+
const payload = {
|
|
29
|
+
assetId: options.assetId,
|
|
30
|
+
previewAssetId: options.previewAssetId,
|
|
31
|
+
matrixUrl: options.matrixUrl,
|
|
32
|
+
};
|
|
33
|
+
const response = yield apiService.client.post(`${migrationUrl}`, payload, {
|
|
34
|
+
headers: yield (0, _1.getMigrationHeaders)(options.tenant),
|
|
35
|
+
});
|
|
36
|
+
if (response.status !== 200 && response.status !== 201) {
|
|
37
|
+
throw new Error(`Migration creation failed with status: ${response.status}`);
|
|
38
|
+
}
|
|
39
|
+
// Validate response structure
|
|
40
|
+
const { assetMigration, uploadUrl } = response.data || {};
|
|
41
|
+
if (!(assetMigration === null || assetMigration === void 0 ? void 0 : assetMigration.migrationId) ||
|
|
42
|
+
!(assetMigration === null || assetMigration === void 0 ? void 0 : assetMigration.assetId) ||
|
|
43
|
+
!(assetMigration === null || assetMigration === void 0 ? void 0 : assetMigration.stage) ||
|
|
44
|
+
!(assetMigration === null || assetMigration === void 0 ? void 0 : assetMigration.status)) {
|
|
45
|
+
throw new Error('Invalid response format from migration service');
|
|
46
|
+
}
|
|
47
|
+
if (!uploadUrl) {
|
|
48
|
+
throw new Error('Upload URL not found in response');
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
assetMigration,
|
|
52
|
+
uploadUrl,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (error instanceof Error) {
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
throw new Error(`Failed to create migration: ${error}`);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
exports.createMigration = createMigration;
|
|
64
|
+
function validateExportFolder(exportPath) {
|
|
65
|
+
// Check if the export folder exists
|
|
66
|
+
if (!fs_1.default.existsSync(exportPath)) {
|
|
67
|
+
throw new Error(`Export folder does not exist: ${exportPath}`);
|
|
68
|
+
}
|
|
69
|
+
// Check if it's a directory
|
|
70
|
+
if (!fs_1.default.statSync(exportPath).isDirectory()) {
|
|
71
|
+
throw new Error(`Export path is not a directory: ${exportPath}`);
|
|
72
|
+
}
|
|
73
|
+
// Check for nested export folder structure (e.g., ./export/export/...)
|
|
74
|
+
const nestedExportPath = path_1.default.join(exportPath, 'export');
|
|
75
|
+
if (!fs_1.default.existsSync(nestedExportPath)) {
|
|
76
|
+
throw new Error(`Nested export folder does not exist: ${nestedExportPath}`);
|
|
77
|
+
}
|
|
78
|
+
if (!fs_1.default.statSync(nestedExportPath).isDirectory()) {
|
|
79
|
+
throw new Error(`Nested export path is not a directory: ${nestedExportPath}`);
|
|
80
|
+
}
|
|
81
|
+
// Check for export.xml file in the nested export directory
|
|
82
|
+
const exportXmlPath = path_1.default.join(nestedExportPath, 'export.xml');
|
|
83
|
+
if (!fs_1.default.existsSync(exportXmlPath)) {
|
|
84
|
+
throw new Error(`export.xml file does not exist in: ${nestedExportPath}`);
|
|
85
|
+
}
|
|
86
|
+
if (!fs_1.default.statSync(exportXmlPath).isFile()) {
|
|
87
|
+
throw new Error(`export.xml is not a valid file: ${exportXmlPath}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
exports.validateExportFolder = validateExportFolder;
|
|
91
|
+
/**
|
|
92
|
+
* Creates a tar file from the export path.
|
|
93
|
+
* @param exportPath - The path to the export folder.
|
|
94
|
+
* @returns The path to the tar file.
|
|
95
|
+
*/
|
|
96
|
+
function createTarFile(exportPath) {
|
|
97
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
98
|
+
const tarFileName = `export_${Date.now()}.tar.gz`;
|
|
99
|
+
const tarFilePath = path_1.default.join(process.cwd(), tarFileName);
|
|
100
|
+
return new Promise((resolve, reject) => {
|
|
101
|
+
const tar = (0, child_process_1.spawn)('tar', [
|
|
102
|
+
'-czf',
|
|
103
|
+
tarFilePath,
|
|
104
|
+
'-C',
|
|
105
|
+
path_1.default.dirname(exportPath),
|
|
106
|
+
path_1.default.basename(exportPath),
|
|
107
|
+
]);
|
|
108
|
+
tar.on('close', code => {
|
|
109
|
+
if (code === 0) {
|
|
110
|
+
resolve(tarFilePath);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
reject(new Error(`tar command failed with exit code ${code}`));
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
tar.on('error', error => {
|
|
117
|
+
reject(new Error(`Failed to create tar file: ${error.message}`));
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
exports.createTarFile = createTarFile;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|