@contentstack/cli-migration 0.1.1-beta.2 → 0.1.1-beta.3
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 +24 -11
- package/oclif.manifest.json +1 -1
- package/package.json +2 -2
- package/src/commands/cm/migration.js +179 -90
- package/src/config/api-config.js +10 -10
- package/src/modules/parser.js +11 -3
- package/src/utils/constants.js +128 -119
- package/src/utils/fs-helper.js +15 -1
package/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
@contentstack/cli-migration
|
|
2
|
-
===========================
|
|
3
|
-
|
|
4
|
-
The Contentstack CLI’s “Migration” plugin allows developers to automate the content migration process and easily migrate your content from your system to Contentstack.
|
|
1
|
+
# @contentstack/cli-migration
|
|
5
2
|
|
|
3
|
+
The Contentstack CLI’s “Migration” plugin allows developers to automate the content migration process and easily migrate your content from your system to Contentstack.
|
|
6
4
|
|
|
7
5
|
[](https://oclif.io)
|
|
8
6
|
[](https://npmjs.org/package/@contentstack/cli-migration)
|
|
@@ -10,24 +8,29 @@ The Contentstack CLI’s “Migration” plugin allows developers to automate th
|
|
|
10
8
|
[](https://github.com/ninadhatkar/cli-migration/blob/master/package.json)
|
|
11
9
|
|
|
12
10
|
<!-- toc -->
|
|
11
|
+
* [@contentstack/cli-migration](#contentstackcli-migration)
|
|
13
12
|
* [Usage](#usage)
|
|
14
13
|
* [Commands](#commands)
|
|
15
14
|
<!-- tocstop -->
|
|
15
|
+
|
|
16
16
|
# Usage
|
|
17
|
+
|
|
17
18
|
<!-- usage -->
|
|
18
19
|
```sh-session
|
|
19
20
|
$ npm install -g @contentstack/cli-migration
|
|
20
21
|
$ csdx COMMAND
|
|
21
22
|
running command...
|
|
22
23
|
$ csdx (-v|--version|version)
|
|
23
|
-
@contentstack/cli-migration/0.1.1-beta.
|
|
24
|
+
@contentstack/cli-migration/0.1.1-beta.3 linux-x64 node-v16.14.2
|
|
24
25
|
$ csdx --help [COMMAND]
|
|
25
26
|
USAGE
|
|
26
27
|
$ csdx COMMAND
|
|
27
28
|
...
|
|
28
29
|
```
|
|
29
30
|
<!-- usagestop -->
|
|
31
|
+
|
|
30
32
|
# Commands
|
|
33
|
+
|
|
31
34
|
<!-- commands -->
|
|
32
35
|
* [`csdx cm:migration`](#csdx-cmmigration)
|
|
33
36
|
|
|
@@ -36,8 +39,6 @@ USAGE
|
|
|
36
39
|
Contentstack migration script.
|
|
37
40
|
|
|
38
41
|
```
|
|
39
|
-
Contentstack migration script.
|
|
40
|
-
|
|
41
42
|
USAGE
|
|
42
43
|
$ csdx cm:migration
|
|
43
44
|
|
|
@@ -55,15 +56,27 @@ OPTIONS
|
|
|
55
56
|
-n, --filePath=filePath Use this flag to provide the path of the file of the migration
|
|
56
57
|
script provided by the user.
|
|
57
58
|
|
|
59
|
+
--config=config [optional] inline configuration, <key1>:<value1>
|
|
60
|
+
|
|
61
|
+
--config-file=config-file [optional] Path of the JSON configuration file
|
|
62
|
+
|
|
58
63
|
--multi This flag helps you to migrate multiple content files in a single
|
|
59
64
|
instance.
|
|
65
|
+
|
|
66
|
+
EXAMPLES
|
|
67
|
+
$ csdx cm:migration -A -n <migration/script/file/path> -k <api-key>
|
|
68
|
+
$ csdx cm:migration -A -n <migration/script/file/path> -k <api-key> -B <target branch name>
|
|
69
|
+
$ csdx cm:migration --config <key1>:<value1> <key2>:<value2> ... -n <migration/script/file/path>
|
|
70
|
+
$ csdx cm:migration --config-file <path/to/json/config/file> -n <migration/script/file/path>
|
|
71
|
+
$ csdx cm:migration --multi -n <migration/scripts/dir/path>
|
|
72
|
+
$ csdx cm:migration -a -n <migration/script/file/path> -k <api-key>
|
|
60
73
|
```
|
|
61
74
|
|
|
62
|
-
_See code: [src/commands/cm/migration.js](https://github.com/contentstack/cli-migration/blob/v0.1.1-beta.
|
|
75
|
+
_See code: [src/commands/cm/migration.js](https://github.com/contentstack/cli-migration/blob/v0.1.1-beta.3/src/commands/cm/migration.js)_
|
|
63
76
|
<!-- commandsstop -->
|
|
64
77
|
|
|
65
78
|
### Points to remember
|
|
66
79
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
80
|
+
- Currently, the Migration plugin does not support Group fields migration. You can pass a custom schema to the createField method to migrate Group fields. [here](packages/contentstack-migration/examples/)
|
|
81
|
+
- Currently, the Migration plugin does not support Global fields migration. You can migrate Global fields by creating an SDK instance and adding it to content types using the createField method.[here](packages/contentstack-migration/examples/)
|
|
82
|
+
- Currently, the Migration plugin does not support migration of Entries. You can migrate entries by creating an SDK instance to create/ update/ delete entries for your content type. [here](packages/contentstack-migration/examples/)
|
package/oclif.manifest.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"0.1.1-beta.
|
|
1
|
+
{"version":"0.1.1-beta.3","commands":{"cm:migration":{"id":"cm:migration","description":"Contentstack migration script.","pluginName":"@contentstack/cli-migration","pluginType":"core","aliases":[],"examples":["$ csdx cm:migration -A -n <migration/script/file/path> -k <api-key>","$ csdx cm:migration -A -n <migration/script/file/path> -k <api-key> -B <target branch name>","$ csdx cm:migration --config <key1>:<value1> <key2>:<value2> ... -n <migration/script/file/path>","$ csdx cm:migration --config-file <path/to/json/config/file> -n <migration/script/file/path>","$ csdx cm:migration --multi -n <migration/scripts/dir/path> ","$ csdx cm:migration -a -n <migration/script/file/path> -k <api-key>"],"flags":{"api-key":{"name":"api-key","type":"option","char":"k","description":"With this flag add the API key of your stack."},"authtoken":{"name":"authtoken","type":"boolean","char":"A","description":"Use this flag to use the auth token of the current session. After logging in CLI, an auth token is generated for each new session.","allowNo":false},"management-token-alias":{"name":"management-token-alias","type":"option","char":"a","description":"Use this flag to add the management token alias."},"filePath":{"name":"filePath","type":"option","char":"n","description":"Use this flag to provide the path of the file of the migration script provided by the user."},"branch":{"name":"branch","type":"option","char":"B","description":"Use this flag to add the branch name where you want to perform the migration."},"config-file":{"name":"config-file","type":"option","description":"[optional] Path of the JSON configuration file"},"config":{"name":"config","type":"option","description":"[optional] inline configuration, <key1>:<value1>"},"multi":{"name":"multi","type":"boolean","description":"This flag helps you to migrate multiple content files in a single instance.","allowNo":false}},"args":[]}}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentstack/cli-migration",
|
|
3
|
-
"version": "0.1.1-beta.
|
|
3
|
+
"version": "0.1.1-beta.3",
|
|
4
4
|
"author": "@contentstack",
|
|
5
5
|
"bugs": "https://github.com/contentstack/cli/issues",
|
|
6
6
|
"dependencies": {
|
|
@@ -65,4 +65,4 @@
|
|
|
65
65
|
"test": "nyc mocha \"test/**/*.test.js\"",
|
|
66
66
|
"version": "oclif-dev readme && git add README.md"
|
|
67
67
|
}
|
|
68
|
-
}
|
|
68
|
+
}
|
|
@@ -1,109 +1,165 @@
|
|
|
1
1
|
/* eslint-disable no-unused-expressions */
|
|
2
2
|
/* eslint-disable no-warning-comments */
|
|
3
3
|
/* eslint-disable camelcase */
|
|
4
|
-
|
|
4
|
+
"use strict";
|
|
5
5
|
|
|
6
6
|
// Dependencies
|
|
7
|
-
const Listr = require(
|
|
8
|
-
const {resolve, extname} = require(
|
|
9
|
-
const {Command, flags} = require(
|
|
10
|
-
const {waterfall} = require(
|
|
11
|
-
const {Parser} = require(
|
|
12
|
-
const {ActionList} = require(
|
|
13
|
-
const fs = require(
|
|
14
|
-
const chalk = require(
|
|
7
|
+
const Listr = require("listr");
|
|
8
|
+
const { resolve, extname } = require("path");
|
|
9
|
+
const { Command, flags } = require("@contentstack/cli-command");
|
|
10
|
+
const { waterfall } = require("async");
|
|
11
|
+
const { Parser } = require("../../modules");
|
|
12
|
+
const { ActionList } = require("../../actions");
|
|
13
|
+
const fs = require("fs");
|
|
14
|
+
const chalk = require("chalk");
|
|
15
15
|
|
|
16
|
-
const {
|
|
16
|
+
const {
|
|
17
|
+
ApiError,
|
|
18
|
+
SchemaValidator,
|
|
19
|
+
MigrationError,
|
|
20
|
+
FieldValidator,
|
|
21
|
+
} = require("../../validators");
|
|
17
22
|
|
|
18
23
|
// Utils
|
|
19
|
-
const {
|
|
20
|
-
|
|
24
|
+
const {
|
|
25
|
+
map: _map,
|
|
26
|
+
constants,
|
|
27
|
+
safePromise,
|
|
28
|
+
errorHelper,
|
|
29
|
+
} = require("../../utils");
|
|
30
|
+
const { success } = require("../../utils/logger");
|
|
21
31
|
|
|
22
32
|
// Properties
|
|
23
|
-
const {get, set, getMapInstance, resetMapInstance} = _map
|
|
24
|
-
const {
|
|
33
|
+
const { get, set, getMapInstance, resetMapInstance } = _map;
|
|
34
|
+
const {
|
|
35
|
+
requests: _requests,
|
|
36
|
+
actionMapper,
|
|
37
|
+
MANAGEMENT_SDK,
|
|
38
|
+
MANAGEMENT_TOKEN,
|
|
39
|
+
AUTH_TOKEN,
|
|
40
|
+
API_KEY,
|
|
41
|
+
BRANCH,
|
|
42
|
+
MANAGEMENT_CLIENT,
|
|
43
|
+
} = constants;
|
|
25
44
|
|
|
26
45
|
class MigrationCommand extends Command {
|
|
46
|
+
static examples = [
|
|
47
|
+
"$ csdx cm:migration -A -n <migration/script/file/path> -k <api-key>",
|
|
48
|
+
"$ csdx cm:migration -A -n <migration/script/file/path> -k <api-key> -B <target branch name>",
|
|
49
|
+
"$ csdx cm:migration --config <key1>:<value1> <key2>:<value2> ... -n <migration/script/file/path>",
|
|
50
|
+
"$ csdx cm:migration --config-file <path/to/json/config/file> -n <migration/script/file/path>",
|
|
51
|
+
"$ csdx cm:migration --multi -n <migration/scripts/dir/path> ",
|
|
52
|
+
"$ csdx cm:migration -a -n <migration/script/file/path> -k <api-key>",
|
|
53
|
+
];
|
|
54
|
+
|
|
27
55
|
async run() {
|
|
28
56
|
// TODO: filePath validation required.
|
|
29
|
-
const migrationCommandFlags = this.parse(MigrationCommand).flags
|
|
30
|
-
const {filePath, multi, branch} = migrationCommandFlags
|
|
31
|
-
const authtoken = migrationCommandFlags.authtoken
|
|
32
|
-
const apiKey = migrationCommandFlags[
|
|
33
|
-
const alias = migrationCommandFlags[
|
|
57
|
+
const migrationCommandFlags = this.parse(MigrationCommand).flags;
|
|
58
|
+
const { filePath, multi, branch } = migrationCommandFlags;
|
|
59
|
+
const authtoken = migrationCommandFlags.authtoken;
|
|
60
|
+
const apiKey = migrationCommandFlags["api-key"];
|
|
61
|
+
const alias = migrationCommandFlags["management-token-alias"];
|
|
62
|
+
const config = migrationCommandFlags["config"];
|
|
34
63
|
|
|
35
|
-
|
|
64
|
+
if (!filePath) {
|
|
65
|
+
this.log("Please provide the migration script file path, use -n or --filePath flag");
|
|
66
|
+
this.exit();
|
|
67
|
+
}
|
|
36
68
|
|
|
37
69
|
// Reset map instance
|
|
38
|
-
const mapInstance = getMapInstance()
|
|
39
|
-
resetMapInstance(mapInstance)
|
|
70
|
+
const mapInstance = getMapInstance();
|
|
71
|
+
resetMapInstance(mapInstance);
|
|
72
|
+
if (migrationCommandFlags["config-file"]) {
|
|
73
|
+
set("config-path", mapInstance, migrationCommandFlags["config-file"]);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (Array.isArray(config) && config.length > 0) {
|
|
77
|
+
let configObj = config.reduce((a, v) => {
|
|
78
|
+
let objArr = v.split(":");
|
|
79
|
+
return { ...a, [objArr[0]]: objArr[1] };
|
|
80
|
+
}, {});
|
|
81
|
+
set("config", mapInstance, configObj);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let stackSDKInstance;
|
|
40
85
|
if (branch) {
|
|
41
|
-
set(BRANCH, mapInstance, branch)
|
|
86
|
+
set(BRANCH, mapInstance, branch);
|
|
42
87
|
}
|
|
43
88
|
|
|
44
89
|
if (alias) {
|
|
45
|
-
let managementToken = this.getToken(alias)
|
|
90
|
+
let managementToken = this.getToken(alias);
|
|
46
91
|
if (managementToken) {
|
|
47
|
-
set(MANAGEMENT_TOKEN, mapInstance, managementToken)
|
|
48
|
-
set(API_KEY, mapInstance, managementToken.apiKey)
|
|
92
|
+
set(MANAGEMENT_TOKEN, mapInstance, managementToken);
|
|
93
|
+
set(API_KEY, mapInstance, managementToken.apiKey);
|
|
49
94
|
if (branch) {
|
|
50
|
-
stackSDKInstance = this.managementAPIClient.stack({
|
|
95
|
+
stackSDKInstance = this.managementAPIClient.stack({
|
|
96
|
+
management_token: managementToken.token,
|
|
97
|
+
api_key: managementToken.apiKey,
|
|
98
|
+
branch_uid: branch,
|
|
99
|
+
});
|
|
51
100
|
} else {
|
|
52
|
-
stackSDKInstance = this.managementAPIClient.stack({
|
|
101
|
+
stackSDKInstance = this.managementAPIClient.stack({
|
|
102
|
+
management_token: managementToken.token,
|
|
103
|
+
api_key: managementToken.apiKey,
|
|
104
|
+
});
|
|
53
105
|
}
|
|
54
106
|
}
|
|
55
107
|
}
|
|
56
108
|
|
|
57
109
|
if (authtoken) {
|
|
58
|
-
set(AUTH_TOKEN, mapInstance, authtoken)
|
|
59
|
-
set(API_KEY, mapInstance, apiKey)
|
|
60
|
-
this.managementAPIClient = {authtoken: this.authToken}
|
|
110
|
+
set(AUTH_TOKEN, mapInstance, authtoken);
|
|
111
|
+
set(API_KEY, mapInstance, apiKey);
|
|
112
|
+
this.managementAPIClient = { authtoken: this.authToken };
|
|
61
113
|
if (branch) {
|
|
62
|
-
stackSDKInstance = this.managementAPIClient.stack({
|
|
114
|
+
stackSDKInstance = this.managementAPIClient.stack({
|
|
115
|
+
api_key: apiKey,
|
|
116
|
+
branch_uid: branch,
|
|
117
|
+
});
|
|
63
118
|
} else {
|
|
64
|
-
stackSDKInstance = this.managementAPIClient.stack({api_key: apiKey})
|
|
119
|
+
stackSDKInstance = this.managementAPIClient.stack({ api_key: apiKey });
|
|
65
120
|
}
|
|
66
121
|
}
|
|
67
122
|
|
|
68
|
-
set(MANAGEMENT_SDK, mapInstance, stackSDKInstance)
|
|
123
|
+
set(MANAGEMENT_SDK, mapInstance, stackSDKInstance);
|
|
124
|
+
set(MANAGEMENT_CLIENT, mapInstance, this.managementAPIClient);
|
|
69
125
|
|
|
70
126
|
if (multi) {
|
|
71
|
-
await this.execMultiFiles(filePath, mapInstance)
|
|
127
|
+
await this.execMultiFiles(filePath, mapInstance);
|
|
72
128
|
} else {
|
|
73
|
-
await this.execSingleFile(filePath, mapInstance)
|
|
129
|
+
await this.execSingleFile(filePath, mapInstance);
|
|
74
130
|
}
|
|
75
131
|
}
|
|
76
132
|
|
|
77
133
|
async execSingleFile(filePath, mapInstance) {
|
|
78
134
|
// Resolved absolute path
|
|
79
|
-
const resolvedMigrationPath = resolve(filePath)
|
|
135
|
+
const resolvedMigrationPath = resolve(filePath);
|
|
80
136
|
// User provided migration function
|
|
81
|
-
const migrationFunc = require(resolvedMigrationPath)
|
|
137
|
+
const migrationFunc = require(resolvedMigrationPath);
|
|
82
138
|
|
|
83
|
-
const parser = new Parser()
|
|
139
|
+
const parser = new Parser();
|
|
84
140
|
|
|
85
141
|
try {
|
|
86
|
-
const migrationParser = await parser.getMigrationParser(migrationFunc)
|
|
142
|
+
const migrationParser = await parser.getMigrationParser(migrationFunc);
|
|
87
143
|
if (migrationParser.hasErrors) {
|
|
88
|
-
errorHelper(migrationParser.hasErrors)
|
|
144
|
+
errorHelper(migrationParser.hasErrors);
|
|
89
145
|
// When the process is child, send error message to parent
|
|
90
|
-
if (process.send) process.send({errorOccurred: true})
|
|
91
|
-
this.exit(1)
|
|
146
|
+
if (process.send) process.send({ errorOccurred: true });
|
|
147
|
+
this.exit(1);
|
|
92
148
|
}
|
|
93
149
|
|
|
94
150
|
// Make calls from here
|
|
95
|
-
const requests = get(_requests, mapInstance)
|
|
151
|
+
const requests = get(_requests, mapInstance);
|
|
96
152
|
// Fetches tasks array
|
|
97
|
-
const tasks = this.getTasks(requests)
|
|
153
|
+
const tasks = this.getTasks(requests);
|
|
98
154
|
|
|
99
|
-
const listr = new Listr(tasks)
|
|
155
|
+
const listr = new Listr(tasks);
|
|
100
156
|
|
|
101
|
-
await listr.run().catch(error => {
|
|
102
|
-
this.handleErrors(error)
|
|
157
|
+
await listr.run().catch((error) => {
|
|
158
|
+
this.handleErrors(error);
|
|
103
159
|
// When the process is child, send error message to parent
|
|
104
|
-
if (process.send) process.send({errorOccurred: true})
|
|
105
|
-
})
|
|
106
|
-
requests.splice(0, requests.length)
|
|
160
|
+
if (process.send) process.send({ errorOccurred: true });
|
|
161
|
+
});
|
|
162
|
+
requests.splice(0, requests.length);
|
|
107
163
|
} catch (error) {
|
|
108
164
|
// errorHandler(null, null, null, error)
|
|
109
165
|
}
|
|
@@ -111,72 +167,105 @@ class MigrationCommand extends Command {
|
|
|
111
167
|
|
|
112
168
|
async execMultiFiles(filePath, mapInstance) {
|
|
113
169
|
// Resolved absolute path
|
|
114
|
-
const resolvedMigrationPath = resolve(filePath)
|
|
170
|
+
const resolvedMigrationPath = resolve(filePath);
|
|
115
171
|
try {
|
|
116
|
-
const files = fs.readdirSync(resolvedMigrationPath)
|
|
172
|
+
const files = fs.readdirSync(resolvedMigrationPath);
|
|
117
173
|
for (let index = 0; index < files.length; index++) {
|
|
118
|
-
const file = files[index]
|
|
119
|
-
if (extname(file) ===
|
|
120
|
-
success(chalk`{white Executing file:} {grey {bold ${file}}}`)
|
|
174
|
+
const file = files[index];
|
|
175
|
+
if (extname(file) === ".js") {
|
|
176
|
+
success(chalk`{white Executing file:} {grey {bold ${file}}}`);
|
|
121
177
|
// eslint-disable-next-line no-await-in-loop
|
|
122
|
-
await this.execSingleFile(resolve(filePath, file), mapInstance)
|
|
178
|
+
await this.execSingleFile(resolve(filePath, file), mapInstance);
|
|
123
179
|
}
|
|
124
180
|
}
|
|
125
181
|
} catch (error) {
|
|
126
|
-
error(error)
|
|
182
|
+
error(error);
|
|
127
183
|
}
|
|
128
184
|
}
|
|
129
185
|
|
|
130
186
|
getTasks(requests) {
|
|
131
|
-
const _tasks = []
|
|
132
|
-
const results = []
|
|
187
|
+
const _tasks = [];
|
|
188
|
+
const results = [];
|
|
133
189
|
|
|
134
190
|
for (let i = 0; i < requests.length; i++) {
|
|
135
|
-
let reqObj = requests[i]
|
|
136
|
-
const {title, failedTitle, successTitle, tasks} = reqObj
|
|
191
|
+
let reqObj = requests[i];
|
|
192
|
+
const { title, failedTitle, successTitle, tasks } = reqObj;
|
|
137
193
|
const task = {
|
|
138
194
|
title: title,
|
|
139
195
|
task: async (ctx, task) => {
|
|
140
|
-
const [err, result] = await safePromise(waterfall(tasks))
|
|
196
|
+
const [err, result] = await safePromise(waterfall(tasks));
|
|
141
197
|
if (err) {
|
|
142
|
-
ctx.error = true
|
|
143
|
-
task.title = failedTitle
|
|
144
|
-
throw err
|
|
198
|
+
ctx.error = true;
|
|
199
|
+
task.title = failedTitle;
|
|
200
|
+
throw err;
|
|
145
201
|
}
|
|
146
|
-
result && results.push(result)
|
|
147
|
-
task.title = successTitle
|
|
148
|
-
return result
|
|
202
|
+
result && results.push(result);
|
|
203
|
+
task.title = successTitle;
|
|
204
|
+
return result;
|
|
149
205
|
},
|
|
150
|
-
}
|
|
151
|
-
_tasks.push(task)
|
|
206
|
+
};
|
|
207
|
+
_tasks.push(task);
|
|
152
208
|
}
|
|
153
|
-
return _tasks
|
|
209
|
+
return _tasks;
|
|
154
210
|
}
|
|
155
211
|
|
|
156
212
|
handleErrors() {
|
|
157
|
-
const mapInstance = getMapInstance()
|
|
158
|
-
const actions = get(actionMapper, mapInstance)
|
|
159
|
-
const actionList = new ActionList(actions)
|
|
213
|
+
const mapInstance = getMapInstance();
|
|
214
|
+
const actions = get(actionMapper, mapInstance);
|
|
215
|
+
const actionList = new ActionList(actions);
|
|
160
216
|
|
|
161
|
-
actionList.addValidators(new ApiError())
|
|
162
|
-
actionList.addValidators(new SchemaValidator())
|
|
163
|
-
actionList.addValidators(new MigrationError())
|
|
164
|
-
actionList.addValidators(new FieldValidator())
|
|
217
|
+
actionList.addValidators(new ApiError());
|
|
218
|
+
actionList.addValidators(new SchemaValidator());
|
|
219
|
+
actionList.addValidators(new MigrationError());
|
|
220
|
+
actionList.addValidators(new FieldValidator());
|
|
165
221
|
|
|
166
|
-
const errors = actionList.validate()
|
|
167
|
-
errorHelper(errors)
|
|
222
|
+
const errors = actionList.validate();
|
|
223
|
+
errorHelper(errors);
|
|
168
224
|
}
|
|
169
225
|
}
|
|
170
226
|
|
|
171
|
-
MigrationCommand.description =
|
|
227
|
+
MigrationCommand.description = "Contentstack migration script.";
|
|
172
228
|
|
|
173
229
|
MigrationCommand.flags = {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
230
|
+
"api-key": flags.string({
|
|
231
|
+
char: "k",
|
|
232
|
+
description: "With this flag add the API key of your stack.",
|
|
233
|
+
dependsOn: ["authtoken"],
|
|
234
|
+
exclusive: ["management-token-alias"],
|
|
235
|
+
}),
|
|
236
|
+
authtoken: flags.boolean({
|
|
237
|
+
char: "A",
|
|
238
|
+
description:
|
|
239
|
+
"Use this flag to use the auth token of the current session. After logging in CLI, an auth token is generated for each new session.",
|
|
240
|
+
dependsOn: ["api-key"],
|
|
241
|
+
exclusive: ["management-token-alias"],
|
|
242
|
+
}),
|
|
243
|
+
"management-token-alias": flags.string({
|
|
244
|
+
char: "a",
|
|
245
|
+
description: "Use this flag to add the management token alias.",
|
|
246
|
+
exclusive: ["authtoken"],
|
|
247
|
+
}), // Add a better description
|
|
248
|
+
filePath: flags.string({
|
|
249
|
+
char: "n",
|
|
250
|
+
description:
|
|
251
|
+
"Use this flag to provide the path of the file of the migration script provided by the user.",
|
|
252
|
+
}),
|
|
253
|
+
branch: flags.string({
|
|
254
|
+
char: "B",
|
|
255
|
+
description:
|
|
256
|
+
"Use this flag to add the branch name where you want to perform the migration.",
|
|
257
|
+
}),
|
|
258
|
+
"config-file": flags.string({
|
|
259
|
+
description: "[optional] Path of the JSON configuration file",
|
|
260
|
+
}),
|
|
261
|
+
config: flags.string({
|
|
262
|
+
description: "[optional] inline configuration, <key1>:<value1>",
|
|
263
|
+
multiple: true,
|
|
264
|
+
}),
|
|
265
|
+
multi: flags.boolean({
|
|
266
|
+
description:
|
|
267
|
+
"This flag helps you to migrate multiple content files in a single instance.",
|
|
268
|
+
}), // Add a better description
|
|
269
|
+
};
|
|
181
270
|
|
|
182
|
-
module.exports = MigrationCommand
|
|
271
|
+
module.exports = MigrationCommand;
|
package/src/config/api-config.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/* eslint-disable camelcase */
|
|
2
|
-
|
|
2
|
+
"use strict";
|
|
3
3
|
|
|
4
|
-
const {CONTENTSTACK_API_KEY, CONTENTSTACK_AUTHTOKEN} = process.env
|
|
5
|
-
const {version} = require(
|
|
4
|
+
const { CONTENTSTACK_API_KEY, CONTENTSTACK_AUTHTOKEN } = process.env;
|
|
5
|
+
const { version } = require("../../package.json");
|
|
6
6
|
module.exports = {
|
|
7
|
-
hostname:
|
|
7
|
+
hostname: "dev9-app.contentstack.com",
|
|
8
8
|
// hostname: 'stag-app.contentstack.com',
|
|
9
|
-
version:
|
|
10
|
-
method:
|
|
9
|
+
version: "/v3",
|
|
10
|
+
method: "GET", // Default Http method
|
|
11
11
|
headers: {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
"Content-Type": "application/json",
|
|
13
|
+
"Content-Length": null,
|
|
14
|
+
"X-User-Agent": `@contentstack-migration/v${version}`,
|
|
15
15
|
authtoken: CONTENTSTACK_AUTHTOKEN,
|
|
16
16
|
api_key: CONTENTSTACK_API_KEY,
|
|
17
17
|
// management_token: CONTENTSTACK_MANAGEMENT_TOKEN
|
|
18
18
|
},
|
|
19
|
-
}
|
|
19
|
+
};
|
package/src/modules/parser.js
CHANGED
|
@@ -14,11 +14,11 @@ const Base = require('./base')
|
|
|
14
14
|
|
|
15
15
|
const {ActionList} = require('../actions')
|
|
16
16
|
// Utils
|
|
17
|
-
const {map: _map, constants} = require('../utils')
|
|
17
|
+
const {map: _map, constants, fsHelper} = require('../utils')
|
|
18
18
|
// map properties
|
|
19
19
|
const {getMapInstance, get} = _map
|
|
20
20
|
// Constants
|
|
21
|
-
const {actionMapper, MANAGEMENT_SDK, MANAGEMENT_TOKEN, AUTH_TOKEN, API_KEY, BRANCH} = constants
|
|
21
|
+
const {actionMapper, MANAGEMENT_SDK, MANAGEMENT_TOKEN, AUTH_TOKEN, API_KEY, BRANCH, MANAGEMENT_CLIENT, SOURCE_BRANCH} = constants
|
|
22
22
|
|
|
23
23
|
class Parser {
|
|
24
24
|
async getMigrationParser(migrationFunc) {
|
|
@@ -33,7 +33,15 @@ class Parser {
|
|
|
33
33
|
const authToken = get(AUTH_TOKEN, mapInstance)
|
|
34
34
|
const apiKey = get(API_KEY, mapInstance)
|
|
35
35
|
const branch = get(BRANCH, mapInstance)
|
|
36
|
-
|
|
36
|
+
const managementAPIClient = get(MANAGEMENT_CLIENT, mapInstance)
|
|
37
|
+
const externalConfigPath = get("config-path", mapInstance)
|
|
38
|
+
const externalConfig = get("config", mapInstance)
|
|
39
|
+
let externalFileConfig
|
|
40
|
+
if (typeof externalConfigPath == "string") {
|
|
41
|
+
externalFileConfig = await fsHelper.readJSONFile(externalConfigPath);
|
|
42
|
+
}
|
|
43
|
+
const config = Object.assign({}, externalFileConfig, externalConfig);
|
|
44
|
+
await migrationFunc({migration, stackSDKInstance, managementAPIClient, managementToken, authToken, apiKey, branch, config})
|
|
37
45
|
} catch (error) {
|
|
38
46
|
if (error instanceof TypeError) {
|
|
39
47
|
if (error.message.includes('is not a function')) {
|
package/src/utils/constants.js
CHANGED
|
@@ -1,219 +1,228 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
|
-
exports.mapObject = new Map()
|
|
3
|
+
exports.mapObject = new Map();
|
|
4
4
|
|
|
5
|
-
exports.version = 3 // TODO: Fetch it from CMA
|
|
5
|
+
exports.version = 3; // TODO: Fetch it from CMA
|
|
6
6
|
|
|
7
|
-
exports.defaultDataType =
|
|
7
|
+
exports.defaultDataType = "text";
|
|
8
8
|
|
|
9
|
-
exports.MANAGEMENT_SDK =
|
|
10
|
-
exports.
|
|
11
|
-
exports.
|
|
12
|
-
exports.
|
|
13
|
-
exports.
|
|
9
|
+
exports.MANAGEMENT_SDK = "MANAGEMENT_SDK";
|
|
10
|
+
exports.MANAGEMENT_CLIENT = "MANAGEMENT_CLIENT";
|
|
11
|
+
exports.MANAGEMENT_SDK = "MANAGEMENT_TOKEN";
|
|
12
|
+
exports.AUTH_TOKEN = "AUTH_TOKEN";
|
|
13
|
+
exports.API_KEY = "API_KEY";
|
|
14
|
+
exports.BRANCH = "BRANCH";
|
|
15
|
+
exports.SOURCE_BRANCH = "SOURCE_BRANCH";
|
|
14
16
|
|
|
15
|
-
exports.data_type =
|
|
16
|
-
exports.mandatory =
|
|
17
|
-
exports._default =
|
|
18
|
-
exports.unique =
|
|
19
|
-
exports.display_name =
|
|
20
|
-
exports.reference_to =
|
|
21
|
-
exports.field_metadata =
|
|
17
|
+
exports.data_type = "data_type";
|
|
18
|
+
exports.mandatory = "mandatory";
|
|
19
|
+
exports._default = "default";
|
|
20
|
+
exports.unique = "unique";
|
|
21
|
+
exports.display_name = "display_name";
|
|
22
|
+
exports.reference_to = "reference_to";
|
|
23
|
+
exports.field_metadata = "field_metadata";
|
|
22
24
|
|
|
23
25
|
exports.actions = {
|
|
24
|
-
CUSTOM_TASK:
|
|
25
|
-
CREATE_CT:
|
|
26
|
-
DELETE_CT:
|
|
27
|
-
EDIT_CT:
|
|
28
|
-
LOCALES:
|
|
29
|
-
EDIT_FIELD:
|
|
30
|
-
DELETE_FIELD:
|
|
31
|
-
MOVE_FIELD:
|
|
32
|
-
}
|
|
26
|
+
CUSTOM_TASK: "CUSTOM_TASK",
|
|
27
|
+
CREATE_CT: "CREATE_CT",
|
|
28
|
+
DELETE_CT: "DELETE_CT",
|
|
29
|
+
EDIT_CT: "EDIT_CT",
|
|
30
|
+
LOCALES: "LOCALES",
|
|
31
|
+
EDIT_FIELD: "EDIT_FIELD",
|
|
32
|
+
DELETE_FIELD: "DELETE_FIELD",
|
|
33
|
+
MOVE_FIELD: "MOVE_FIELD",
|
|
34
|
+
};
|
|
33
35
|
|
|
34
36
|
// Http call max retry
|
|
35
|
-
exports.MAX_RETRY = 3
|
|
37
|
+
exports.MAX_RETRY = 3;
|
|
36
38
|
|
|
37
39
|
// This key holds the value for http objects in map
|
|
38
|
-
exports.requests =
|
|
40
|
+
exports.requests = "REQUESTS";
|
|
39
41
|
|
|
40
|
-
exports.limit = 1 // Limit for concurrent tasks executed parallely
|
|
42
|
+
exports.limit = 1; // Limit for concurrent tasks executed parallely
|
|
41
43
|
|
|
42
|
-
exports.nonWritableMethods = [
|
|
44
|
+
exports.nonWritableMethods = ["GET", "DELETE"];
|
|
43
45
|
|
|
44
|
-
exports.ContentType =
|
|
45
|
-
exports.Entry =
|
|
46
|
+
exports.ContentType = "Content type";
|
|
47
|
+
exports.Entry = "Entry";
|
|
46
48
|
|
|
47
49
|
exports.errorMessageHandler = {
|
|
48
|
-
POST:
|
|
49
|
-
GET:
|
|
50
|
-
PUT:
|
|
51
|
-
DELETE:
|
|
52
|
-
}
|
|
50
|
+
POST: "saving",
|
|
51
|
+
GET: "fetching",
|
|
52
|
+
PUT: "updating",
|
|
53
|
+
DELETE: "deleting",
|
|
54
|
+
};
|
|
53
55
|
|
|
54
56
|
exports.successMessageHandler = {
|
|
55
|
-
POST:
|
|
56
|
-
GET:
|
|
57
|
-
PUT:
|
|
58
|
-
DELETE:
|
|
59
|
-
}
|
|
57
|
+
POST: "saved",
|
|
58
|
+
GET: "fetched",
|
|
59
|
+
PUT: "updated",
|
|
60
|
+
DELETE: "deleted",
|
|
61
|
+
};
|
|
60
62
|
// map key
|
|
61
|
-
exports.actionMapper =
|
|
63
|
+
exports.actionMapper = "actions";
|
|
62
64
|
|
|
63
|
-
exports.batchLimit = 20
|
|
65
|
+
exports.batchLimit = 20;
|
|
64
66
|
|
|
65
|
-
exports.contentTypeProperties = [
|
|
67
|
+
exports.contentTypeProperties = [
|
|
68
|
+
"description",
|
|
69
|
+
"title",
|
|
70
|
+
"uid",
|
|
71
|
+
"options",
|
|
72
|
+
"force",
|
|
73
|
+
"schema",
|
|
74
|
+
];
|
|
66
75
|
|
|
67
76
|
exports.validationAction = {
|
|
68
|
-
create:
|
|
69
|
-
edit:
|
|
70
|
-
customTask:
|
|
71
|
-
transformEntries:
|
|
72
|
-
deriveLinkedEntries:
|
|
73
|
-
transformEntriesToType:
|
|
74
|
-
typeError:
|
|
75
|
-
apiError:
|
|
76
|
-
schema:
|
|
77
|
-
__migrationError:
|
|
78
|
-
field:
|
|
79
|
-
}
|
|
77
|
+
create: "create",
|
|
78
|
+
edit: "edit",
|
|
79
|
+
customTask: "customTask",
|
|
80
|
+
transformEntries: "transformEntries",
|
|
81
|
+
deriveLinkedEntries: "deriveLinkedEntries",
|
|
82
|
+
transformEntriesToType: "transformEntriesToType",
|
|
83
|
+
typeError: "typeError",
|
|
84
|
+
apiError: "apiError",
|
|
85
|
+
schema: "schema",
|
|
86
|
+
__migrationError: "migrationError",
|
|
87
|
+
field: "field",
|
|
88
|
+
};
|
|
80
89
|
|
|
81
90
|
exports.transformEntriesProperties = [
|
|
82
91
|
{
|
|
83
|
-
name:
|
|
84
|
-
type:
|
|
92
|
+
name: "contentType",
|
|
93
|
+
type: "string",
|
|
85
94
|
mandatory: true,
|
|
86
95
|
},
|
|
87
96
|
{
|
|
88
|
-
name:
|
|
89
|
-
type:
|
|
97
|
+
name: "from",
|
|
98
|
+
type: "array",
|
|
90
99
|
mandatory: true,
|
|
91
100
|
},
|
|
92
101
|
{
|
|
93
|
-
name:
|
|
94
|
-
type:
|
|
102
|
+
name: "to",
|
|
103
|
+
type: "array",
|
|
95
104
|
mandatory: true,
|
|
96
105
|
},
|
|
97
106
|
{
|
|
98
|
-
name:
|
|
99
|
-
type:
|
|
107
|
+
name: "shouldPublish",
|
|
108
|
+
type: "boolean",
|
|
100
109
|
mandatory: false,
|
|
101
|
-
dependsOn:
|
|
110
|
+
dependsOn: "environments",
|
|
102
111
|
},
|
|
103
112
|
{
|
|
104
|
-
name:
|
|
105
|
-
type:
|
|
113
|
+
name: "environments",
|
|
114
|
+
type: "array",
|
|
106
115
|
mandatory: false,
|
|
107
116
|
},
|
|
108
117
|
{
|
|
109
|
-
name:
|
|
110
|
-
type:
|
|
118
|
+
name: "transformEntryForLocale",
|
|
119
|
+
type: "function",
|
|
111
120
|
mandatory: true,
|
|
112
121
|
},
|
|
113
|
-
]
|
|
122
|
+
];
|
|
114
123
|
|
|
115
124
|
exports.deriveLinkedEntriesProperties = [
|
|
116
125
|
{
|
|
117
|
-
name:
|
|
118
|
-
type:
|
|
126
|
+
name: "contentType",
|
|
127
|
+
type: "string",
|
|
119
128
|
mandatory: true,
|
|
120
129
|
},
|
|
121
130
|
{
|
|
122
|
-
name:
|
|
123
|
-
type:
|
|
131
|
+
name: "derivedContentType",
|
|
132
|
+
type: "string",
|
|
124
133
|
mandatory: true,
|
|
125
134
|
},
|
|
126
135
|
{
|
|
127
|
-
name:
|
|
128
|
-
type:
|
|
136
|
+
name: "from",
|
|
137
|
+
type: "array",
|
|
129
138
|
mandatory: true,
|
|
130
139
|
},
|
|
131
140
|
{
|
|
132
|
-
name:
|
|
133
|
-
type:
|
|
141
|
+
name: "toReferenceField",
|
|
142
|
+
type: "string",
|
|
134
143
|
mandatory: true,
|
|
135
144
|
},
|
|
136
145
|
{
|
|
137
|
-
name:
|
|
138
|
-
type:
|
|
146
|
+
name: "derivedFields",
|
|
147
|
+
type: "array",
|
|
139
148
|
mandatory: true,
|
|
140
149
|
},
|
|
141
150
|
{
|
|
142
|
-
name:
|
|
143
|
-
type:
|
|
151
|
+
name: "identityKey",
|
|
152
|
+
type: "function",
|
|
144
153
|
mandatory: true,
|
|
145
154
|
},
|
|
146
155
|
{
|
|
147
|
-
name:
|
|
148
|
-
type:
|
|
156
|
+
name: "shouldPublish",
|
|
157
|
+
type: "boolean",
|
|
149
158
|
mandatory: false,
|
|
150
|
-
dependsOn:
|
|
159
|
+
dependsOn: "environments",
|
|
151
160
|
},
|
|
152
161
|
{
|
|
153
|
-
name:
|
|
154
|
-
type:
|
|
162
|
+
name: "environments",
|
|
163
|
+
type: "array",
|
|
155
164
|
mandatory: false,
|
|
156
165
|
},
|
|
157
166
|
{
|
|
158
|
-
name:
|
|
159
|
-
type:
|
|
167
|
+
name: "deriveEntryForLocale",
|
|
168
|
+
type: "function",
|
|
160
169
|
mandatory: true,
|
|
161
170
|
},
|
|
162
|
-
]
|
|
171
|
+
];
|
|
163
172
|
|
|
164
173
|
exports.transformEntriesToTypeProperties = [
|
|
165
174
|
{
|
|
166
|
-
name:
|
|
167
|
-
type:
|
|
175
|
+
name: "sourceContentType",
|
|
176
|
+
type: "string",
|
|
168
177
|
mandatory: true,
|
|
169
178
|
},
|
|
170
179
|
{
|
|
171
|
-
name:
|
|
172
|
-
type:
|
|
180
|
+
name: "targetContentType",
|
|
181
|
+
type: "string",
|
|
173
182
|
mandatory: true,
|
|
174
183
|
},
|
|
175
184
|
{
|
|
176
|
-
name:
|
|
177
|
-
type:
|
|
185
|
+
name: "from",
|
|
186
|
+
type: "array",
|
|
178
187
|
mandatory: true,
|
|
179
188
|
},
|
|
180
189
|
{
|
|
181
|
-
name:
|
|
182
|
-
type:
|
|
190
|
+
name: "shouldPublish",
|
|
191
|
+
type: "boolean",
|
|
183
192
|
mandatory: false,
|
|
184
|
-
dependsOn:
|
|
193
|
+
dependsOn: "environments",
|
|
185
194
|
},
|
|
186
195
|
{
|
|
187
|
-
name:
|
|
188
|
-
type:
|
|
196
|
+
name: "environments",
|
|
197
|
+
type: "array",
|
|
189
198
|
mandatory: false,
|
|
190
199
|
},
|
|
191
200
|
{
|
|
192
|
-
name:
|
|
193
|
-
type:
|
|
201
|
+
name: "removeOldEntries",
|
|
202
|
+
type: "boolean",
|
|
194
203
|
mandatory: false,
|
|
195
204
|
},
|
|
196
205
|
{
|
|
197
|
-
name:
|
|
198
|
-
type:
|
|
206
|
+
name: "identityKey",
|
|
207
|
+
type: "function",
|
|
199
208
|
mandatory: true,
|
|
200
209
|
},
|
|
201
210
|
{
|
|
202
|
-
name:
|
|
203
|
-
type:
|
|
211
|
+
name: "transformEntryForLocale",
|
|
212
|
+
type: "function",
|
|
204
213
|
mandatory: true,
|
|
205
214
|
},
|
|
206
|
-
]
|
|
215
|
+
];
|
|
207
216
|
|
|
208
217
|
exports.SDK_ACTIONS = {
|
|
209
|
-
CONTENTTYPE_GET:
|
|
210
|
-
CONTENTTYPE_POST:
|
|
211
|
-
CONTENTTYPE_DELETE:
|
|
212
|
-
CONTENTTYPE_PUT:
|
|
213
|
-
LOCALES_GET:
|
|
214
|
-
ENTRY_GET:
|
|
215
|
-
ENTRY_POST:
|
|
216
|
-
ENTRY_PUT:
|
|
217
|
-
ENTRY_DELETE:
|
|
218
|
-
ENTRY_PUBLISH:
|
|
219
|
-
}
|
|
218
|
+
CONTENTTYPE_GET: "CONTENTTYPE_GET",
|
|
219
|
+
CONTENTTYPE_POST: "CONTENTTYPE_POST",
|
|
220
|
+
CONTENTTYPE_DELETE: "CONTENTTYPE_GET",
|
|
221
|
+
CONTENTTYPE_PUT: "CONTENTTYPE_PUT",
|
|
222
|
+
LOCALES_GET: "LOCALES_GET",
|
|
223
|
+
ENTRY_GET: "ENTRY_GET",
|
|
224
|
+
ENTRY_POST: "ENTRY_POST",
|
|
225
|
+
ENTRY_PUT: "ENTRY_PUT",
|
|
226
|
+
ENTRY_DELETE: "ENTRY_DELETE",
|
|
227
|
+
ENTRY_PUBLISH: "ENTRY_PUBLISH",
|
|
228
|
+
};
|
package/src/utils/fs-helper.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {existsSync, mkdirSync, readFileSync} = require('fs')
|
|
3
|
+
const { existsSync, mkdirSync, readFileSync, readFile } = require('fs')
|
|
4
|
+
const path = require("path")
|
|
4
5
|
|
|
5
6
|
exports.makeDir = dirname => {
|
|
6
7
|
!this.existsSync(dirname) && mkdirSync(dirname)
|
|
@@ -12,3 +13,16 @@ exports.readFile = filePath => {
|
|
|
12
13
|
if (!existsSync(filePath)) throw new Error('File does not exist')
|
|
13
14
|
return readFileSync(filePath, 'utf-8')
|
|
14
15
|
}
|
|
16
|
+
|
|
17
|
+
exports.readJSONFile = (filePath) => {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
filePath = path.resolve(filePath);
|
|
20
|
+
readFile(filePath, 'utf-8', (error, data) => {
|
|
21
|
+
if (error) {
|
|
22
|
+
reject(error);
|
|
23
|
+
} else {
|
|
24
|
+
resolve(JSON.parse(data));
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
};
|