@contentstack/cli-cm-import 2.0.0-beta.2 → 2.0.0-beta.4
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/LICENSE +1 -1
- package/README.md +50 -96
- package/lib/commands/cm/stacks/import.d.ts +0 -1
- package/lib/commands/cm/stacks/import.js +9 -42
- package/lib/config/index.js +7 -0
- package/lib/import/module-importer.js +0 -4
- package/lib/import/modules/composable-studio.d.ts +44 -0
- package/lib/import/modules/composable-studio.js +234 -0
- package/lib/import/modules/content-types.d.ts +2 -0
- package/lib/import/modules/content-types.js +48 -9
- package/lib/import/modules/entries.d.ts +2 -0
- package/lib/import/modules/entries.js +19 -10
- package/lib/import/modules/global-fields.d.ts +1 -1
- package/lib/import/modules/global-fields.js +8 -8
- package/lib/import/modules/locales.d.ts +1 -1
- package/lib/import/modules/locales.js +6 -6
- package/lib/import/modules/marketplace-apps.js +2 -2
- package/lib/import/modules/personalize.js +1 -1
- package/lib/import/modules/stack.js +1 -1
- package/lib/import/modules/variant-entries.js +2 -2
- package/lib/import/modules/workflows.js +1 -1
- package/lib/types/default-config.d.ts +6 -0
- package/lib/types/import-config.d.ts +0 -1
- package/lib/types/index.d.ts +37 -11
- package/lib/utils/asset-helper.js +1 -1
- package/lib/utils/common-helper.d.ts +1 -1
- package/lib/utils/common-helper.js +6 -6
- package/lib/utils/content-type-helper.d.ts +1 -1
- package/lib/utils/content-type-helper.js +3 -3
- package/lib/utils/extension-helper.js +1 -1
- package/lib/utils/file-helper.js +1 -1
- package/lib/utils/import-config-handler.js +7 -11
- package/lib/utils/import-path-resolver.js +2 -8
- package/lib/utils/logger.d.ts +1 -1
- package/lib/utils/logger.js +2 -2
- package/lib/utils/login-handler.d.ts +1 -1
- package/lib/utils/login-handler.js +4 -4
- package/lib/utils/marketplace-app-helper.js +9 -6
- package/lib/utils/taxonomies-helper.js +1 -1
- package/messages/index.json +10 -1
- package/oclif.manifest.json +4 -48
- package/package.json +9 -13
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ $ npm install -g @contentstack/cli-cm-import
|
|
|
47
47
|
$ csdx COMMAND
|
|
48
48
|
running command...
|
|
49
49
|
$ csdx (--version)
|
|
50
|
-
@contentstack/cli-cm-import/2.0.0-beta.
|
|
50
|
+
@contentstack/cli-cm-import/2.0.0-beta.4 linux-x64 node-v22.22.0
|
|
51
51
|
$ csdx --help [COMMAND]
|
|
52
52
|
USAGE
|
|
53
53
|
$ csdx COMMAND
|
|
@@ -58,127 +58,81 @@ USAGE
|
|
|
58
58
|
# Commands
|
|
59
59
|
|
|
60
60
|
<!-- commands -->
|
|
61
|
-
* [`csdx cm:stacks:import [
|
|
62
|
-
* [`csdx cm:stacks:import [-c <value>] [-k <value>] [-d <value>] [-a <value>] [--module <value>] [--backup-dir <value>] [--branch <value>] [--import-webhook-status disable|current]`](#csdx-cmstacksimport--c-value--k-value--d-value--a-value---module-value---backup-dir-value---branch-value---import-webhook-status-disablecurrent)
|
|
61
|
+
* [`csdx cm:stacks:import [--config <value>] [--stack-api-key <value>] [--data-dir <value>] [--alias <value>] [--module <value>] [--backup-dir <value>] [--branch <value>] [--import-webhook-status disable|current]`](#csdx-cmstacksimport---config-value---stack-api-key-value---data-dir-value---alias-value---module-value---backup-dir-value---branch-value---import-webhook-status-disablecurrent)
|
|
63
62
|
|
|
64
|
-
## `csdx cm:stacks:import [
|
|
63
|
+
## `csdx cm:stacks:import [--config <value>] [--stack-api-key <value>] [--data-dir <value>] [--alias <value>] [--module <value>] [--backup-dir <value>] [--branch <value>] [--import-webhook-status disable|current]`
|
|
65
64
|
|
|
66
65
|
Import content from a stack
|
|
67
66
|
|
|
68
67
|
```
|
|
69
68
|
USAGE
|
|
70
|
-
$ csdx cm:
|
|
71
|
-
[--backup-dir <value>] [--branch <value>] [--import-webhook-status disable|current]
|
|
69
|
+
$ csdx cm:stacks:import [--config <value>] [--stack-api-key <value>] [--data-dir <value>] [--alias <value>]
|
|
70
|
+
[--module <value>] [--backup-dir <value>] [--branch <value>] [--import-webhook-status disable|current]
|
|
72
71
|
|
|
73
72
|
FLAGS
|
|
74
|
-
-
|
|
75
|
-
|
|
76
|
-
main branch.
|
|
77
|
-
-a, --alias=<value> The management token of the destination stack where you will import the
|
|
78
|
-
content.
|
|
79
|
-
-b, --backup-dir=<value> [optional] Backup directory name when using specific module.
|
|
80
|
-
-c, --config=<value> [optional] The path of the configuration JSON file containing all the options
|
|
81
|
-
for a single run.
|
|
82
|
-
-d, --data-dir=<value> The path or the location in your file system where the content, you intend to
|
|
83
|
-
import, is stored. For example, -d "C:\Users\Name\Desktop\cli\content". If the
|
|
84
|
-
export folder has branches involved, then the path should point till the
|
|
85
|
-
particular branch. For example, “-d
|
|
86
|
-
"C:\Users\Name\Desktop\cli\content\branch_name"
|
|
87
|
-
-k, --stack-api-key=<value> API Key of the target stack
|
|
88
|
-
-m, --module=<value> [optional] Specify the module to import into the target stack. If not
|
|
89
|
-
specified, the import command will import all the modules into the stack. The
|
|
90
|
-
available modules are assets, content-types, entries, environments,
|
|
91
|
-
extensions, marketplace-apps, global-fields, labels, locales, webhooks,
|
|
92
|
-
workflows, custom-roles, personalize projects, and taxonomies.
|
|
93
|
-
-y, --yes [optional] Force override all Marketplace prompts.
|
|
94
|
-
--branch-alias=<value> Specify the branch alias where you want to import your content. If not
|
|
95
|
-
specified, the content is imported into the main branch by default.
|
|
96
|
-
--exclude-global-modules Excludes the branch-independent module from the import operation.
|
|
97
|
-
--import-webhook-status=<option> [default: disable] [default: disable] (optional) This webhook state keeps the
|
|
98
|
-
same state of webhooks as the source stack. <options: disable|current>
|
|
99
|
-
<options: disable|current>
|
|
100
|
-
--personalize-project-name=<value> (optional) Provide a unique name for the Personalize project.
|
|
101
|
-
--replace-existing Replaces the existing module in the target stack.
|
|
102
|
-
--skip-app-recreation (optional) Skips the recreation of private apps if they already exist.
|
|
103
|
-
--skip-assets-publish Skips asset publishing during the import process.
|
|
104
|
-
--skip-audit Skips the audit fix that occurs during an import operation.
|
|
105
|
-
--skip-entries-publish Skips entry publishing during the import process
|
|
106
|
-
--skip-existing Skips the module exists warning messages.
|
|
73
|
+
-a, --alias=<value>
|
|
74
|
+
The management token of the destination stack where you will import the content.
|
|
107
75
|
|
|
108
|
-
|
|
109
|
-
|
|
76
|
+
-c, --config=<value>
|
|
77
|
+
[optional] The path of the configuration JSON file containing all the options for a single run.
|
|
110
78
|
|
|
111
|
-
|
|
112
|
-
|
|
79
|
+
-d, --data-dir=<value>
|
|
80
|
+
The path or the location in your file system where the content, you intend to import, is stored. For example, -d
|
|
81
|
+
"C:\Users\Name\Desktop\cli\content". If the export folder has branches involved, then the path should point till the
|
|
82
|
+
particular branch. For example, “-d "C:\Users\Name\Desktop\cli\content\branch_name"
|
|
113
83
|
|
|
114
|
-
|
|
115
|
-
|
|
84
|
+
-k, --stack-api-key=<value>
|
|
85
|
+
API Key of the target stack
|
|
116
86
|
|
|
117
|
-
|
|
87
|
+
-y, --yes
|
|
88
|
+
[optional] Force override all Marketplace prompts.
|
|
118
89
|
|
|
119
|
-
|
|
90
|
+
--backup-dir=<value>
|
|
91
|
+
[optional] Backup directory name when using specific module.
|
|
120
92
|
|
|
121
|
-
|
|
93
|
+
--branch=<value>
|
|
94
|
+
The name of the branch where you want to import your content. If you don't mention the branch name, then by default
|
|
95
|
+
the content will be imported to the main branch.
|
|
122
96
|
|
|
123
|
-
|
|
97
|
+
--branch-alias=<value>
|
|
98
|
+
Specify the branch alias where you want to import your content. If not specified, the content is imported into the
|
|
99
|
+
main branch by default.
|
|
124
100
|
|
|
125
|
-
|
|
101
|
+
--exclude-global-modules
|
|
102
|
+
Excludes the branch-independent module from the import operation.
|
|
126
103
|
|
|
127
|
-
|
|
104
|
+
--import-webhook-status=<option>
|
|
105
|
+
[default: disable] [default: disable] (optional) This webhook state keeps the same state of webhooks as the source
|
|
106
|
+
stack. <options: disable|current>
|
|
107
|
+
<options: disable|current>
|
|
128
108
|
|
|
129
|
-
|
|
130
|
-
|
|
109
|
+
--module=<value>
|
|
110
|
+
[optional] Specify the module to import into the target stack. If not specified, the import command will import all
|
|
111
|
+
the modules into the stack. The available modules are assets, content-types, entries, environments, extensions,
|
|
112
|
+
marketplace-apps, global-fields, labels, locales, webhooks, workflows, custom-roles, personalize projects,
|
|
113
|
+
taxonomies, and composable-studio.
|
|
131
114
|
|
|
132
|
-
|
|
115
|
+
--personalize-project-name=<value>
|
|
116
|
+
(optional) Provide a unique name for the Personalize project.
|
|
133
117
|
|
|
134
|
-
|
|
118
|
+
--replace-existing
|
|
119
|
+
Replaces the existing module in the target stack.
|
|
135
120
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
$ csdx cm:stacks:import [-c <value>] [-k <value>] [-d <value>] [-a <value>] [--module <value>] [--backup-dir
|
|
139
|
-
<value>] [--branch <value>] [--import-webhook-status disable|current]
|
|
121
|
+
--skip-assets-publish
|
|
122
|
+
Skips asset publishing during the import process.
|
|
140
123
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
for a single run.
|
|
150
|
-
-d, --data-dir=<value> The path or the location in your file system where the content, you intend to
|
|
151
|
-
import, is stored. For example, -d "C:\Users\Name\Desktop\cli\content". If the
|
|
152
|
-
export folder has branches involved, then the path should point till the
|
|
153
|
-
particular branch. For example, “-d
|
|
154
|
-
"C:\Users\Name\Desktop\cli\content\branch_name"
|
|
155
|
-
-k, --stack-api-key=<value> API Key of the target stack
|
|
156
|
-
-m, --module=<value> [optional] Specify the module to import into the target stack. If not
|
|
157
|
-
specified, the import command will import all the modules into the stack. The
|
|
158
|
-
available modules are assets, content-types, entries, environments,
|
|
159
|
-
extensions, marketplace-apps, global-fields, labels, locales, webhooks,
|
|
160
|
-
workflows, custom-roles, personalize projects, and taxonomies.
|
|
161
|
-
-y, --yes [optional] Force override all Marketplace prompts.
|
|
162
|
-
--branch-alias=<value> Specify the branch alias where you want to import your content. If not
|
|
163
|
-
specified, the content is imported into the main branch by default.
|
|
164
|
-
--exclude-global-modules Excludes the branch-independent module from the import operation.
|
|
165
|
-
--import-webhook-status=<option> [default: disable] [default: disable] (optional) This webhook state keeps the
|
|
166
|
-
same state of webhooks as the source stack. <options: disable|current>
|
|
167
|
-
<options: disable|current>
|
|
168
|
-
--personalize-project-name=<value> (optional) Provide a unique name for the Personalize project.
|
|
169
|
-
--replace-existing Replaces the existing module in the target stack.
|
|
170
|
-
--skip-app-recreation (optional) Skips the recreation of private apps if they already exist.
|
|
171
|
-
--skip-assets-publish Skips asset publishing during the import process.
|
|
172
|
-
--skip-audit Skips the audit fix that occurs during an import operation.
|
|
173
|
-
--skip-entries-publish Skips entry publishing during the import process
|
|
174
|
-
--skip-existing Skips the module exists warning messages.
|
|
124
|
+
--skip-audit
|
|
125
|
+
Skips the audit fix that occurs during an import operation.
|
|
126
|
+
|
|
127
|
+
--skip-entries-publish
|
|
128
|
+
Skips entry publishing during the import process
|
|
129
|
+
|
|
130
|
+
--skip-existing
|
|
131
|
+
Skips the module exists warning messages.
|
|
175
132
|
|
|
176
133
|
DESCRIPTION
|
|
177
134
|
Import content from a stack
|
|
178
135
|
|
|
179
|
-
ALIASES
|
|
180
|
-
$ csdx cm:import
|
|
181
|
-
|
|
182
136
|
EXAMPLES
|
|
183
137
|
$ csdx cm:stacks:import --stack-api-key <stack_api_key> --data-dir <path/of/export/destination/dir>
|
|
184
138
|
|
|
@@ -6,6 +6,7 @@ const import_1 = require("../../../import");
|
|
|
6
6
|
const utils_1 = require("../../../utils");
|
|
7
7
|
class ImportCommand extends cli_command_1.Command {
|
|
8
8
|
async run() {
|
|
9
|
+
var _a, _b;
|
|
9
10
|
// setup import config
|
|
10
11
|
// initialize the importer
|
|
11
12
|
// start import
|
|
@@ -15,6 +16,7 @@ class ImportCommand extends cli_command_1.Command {
|
|
|
15
16
|
const { flags } = await this.parse(ImportCommand);
|
|
16
17
|
importConfig = await (0, utils_1.setupImportConfig)(flags);
|
|
17
18
|
// Prepare the context object
|
|
19
|
+
(0, cli_utilities_1.createLogContext)(((_b = (_a = this.context) === null || _a === void 0 ? void 0 : _a.info) === null || _b === void 0 ? void 0 : _b.command) || 'cm:stacks:export', importConfig.apiKey, importConfig.authenticationMethod);
|
|
18
20
|
const context = this.createImportContext(importConfig.apiKey, importConfig.authenticationMethod);
|
|
19
21
|
importConfig.context = Object.assign({}, context);
|
|
20
22
|
//log.info(`Using Cli Version: ${this.context?.cliVersion}`, importConfig.context);
|
|
@@ -25,6 +27,8 @@ class ImportCommand extends cli_command_1.Command {
|
|
|
25
27
|
importConfig.developerHubBaseUrl = this.developerHubUrl;
|
|
26
28
|
if (this.personalizeUrl)
|
|
27
29
|
importConfig.modules.personalize.baseURL[importConfig.region.name] = this.personalizeUrl;
|
|
30
|
+
if (this.composableStudioUrl)
|
|
31
|
+
importConfig.modules['composable-studio'].apiBaseUrl = this.composableStudioUrl;
|
|
28
32
|
const managementAPIClient = await (0, cli_utilities_1.managementSDKClient)(importConfig);
|
|
29
33
|
if (flags.branch) {
|
|
30
34
|
cli_utilities_1.CLIProgressManager.initializeGlobalSummary(`IMPORT-${flags.branch}`, flags.branch, `Importing content into "${flags.branch}" branch...`);
|
|
@@ -35,12 +39,7 @@ class ImportCommand extends cli_command_1.Command {
|
|
|
35
39
|
const moduleImporter = new import_1.ModuleImporter(managementAPIClient, importConfig);
|
|
36
40
|
const result = await moduleImporter.start();
|
|
37
41
|
backupDir = importConfig.backupDir;
|
|
38
|
-
|
|
39
|
-
const successMessage = importConfig.stackName
|
|
40
|
-
? `Successfully imported the content to the stack named ${importConfig.stackName} with the API key ${importConfig.apiKey} .`
|
|
41
|
-
: `The content has been imported to the stack ${importConfig.apiKey} successfully!`;
|
|
42
|
-
cli_utilities_1.log.success(successMessage, importConfig.context);
|
|
43
|
-
}
|
|
42
|
+
//Note: Final summary is now handled by summary manager
|
|
44
43
|
cli_utilities_1.CLIProgressManager.printGlobalSummary();
|
|
45
44
|
this.logSuccessAndBackupMessages(backupDir, importConfig);
|
|
46
45
|
// Clear progress module setting now that import is complete
|
|
@@ -92,6 +91,7 @@ class ImportCommand extends cli_command_1.Command {
|
|
|
92
91
|
command: ((_b = (_a = this.context) === null || _a === void 0 ? void 0 : _a.info) === null || _b === void 0 ? void 0 : _b.command) || 'cm:stacks:import',
|
|
93
92
|
module: '',
|
|
94
93
|
userId: cli_utilities_1.configHandler.get('userUid') || '',
|
|
94
|
+
email: cli_utilities_1.configHandler.get('email') || '',
|
|
95
95
|
sessionId: (_c = this.context) === null || _c === void 0 ? void 0 : _c.sessionId,
|
|
96
96
|
apiKey: apiKey || '',
|
|
97
97
|
orgId: cli_utilities_1.configHandler.get('oauthOrgUid') || '',
|
|
@@ -116,21 +116,10 @@ ImportCommand.flags = {
|
|
|
116
116
|
char: 'c',
|
|
117
117
|
description: '[optional] The path of the configuration JSON file containing all the options for a single run.',
|
|
118
118
|
}),
|
|
119
|
-
'stack-uid': cli_utilities_1.flags.string({
|
|
120
|
-
char: 's',
|
|
121
|
-
description: 'API key of the target stack.',
|
|
122
|
-
hidden: true,
|
|
123
|
-
parse: (0, cli_utilities_1.printFlagDeprecation)(['-s', '--stack-uid'], ['-k', '--stack-api-key']),
|
|
124
|
-
}),
|
|
125
119
|
'stack-api-key': cli_utilities_1.flags.string({
|
|
126
120
|
char: 'k',
|
|
127
121
|
description: 'API Key of the target stack',
|
|
128
122
|
}),
|
|
129
|
-
data: cli_utilities_1.flags.string({
|
|
130
|
-
description: 'path and location where data is stored',
|
|
131
|
-
hidden: true,
|
|
132
|
-
parse: (0, cli_utilities_1.printFlagDeprecation)(['--data'], ['--data-dir']),
|
|
133
|
-
}),
|
|
134
123
|
'data-dir': cli_utilities_1.flags.string({
|
|
135
124
|
char: 'd',
|
|
136
125
|
description: `The path or the location in your file system where the content, you intend to import, is stored. For example, -d "C:\\Users\\Name\\Desktop\\cli\\content". If the export folder has branches involved, then the path should point till the particular branch. For example, “-d "C:\\Users\\Name\\Desktop\\cli\\content\\branch_name"`,
|
|
@@ -139,36 +128,19 @@ ImportCommand.flags = {
|
|
|
139
128
|
char: 'a',
|
|
140
129
|
description: 'The management token of the destination stack where you will import the content.',
|
|
141
130
|
}),
|
|
142
|
-
'management-token-alias': cli_utilities_1.flags.string({
|
|
143
|
-
description: 'alias of the management token',
|
|
144
|
-
hidden: true,
|
|
145
|
-
parse: (0, cli_utilities_1.printFlagDeprecation)(['--management-token-alias'], ['-a', '--alias']),
|
|
146
|
-
}),
|
|
147
|
-
'auth-token': cli_utilities_1.flags.boolean({
|
|
148
|
-
char: 'A',
|
|
149
|
-
description: 'to use auth token',
|
|
150
|
-
hidden: true,
|
|
151
|
-
parse: (0, cli_utilities_1.printFlagDeprecation)(['-A', '--auth-token']),
|
|
152
|
-
}),
|
|
153
131
|
module: cli_utilities_1.flags.string({
|
|
154
132
|
required: false,
|
|
155
|
-
|
|
156
|
-
description: '[optional] Specify the module to import into the target stack. If not specified, the import command will import all the modules into the stack. The available modules are assets, content-types, entries, environments, extensions, marketplace-apps, global-fields, labels, locales, webhooks, workflows, custom-roles, personalize projects, and taxonomies.',
|
|
157
|
-
parse: (0, cli_utilities_1.printFlagDeprecation)(['-m'], ['--module']),
|
|
133
|
+
description: '[optional] Specify the module to import into the target stack. If not specified, the import command will import all the modules into the stack. The available modules are assets, content-types, entries, environments, extensions, marketplace-apps, global-fields, labels, locales, webhooks, workflows, custom-roles, personalize projects, taxonomies, and composable-studio.',
|
|
158
134
|
}),
|
|
159
135
|
'backup-dir': cli_utilities_1.flags.string({
|
|
160
|
-
char: 'b',
|
|
161
136
|
description: '[optional] Backup directory name when using specific module.',
|
|
162
|
-
parse: (0, cli_utilities_1.printFlagDeprecation)(['-b'], ['--backup-dir']),
|
|
163
137
|
}),
|
|
164
138
|
branch: cli_utilities_1.flags.string({
|
|
165
|
-
char: 'B',
|
|
166
139
|
description: "The name of the branch where you want to import your content. If you don't mention the branch name, then by default the content will be imported to the main branch.",
|
|
167
|
-
parse: (0, cli_utilities_1.printFlagDeprecation)(['-B'], ['--branch']),
|
|
168
140
|
exclusive: ['branch-alias'],
|
|
169
141
|
}),
|
|
170
142
|
'branch-alias': cli_utilities_1.flags.string({
|
|
171
|
-
description:
|
|
143
|
+
description: 'Specify the branch alias where you want to import your content. If not specified, the content is imported into the main branch by default.',
|
|
172
144
|
exclusive: ['branch'],
|
|
173
145
|
}),
|
|
174
146
|
'import-webhook-status': cli_utilities_1.flags.string({
|
|
@@ -182,10 +154,6 @@ ImportCommand.flags = {
|
|
|
182
154
|
required: false,
|
|
183
155
|
description: '[optional] Force override all Marketplace prompts.',
|
|
184
156
|
}),
|
|
185
|
-
'skip-app-recreation': cli_utilities_1.flags.boolean({
|
|
186
|
-
description: '(optional) Skips the recreation of private apps if they already exist.',
|
|
187
|
-
parse: (0, cli_utilities_1.printFlagDeprecation)(['--skip-app-recreation']),
|
|
188
|
-
}),
|
|
189
157
|
'replace-existing': cli_utilities_1.flags.boolean({
|
|
190
158
|
required: false,
|
|
191
159
|
description: 'Replaces the existing module in the target stack.',
|
|
@@ -215,5 +183,4 @@ ImportCommand.flags = {
|
|
|
215
183
|
default: false,
|
|
216
184
|
}),
|
|
217
185
|
};
|
|
218
|
-
ImportCommand.
|
|
219
|
-
ImportCommand.usage = 'cm:stacks:import [-c <value>] [-k <value>] [-d <value>] [-a <value>] [--module <value>] [--backup-dir <value>] [--branch <value>] [--import-webhook-status disable|current]';
|
|
186
|
+
ImportCommand.usage = 'cm:stacks:import [--config <value>] [--stack-api-key <value>] [--data-dir <value>] [--alias <value>] [--module <value>] [--backup-dir <value>] [--branch <value>] [--import-webhook-status disable|current]';
|
package/lib/config/index.js
CHANGED
|
@@ -33,6 +33,7 @@ const config = {
|
|
|
33
33
|
'stack',
|
|
34
34
|
'assets',
|
|
35
35
|
'taxonomies',
|
|
36
|
+
'composable-studio',
|
|
36
37
|
'extensions',
|
|
37
38
|
'marketplace-apps',
|
|
38
39
|
'global-fields',
|
|
@@ -199,6 +200,12 @@ const config = {
|
|
|
199
200
|
locale: 'en-us',
|
|
200
201
|
},
|
|
201
202
|
},
|
|
203
|
+
'composable-studio': {
|
|
204
|
+
dirName: 'composable_studio',
|
|
205
|
+
fileName: 'composable_studio.json',
|
|
206
|
+
apiBaseUrl: 'https://composable-studio-api.contentstack.com',
|
|
207
|
+
apiVersion: 'v1',
|
|
208
|
+
},
|
|
202
209
|
},
|
|
203
210
|
languagesCode: [
|
|
204
211
|
'af-za',
|
|
@@ -37,8 +37,6 @@ class ModuleImporter {
|
|
|
37
37
|
const backupDir = await (0, utils_1.backupHandler)(this.importConfig);
|
|
38
38
|
if (backupDir) {
|
|
39
39
|
this.importConfig.backupDir = backupDir;
|
|
40
|
-
// To support the old config
|
|
41
|
-
this.importConfig.data = backupDir;
|
|
42
40
|
}
|
|
43
41
|
// NOTE audit and fix the import content.
|
|
44
42
|
if (!this.importConfig.skipAudit &&
|
|
@@ -66,8 +64,6 @@ class ModuleImporter {
|
|
|
66
64
|
}
|
|
67
65
|
async importByModuleByName(moduleName) {
|
|
68
66
|
cli_utilities_1.log.info(`Starting import of ${moduleName} module`, this.importConfig.context);
|
|
69
|
-
// import the modules by name
|
|
70
|
-
// calls the module runner which inturn calls the module itself
|
|
71
67
|
return (0, modules_1.default)({
|
|
72
68
|
stackAPIClient: this.stackAPIClient,
|
|
73
69
|
importConfig: this.importConfig,
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ModuleClassParams, ComposableStudioProject } from '../../types';
|
|
2
|
+
export default class ImportComposableStudio {
|
|
3
|
+
private importConfig;
|
|
4
|
+
private composableStudioConfig;
|
|
5
|
+
private composableStudioPath;
|
|
6
|
+
private composableStudioFilePath;
|
|
7
|
+
private apiClient;
|
|
8
|
+
private envUidMapperPath;
|
|
9
|
+
private envUidMapper;
|
|
10
|
+
private projectMapperPath;
|
|
11
|
+
constructor({ importConfig }: ModuleClassParams);
|
|
12
|
+
/**
|
|
13
|
+
* Entry point for Studio import
|
|
14
|
+
*/
|
|
15
|
+
start(): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Initialize authentication headers for API calls
|
|
18
|
+
*/
|
|
19
|
+
addAuthHeaders(): Promise<boolean>;
|
|
20
|
+
/**
|
|
21
|
+
* Load environment UID mapper from backup directory
|
|
22
|
+
*/
|
|
23
|
+
loadEnvironmentMapper(): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Read exported project from file system
|
|
26
|
+
*/
|
|
27
|
+
readExportedProject(): Promise<ComposableStudioProject | null>;
|
|
28
|
+
/**
|
|
29
|
+
* Check if target stack already has a connected project
|
|
30
|
+
*/
|
|
31
|
+
getExistingProject(): Promise<ComposableStudioProject | null>;
|
|
32
|
+
/**
|
|
33
|
+
* Import project with name conflict handling
|
|
34
|
+
*/
|
|
35
|
+
importProject(exportedProject: ComposableStudioProject): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Map environment UID from source to target
|
|
38
|
+
*/
|
|
39
|
+
mapEnvironmentUid(sourceEnvUid: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Prompt user for a new project name when conflict occurs
|
|
42
|
+
*/
|
|
43
|
+
promptForNewProjectName(currentName: string): Promise<string>;
|
|
44
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const node_path_1 = require("node:path");
|
|
5
|
+
const cli_utilities_1 = require("@contentstack/cli-utilities");
|
|
6
|
+
const isEmpty_1 = tslib_1.__importDefault(require("lodash/isEmpty"));
|
|
7
|
+
const utils_1 = require("../../utils");
|
|
8
|
+
class ImportComposableStudio {
|
|
9
|
+
constructor({ importConfig }) {
|
|
10
|
+
this.importConfig = importConfig;
|
|
11
|
+
this.importConfig.context.module = 'composable-studio';
|
|
12
|
+
this.composableStudioConfig = importConfig.modules['composable-studio'];
|
|
13
|
+
// Setup paths
|
|
14
|
+
this.composableStudioPath = (0, node_path_1.join)(this.importConfig.backupDir, this.composableStudioConfig.dirName);
|
|
15
|
+
this.projectMapperPath = (0, node_path_1.join)(this.importConfig.backupDir, 'mapper', this.composableStudioConfig.dirName);
|
|
16
|
+
this.composableStudioFilePath = (0, node_path_1.join)(this.composableStudioPath, this.composableStudioConfig.fileName);
|
|
17
|
+
this.envUidMapperPath = (0, node_path_1.join)(this.importConfig.backupDir, 'mapper', 'environments', 'uid-mapping.json');
|
|
18
|
+
this.envUidMapper = {};
|
|
19
|
+
// Initialize HttpClient with Studio API base URL
|
|
20
|
+
this.apiClient = new cli_utilities_1.HttpClient();
|
|
21
|
+
this.apiClient.baseUrl(`${this.composableStudioConfig.apiBaseUrl}/${this.composableStudioConfig.apiVersion}`);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Entry point for Studio import
|
|
25
|
+
*/
|
|
26
|
+
async start() {
|
|
27
|
+
if (this.importConfig.management_token) {
|
|
28
|
+
cli_utilities_1.log.warn('Skipping Studio project import when using management token', this.importConfig.context);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
cli_utilities_1.log.debug('Starting Studio project import process...', this.importConfig.context);
|
|
32
|
+
try {
|
|
33
|
+
// Initialize authentication
|
|
34
|
+
const authInitialized = await this.addAuthHeaders();
|
|
35
|
+
if (!authInitialized) {
|
|
36
|
+
cli_utilities_1.log.warn('Skipping Studio project import when using OAuth authentication', this.importConfig.context);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
// Load environment UID mapper
|
|
40
|
+
await this.loadEnvironmentMapper();
|
|
41
|
+
// Read exported project data
|
|
42
|
+
const exportedProject = await this.readExportedProject();
|
|
43
|
+
if (!exportedProject) {
|
|
44
|
+
cli_utilities_1.log.warn(cli_utilities_1.messageHandler.parse('COMPOSABLE_STUDIO_NOT_FOUND'), this.importConfig.context);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
cli_utilities_1.log.debug(`Exported project found: ${exportedProject.name}`, this.importConfig.context);
|
|
48
|
+
// Check if target stack already has a connected project
|
|
49
|
+
const existingProject = await this.getExistingProject();
|
|
50
|
+
if (existingProject) {
|
|
51
|
+
cli_utilities_1.log.warn(cli_utilities_1.messageHandler.parse('COMPOSABLE_STUDIO_SKIP_EXISTING'), this.importConfig.context);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// Import the project with name conflict handling
|
|
55
|
+
await this.importProject(exportedProject);
|
|
56
|
+
cli_utilities_1.log.success(cli_utilities_1.messageHandler.parse('COMPOSABLE_STUDIO_IMPORT_COMPLETE', exportedProject.name), this.importConfig.context);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
(0, cli_utilities_1.handleAndLogError)(error, Object.assign({}, this.importConfig.context));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Initialize authentication headers for API calls
|
|
64
|
+
*/
|
|
65
|
+
async addAuthHeaders() {
|
|
66
|
+
cli_utilities_1.log.debug('Initializing Studio API authentication...', this.importConfig.context);
|
|
67
|
+
// Get authentication details - following personalization-api-adapter pattern
|
|
68
|
+
await cli_utilities_1.authenticationHandler.getAuthDetails();
|
|
69
|
+
const token = cli_utilities_1.authenticationHandler.accessToken;
|
|
70
|
+
cli_utilities_1.log.debug(`Authentication type: ${cli_utilities_1.authenticationHandler.isOauthEnabled ? 'OAuth' : 'Token'}`, this.importConfig.context);
|
|
71
|
+
// Set authentication headers based on auth type
|
|
72
|
+
if (cli_utilities_1.authenticationHandler.isOauthEnabled) {
|
|
73
|
+
cli_utilities_1.log.debug('Skipping setting OAuth authorization header when using OAuth authentication', this.importConfig.context);
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// TODO: Currenlty assuming if auth type is not OAuth, it is Basic Auth and we are setting authtoken header
|
|
78
|
+
cli_utilities_1.log.debug('Setting authtoken header', this.importConfig.context);
|
|
79
|
+
this.apiClient.headers({ authtoken: token });
|
|
80
|
+
}
|
|
81
|
+
// Set organization_uid header
|
|
82
|
+
this.apiClient.headers({
|
|
83
|
+
organization_uid: this.importConfig.org_uid,
|
|
84
|
+
'Content-Type': 'application/json',
|
|
85
|
+
Accept: 'application/json',
|
|
86
|
+
});
|
|
87
|
+
cli_utilities_1.log.debug('Studio API authentication initialized', this.importConfig.context);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Load environment UID mapper from backup directory
|
|
92
|
+
*/
|
|
93
|
+
async loadEnvironmentMapper() {
|
|
94
|
+
cli_utilities_1.log.debug('Loading environment UID mapper...', this.importConfig.context);
|
|
95
|
+
if (utils_1.fileHelper.fileExistsSync(this.envUidMapperPath)) {
|
|
96
|
+
this.envUidMapper = utils_1.fileHelper.readFileSync(this.envUidMapperPath);
|
|
97
|
+
cli_utilities_1.log.debug(`Environment mapper loaded with ${Object.keys(this.envUidMapper).length} mappings`, this.importConfig.context);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
cli_utilities_1.log.debug('No environment UID mapper found', this.importConfig.context);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Read exported project from file system
|
|
105
|
+
*/
|
|
106
|
+
async readExportedProject() {
|
|
107
|
+
cli_utilities_1.log.debug(`Reading exported project from: ${this.composableStudioFilePath}`, this.importConfig.context);
|
|
108
|
+
if (!utils_1.fileHelper.fileExistsSync(this.composableStudioFilePath)) {
|
|
109
|
+
cli_utilities_1.log.debug('Studio project file does not exist', this.importConfig.context);
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
const projectData = utils_1.fileHelper.readFileSync(this.composableStudioFilePath);
|
|
113
|
+
if (!projectData || (0, isEmpty_1.default)(projectData)) {
|
|
114
|
+
cli_utilities_1.log.debug('Studio project file is empty', this.importConfig.context);
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
return projectData;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Check if target stack already has a connected project
|
|
121
|
+
*/
|
|
122
|
+
async getExistingProject() {
|
|
123
|
+
var _a;
|
|
124
|
+
cli_utilities_1.log.debug('Checking if target stack already has a connected project...', this.importConfig.context);
|
|
125
|
+
try {
|
|
126
|
+
const apiUrl = '/projects';
|
|
127
|
+
cli_utilities_1.log.debug(`Fetching projects from: ${this.composableStudioConfig.apiBaseUrl}${apiUrl}`, this.importConfig.context);
|
|
128
|
+
const response = await this.apiClient.get(apiUrl);
|
|
129
|
+
if (response.status < 200 || response.status >= 300) {
|
|
130
|
+
throw new Error(`API call failed with status ${response.status}: ${JSON.stringify(response.data)}`);
|
|
131
|
+
}
|
|
132
|
+
const projects = ((_a = response.data) === null || _a === void 0 ? void 0 : _a.projects) || [];
|
|
133
|
+
cli_utilities_1.log.debug(`Found ${projects.length} projects in organization`, this.importConfig.context);
|
|
134
|
+
// Filter projects by connected stack API key
|
|
135
|
+
const connectedProject = projects.find((project) => project.connectedStackApiKey === this.importConfig.apiKey);
|
|
136
|
+
if (connectedProject) {
|
|
137
|
+
cli_utilities_1.log.debug(`Target stack already has connected project: ${connectedProject.name}`, this.importConfig.context);
|
|
138
|
+
return connectedProject;
|
|
139
|
+
}
|
|
140
|
+
cli_utilities_1.log.debug('Target stack does not have a connected project', this.importConfig.context);
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
cli_utilities_1.log.debug(`Error checking for existing project: ${error.message}`, this.importConfig.context);
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Import project with name conflict handling
|
|
150
|
+
*/
|
|
151
|
+
async importProject(exportedProject) {
|
|
152
|
+
var _a, _b, _c;
|
|
153
|
+
cli_utilities_1.log.debug('Starting project import...', this.importConfig.context);
|
|
154
|
+
// Map environment UID
|
|
155
|
+
const mappedEnvironmentUid = this.mapEnvironmentUid(exportedProject.settings.configuration.environment);
|
|
156
|
+
// Prepare project data for import
|
|
157
|
+
const projectData = {
|
|
158
|
+
name: exportedProject.name,
|
|
159
|
+
connectedStackApiKey: this.importConfig.apiKey,
|
|
160
|
+
contentTypeUid: exportedProject.contentTypeUid,
|
|
161
|
+
description: exportedProject.description || '',
|
|
162
|
+
canvasUrl: exportedProject.canvasUrl || '/',
|
|
163
|
+
settings: {
|
|
164
|
+
configuration: {
|
|
165
|
+
environment: mappedEnvironmentUid,
|
|
166
|
+
locale: ((_b = (_a = exportedProject === null || exportedProject === void 0 ? void 0 : exportedProject.settings) === null || _a === void 0 ? void 0 : _a.configuration) === null || _b === void 0 ? void 0 : _b.locale) || '',
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
cli_utilities_1.log.debug(`Project data prepared: ${JSON.stringify(projectData, null, 2)}`, this.importConfig.context);
|
|
171
|
+
// Try to create project with name conflict retry loop
|
|
172
|
+
let projectCreated = false;
|
|
173
|
+
let currentName = projectData.name;
|
|
174
|
+
let attemptCount = 0;
|
|
175
|
+
while (!projectCreated) {
|
|
176
|
+
attemptCount++;
|
|
177
|
+
cli_utilities_1.log.debug(`Attempt ${attemptCount} to create project with name: ${currentName}`, this.importConfig.context);
|
|
178
|
+
projectData.name = currentName;
|
|
179
|
+
const response = await this.apiClient.post('/projects', projectData);
|
|
180
|
+
if (response.status >= 200 && response.status < 300) {
|
|
181
|
+
projectCreated = true;
|
|
182
|
+
cli_utilities_1.log.debug(`Project created successfully with UID: ${(_c = response.data) === null || _c === void 0 ? void 0 : _c.uid}`, this.importConfig.context);
|
|
183
|
+
// Create mapper directory if it doesn't exist
|
|
184
|
+
await utils_1.fsUtil.makeDirectory(this.projectMapperPath);
|
|
185
|
+
// write the project to file
|
|
186
|
+
const projectFileSuccessPath = (0, node_path_1.join)(this.projectMapperPath, this.composableStudioConfig.fileName);
|
|
187
|
+
utils_1.fsUtil.writeFile(projectFileSuccessPath, response.data);
|
|
188
|
+
cli_utilities_1.log.debug(`Project written to: ${projectFileSuccessPath}`, this.importConfig.context);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
throw new Error(`API call failed with status ${response.status}: ${JSON.stringify(response.data)}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Map environment UID from source to target
|
|
197
|
+
*/
|
|
198
|
+
mapEnvironmentUid(sourceEnvUid) {
|
|
199
|
+
if (!sourceEnvUid) {
|
|
200
|
+
cli_utilities_1.log.debug('Source environment UID is empty', this.importConfig.context);
|
|
201
|
+
return '';
|
|
202
|
+
}
|
|
203
|
+
cli_utilities_1.log.debug(`Mapping source environment UID: ${sourceEnvUid}`, this.importConfig.context);
|
|
204
|
+
if ((0, isEmpty_1.default)(this.envUidMapper)) {
|
|
205
|
+
cli_utilities_1.log.warn(cli_utilities_1.messageHandler.parse('COMPOSABLE_STUDIO_ENV_MAPPING_FAILED', sourceEnvUid), this.importConfig.context);
|
|
206
|
+
return '';
|
|
207
|
+
}
|
|
208
|
+
const mappedUid = this.envUidMapper[sourceEnvUid];
|
|
209
|
+
if (!mappedUid) {
|
|
210
|
+
cli_utilities_1.log.warn(cli_utilities_1.messageHandler.parse('COMPOSABLE_STUDIO_ENV_MAPPING_FAILED', sourceEnvUid), this.importConfig.context);
|
|
211
|
+
return '';
|
|
212
|
+
}
|
|
213
|
+
cli_utilities_1.log.debug(`Mapped environment UID: ${sourceEnvUid} → ${mappedUid}`, this.importConfig.context);
|
|
214
|
+
return mappedUid;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Prompt user for a new project name when conflict occurs
|
|
218
|
+
*/
|
|
219
|
+
async promptForNewProjectName(currentName) {
|
|
220
|
+
const suggestedName = `Copy of ${currentName}`;
|
|
221
|
+
cli_utilities_1.log.warn(cli_utilities_1.messageHandler.parse('COMPOSABLE_STUDIO_NAME_CONFLICT', currentName), this.importConfig.context);
|
|
222
|
+
cli_utilities_1.log.info(cli_utilities_1.messageHandler.parse('COMPOSABLE_STUDIO_SUGGEST_NAME', suggestedName), this.importConfig.context);
|
|
223
|
+
const response = await cli_utilities_1.cliux.inquire({
|
|
224
|
+
type: 'input',
|
|
225
|
+
name: 'projectName',
|
|
226
|
+
message: 'Enter new project name:',
|
|
227
|
+
default: suggestedName,
|
|
228
|
+
});
|
|
229
|
+
const newName = response.projectName || suggestedName;
|
|
230
|
+
cli_utilities_1.log.debug(`User provided new project name: ${newName}`, this.importConfig.context);
|
|
231
|
+
return newName;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
exports.default = ImportComposableStudio;
|