@contentstack/cli-cm-import 1.25.1 → 1.26.1
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 +3 -3
- package/lib/commands/cm/stacks/import.d.ts +1 -0
- package/lib/commands/cm/stacks/import.js +39 -12
- package/lib/import/module-importer.js +20 -3
- package/lib/import/modules/assets.d.ts +1 -1
- package/lib/import/modules/assets.js +93 -39
- package/lib/import/modules/content-types.js +76 -31
- package/lib/import/modules/custom-roles.js +95 -19
- package/lib/import/modules/entries.js +128 -57
- package/lib/import/modules/environments.js +48 -14
- package/lib/import/modules/extensions.js +78 -16
- package/lib/import/modules/global-fields.js +85 -20
- package/lib/import/modules/labels.d.ts +4 -4
- package/lib/import/modules/labels.js +60 -18
- package/lib/import/modules/locales.js +63 -20
- package/lib/import/modules/marketplace-apps.js +160 -31
- package/lib/import/modules/personalize.js +33 -7
- package/lib/import/modules/stack.js +5 -0
- package/lib/import/modules/taxonomies.js +52 -13
- package/lib/import/modules/variant-entries.js +21 -3
- package/lib/import/modules/webhooks.js +44 -12
- package/lib/import/modules/workflows.js +65 -21
- package/lib/types/import-config.d.ts +3 -1
- package/lib/types/index.d.ts +22 -0
- package/lib/utils/asset-helper.js +24 -1
- package/lib/utils/backup-handler.js +15 -1
- package/lib/utils/common-helper.js +41 -16
- package/lib/utils/content-type-helper.js +35 -2
- package/lib/utils/entries-helper.js +24 -2
- package/lib/utils/extension-helper.js +35 -1
- package/lib/utils/import-config-handler.js +21 -0
- package/lib/utils/login-handler.js +8 -4
- package/lib/utils/marketplace-app-helper.js +50 -11
- package/lib/utils/taxonomies-helper.js +22 -4
- package/oclif.manifest.json +2 -2
- package/package.json +6 -6
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/1.
|
|
50
|
+
@contentstack/cli-cm-import/1.26.1 linux-x64 node-v22.17.1
|
|
51
51
|
$ csdx --help [COMMAND]
|
|
52
52
|
USAGE
|
|
53
53
|
$ csdx COMMAND
|
|
@@ -89,7 +89,7 @@ FLAGS
|
|
|
89
89
|
specified, the import command will import all the modules into the stack. The
|
|
90
90
|
available modules are assets, content-types, entries, environments,
|
|
91
91
|
extensions, marketplace-apps, global-fields, labels, locales, webhooks,
|
|
92
|
-
workflows, custom-roles, and taxonomies.
|
|
92
|
+
workflows, custom-roles, personalize projects, and taxonomies.
|
|
93
93
|
-y, --yes [optional] Force override all Marketplace prompts.
|
|
94
94
|
--exclude-global-modules Excludes the branch-independent module from the import operation.
|
|
95
95
|
--import-webhook-status=<option> [default: disable] [default: disable] (optional) This webhook state keeps the
|
|
@@ -155,7 +155,7 @@ FLAGS
|
|
|
155
155
|
specified, the import command will import all the modules into the stack. The
|
|
156
156
|
available modules are assets, content-types, entries, environments,
|
|
157
157
|
extensions, marketplace-apps, global-fields, labels, locales, webhooks,
|
|
158
|
-
workflows, custom-roles, and taxonomies.
|
|
158
|
+
workflows, custom-roles, personalize projects, and taxonomies.
|
|
159
159
|
-y, --yes [optional] Force override all Marketplace prompts.
|
|
160
160
|
--exclude-global-modules Excludes the branch-independent module from the import operation.
|
|
161
161
|
--import-webhook-status=<option> [default: disable] [default: disable] (optional) This webhook state keeps the
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tslib_1 = require("tslib");
|
|
4
|
-
const node_path_1 = tslib_1.__importDefault(require("node:path"));
|
|
5
3
|
const cli_command_1 = require("@contentstack/cli-command");
|
|
6
4
|
const cli_utilities_1 = require("@contentstack/cli-utilities");
|
|
7
5
|
const import_1 = require("../../../import");
|
|
@@ -15,6 +13,10 @@ class ImportCommand extends cli_command_1.Command {
|
|
|
15
13
|
try {
|
|
16
14
|
const { flags } = await this.parse(ImportCommand);
|
|
17
15
|
let importConfig = await (0, utils_1.setupImportConfig)(flags);
|
|
16
|
+
// Prepare the context object
|
|
17
|
+
const context = this.createImportContext(importConfig.apiKey, importConfig.authenticationMethod);
|
|
18
|
+
importConfig.context = Object.assign({}, context);
|
|
19
|
+
//log.info(`Using Cli Version: ${this.context?.cliVersion}`, importConfig.context);
|
|
18
20
|
// Note setting host to create cma client
|
|
19
21
|
importConfig.host = this.cmaHost;
|
|
20
22
|
importConfig.region = this.region;
|
|
@@ -22,12 +24,15 @@ class ImportCommand extends cli_command_1.Command {
|
|
|
22
24
|
importConfig.developerHubBaseUrl = this.developerHubUrl;
|
|
23
25
|
if (this.personalizeUrl)
|
|
24
26
|
importConfig.modules.personalize.baseURL[importConfig.region.name] = this.personalizeUrl;
|
|
25
|
-
backupDir = importConfig.cliLogsPath || importConfig.backupDir;
|
|
26
27
|
const managementAPIClient = await (0, cli_utilities_1.managementSDKClient)(importConfig);
|
|
27
28
|
if (!flags.branch) {
|
|
28
29
|
try {
|
|
30
|
+
// Use stack configuration to check for branch availability
|
|
31
|
+
// false positive - no hardcoded secret here
|
|
32
|
+
// @ts-ignore-next-line secret-detection
|
|
33
|
+
const keyProp = 'api_key';
|
|
29
34
|
const branches = await managementAPIClient
|
|
30
|
-
.stack({
|
|
35
|
+
.stack({ [keyProp]: importConfig.apiKey })
|
|
31
36
|
.branch()
|
|
32
37
|
.query()
|
|
33
38
|
.find()
|
|
@@ -40,22 +45,44 @@ class ImportCommand extends cli_command_1.Command {
|
|
|
40
45
|
// Branch not enabled, just the let flow continue
|
|
41
46
|
}
|
|
42
47
|
}
|
|
48
|
+
// Set backupDir early so it's available in error handling
|
|
49
|
+
backupDir = importConfig.backupDir;
|
|
43
50
|
const moduleImporter = new import_1.ModuleImporter(managementAPIClient, importConfig);
|
|
44
51
|
const result = await moduleImporter.start();
|
|
45
52
|
if (!(result === null || result === void 0 ? void 0 : result.noSuccessMsg)) {
|
|
46
|
-
|
|
53
|
+
const successMessage = importConfig.stackName
|
|
47
54
|
? `Successfully imported the content to the stack named ${importConfig.stackName} with the API key ${importConfig.apiKey} .`
|
|
48
|
-
: `The content has been imported to the stack ${importConfig.apiKey} successfully
|
|
55
|
+
: `The content has been imported to the stack ${importConfig.apiKey} successfully!`;
|
|
56
|
+
cli_utilities_1.log.success(successMessage, importConfig.context);
|
|
49
57
|
}
|
|
50
|
-
|
|
58
|
+
cli_utilities_1.log.success(`The log has been stored at '${(0, cli_utilities_1.getLogPath)()}'`, importConfig.context);
|
|
59
|
+
cli_utilities_1.log.info(`The backup content has been stored at '${backupDir}'`, importConfig.context);
|
|
51
60
|
}
|
|
52
61
|
catch (error) {
|
|
53
|
-
(0,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
62
|
+
(0, cli_utilities_1.handleAndLogError)(error);
|
|
63
|
+
cli_utilities_1.log.info(`The log has been stored at '${(0, cli_utilities_1.getLogPath)()}'`);
|
|
64
|
+
if (backupDir) {
|
|
65
|
+
cli_utilities_1.log.info(`The backup content has been stored at '${backupDir}'`);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
cli_utilities_1.log.info('No backup directory was created due to early termination');
|
|
69
|
+
}
|
|
57
70
|
}
|
|
58
71
|
}
|
|
72
|
+
// Create export context object
|
|
73
|
+
createImportContext(apiKey, authenticationMethod) {
|
|
74
|
+
var _a, _b, _c;
|
|
75
|
+
return {
|
|
76
|
+
command: ((_b = (_a = this.context) === null || _a === void 0 ? void 0 : _a.info) === null || _b === void 0 ? void 0 : _b.command) || 'cm:stacks:import',
|
|
77
|
+
module: '',
|
|
78
|
+
userId: cli_utilities_1.configHandler.get('userUid') || '',
|
|
79
|
+
email: cli_utilities_1.configHandler.get('email') || '',
|
|
80
|
+
sessionId: (_c = this.context) === null || _c === void 0 ? void 0 : _c.sessionId,
|
|
81
|
+
apiKey: apiKey || '',
|
|
82
|
+
orgId: cli_utilities_1.configHandler.get('oauthOrgUid') || '',
|
|
83
|
+
authenticationMethod: authenticationMethod || 'Basic Auth',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
59
86
|
}
|
|
60
87
|
exports.default = ImportCommand;
|
|
61
88
|
ImportCommand.description = cli_utilities_1.messageHandler.parse('Import content from a stack');
|
|
@@ -111,7 +138,7 @@ ImportCommand.flags = {
|
|
|
111
138
|
module: cli_utilities_1.flags.string({
|
|
112
139
|
required: false,
|
|
113
140
|
char: 'm',
|
|
114
|
-
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, and taxonomies.',
|
|
141
|
+
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.',
|
|
115
142
|
parse: (0, cli_utilities_1.printFlagDeprecation)(['-m'], ['--module']),
|
|
116
143
|
}),
|
|
117
144
|
'backup-dir': cli_utilities_1.flags.string({
|
|
@@ -40,7 +40,15 @@ class ModuleImporter {
|
|
|
40
40
|
// NOTE audit and fix the import content.
|
|
41
41
|
if (!this.importConfig.skipAudit &&
|
|
42
42
|
(!this.importConfig.moduleName ||
|
|
43
|
-
[
|
|
43
|
+
[
|
|
44
|
+
'content-types',
|
|
45
|
+
'global-fields',
|
|
46
|
+
'entries',
|
|
47
|
+
'extensions',
|
|
48
|
+
'workflows',
|
|
49
|
+
'custom-roles',
|
|
50
|
+
'assets'
|
|
51
|
+
].includes(this.importConfig.moduleName))) {
|
|
44
52
|
if (!(await this.auditImportData(logger))) {
|
|
45
53
|
return { noSuccessMsg: true };
|
|
46
54
|
}
|
|
@@ -50,7 +58,7 @@ class ModuleImporter {
|
|
|
50
58
|
this.importConfig['master_locale'] = { code: masterLocalResponse.code };
|
|
51
59
|
this.importConfig.masterLocale = { code: masterLocalResponse.code };
|
|
52
60
|
}
|
|
53
|
-
await (0, utils_1.sanitizeStack)(this.
|
|
61
|
+
await (0, utils_1.sanitizeStack)(this.importConfig);
|
|
54
62
|
return this.import();
|
|
55
63
|
}
|
|
56
64
|
async import() {
|
|
@@ -119,11 +127,20 @@ class ModuleImporter {
|
|
|
119
127
|
}
|
|
120
128
|
else if (this.importConfig.modules.types.length) {
|
|
121
129
|
this.importConfig.modules.types
|
|
122
|
-
.filter((val) => [
|
|
130
|
+
.filter((val) => [
|
|
131
|
+
'content-types',
|
|
132
|
+
'global-fields',
|
|
133
|
+
'entries',
|
|
134
|
+
'extensions',
|
|
135
|
+
'workflows',
|
|
136
|
+
'custom-roles',
|
|
137
|
+
'assets'
|
|
138
|
+
].includes(val))
|
|
123
139
|
.forEach((val) => {
|
|
124
140
|
args.push('--modules', val);
|
|
125
141
|
});
|
|
126
142
|
}
|
|
143
|
+
args.push('--modules', 'field-rules');
|
|
127
144
|
(0, utils_1.log)(this.importConfig, 'Starting audit process', 'info');
|
|
128
145
|
const result = await cli_audit_1.AuditFix.run(args);
|
|
129
146
|
(0, utils_1.log)(this.importConfig, 'Audit process completed', 'info');
|
|
@@ -24,6 +24,7 @@ class ImportAssets extends base_class_1.default {
|
|
|
24
24
|
this.assetsUidMap = {};
|
|
25
25
|
this.assetsUrlMap = {};
|
|
26
26
|
this.assetsFolderMap = {};
|
|
27
|
+
this.importConfig.context.module = 'assets';
|
|
27
28
|
this.assetsPath = (0, node_path_1.join)(this.importConfig.backupDir, 'assets');
|
|
28
29
|
this.mapperDirPath = (0, node_path_1.join)(this.importConfig.backupDir, 'mapper', 'assets');
|
|
29
30
|
this.assetUidMapperPath = (0, node_path_1.join)(this.mapperDirPath, 'uid-mapping.json');
|
|
@@ -38,52 +39,76 @@ class ImportAssets extends base_class_1.default {
|
|
|
38
39
|
* @returns {Promise<void>} Promise<any>
|
|
39
40
|
*/
|
|
40
41
|
async start() {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
(0,
|
|
42
|
+
try {
|
|
43
|
+
// NOTE Step 1: Import folders and create uid mapping file
|
|
44
|
+
cli_utilities_1.log.debug('Starting folder import process', this.importConfig.context);
|
|
45
|
+
await this.importFolders();
|
|
46
|
+
// NOTE Step 2: Import versioned assets and create it mapping files (uid, url)
|
|
47
|
+
if (this.assetConfig.includeVersionedAssets) {
|
|
48
|
+
const versionsPath = `${this.assetsPath}/versions`;
|
|
49
|
+
if ((0, node_fs_1.existsSync)(versionsPath)) {
|
|
50
|
+
cli_utilities_1.log.debug('Starting versioned assets import', this.importConfig.context);
|
|
51
|
+
await this.importAssets(true);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
cli_utilities_1.log.info('No Versioned assets found to import', this.importConfig.context);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// NOTE Step 3: Import Assets and create it mapping files (uid, url)
|
|
58
|
+
cli_utilities_1.log.debug('Starting assets import', this.importConfig.context);
|
|
59
|
+
await this.importAssets();
|
|
60
|
+
// NOTE Step 4: Publish assets
|
|
61
|
+
if (!this.importConfig.skipAssetsPublish) {
|
|
62
|
+
cli_utilities_1.log.debug('Starting assets publishing', this.importConfig.context);
|
|
63
|
+
await this.publish();
|
|
64
|
+
}
|
|
65
|
+
cli_utilities_1.log.success('Assets imported successfully!', this.importConfig.context);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
(0, cli_utilities_1.handleAndLogError)(error, Object.assign({}, this.importConfig.context));
|
|
49
69
|
}
|
|
50
|
-
// NOTE Step 3: Import Assets and create it mapping files (uid, url)
|
|
51
|
-
await this.importAssets();
|
|
52
|
-
// NOTE Step 4: Publish assets
|
|
53
|
-
if (!this.importConfig.skipAssetsPublish)
|
|
54
|
-
await this.publish();
|
|
55
70
|
}
|
|
56
71
|
/**
|
|
57
72
|
* @method importFolders
|
|
58
73
|
* @returns {Promise<any>} Promise<any>
|
|
59
74
|
*/
|
|
60
75
|
async importFolders() {
|
|
61
|
-
const
|
|
76
|
+
const foldersPath = (0, node_path_1.resolve)(this.assetsRootPath, 'folders.json');
|
|
77
|
+
cli_utilities_1.log.debug(`Reading folders from: ${foldersPath}`, this.importConfig.context);
|
|
78
|
+
const folders = this.fs.readFile(foldersPath);
|
|
62
79
|
if ((0, isEmpty_1.default)(folders)) {
|
|
63
|
-
|
|
80
|
+
cli_utilities_1.log.info('No folders found to import', this.importConfig.context);
|
|
64
81
|
return;
|
|
65
82
|
}
|
|
83
|
+
cli_utilities_1.log.debug(`Found ${folders.length} folders to import`, this.importConfig.context);
|
|
66
84
|
const batches = this.constructFolderImportOrder(folders);
|
|
85
|
+
cli_utilities_1.log.debug(`Organized folders into ${batches.length} batches for import`, this.importConfig.context);
|
|
67
86
|
const onSuccess = ({ response, apiData: { uid, name } = { uid: null, name: '' } }) => {
|
|
68
87
|
this.assetsFolderMap[uid] = response.uid;
|
|
69
|
-
|
|
88
|
+
cli_utilities_1.log.debug(`Created folder: ${name} (Mapped ${uid} → ${response.uid})`, this.importConfig.context);
|
|
89
|
+
cli_utilities_1.log.success(`Created folder: '${name}'`, this.importConfig.context);
|
|
70
90
|
};
|
|
71
91
|
const onReject = ({ error, apiData: { name } = { name: '' } }) => {
|
|
72
|
-
|
|
73
|
-
(0,
|
|
92
|
+
cli_utilities_1.log.error(`${name} folder creation failed.!`, this.importConfig.context);
|
|
93
|
+
(0, cli_utilities_1.handleAndLogError)(error, Object.assign(Object.assign({}, this.importConfig.context), { name }));
|
|
74
94
|
};
|
|
75
95
|
const serializeData = (apiOptions) => {
|
|
76
96
|
if (apiOptions.apiData.parent_uid) {
|
|
97
|
+
const originalParent = apiOptions.apiData.parent_uid;
|
|
77
98
|
apiOptions.apiData.parent_uid = this.assetsFolderMap[apiOptions.apiData.parent_uid];
|
|
99
|
+
cli_utilities_1.log.debug(`Mapped parent folder: ${originalParent} → ${apiOptions.apiData.parent_uid}`, this.importConfig.context);
|
|
78
100
|
}
|
|
79
101
|
return apiOptions;
|
|
80
102
|
};
|
|
81
103
|
const batch = (0, map_1.default)((0, unionBy_1.default)(batches, 'parent_uid'), 'parent_uid');
|
|
104
|
+
cli_utilities_1.log.debug(`Processing ${batch.length} folder batches`, this.importConfig.context);
|
|
82
105
|
for (const parent_uid of batch) {
|
|
106
|
+
const currentBatch = (0, filter_1.default)(batches, { parent_uid });
|
|
107
|
+
cli_utilities_1.log.debug(`Processing batch with parent_uid: ${parent_uid} (${currentBatch.length} folders)`, this.importConfig.context);
|
|
83
108
|
// NOTE create parent folders
|
|
84
109
|
/* eslint-disable no-await-in-loop */
|
|
85
110
|
await this.makeConcurrentCall({
|
|
86
|
-
apiContent: (0, orderBy_1.default)(
|
|
111
|
+
apiContent: (0, orderBy_1.default)(currentBatch, 'created_at'),
|
|
87
112
|
processName: 'import assets folders',
|
|
88
113
|
apiParams: {
|
|
89
114
|
serializeData,
|
|
@@ -96,6 +121,7 @@ class ImportAssets extends base_class_1.default {
|
|
|
96
121
|
}, undefined, false);
|
|
97
122
|
}
|
|
98
123
|
if (!(0, isEmpty_1.default)(this.assetsFolderMap)) {
|
|
124
|
+
cli_utilities_1.log.debug(`Writing folder mappings to ${this.assetFolderUidMapperPath}`, this.importConfig.context);
|
|
99
125
|
this.fs.writeFile(this.assetFolderUidMapperPath, this.assetsFolderMap);
|
|
100
126
|
}
|
|
101
127
|
}
|
|
@@ -108,32 +134,38 @@ class ImportAssets extends base_class_1.default {
|
|
|
108
134
|
const processName = isVersion ? 'import versioned assets' : 'import assets';
|
|
109
135
|
const indexFileName = isVersion ? 'versioned-assets.json' : 'assets.json';
|
|
110
136
|
const basePath = isVersion ? (0, node_path_1.join)(this.assetsPath, 'versions') : this.assetsPath;
|
|
137
|
+
cli_utilities_1.log.debug(`Importing ${processName} from ${basePath}`, this.importConfig.context);
|
|
111
138
|
const fs = new cli_utilities_1.FsUtility({ basePath, indexFileName });
|
|
112
139
|
const indexer = fs.indexFileContent;
|
|
113
140
|
const indexerCount = (0, values_1.default)(indexer).length;
|
|
141
|
+
cli_utilities_1.log.debug(`Found ${indexerCount} asset chunks to process`, this.importConfig.context);
|
|
114
142
|
const onSuccess = ({ response = {}, apiData: { uid, url, title } = undefined }) => {
|
|
115
143
|
this.assetsUidMap[uid] = response.uid;
|
|
116
144
|
this.assetsUrlMap[url] = response.url;
|
|
117
|
-
|
|
145
|
+
cli_utilities_1.log.debug(`Created asset: ${title} (Mapped ${uid} → ${response.uid})`, this.importConfig.context);
|
|
146
|
+
cli_utilities_1.log.success(`Created asset: '${title}'`, this.importConfig.context);
|
|
118
147
|
};
|
|
119
148
|
const onReject = ({ error, apiData: { title } = undefined }) => {
|
|
120
|
-
|
|
121
|
-
(0,
|
|
149
|
+
cli_utilities_1.log.error(`${title} asset upload failed.!`, this.importConfig.context);
|
|
150
|
+
(0, cli_utilities_1.handleAndLogError)(error, Object.assign(Object.assign({}, this.importConfig.context), { title }));
|
|
122
151
|
};
|
|
123
152
|
/* eslint-disable @typescript-eslint/no-unused-vars, guard-for-in */
|
|
124
153
|
for (const index in indexer) {
|
|
154
|
+
cli_utilities_1.log.debug(`Processing chunk ${index} of ${indexerCount}`, this.importConfig.context);
|
|
125
155
|
const chunk = await fs.readChunkFiles.next().catch((error) => {
|
|
126
|
-
(0,
|
|
156
|
+
(0, cli_utilities_1.handleAndLogError)(error, Object.assign({}, this.importConfig.context));
|
|
127
157
|
});
|
|
128
158
|
if (chunk) {
|
|
129
159
|
let apiContent = (0, orderBy_1.default)((0, values_1.default)(chunk), '_version');
|
|
160
|
+
cli_utilities_1.log.debug(`Processing ${apiContent.length} assets in chunk`, this.importConfig.context);
|
|
130
161
|
if (isVersion && this.assetConfig.importSameStructure) {
|
|
131
|
-
|
|
162
|
+
cli_utilities_1.log.debug('Processing version 1 assets first', this.importConfig.context);
|
|
163
|
+
const versionOneAssets = (0, filter_1.default)(apiContent, ({ _version }) => _version === 1);
|
|
132
164
|
await this.makeConcurrentCall({
|
|
133
165
|
processName,
|
|
134
166
|
indexerCount,
|
|
135
167
|
currentIndexer: +index,
|
|
136
|
-
apiContent:
|
|
168
|
+
apiContent: versionOneAssets,
|
|
137
169
|
apiParams: {
|
|
138
170
|
reject: onReject,
|
|
139
171
|
resolve: onSuccess,
|
|
@@ -144,6 +176,7 @@ class ImportAssets extends base_class_1.default {
|
|
|
144
176
|
concurrencyLimit: this.assetConfig.uploadAssetsConcurrency,
|
|
145
177
|
});
|
|
146
178
|
apiContent = (0, filter_1.default)(apiContent, ({ _version }) => _version > 1);
|
|
179
|
+
cli_utilities_1.log.debug(`Processing ${apiContent.length} versioned assets after version 1`, this.importConfig.context);
|
|
147
180
|
}
|
|
148
181
|
await this.makeConcurrentCall({
|
|
149
182
|
apiContent,
|
|
@@ -161,9 +194,17 @@ class ImportAssets extends base_class_1.default {
|
|
|
161
194
|
}, undefined, !isVersion);
|
|
162
195
|
}
|
|
163
196
|
}
|
|
164
|
-
if (!isVersion
|
|
165
|
-
|
|
166
|
-
|
|
197
|
+
if (!isVersion) {
|
|
198
|
+
if (!(0, isEmpty_1.default)(this.assetsUidMap)) {
|
|
199
|
+
const uidMappingCount = Object.keys(this.assetsUidMap).length;
|
|
200
|
+
cli_utilities_1.log.debug(`Writing ${uidMappingCount} UID mappings`, this.importConfig.context);
|
|
201
|
+
this.fs.writeFile(this.assetUidMapperPath, this.assetsUidMap);
|
|
202
|
+
}
|
|
203
|
+
if (!(0, isEmpty_1.default)(this.assetsUrlMap)) {
|
|
204
|
+
const urlMappingCount = Object.keys(this.assetsUrlMap).length;
|
|
205
|
+
cli_utilities_1.log.debug(`Writing ${urlMappingCount} URL mappings`, this.importConfig.context);
|
|
206
|
+
this.fs.writeFile(this.assetUrlMapperPath, this.assetsUrlMap);
|
|
207
|
+
}
|
|
167
208
|
}
|
|
168
209
|
}
|
|
169
210
|
/**
|
|
@@ -175,24 +216,27 @@ class ImportAssets extends base_class_1.default {
|
|
|
175
216
|
const { apiData: asset } = apiOptions;
|
|
176
217
|
if (!this.assetConfig.importSameStructure &&
|
|
177
218
|
!this.assetConfig.includeVersionedAssets &&
|
|
178
|
-
/* eslint-disable @typescript-eslint/no-unused-vars, no-prototype-builtins */
|
|
179
219
|
this.assetsUidMap.hasOwnProperty(asset.uid)) {
|
|
180
|
-
|
|
220
|
+
cli_utilities_1.log.info(`Skipping existing asset: ${asset.uid} (${asset.title})`, this.importConfig.context);
|
|
181
221
|
apiOptions.entity = undefined;
|
|
182
222
|
return apiOptions;
|
|
183
223
|
}
|
|
184
224
|
asset.upload = (0, node_path_1.join)(this.assetsPath, 'files', asset.uid, asset.filename);
|
|
225
|
+
cli_utilities_1.log.debug(`Asset file path resolved to: ${asset.upload}`, this.importConfig.context);
|
|
185
226
|
if (asset.parent_uid) {
|
|
227
|
+
const originalParent = asset.parent_uid;
|
|
186
228
|
asset.parent_uid = this.assetsFolderMap[asset.parent_uid];
|
|
229
|
+
cli_utilities_1.log.debug(`Mapped parent UID: ${originalParent} → ${asset.parent_uid}`, this.importConfig.context);
|
|
187
230
|
}
|
|
188
231
|
else if (this.importConfig.replaceExisting) {
|
|
189
|
-
// adds the root folder as parent for all assets in the root level
|
|
190
232
|
asset.parent_uid = this.assetsFolderMap[this.rootFolder.uid];
|
|
233
|
+
cli_utilities_1.log.debug(`Assigned root folder as parent: ${asset.parent_uid}`, this.importConfig.context);
|
|
191
234
|
}
|
|
192
235
|
apiOptions.apiData = asset;
|
|
193
236
|
if (this.assetsUidMap[asset.uid] && this.assetConfig.importSameStructure) {
|
|
194
237
|
apiOptions.entity = 'replace-assets';
|
|
195
238
|
apiOptions.uid = this.assetsUidMap[asset.uid];
|
|
239
|
+
cli_utilities_1.log.debug(`Preparing to replace asset: ${asset.uid} → ${apiOptions.uid}`, this.importConfig.context);
|
|
196
240
|
}
|
|
197
241
|
return apiOptions;
|
|
198
242
|
}
|
|
@@ -203,16 +247,18 @@ class ImportAssets extends base_class_1.default {
|
|
|
203
247
|
async publish() {
|
|
204
248
|
const fs = new cli_utilities_1.FsUtility({ basePath: this.assetsPath, indexFileName: 'assets.json' });
|
|
205
249
|
if ((0, isEmpty_1.default)(this.assetsUidMap)) {
|
|
250
|
+
cli_utilities_1.log.debug('Loading asset UID mappings from file', this.importConfig.context);
|
|
206
251
|
this.assetsUidMap = fs.readFile(this.assetUidMapperPath, true);
|
|
207
252
|
}
|
|
208
253
|
const indexer = fs.indexFileContent;
|
|
209
254
|
const indexerCount = (0, values_1.default)(indexer).length;
|
|
255
|
+
cli_utilities_1.log.debug(`Found ${indexerCount} asset chunks to publish`, this.importConfig.context);
|
|
210
256
|
const onSuccess = ({ apiData: { uid, title } = undefined }) => {
|
|
211
|
-
|
|
257
|
+
cli_utilities_1.log.success(`Asset '${uid}: ${title}' published successfully`, this.importConfig.context);
|
|
212
258
|
};
|
|
213
259
|
const onReject = ({ error, apiData: { uid, title } = undefined }) => {
|
|
214
|
-
|
|
215
|
-
(0,
|
|
260
|
+
cli_utilities_1.log.error(`Asset '${uid}: ${title}' not published`, this.importConfig.context);
|
|
261
|
+
(0, cli_utilities_1.handleAndLogError)(error, Object.assign(Object.assign({}, this.importConfig.context), { uid, title }));
|
|
216
262
|
};
|
|
217
263
|
const serializeData = (apiOptions) => {
|
|
218
264
|
const { apiData: asset } = apiOptions;
|
|
@@ -224,21 +270,26 @@ class ImportAssets extends base_class_1.default {
|
|
|
224
270
|
const environments = (0, uniq_1.default)((0, map_1.default)(publishDetails, ({ environment }) => this.environments[environment].name));
|
|
225
271
|
const locales = (0, uniq_1.default)((0, map_1.default)(publishDetails, 'locale'));
|
|
226
272
|
if (environments.length === 0 || locales.length === 0) {
|
|
273
|
+
cli_utilities_1.log.debug(`Skipping publish for asset ${asset.uid} - no valid environments/locales`, this.importConfig.context);
|
|
227
274
|
apiOptions.entity = undefined;
|
|
228
275
|
return apiOptions;
|
|
229
276
|
}
|
|
230
277
|
asset.locales = locales;
|
|
231
278
|
asset.environments = environments;
|
|
232
279
|
apiOptions.apiData.publishDetails = { locales, environments };
|
|
280
|
+
cli_utilities_1.log.debug(`Prepared publish details for asset ${asset.uid}`, this.importConfig.context);
|
|
233
281
|
}
|
|
234
282
|
apiOptions.uid = this.assetsUidMap[asset.uid];
|
|
235
|
-
if (!apiOptions.uid)
|
|
283
|
+
if (!apiOptions.uid) {
|
|
284
|
+
cli_utilities_1.log.debug(`Skipping publish for asset ${asset.uid} - no UID mapping found`, this.importConfig.context);
|
|
236
285
|
apiOptions.entity = undefined;
|
|
286
|
+
}
|
|
237
287
|
return apiOptions;
|
|
238
288
|
};
|
|
239
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
240
289
|
for (const index in indexer) {
|
|
290
|
+
cli_utilities_1.log.debug(`Processing publish chunk ${index} of ${indexerCount}`, this.importConfig.context);
|
|
241
291
|
const apiContent = (0, filter_1.default)((0, values_1.default)(await fs.readChunkFiles.next()), ({ publish_details }) => !(0, isEmpty_1.default)(publish_details));
|
|
292
|
+
cli_utilities_1.log.debug(`Found ${apiContent.length} publishable assets in chunk`, this.importConfig.context);
|
|
242
293
|
await this.makeConcurrentCall({
|
|
243
294
|
apiContent,
|
|
244
295
|
indexerCount,
|
|
@@ -262,19 +313,22 @@ class ImportAssets extends base_class_1.default {
|
|
|
262
313
|
*/
|
|
263
314
|
constructFolderImportOrder(folders) {
|
|
264
315
|
let parentUIds = [];
|
|
265
|
-
// NOTE: Read root folder
|
|
266
316
|
const importOrder = (0, filter_1.default)(folders, { parent_uid: null }).map(({ uid, name, parent_uid, created_at }) => {
|
|
267
317
|
parentUIds.push(uid);
|
|
268
318
|
return { uid, name, parent_uid, created_at };
|
|
269
319
|
});
|
|
320
|
+
cli_utilities_1.log.debug(`Found ${importOrder.length} root folders`, this.importConfig.context);
|
|
270
321
|
while (!(0, isEmpty_1.default)(parentUIds)) {
|
|
271
322
|
// NOTE: Read nested folders every iteration until we find empty folders
|
|
272
|
-
|
|
323
|
+
const nestedFolders = (0, filter_1.default)(folders, ({ parent_uid }) => (0, includes_1.default)(parentUIds, parent_uid));
|
|
324
|
+
cli_utilities_1.log.debug(`Processing ${nestedFolders.length} nested folders`, this.importConfig.context);
|
|
325
|
+
parentUIds = nestedFolders.map(({ uid, name, parent_uid, created_at }) => {
|
|
273
326
|
importOrder.push({ uid, name, parent_uid, created_at });
|
|
274
327
|
return uid;
|
|
275
328
|
});
|
|
276
329
|
}
|
|
277
330
|
if (this.importConfig.replaceExisting) {
|
|
331
|
+
cli_utilities_1.log.debug('Setting up root folder for import', this.importConfig.context);
|
|
278
332
|
// Note: adds a root folder to distinguish latest asset uploads
|
|
279
333
|
// Todo: This temporary approach should be updated with asset and folder overwrite strategy, which follows
|
|
280
334
|
// folder overwrite
|
|
@@ -295,8 +349,8 @@ class ImportAssets extends base_class_1.default {
|
|
|
295
349
|
importOrder.splice(index, 1, Object.assign(Object.assign({}, folder), { parent_uid: this.rootFolder.uid }));
|
|
296
350
|
}
|
|
297
351
|
});
|
|
298
|
-
// NOTE: Adds root folder
|
|
299
352
|
importOrder.unshift(this.rootFolder);
|
|
353
|
+
cli_utilities_1.log.debug('Added root folder to import order', this.importConfig.context);
|
|
300
354
|
}
|
|
301
355
|
return importOrder;
|
|
302
356
|
}
|