@contentstack/cli-migration 1.5.0 → 1.5.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/README.md +1 -1
- package/package.json +3 -3
- package/src/commands/cm/stacks/migration.js +19 -21
- package/src/services/content-types.js +6 -6
- package/src/utils/callsite.js +2 -1
- package/src/utils/error-helper.js +80 -36
- package/src/utils/fs-helper.js +2 -1
- package/src/utils/index.js +0 -1
- package/src/utils/logger.js +6 -5
- package/src/utils/migration-logger.js +20 -0
- package/src/utils/error-handler.js +0 -21
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ $ npm install -g @contentstack/cli-migration
|
|
|
21
21
|
$ csdx COMMAND
|
|
22
22
|
running command...
|
|
23
23
|
$ csdx (--version)
|
|
24
|
-
@contentstack/cli-migration/1.5.
|
|
24
|
+
@contentstack/cli-migration/1.5.1 darwin-arm64 node-v20.8.0
|
|
25
25
|
$ csdx --help [COMMAND]
|
|
26
26
|
USAGE
|
|
27
27
|
$ csdx COMMAND
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentstack/cli-migration",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.2",
|
|
4
4
|
"author": "@contentstack",
|
|
5
5
|
"bugs": "https://github.com/contentstack/cli/issues",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@contentstack/cli-command": "~1.2.16",
|
|
8
|
-
"@contentstack/cli-utilities": "~1.
|
|
8
|
+
"@contentstack/cli-utilities": "~1.6.0",
|
|
9
9
|
"async": "^3.2.4",
|
|
10
10
|
"callsites": "^3.1.0",
|
|
11
11
|
"cardinal": "^2.1.1",
|
|
@@ -66,4 +66,4 @@
|
|
|
66
66
|
"cm:migration": "O-MGRTN"
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
|
-
}
|
|
69
|
+
}
|
|
@@ -12,14 +12,19 @@ const { Parser } = require('../../../modules');
|
|
|
12
12
|
const { ActionList } = require('../../../actions');
|
|
13
13
|
const fs = require('fs');
|
|
14
14
|
const chalk = require('chalk');
|
|
15
|
-
const
|
|
15
|
+
const isEmpty = require('lodash/isEmpty');
|
|
16
|
+
const {
|
|
17
|
+
printFlagDeprecation,
|
|
18
|
+
managementSDKClient,
|
|
19
|
+
flags,
|
|
20
|
+
isAuthenticated,
|
|
21
|
+
pathValidator,
|
|
22
|
+
} = require('@contentstack/cli-utilities');
|
|
16
23
|
|
|
17
24
|
const { ApiError, SchemaValidator, MigrationError, FieldValidator } = require('../../../validators');
|
|
18
25
|
|
|
19
26
|
// Utils
|
|
20
27
|
const { map: _map, constants, safePromise, errorHelper } = require('../../../utils');
|
|
21
|
-
const { success } = require('../../../utils/logger');
|
|
22
|
-
|
|
23
28
|
// Properties
|
|
24
29
|
const { get, set, getMapInstance, resetMapInstance } = _map;
|
|
25
30
|
const {
|
|
@@ -77,7 +82,7 @@ class MigrationCommand extends Command {
|
|
|
77
82
|
let configObj = config.reduce((a, v) => {
|
|
78
83
|
//NOTE: Temp code to handle only one spilt(Window absolute path issue).Need to replace with hardcoded config key
|
|
79
84
|
let [key, ...value] = v.split(':');
|
|
80
|
-
value = value?.length > 1 ? value?.join(':') : value?.join(
|
|
85
|
+
value = value?.length > 1 ? value?.join(':') : value?.join();
|
|
81
86
|
return { ...a, [key]: value };
|
|
82
87
|
}, {});
|
|
83
88
|
set('config', mapInstance, configObj);
|
|
@@ -128,11 +133,15 @@ class MigrationCommand extends Command {
|
|
|
128
133
|
} else {
|
|
129
134
|
await this.execSingleFile(filePath, mapInstance);
|
|
130
135
|
}
|
|
136
|
+
const errLogPath = `${process.cwd()}/migration-logs`;
|
|
137
|
+
if (fs.existsSync(errLogPath)) {
|
|
138
|
+
this.log(`The log has been stored at: `, errLogPath);
|
|
139
|
+
}
|
|
131
140
|
}
|
|
132
141
|
|
|
133
142
|
async execSingleFile(filePath, mapInstance) {
|
|
134
143
|
// Resolved absolute path
|
|
135
|
-
const resolvedMigrationPath =
|
|
144
|
+
const resolvedMigrationPath = pathValidator(filePath);
|
|
136
145
|
// User provided migration function
|
|
137
146
|
const migrationFunc = require(resolvedMigrationPath);
|
|
138
147
|
|
|
@@ -154,35 +163,24 @@ class MigrationCommand extends Command {
|
|
|
154
163
|
|
|
155
164
|
const listr = new Listr(tasks);
|
|
156
165
|
|
|
157
|
-
await listr.run()
|
|
158
|
-
this.handleErrors(error);
|
|
159
|
-
// When the process is child, send error message to parent
|
|
160
|
-
if (process.send) process.send({ errorOccurred: true });
|
|
161
|
-
});
|
|
166
|
+
await listr.run();
|
|
162
167
|
requests.splice(0, requests.length);
|
|
163
168
|
} catch (error) {
|
|
164
|
-
|
|
165
|
-
if (
|
|
166
|
-
this.log(error.message);
|
|
167
|
-
} else if (error.errorMessage) {
|
|
168
|
-
this.log(error.errorMessage);
|
|
169
|
-
}else{
|
|
170
|
-
this.log(error)
|
|
171
|
-
}
|
|
169
|
+
errorHelper(error, filePath);
|
|
170
|
+
if (process.send) process.send({ errorOccurred: true });
|
|
172
171
|
}
|
|
173
172
|
}
|
|
174
173
|
|
|
175
174
|
async execMultiFiles(filePath, mapInstance) {
|
|
176
175
|
// Resolved absolute path
|
|
177
|
-
const resolvedMigrationPath =
|
|
176
|
+
const resolvedMigrationPath = pathValidator(filePath);
|
|
178
177
|
try {
|
|
179
178
|
const files = fs.readdirSync(resolvedMigrationPath);
|
|
180
179
|
for (const element of files) {
|
|
181
180
|
const file = element;
|
|
182
181
|
if (extname(file) === '.js') {
|
|
183
|
-
success(chalk`{white Executing file:} {grey {bold ${file}}}`);
|
|
184
182
|
// eslint-disable-next-line no-await-in-loop
|
|
185
|
-
await this.execSingleFile(resolve(filePath, file), mapInstance);
|
|
183
|
+
await this.execSingleFile(pathValidator(resolve(filePath, file)), mapInstance);
|
|
186
184
|
}
|
|
187
185
|
}
|
|
188
186
|
} catch (error) {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const Base = require('../modules/base');
|
|
6
6
|
// Utils
|
|
7
|
-
const { map: _map, safePromise, successHandler, errorHandler, constants } = require('../utils');
|
|
7
|
+
const { map: _map, safePromise, successHandler, errorHandler, constants, errorHelper } = require('../utils');
|
|
8
8
|
// Map methods
|
|
9
9
|
const { get, getMapInstance, getDataWithAction } = _map;
|
|
10
10
|
const mapInstance = getMapInstance();
|
|
@@ -23,7 +23,7 @@ class ContentTypeService {
|
|
|
23
23
|
|
|
24
24
|
const [err, result] = await safePromise(this.stackSDKInstance.contentType(id).fetch());
|
|
25
25
|
if (err) {
|
|
26
|
-
|
|
26
|
+
errorHelper(err);
|
|
27
27
|
this.base.dispatch(callsite, id, err, 'apiError');
|
|
28
28
|
throw err;
|
|
29
29
|
}
|
|
@@ -36,7 +36,7 @@ class ContentTypeService {
|
|
|
36
36
|
const data = getDataWithAction(id, mapInstance, action);
|
|
37
37
|
const [err, result] = await safePromise(this.stackSDKInstance.contentType().create(data));
|
|
38
38
|
if (err) {
|
|
39
|
-
|
|
39
|
+
errorHelper(err);
|
|
40
40
|
this.base.dispatch(callsite, id, err, 'apiError');
|
|
41
41
|
throw err;
|
|
42
42
|
}
|
|
@@ -51,7 +51,7 @@ class ContentTypeService {
|
|
|
51
51
|
const method = 'PUT';
|
|
52
52
|
const [err, result] = await safePromise(data.update());
|
|
53
53
|
if (err) {
|
|
54
|
-
|
|
54
|
+
errorHelper(err);
|
|
55
55
|
this.base.dispatch(callsite, data.uid, err, 'apiError');
|
|
56
56
|
throw err;
|
|
57
57
|
}
|
|
@@ -66,7 +66,7 @@ class ContentTypeService {
|
|
|
66
66
|
const [err, result] = await safePromise(this.stackSDKInstance.contentType(id).delete());
|
|
67
67
|
|
|
68
68
|
if (err) {
|
|
69
|
-
|
|
69
|
+
errorHelper(err);
|
|
70
70
|
this.base.dispatch(callsite, id, err, 'apiError');
|
|
71
71
|
throw err;
|
|
72
72
|
}
|
|
@@ -306,7 +306,7 @@ class ContentTypeService {
|
|
|
306
306
|
}
|
|
307
307
|
|
|
308
308
|
// Handling both the scenarios
|
|
309
|
-
if (
|
|
309
|
+
if (found === 0 || (against && found === 1)) {
|
|
310
310
|
isValid = false;
|
|
311
311
|
}
|
|
312
312
|
|
package/src/utils/callsite.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
const getCallsites = require('callsites');
|
|
4
4
|
const { parse, resolve } = require('path');
|
|
5
|
+
const { pathValidator } = require('@contentstack/cli-utilities');
|
|
5
6
|
|
|
6
7
|
function getFileDirectory(path) {
|
|
7
|
-
const parentPath = resolve(path, '../'); // Assuming that will be 2 folders up
|
|
8
|
+
const parentPath = pathValidator(resolve(path, '../')); // Assuming that will be 2 folders up
|
|
8
9
|
return parse(parentPath).dir;
|
|
9
10
|
}
|
|
10
11
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
const { highlight } = require('cardinal');
|
|
2
2
|
const { keys } = Object;
|
|
3
3
|
const chalk = require('chalk');
|
|
4
|
-
const isEmpty = require('lodash/isEmpty')
|
|
5
|
-
|
|
4
|
+
const isEmpty = require('lodash/isEmpty');
|
|
5
|
+
const MigrationLogger = require('./migration-logger');
|
|
6
|
+
const fs = require('fs');
|
|
6
7
|
const { readFile } = require('./fs-helper');
|
|
7
8
|
const groupBy = require('./group-by');
|
|
8
9
|
|
|
@@ -19,43 +20,86 @@ const getLineWithContext = (lines, lineNumber, context) => {
|
|
|
19
20
|
};
|
|
20
21
|
};
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
function removeSpecialCharacter(str) {
|
|
24
|
+
return str.replace(/\u001b\[\d+m/g, '');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = (errors, filePath) => {
|
|
28
|
+
const logger = new MigrationLogger(process.cwd());
|
|
29
|
+
|
|
23
30
|
const errorsByFile = groupBy(errors, 'file');
|
|
24
31
|
const messages = [];
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const context = 2;
|
|
35
|
-
const { before, line, after } = getLineWithContext(lines, callsite.line, context);
|
|
36
|
-
|
|
37
|
-
const beforeLines = before.map((_line) => chalk`${_line}\n`);
|
|
38
|
-
const afterLines = after.map((_line) => chalk`${_line}\n`);
|
|
39
|
-
const highlightedLine = chalk`{bold ${line}}\n`;
|
|
40
|
-
|
|
41
|
-
const formattedCode = beforeLines + highlightedLine + afterLines;
|
|
42
|
-
if (error.payload.apiError) {
|
|
43
|
-
return chalk`{red Line ${String(callsite.line)}:} {bold ${error.payload.apiError.message}}\n${formattedCode}`;
|
|
44
|
-
}
|
|
45
|
-
if (error.message) {
|
|
46
|
-
return chalk`{red Line ${String(callsite.line)}:} {bold ${error.message}}\n${formattedCode}`;
|
|
47
|
-
}
|
|
48
|
-
return chalk`{red Line ${String(callsite.line)}:} {bold something went wrong here.}\n${formattedCode}`;
|
|
49
|
-
})
|
|
50
|
-
.join('\n');
|
|
51
|
-
|
|
52
|
-
messages.push(`${fileErrorsMessage}${errorMessages}`);
|
|
53
|
-
}
|
|
54
|
-
if (isEmpty(messages) && errors !== undefined && isEmpty(errorsByFile)) {
|
|
55
|
-
console.error('Migration error---', errors);
|
|
32
|
+
if (filePath) {
|
|
33
|
+
if (errors.request) {
|
|
34
|
+
errors.data = errors.request?.data;
|
|
35
|
+
delete errors.request;
|
|
36
|
+
}
|
|
37
|
+
if (errors.message) {
|
|
38
|
+
delete errors.message;
|
|
39
|
+
}
|
|
40
|
+
logger.log('error', { [filePath]: errors });
|
|
56
41
|
} else {
|
|
57
|
-
|
|
42
|
+
for (const file of keys(errorsByFile)) {
|
|
43
|
+
const errorLogs = {};
|
|
44
|
+
errorLogs[file] = {};
|
|
45
|
+
const fileContents = readFile(file);
|
|
46
|
+
const highlightedCode = highlight(fileContents, { linenos: true });
|
|
47
|
+
const lines = highlightedCode.split('\n');
|
|
48
|
+
|
|
49
|
+
const fileErrorsMessage = chalk`{red Errors in ${file}}\n\n`;
|
|
50
|
+
errorLogs[file].fileErrorsMessage = fileErrorsMessage.replace(/\u001b\[\d+m/g, '');
|
|
51
|
+
const errorMessages = errorsByFile[file]
|
|
52
|
+
.map((error) => {
|
|
53
|
+
const callsite = error.meta.callsite;
|
|
54
|
+
const context = 2;
|
|
55
|
+
let { before, line, after } = getLineWithContext(lines, callsite.line, context);
|
|
56
|
+
|
|
57
|
+
const beforeLines = before.map((_line) => chalk`${_line}\n`);
|
|
58
|
+
const afterLines = after.map((_line) => chalk`${_line}\n`);
|
|
59
|
+
const highlightedLine = chalk`{bold ${line}}\n`;
|
|
60
|
+
|
|
61
|
+
before = removeSpecialCharacter(before.join('\n'));
|
|
62
|
+
after = removeSpecialCharacter(after.join('\n'));
|
|
63
|
+
line = removeSpecialCharacter(line);
|
|
64
|
+
errorLogs[file].lines = { before, line, after };
|
|
65
|
+
if (error.request) {
|
|
66
|
+
error.data = error.request?.data;
|
|
67
|
+
delete error.request;
|
|
68
|
+
}
|
|
69
|
+
if (error.message) {
|
|
70
|
+
delete error.message;
|
|
71
|
+
}
|
|
72
|
+
const formattedCode = beforeLines + highlightedLine + afterLines;
|
|
73
|
+
if (error.payload?.apiError) {
|
|
74
|
+
errorLogs[file].apiError = true;
|
|
75
|
+
errorLogs[file].errorCode = error.payload.apiError.errorCode;
|
|
76
|
+
errorLogs[file].errors = error.payload.apiError.errors;
|
|
77
|
+
errorLogs[file].data = error.data;
|
|
78
|
+
}
|
|
79
|
+
if (error.message && !error.payload.apiError) {
|
|
80
|
+
errorLogs[file].apiError = false;
|
|
81
|
+
errorLogs[file].error = error.message;
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
.join('\n');
|
|
85
|
+
|
|
86
|
+
messages.push(`${fileErrorsMessage}${errorMessages}`);
|
|
87
|
+
logger.log('error', errorLogs);
|
|
88
|
+
}
|
|
89
|
+
if (errors?.request) {
|
|
90
|
+
errors.data = errors.request?.data;
|
|
91
|
+
delete errors.request;
|
|
92
|
+
}
|
|
93
|
+
if (errors?.message) {
|
|
94
|
+
delete errors.message;
|
|
95
|
+
}
|
|
96
|
+
if (isEmpty(messages) && errors !== undefined && isEmpty(errorsByFile)) {
|
|
97
|
+
logger.log('error', { errors: errors });
|
|
98
|
+
console.log(chalk`{bold.red Migration unsuccessful}`);
|
|
99
|
+
} else {
|
|
100
|
+
logger.log('error', { error: messages.join('\n') });
|
|
101
|
+
}
|
|
58
102
|
}
|
|
59
103
|
// eslint-disable-next-line
|
|
60
|
-
console.log(chalk`{bold.red Migration unsuccessful}`);
|
|
104
|
+
// console.log(chalk`{bold.red Migration unsuccessful}`);
|
|
61
105
|
};
|
package/src/utils/fs-helper.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { existsSync, mkdirSync, readFileSync, readFile } = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const { pathValidator } = require('@contentstack/cli-utilities');
|
|
5
6
|
|
|
6
7
|
exports.makeDir = (dirname) => {
|
|
7
8
|
!this.existsSync(dirname) && mkdirSync(dirname);
|
|
@@ -16,7 +17,7 @@ exports.readFile = (filePath) => {
|
|
|
16
17
|
|
|
17
18
|
exports.readJSONFile = (filePath) => {
|
|
18
19
|
return new Promise((resolve, reject) => {
|
|
19
|
-
filePath =
|
|
20
|
+
filePath = pathValidator(filePath);
|
|
20
21
|
readFile(filePath, 'utf-8', (error, data) => {
|
|
21
22
|
if (error) {
|
|
22
23
|
reject(error);
|
package/src/utils/index.js
CHANGED
|
@@ -11,7 +11,6 @@ module.exports = {
|
|
|
11
11
|
safePromise: require('./safe-promise'),
|
|
12
12
|
getConfig: require('./get-config'),
|
|
13
13
|
successHandler: require('./success-handler'),
|
|
14
|
-
errorHandler: require('./error-handler'),
|
|
15
14
|
getCallsite: require('./callsite'),
|
|
16
15
|
errorHelper: require('./error-helper'),
|
|
17
16
|
groupBy: require('./group-by'),
|
package/src/utils/logger.js
CHANGED
|
@@ -4,6 +4,7 @@ const { createLogger, format, transports } = require('winston');
|
|
|
4
4
|
const { resolve, join } = require('path');
|
|
5
5
|
const { slice } = Array.prototype;
|
|
6
6
|
const { stringify } = JSON;
|
|
7
|
+
const { pathValidator } = require('@contentstack/cli-utilities');
|
|
7
8
|
|
|
8
9
|
const { combine, label, printf, colorize } = format;
|
|
9
10
|
|
|
@@ -32,10 +33,10 @@ function init(logFileName) {
|
|
|
32
33
|
// Create dir if does not exist
|
|
33
34
|
makeDir(logsDir);
|
|
34
35
|
|
|
35
|
-
const logPath = join(logsDir, logFileName + '.log');
|
|
36
|
+
const logPath = pathValidator(join(logsDir, logFileName + '.log'));
|
|
36
37
|
const logger = createLogger({
|
|
37
38
|
format: combine(colorize(), label({ label: 'Migration' }), customFormat),
|
|
38
|
-
transports: [new transports.File({ filename: logPath })
|
|
39
|
+
transports: [new transports.File({ filename: logPath })],
|
|
39
40
|
});
|
|
40
41
|
|
|
41
42
|
let args;
|
|
@@ -53,9 +54,9 @@ function init(logFileName) {
|
|
|
53
54
|
logString && logger.log('warn', logString);
|
|
54
55
|
},
|
|
55
56
|
error: function () {
|
|
56
|
-
args = slice.call(arguments);
|
|
57
|
-
logString = getString(args);
|
|
58
|
-
logString && logger.log('error', logString);
|
|
57
|
+
// args = slice.call(arguments);
|
|
58
|
+
// logString = getString(args);
|
|
59
|
+
// logString && logger.log('error', logString);
|
|
59
60
|
},
|
|
60
61
|
debug: function () {
|
|
61
62
|
args = slice.call(arguments);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const winston = require('winston');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
module.exports = class MigrationLogger {
|
|
4
|
+
constructor(filePath) {
|
|
5
|
+
this.filePath = path.join(filePath, 'migration-logs');
|
|
6
|
+
this.logger = winston.createLogger({
|
|
7
|
+
levels: { error: 1 },
|
|
8
|
+
transports: [
|
|
9
|
+
new winston.transports.File({
|
|
10
|
+
level: 'error',
|
|
11
|
+
filename: path.join(this.filePath, 'error.logs'),
|
|
12
|
+
format: winston.format.combine(winston.format.timestamp(), winston.format.json()),
|
|
13
|
+
}),
|
|
14
|
+
],
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
log(level, message) {
|
|
18
|
+
this.logger.log('error', message);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { error } = require('./logger');
|
|
4
|
-
const { errorMessageHandler } = require('./constants');
|
|
5
|
-
|
|
6
|
-
module.exports = (data, type, method, err) => {
|
|
7
|
-
if (data && type && method) {
|
|
8
|
-
error(`Error occurred while ${errorMessageHandler[method]} ${type}: ${data}.`);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
if (err.errorMessage) {
|
|
12
|
-
error(err.errorMessage);
|
|
13
|
-
}
|
|
14
|
-
if (err instanceof Error && err && err.message && err.stack) {
|
|
15
|
-
error(err.message);
|
|
16
|
-
// error(err.stack)
|
|
17
|
-
} else {
|
|
18
|
-
error(err);
|
|
19
|
-
}
|
|
20
|
-
// throw new Error(err);
|
|
21
|
-
};
|