@contentstack/cli-cm-import 1.8.0 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/import/modules/base-class.d.ts +2 -2
- package/lib/import/modules/workflows.d.ts +1 -0
- package/lib/import/modules/workflows.js +44 -5
- package/lib/import/modules-js/entries.js +9 -2
- package/lib/import/modules-js/global-fields.js +0 -1
- package/lib/import/modules-js/workflows.d.ts +1 -0
- package/lib/import/modules-js/workflows.js +38 -4
- package/lib/utils/asset-helper.js +13 -1
- package/lib/utils/content-type-helper.js +10 -3
- package/lib/utils/entries-helper.js +7 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
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.8.
|
|
50
|
+
@contentstack/cli-cm-import/1.8.2 linux-x64 node-v18.17.1
|
|
51
51
|
$ csdx --help [COMMAND]
|
|
52
52
|
USAGE
|
|
53
53
|
$ csdx COMMAND
|
|
@@ -9,8 +9,8 @@ export type ApiOptions = {
|
|
|
9
9
|
url?: string;
|
|
10
10
|
entity: ApiModuleType;
|
|
11
11
|
apiData?: Record<any, any> | any;
|
|
12
|
-
resolve: (value: any) => void;
|
|
13
|
-
reject: (error: any) => void;
|
|
12
|
+
resolve: (value: any) => Promise<void> | void;
|
|
13
|
+
reject: (error: any) => Promise<void> | void;
|
|
14
14
|
additionalInfo?: Record<any, any>;
|
|
15
15
|
includeParamOnCompletion?: boolean;
|
|
16
16
|
serializeData?: (input: ApiOptions) => any;
|
|
@@ -20,6 +20,7 @@ export default class ImportWorkflows extends BaseClass {
|
|
|
20
20
|
start(): Promise<void>;
|
|
21
21
|
getRoles(): Promise<void>;
|
|
22
22
|
importWorkflows(): Promise<void>;
|
|
23
|
+
updateNextAvailableStagesUid(workflow: Record<string, any>, newWorkflowStages: Record<string, any>[], oldWorkflowStages: Record<string, any>[]): Promise<import("@contentstack/management/types/stack/workflow").Workflow>;
|
|
23
24
|
/**
|
|
24
25
|
* @method serializeWorkflows
|
|
25
26
|
* @param {ApiOptions} apiOptions ApiOptions
|
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
5
|
-
const
|
|
6
|
-
const values_1 = tslib_1.__importDefault(require("lodash/values"));
|
|
5
|
+
const map_1 = tslib_1.__importDefault(require("lodash/map"));
|
|
7
6
|
const find_1 = tslib_1.__importDefault(require("lodash/find"));
|
|
8
|
-
const findIndex_1 = tslib_1.__importDefault(require("lodash/findIndex"));
|
|
9
7
|
const node_path_1 = require("node:path");
|
|
8
|
+
const values_1 = tslib_1.__importDefault(require("lodash/values"));
|
|
9
|
+
const filter_1 = tslib_1.__importDefault(require("lodash/filter"));
|
|
10
|
+
const isEmpty_1 = tslib_1.__importDefault(require("lodash/isEmpty"));
|
|
11
|
+
const cloneDeep_1 = tslib_1.__importDefault(require("lodash/cloneDeep"));
|
|
12
|
+
const findIndex_1 = tslib_1.__importDefault(require("lodash/findIndex"));
|
|
10
13
|
const config_1 = tslib_1.__importDefault(require("../../config"));
|
|
11
14
|
const base_class_1 = tslib_1.__importDefault(require("./base-class"));
|
|
12
15
|
const utils_1 = require("../../utils");
|
|
@@ -66,19 +69,30 @@ class ImportWorkflows extends base_class_1.default {
|
|
|
66
69
|
.fetchAll()
|
|
67
70
|
.then((data) => data)
|
|
68
71
|
.catch((err) => (0, utils_1.log)(this.importConfig, `Failed to fetch roles. ${(0, utils_1.formatError)(err)}`, 'error'));
|
|
69
|
-
for (const role of roles === null || roles === void 0 ? void 0 : roles.items) {
|
|
72
|
+
for (const role of (roles === null || roles === void 0 ? void 0 : roles.items) || []) {
|
|
70
73
|
this.roleNameMap[role.name] = role.uid;
|
|
71
74
|
}
|
|
72
75
|
}
|
|
73
76
|
async importWorkflows() {
|
|
74
77
|
const apiContent = (0, values_1.default)(this.workflows);
|
|
78
|
+
const oldWorkflows = (0, cloneDeep_1.default)((0, values_1.default)(this.workflows));
|
|
75
79
|
//check and create custom roles if not exists
|
|
76
80
|
for (const workflow of (0, values_1.default)(this.workflows)) {
|
|
77
81
|
if (!this.workflowUidMapper.hasOwnProperty(workflow.uid)) {
|
|
78
82
|
await this.createCustomRoleIfNotExists(workflow);
|
|
79
83
|
}
|
|
80
84
|
}
|
|
81
|
-
const onSuccess = ({ response, apiData: { uid, name } = { uid: null, name: '' } }) => {
|
|
85
|
+
const onSuccess = async ({ response, apiData: { uid, name } = { uid: null, name: '' } }) => {
|
|
86
|
+
var _a;
|
|
87
|
+
const oldWorkflowStages = (_a = (0, find_1.default)(oldWorkflows, { uid })) === null || _a === void 0 ? void 0 : _a.workflow_stages;
|
|
88
|
+
if (!(0, isEmpty_1.default)((0, filter_1.default)(oldWorkflowStages, ({ next_available_stages }) => !(0, isEmpty_1.default)(next_available_stages)))) {
|
|
89
|
+
let updateRresponse = await this.updateNextAvailableStagesUid(response, response.workflow_stages, oldWorkflowStages).catch((error) => {
|
|
90
|
+
(0, utils_1.log)(this.importConfig, `Workflow '${name}' update failed.`, 'error');
|
|
91
|
+
(0, utils_1.log)(this.importConfig, error, 'error');
|
|
92
|
+
});
|
|
93
|
+
if (updateRresponse)
|
|
94
|
+
response = updateRresponse;
|
|
95
|
+
}
|
|
82
96
|
this.createdWorkflows.push(response);
|
|
83
97
|
this.workflowUidMapper[uid] = response.uid;
|
|
84
98
|
(0, utils_1.log)(this.importConfig, `Workflow '${name}' imported successfully`, 'success');
|
|
@@ -114,6 +128,25 @@ class ImportWorkflows extends base_class_1.default {
|
|
|
114
128
|
concurrencyLimit: config_1.default.fetchConcurrency || 1,
|
|
115
129
|
}, undefined, false);
|
|
116
130
|
}
|
|
131
|
+
updateNextAvailableStagesUid(workflow, newWorkflowStages, oldWorkflowStages) {
|
|
132
|
+
newWorkflowStages = (0, map_1.default)(newWorkflowStages, (newStage, index) => {
|
|
133
|
+
const oldStage = oldWorkflowStages[index];
|
|
134
|
+
if (!(0, isEmpty_1.default)(oldStage.next_available_stages)) {
|
|
135
|
+
newStage.next_available_stages = (0, map_1.default)(oldStage.next_available_stages, (stageUid) => {
|
|
136
|
+
var _a, _b;
|
|
137
|
+
if (stageUid === '$all')
|
|
138
|
+
return stageUid;
|
|
139
|
+
const stageName = (_a = (0, find_1.default)(oldWorkflowStages, { uid: stageUid })) === null || _a === void 0 ? void 0 : _a.name;
|
|
140
|
+
return (_b = (0, find_1.default)(newWorkflowStages, { name: stageName })) === null || _b === void 0 ? void 0 : _b.uid;
|
|
141
|
+
}).filter((val) => val);
|
|
142
|
+
}
|
|
143
|
+
return newStage;
|
|
144
|
+
});
|
|
145
|
+
workflow.workflow_stages = newWorkflowStages;
|
|
146
|
+
const updateWorkflow = this.stack.workflow(workflow.uid);
|
|
147
|
+
Object.assign(updateWorkflow, workflow);
|
|
148
|
+
return updateWorkflow.update();
|
|
149
|
+
}
|
|
117
150
|
/**
|
|
118
151
|
* @method serializeWorkflows
|
|
119
152
|
* @param {ApiOptions} apiOptions ApiOptions
|
|
@@ -134,6 +167,12 @@ class ImportWorkflows extends base_class_1.default {
|
|
|
134
167
|
if (!workflow.branches) {
|
|
135
168
|
workflow.branches = ['main'];
|
|
136
169
|
}
|
|
170
|
+
for (const stage of workflow.workflow_stages) {
|
|
171
|
+
delete stage.uid;
|
|
172
|
+
if (!(0, isEmpty_1.default)(stage.next_available_stages)) {
|
|
173
|
+
stage.next_available_stages = ['$all'];
|
|
174
|
+
}
|
|
175
|
+
}
|
|
137
176
|
apiOptions.apiData = workflow;
|
|
138
177
|
}
|
|
139
178
|
return apiOptions;
|
|
@@ -395,12 +395,12 @@ module.exports = class ImportEntries {
|
|
|
395
395
|
})
|
|
396
396
|
.catch((error) => {
|
|
397
397
|
var _a, _b;
|
|
398
|
-
let title = (_b = JSON.parse(((_a = error === null || error === void 0 ? void 0 : error.request) === null || _a === void 0 ? void 0 : _a.data) ||
|
|
398
|
+
let title = (_b = JSON.parse(((_a = error === null || error === void 0 ? void 0 : error.request) === null || _a === void 0 ? void 0 : _a.data) || '{}').entry) === null || _b === void 0 ? void 0 : _b.title;
|
|
399
399
|
addlogs(this.config, chalk.red("Failed to create entries in '" +
|
|
400
400
|
lang +
|
|
401
401
|
"' language. " +
|
|
402
402
|
'Title of the failed entry: ' +
|
|
403
|
-
`'${title ||
|
|
403
|
+
`'${title || ''}'`), 'error');
|
|
404
404
|
return reject(error);
|
|
405
405
|
});
|
|
406
406
|
});
|
|
@@ -990,6 +990,7 @@ module.exports = class ImportEntries {
|
|
|
990
990
|
break;
|
|
991
991
|
}
|
|
992
992
|
case 'json': {
|
|
993
|
+
const structuredPTag = '{"type":"p","attrs":{},"children":[{"text":""}]}';
|
|
993
994
|
if (entry[element.uid] && element.field_metadata.rich_text_type) {
|
|
994
995
|
if (element.multiple) {
|
|
995
996
|
entry[element.uid] = entry[element.uid].map((jsonRteData) => {
|
|
@@ -997,6 +998,9 @@ module.exports = class ImportEntries {
|
|
|
997
998
|
let entryReferences = jsonRteData.children.filter((e) => this.doEntryReferencesExist(e));
|
|
998
999
|
if (entryReferences.length > 0) {
|
|
999
1000
|
jsonRteData.children = jsonRteData.children.filter((e) => !this.doEntryReferencesExist(e));
|
|
1001
|
+
if (jsonRteData.children.length === 0) {
|
|
1002
|
+
jsonRteData.children.push(JSON.parse(structuredPTag));
|
|
1003
|
+
}
|
|
1000
1004
|
return jsonRteData; // return jsonRteData without entry references
|
|
1001
1005
|
}
|
|
1002
1006
|
else {
|
|
@@ -1008,6 +1012,9 @@ module.exports = class ImportEntries {
|
|
|
1008
1012
|
let entryReferences = entry[element.uid].children.filter((e) => this.doEntryReferencesExist(e));
|
|
1009
1013
|
if (entryReferences.length > 0) {
|
|
1010
1014
|
entry[element.uid].children = entry[element.uid].children.filter((e) => !this.doEntryReferencesExist(e));
|
|
1015
|
+
if (entry[element.uid].children.length === 0) {
|
|
1016
|
+
entry[element.uid].children.push(JSON.parse(structuredPTag));
|
|
1017
|
+
}
|
|
1011
1018
|
}
|
|
1012
1019
|
}
|
|
1013
1020
|
}
|
|
@@ -71,7 +71,6 @@ module.exports = class ImportGlobalFields {
|
|
|
71
71
|
log(self.config, chalk.green('Global field ' + global_field_uid + ' created successfully'), 'success');
|
|
72
72
|
})
|
|
73
73
|
.catch(function (err) {
|
|
74
|
-
console.log;
|
|
75
74
|
let error = JSON.parse(err.message);
|
|
76
75
|
if (error.errors.title) {
|
|
77
76
|
// eslint-disable-next-line no-undef
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
* Copyright (c) 2019 Contentstack LLC
|
|
4
4
|
* MIT Licensed
|
|
5
5
|
*/
|
|
6
|
-
const mkdirp = require('mkdirp');
|
|
7
6
|
const fs = require('fs');
|
|
8
7
|
const path = require('path');
|
|
9
|
-
const Promise = require('bluebird');
|
|
10
8
|
const chalk = require('chalk');
|
|
11
|
-
const
|
|
9
|
+
const mkdirp = require('mkdirp');
|
|
10
|
+
const Promise = require('bluebird');
|
|
11
|
+
const { isEmpty, merge, filter, map, cloneDeep, find } = require('lodash');
|
|
12
12
|
let { default: config } = require('../../config');
|
|
13
13
|
const { fileHelper, log, formatError } = require('../../utils');
|
|
14
14
|
module.exports = class importWorkflows {
|
|
@@ -46,11 +46,16 @@ module.exports = class importWorkflows {
|
|
|
46
46
|
if (!self.workflowUidMapper.hasOwnProperty(workflowUid)) {
|
|
47
47
|
const roleNameMap = {};
|
|
48
48
|
const workflowStages = workflow.workflow_stages;
|
|
49
|
+
const oldWorkflowStages = cloneDeep(workflow.workflow_stages);
|
|
49
50
|
const roles = await self.stackAPIClient.role().fetchAll();
|
|
50
51
|
for (const role of roles.items) {
|
|
51
52
|
roleNameMap[role.name] = role.uid;
|
|
52
53
|
}
|
|
53
54
|
for (const stage of workflowStages) {
|
|
55
|
+
delete stage.uid;
|
|
56
|
+
if (!isEmpty(stage.next_available_stages)) {
|
|
57
|
+
stage.next_available_stages = ['$all'];
|
|
58
|
+
}
|
|
54
59
|
if (stage.SYS_ACL.users.uids.length && stage.SYS_ACL.users.uids[0] !== '$all') {
|
|
55
60
|
stage.SYS_ACL.users.uids = ['$all'];
|
|
56
61
|
}
|
|
@@ -94,7 +99,17 @@ module.exports = class importWorkflows {
|
|
|
94
99
|
return self.stackAPIClient
|
|
95
100
|
.workflow()
|
|
96
101
|
.create({ workflow })
|
|
97
|
-
.then(function (response) {
|
|
102
|
+
.then(async function (response) {
|
|
103
|
+
if (!isEmpty(filter(oldWorkflowStages, ({ next_available_stages }) => !isEmpty(next_available_stages)))) {
|
|
104
|
+
let updateRresponse = await self
|
|
105
|
+
.updateNextAvailableStagesUid(response, response.workflow_stages, oldWorkflowStages)
|
|
106
|
+
.catch((error) => {
|
|
107
|
+
log(self.config, `Workflow '${workflow.name}' update failed.`, 'error');
|
|
108
|
+
log(self.config, error, 'error');
|
|
109
|
+
});
|
|
110
|
+
if (updateRresponse)
|
|
111
|
+
response = updateRresponse;
|
|
112
|
+
}
|
|
98
113
|
self.workflowUidMapper[workflowUid] = response;
|
|
99
114
|
fileHelper.writeFileSync(workflowUidMapperPath, self.workflowUidMapper);
|
|
100
115
|
})
|
|
@@ -129,4 +144,23 @@ module.exports = class importWorkflows {
|
|
|
129
144
|
});
|
|
130
145
|
});
|
|
131
146
|
}
|
|
147
|
+
updateNextAvailableStagesUid(workflow, newWorkflowStages, oldWorkflowStages) {
|
|
148
|
+
newWorkflowStages = map(newWorkflowStages, (newStage, index) => {
|
|
149
|
+
const oldStage = oldWorkflowStages[index];
|
|
150
|
+
if (!isEmpty(oldStage.next_available_stages)) {
|
|
151
|
+
newStage.next_available_stages = map(oldStage.next_available_stages, (stageUid) => {
|
|
152
|
+
var _a, _b;
|
|
153
|
+
if (stageUid === '$all')
|
|
154
|
+
return stageUid;
|
|
155
|
+
const stageName = (_a = find(oldWorkflowStages, { uid: stageUid })) === null || _a === void 0 ? void 0 : _a.name;
|
|
156
|
+
return (_b = find(newWorkflowStages, { name: stageName })) === null || _b === void 0 ? void 0 : _b.uid;
|
|
157
|
+
}).filter((val) => val);
|
|
158
|
+
}
|
|
159
|
+
return newStage;
|
|
160
|
+
});
|
|
161
|
+
workflow.workflow_stages = newWorkflowStages;
|
|
162
|
+
const updateWorkflow = this.stackAPIClient.workflow(workflow.uid);
|
|
163
|
+
Object.assign(updateWorkflow, workflow);
|
|
164
|
+
return updateWorkflow.update();
|
|
165
|
+
}
|
|
132
166
|
};
|
|
@@ -75,6 +75,9 @@ const lookupAssets = function (data, mappedAssetUids, mappedAssetUrls, assetUidM
|
|
|
75
75
|
(schema[i].field_metadata.markdown || schema[i].field_metadata.rich_text_type)) {
|
|
76
76
|
parent.push(schema[i].uid);
|
|
77
77
|
findFileUrls(schema[i], entryToFind, assetUrls);
|
|
78
|
+
if (schema[i].field_metadata.rich_text_type) {
|
|
79
|
+
findAssetIdsFromHtmlRte(entryToFind, schema[i]);
|
|
80
|
+
}
|
|
78
81
|
parent.pop();
|
|
79
82
|
}
|
|
80
83
|
if (schema[i].data_type === 'group' || schema[i].data_type === 'global_field') {
|
|
@@ -131,6 +134,14 @@ const lookupAssets = function (data, mappedAssetUids, mappedAssetUrls, assetUidM
|
|
|
131
134
|
return row;
|
|
132
135
|
});
|
|
133
136
|
}
|
|
137
|
+
function findAssetIdsFromHtmlRte(entryObj, ctSchema) {
|
|
138
|
+
const regex = /<img asset_uid=\\"([^"]+)\\"/g;
|
|
139
|
+
let match;
|
|
140
|
+
const entry = JSON.stringify(entryObj);
|
|
141
|
+
while ((match = regex.exec(entry)) !== null) {
|
|
142
|
+
assetUids.push(match[1]);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
134
145
|
function findAssetIdsFromJsonRte(entryObj, ctSchema) {
|
|
135
146
|
for (const element of ctSchema) {
|
|
136
147
|
switch (element.data_type) {
|
|
@@ -329,7 +340,8 @@ function findFileUrls(schema, _entry, assetUrls) {
|
|
|
329
340
|
markdownRegEx = new RegExp('(https://(assets|(eu-|azure-na-|azure-eu-)?images).contentstack.(io|com)/v3/assets/(.*?)/(.*?)/(.*?)/(.*?)(?="))', 'g');
|
|
330
341
|
while ((markdownMatch = markdownRegEx.exec(text)) !== null) {
|
|
331
342
|
if (markdownMatch && typeof markdownMatch[0] === 'string') {
|
|
332
|
-
|
|
343
|
+
let assetUrl = markdownMatch[0].replace(/\\/g, '');
|
|
344
|
+
assetUrls.push(assetUrl);
|
|
333
345
|
}
|
|
334
346
|
}
|
|
335
347
|
}
|
|
@@ -89,7 +89,7 @@ const removeReferenceFields = async function (schema, flag = { supressed: false
|
|
|
89
89
|
await (0, exports.removeReferenceFields)(schema[i].blocks[block].schema, flag, stackAPIClient);
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
-
else if (schema[i].data_type === 'reference'
|
|
92
|
+
else if (schema[i].data_type === 'reference') {
|
|
93
93
|
flag.supressed = true;
|
|
94
94
|
// Check if content-type exists
|
|
95
95
|
// If exists, then no change should be required.
|
|
@@ -128,7 +128,15 @@ const removeReferenceFields = async function (schema, flag = { supressed: false
|
|
|
128
128
|
});
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
|
-
|
|
131
|
+
}
|
|
132
|
+
else if (
|
|
133
|
+
// handling entry references in json rte
|
|
134
|
+
schema[i].data_type === 'json' &&
|
|
135
|
+
schema[i].field_metadata.allow_json_rte &&
|
|
136
|
+
schema[i].field_metadata.embed_entry &&
|
|
137
|
+
schema[i].reference_to.length > 1) {
|
|
138
|
+
flag.supressed = true;
|
|
139
|
+
schema[i].reference_to = ['sys_assets'];
|
|
132
140
|
}
|
|
133
141
|
else if (
|
|
134
142
|
// handling entry references in json rte
|
|
@@ -138,7 +146,6 @@ const removeReferenceFields = async function (schema, flag = { supressed: false
|
|
|
138
146
|
schema[i].reference_to.length > 1) {
|
|
139
147
|
flag.supressed = true;
|
|
140
148
|
schema[i].reference_to = ['sys_assets'];
|
|
141
|
-
return true;
|
|
142
149
|
}
|
|
143
150
|
}
|
|
144
151
|
};
|
|
@@ -372,6 +372,7 @@ const removeEntryRefsFromJSONRTE = (entry, ctSchema) => {
|
|
|
372
372
|
break;
|
|
373
373
|
}
|
|
374
374
|
case 'json': {
|
|
375
|
+
const structuredPTag = '{"type":"p","attrs":{},"children":[{"text":""}]}';
|
|
375
376
|
if (entry[element.uid] && element.field_metadata.rich_text_type) {
|
|
376
377
|
if (element.multiple) {
|
|
377
378
|
entry[element.uid] = entry[element.uid].map((jsonRteData) => {
|
|
@@ -379,6 +380,9 @@ const removeEntryRefsFromJSONRTE = (entry, ctSchema) => {
|
|
|
379
380
|
let entryReferences = jsonRteData.children.filter((e) => doEntryReferencesExist(e));
|
|
380
381
|
if (entryReferences.length > 0) {
|
|
381
382
|
jsonRteData.children = jsonRteData.children.filter((e) => !doEntryReferencesExist(e));
|
|
383
|
+
if (jsonRteData.children.length === 0) { // empty children array are no longer acceptable by the API, a default structure must be there
|
|
384
|
+
jsonRteData.children.push(JSON.parse(structuredPTag));
|
|
385
|
+
}
|
|
382
386
|
return jsonRteData; // return jsonRteData without entry references
|
|
383
387
|
}
|
|
384
388
|
else {
|
|
@@ -390,6 +394,9 @@ const removeEntryRefsFromJSONRTE = (entry, ctSchema) => {
|
|
|
390
394
|
let entryReferences = entry[element.uid].children.filter((e) => doEntryReferencesExist(e));
|
|
391
395
|
if (entryReferences.length > 0) {
|
|
392
396
|
entry[element.uid].children = entry[element.uid].children.filter((e) => !doEntryReferencesExist(e));
|
|
397
|
+
if (entry[element.uid].children.length === 0) {
|
|
398
|
+
entry[element.uid].children.push(JSON.parse(structuredPTag));
|
|
399
|
+
}
|
|
393
400
|
}
|
|
394
401
|
}
|
|
395
402
|
}
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED