@contentstack/cli-cm-import 1.7.1 → 1.8.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 +1 -1
- package/lib/config/index.js +2 -0
- package/lib/import/module-importer.js +15 -0
- package/lib/import/modules/base-class.d.ts +1 -1
- package/lib/import/modules/base-class.js +31 -1
- package/lib/import/modules/content-types.js +1 -1
- package/lib/import/modules/entries.d.ts +93 -0
- package/lib/import/modules/entries.js +536 -0
- package/lib/import/modules/marketplace-apps.d.ts +5 -2
- package/lib/import/modules/marketplace-apps.js +21 -16
- package/lib/import/modules/workflows.d.ts +41 -0
- package/lib/import/modules/workflows.js +209 -0
- package/lib/import/modules-js/custom-roles.js +1 -1
- package/lib/import/modules-js/entries.js +20 -7
- package/lib/import/modules-js/environments.js +1 -1
- package/lib/import/modules-js/extensions.js +1 -1
- package/lib/import/modules-js/global-fields.js +2 -4
- package/lib/import/modules-js/marketplace-apps.d.ts +6 -4
- package/lib/import/modules-js/marketplace-apps.js +19 -15
- package/lib/import/modules-js/workflows.js +2 -2
- package/lib/types/default-config.d.ts +1 -0
- package/lib/utils/asset-helper.d.ts +1 -1
- package/lib/utils/asset-helper.js +13 -1
- package/lib/utils/backup-handler.js +10 -1
- package/lib/utils/content-type-helper.js +10 -3
- package/lib/utils/entries-helper.d.ts +4 -1
- package/lib/utils/entries-helper.js +329 -2
- package/lib/utils/file-helper.d.ts +1 -0
- package/lib/utils/file-helper.js +5 -1
- package/lib/utils/import-config-handler.js +1 -1
- package/lib/utils/index.d.ts +2 -2
- package/lib/utils/index.js +4 -1
- package/oclif.manifest.json +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import BaseClass, { ApiOptions } from './base-class';
|
|
2
|
+
import { ModuleClassParams } from '../../types';
|
|
3
|
+
export default class ImportWorkflows extends BaseClass {
|
|
4
|
+
private mapperDirPath;
|
|
5
|
+
private workflowsFolderPath;
|
|
6
|
+
private workflowUidMapperPath;
|
|
7
|
+
private createdWorkflowsPath;
|
|
8
|
+
private failedWorkflowsPath;
|
|
9
|
+
private workflowsConfig;
|
|
10
|
+
private workflows;
|
|
11
|
+
private workflowUidMapper;
|
|
12
|
+
private createdWorkflows;
|
|
13
|
+
private failedWebhooks;
|
|
14
|
+
private roleNameMap;
|
|
15
|
+
constructor({ importConfig, stackAPIClient }: ModuleClassParams);
|
|
16
|
+
/**
|
|
17
|
+
* @method start
|
|
18
|
+
* @returns {Promise<void>} Promise<void>
|
|
19
|
+
*/
|
|
20
|
+
start(): Promise<void>;
|
|
21
|
+
getRoles(): Promise<void>;
|
|
22
|
+
importWorkflows(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* @method serializeWorkflows
|
|
25
|
+
* @param {ApiOptions} apiOptions ApiOptions
|
|
26
|
+
* @returns {ApiOptions} ApiOptions
|
|
27
|
+
*/
|
|
28
|
+
serializeWorkflows(apiOptions: ApiOptions): ApiOptions;
|
|
29
|
+
createCustomRoleIfNotExists(workflow: Record<string, any>): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* @method serializeCustomRoles
|
|
32
|
+
* @param {ApiOptions} apiOptions ApiOptions
|
|
33
|
+
* @returns {ApiOptions} ApiOptions
|
|
34
|
+
*/
|
|
35
|
+
serializeCustomRoles(apiOptions: ApiOptions): ApiOptions;
|
|
36
|
+
updateRoleData(params: {
|
|
37
|
+
workflowUid: string;
|
|
38
|
+
stageIndex: number;
|
|
39
|
+
roleData: any;
|
|
40
|
+
}): void;
|
|
41
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
5
|
+
const isEmpty_1 = tslib_1.__importDefault(require("lodash/isEmpty"));
|
|
6
|
+
const values_1 = tslib_1.__importDefault(require("lodash/values"));
|
|
7
|
+
const find_1 = tslib_1.__importDefault(require("lodash/find"));
|
|
8
|
+
const findIndex_1 = tslib_1.__importDefault(require("lodash/findIndex"));
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const config_1 = tslib_1.__importDefault(require("../../config"));
|
|
11
|
+
const base_class_1 = tslib_1.__importDefault(require("./base-class"));
|
|
12
|
+
const utils_1 = require("../../utils");
|
|
13
|
+
class ImportWorkflows extends base_class_1.default {
|
|
14
|
+
constructor({ importConfig, stackAPIClient }) {
|
|
15
|
+
super({ importConfig, stackAPIClient });
|
|
16
|
+
this.workflowsConfig = config_1.default.modules.workflows;
|
|
17
|
+
this.mapperDirPath = (0, node_path_1.join)(this.importConfig.backupDir, 'mapper', 'workflows');
|
|
18
|
+
this.workflowsFolderPath = (0, node_path_1.join)(this.importConfig.backupDir, this.workflowsConfig.dirName);
|
|
19
|
+
this.workflowUidMapperPath = (0, node_path_1.join)(this.mapperDirPath, 'uid-mapping.json');
|
|
20
|
+
this.createdWorkflowsPath = (0, node_path_1.join)(this.mapperDirPath, 'success.json');
|
|
21
|
+
this.failedWorkflowsPath = (0, node_path_1.join)(this.mapperDirPath, 'fails.json');
|
|
22
|
+
this.workflows = {};
|
|
23
|
+
this.failedWebhooks = [];
|
|
24
|
+
this.createdWorkflows = [];
|
|
25
|
+
this.workflowUidMapper = {};
|
|
26
|
+
this.roleNameMap = {};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* @method start
|
|
30
|
+
* @returns {Promise<void>} Promise<void>
|
|
31
|
+
*/
|
|
32
|
+
async start() {
|
|
33
|
+
var _a, _b;
|
|
34
|
+
(0, utils_1.log)(this.importConfig, 'Migrating workflows', 'info');
|
|
35
|
+
//Step1 check folder exists or not
|
|
36
|
+
if (utils_1.fileHelper.fileExistsSync(this.workflowsFolderPath)) {
|
|
37
|
+
this.workflows = utils_1.fsUtil.readFile((0, node_path_1.join)(this.workflowsFolderPath, this.workflowsConfig.fileName), true);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
(0, utils_1.log)(this.importConfig, `No such file or directory - '${this.workflowsFolderPath}'`, 'error');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
//create workflows in mapper directory
|
|
44
|
+
await utils_1.fsUtil.makeDirectory(this.mapperDirPath);
|
|
45
|
+
this.workflowUidMapper = utils_1.fileHelper.fileExistsSync(this.workflowUidMapperPath)
|
|
46
|
+
? utils_1.fsUtil.readFile((0, node_path_1.join)(this.workflowUidMapperPath), true)
|
|
47
|
+
: {};
|
|
48
|
+
if (this.workflows === undefined || (0, isEmpty_1.default)(this.workflows)) {
|
|
49
|
+
(0, utils_1.log)(this.importConfig, 'No Workflow Found', 'info');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
//fetch all roles
|
|
53
|
+
await this.getRoles();
|
|
54
|
+
await this.importWorkflows();
|
|
55
|
+
if ((_a = this.createdWorkflows) === null || _a === void 0 ? void 0 : _a.length) {
|
|
56
|
+
utils_1.fsUtil.writeFile(this.createdWorkflowsPath, this.createdWorkflows);
|
|
57
|
+
}
|
|
58
|
+
if ((_b = this.failedWebhooks) === null || _b === void 0 ? void 0 : _b.length) {
|
|
59
|
+
utils_1.fsUtil.writeFile(this.failedWorkflowsPath, this.failedWebhooks);
|
|
60
|
+
}
|
|
61
|
+
(0, utils_1.log)(this.importConfig, 'Workflows have been imported successfully!', 'success');
|
|
62
|
+
}
|
|
63
|
+
async getRoles() {
|
|
64
|
+
const roles = await this.stack
|
|
65
|
+
.role()
|
|
66
|
+
.fetchAll()
|
|
67
|
+
.then((data) => data)
|
|
68
|
+
.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) {
|
|
70
|
+
this.roleNameMap[role.name] = role.uid;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async importWorkflows() {
|
|
74
|
+
const apiContent = (0, values_1.default)(this.workflows);
|
|
75
|
+
//check and create custom roles if not exists
|
|
76
|
+
for (const workflow of (0, values_1.default)(this.workflows)) {
|
|
77
|
+
if (!this.workflowUidMapper.hasOwnProperty(workflow.uid)) {
|
|
78
|
+
await this.createCustomRoleIfNotExists(workflow);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const onSuccess = ({ response, apiData: { uid, name } = { uid: null, name: '' } }) => {
|
|
82
|
+
this.createdWorkflows.push(response);
|
|
83
|
+
this.workflowUidMapper[uid] = response.uid;
|
|
84
|
+
(0, utils_1.log)(this.importConfig, `Workflow '${name}' imported successfully`, 'success');
|
|
85
|
+
utils_1.fsUtil.writeFile(this.workflowUidMapperPath, this.workflowUidMapper);
|
|
86
|
+
};
|
|
87
|
+
const onReject = ({ error, apiData }) => {
|
|
88
|
+
var _a;
|
|
89
|
+
const err = (error === null || error === void 0 ? void 0 : error.message) ? JSON.parse(error.message) : error;
|
|
90
|
+
const { name } = apiData;
|
|
91
|
+
if ((_a = err === null || err === void 0 ? void 0 : err.errors) === null || _a === void 0 ? void 0 : _a.name) {
|
|
92
|
+
(0, utils_1.log)(this.importConfig, `Workflow '${name}' already exists`, 'info');
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
this.failedWebhooks.push(apiData);
|
|
96
|
+
if (error.errors['workflow_stages.0.users']) {
|
|
97
|
+
(0, utils_1.log)(this.importConfig, "Failed to import Workflows as you've specified certain roles in the Stage transition and access rules section. We currently don't import roles to the stack.", 'error');
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
(0, utils_1.log)(this.importConfig, `Workflow '${name}' failed to be import. ${(0, utils_1.formatError)(error)}`, 'error');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
await this.makeConcurrentCall({
|
|
105
|
+
apiContent,
|
|
106
|
+
processName: 'create workflows',
|
|
107
|
+
apiParams: {
|
|
108
|
+
serializeData: this.serializeWorkflows.bind(this),
|
|
109
|
+
reject: onReject,
|
|
110
|
+
resolve: onSuccess,
|
|
111
|
+
entity: 'create-workflows',
|
|
112
|
+
includeParamOnCompletion: true,
|
|
113
|
+
},
|
|
114
|
+
concurrencyLimit: config_1.default.fetchConcurrency || 1,
|
|
115
|
+
}, undefined, false);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* @method serializeWorkflows
|
|
119
|
+
* @param {ApiOptions} apiOptions ApiOptions
|
|
120
|
+
* @returns {ApiOptions} ApiOptions
|
|
121
|
+
*/
|
|
122
|
+
serializeWorkflows(apiOptions) {
|
|
123
|
+
let { apiData: workflow } = apiOptions;
|
|
124
|
+
if (this.workflowUidMapper.hasOwnProperty(workflow.uid)) {
|
|
125
|
+
(0, utils_1.log)(this.importConfig, `Workflow '${workflow.name}' already exists. Skipping it to avoid duplicates!`, 'info');
|
|
126
|
+
apiOptions.entity = undefined;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
if (workflow.admin_users !== undefined) {
|
|
130
|
+
(0, utils_1.log)(this.importConfig, chalk_1.default.yellow('We are skipping import of `Workflow superuser(s)` from workflow'), 'info');
|
|
131
|
+
delete workflow.admin_users;
|
|
132
|
+
}
|
|
133
|
+
// One branch is required to create workflow.
|
|
134
|
+
if (!workflow.branches) {
|
|
135
|
+
workflow.branches = ['main'];
|
|
136
|
+
}
|
|
137
|
+
apiOptions.apiData = workflow;
|
|
138
|
+
}
|
|
139
|
+
return apiOptions;
|
|
140
|
+
}
|
|
141
|
+
async createCustomRoleIfNotExists(workflow) {
|
|
142
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
143
|
+
const onSuccess = ({ response, apiData, additionalInfo: { workflowUid, stageIndex } }) => {
|
|
144
|
+
const { name } = apiData;
|
|
145
|
+
this.updateRoleData({ workflowUid, stageIndex, roleData: apiData });
|
|
146
|
+
this.roleNameMap[name] = response === null || response === void 0 ? void 0 : response.uid;
|
|
147
|
+
};
|
|
148
|
+
const onReject = ({ error, apiData: { name } = { name: '' } }) => {
|
|
149
|
+
(0, utils_1.log)(this.importConfig, `Failed to create custom roles '${name}'.${(0, utils_1.formatError)(error)}`, 'error');
|
|
150
|
+
};
|
|
151
|
+
const workflowStages = workflow.workflow_stages;
|
|
152
|
+
let stageIndex = 0;
|
|
153
|
+
for (const stage of workflowStages) {
|
|
154
|
+
if (((_c = (_b = (_a = stage === null || stage === void 0 ? void 0 : stage.SYS_ACL) === null || _a === void 0 ? void 0 : _a.users) === null || _b === void 0 ? void 0 : _b.uids) === null || _c === void 0 ? void 0 : _c.length) && ((_e = (_d = stage === null || stage === void 0 ? void 0 : stage.SYS_ACL) === null || _d === void 0 ? void 0 : _d.users) === null || _e === void 0 ? void 0 : _e.uids[0]) !== '$all') {
|
|
155
|
+
stage.SYS_ACL.users.uids = ['$all'];
|
|
156
|
+
}
|
|
157
|
+
if ((_h = (_g = (_f = stage === null || stage === void 0 ? void 0 : stage.SYS_ACL) === null || _f === void 0 ? void 0 : _f.roles) === null || _g === void 0 ? void 0 : _g.uids) === null || _h === void 0 ? void 0 : _h.length) {
|
|
158
|
+
const apiContent = stage.SYS_ACL.roles.uids;
|
|
159
|
+
await this.makeConcurrentCall({
|
|
160
|
+
apiContent,
|
|
161
|
+
processName: 'create custom role',
|
|
162
|
+
apiParams: {
|
|
163
|
+
serializeData: this.serializeCustomRoles.bind(this),
|
|
164
|
+
reject: onReject,
|
|
165
|
+
resolve: onSuccess,
|
|
166
|
+
entity: 'create-custom-role',
|
|
167
|
+
includeParamOnCompletion: true,
|
|
168
|
+
additionalInfo: { workflowUid: workflow.uid, stageIndex },
|
|
169
|
+
},
|
|
170
|
+
concurrencyLimit: config_1.default.fetchConcurrency || 1,
|
|
171
|
+
}, undefined, false);
|
|
172
|
+
}
|
|
173
|
+
stageIndex++;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* @method serializeCustomRoles
|
|
178
|
+
* @param {ApiOptions} apiOptions ApiOptions
|
|
179
|
+
* @returns {ApiOptions} ApiOptions
|
|
180
|
+
*/
|
|
181
|
+
serializeCustomRoles(apiOptions) {
|
|
182
|
+
let { apiData: roleData, additionalInfo: { workflowUid, stageIndex }, } = apiOptions;
|
|
183
|
+
if (!this.roleNameMap[roleData.name]) {
|
|
184
|
+
// rules.branch is required to create custom roles.
|
|
185
|
+
const branchRuleExists = (0, find_1.default)(roleData.rules, (rule) => rule.module === 'branch');
|
|
186
|
+
if (!branchRuleExists) {
|
|
187
|
+
roleData.rules.push({
|
|
188
|
+
module: 'branch',
|
|
189
|
+
branches: ['main'],
|
|
190
|
+
acl: { read: true },
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
apiOptions = roleData;
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
apiOptions.entity = undefined;
|
|
197
|
+
this.updateRoleData({ workflowUid, stageIndex, roleData });
|
|
198
|
+
}
|
|
199
|
+
return apiOptions;
|
|
200
|
+
}
|
|
201
|
+
updateRoleData(params) {
|
|
202
|
+
const { workflowUid, stageIndex, roleData } = params;
|
|
203
|
+
const workflowStage = this.workflows[workflowUid].workflow_stages;
|
|
204
|
+
const roles = workflowStage[stageIndex].SYS_ACL.roles.uids;
|
|
205
|
+
const index = (0, findIndex_1.default)(roles, ['uid', roleData.uid]);
|
|
206
|
+
roles[index >= 0 ? index : roles.length] = this.roleNameMap[roleData.name];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
exports.default = ImportWorkflows;
|
|
@@ -74,7 +74,7 @@ module.exports = class ImportCustomRoles {
|
|
|
74
74
|
catch (error) {
|
|
75
75
|
self.fails.push(customRole);
|
|
76
76
|
if (((error && error.errors && error.errors.name) || '').includes('is not a unique.')) {
|
|
77
|
-
log(self.config, `custom-role ${customRole.name} already exists`, 'info');
|
|
77
|
+
log(self.config, `custom-role '${customRole.name}' already exists`, 'info');
|
|
78
78
|
}
|
|
79
79
|
else {
|
|
80
80
|
if (!(error && error.errors && error.errors.name)) {
|
|
@@ -324,12 +324,12 @@ module.exports = class ImportEntries {
|
|
|
324
324
|
}
|
|
325
325
|
})
|
|
326
326
|
.catch((error) => {
|
|
327
|
-
if (error.hasOwnProperty('
|
|
327
|
+
if (error.hasOwnProperty('errorCode') && error.errorCode === 119) {
|
|
328
328
|
if (error.errors.title) {
|
|
329
329
|
log(this.config, 'Entry ' + eUid + ' already exist, skip to avoid creating a duplicate entry', 'error');
|
|
330
330
|
}
|
|
331
331
|
else {
|
|
332
|
-
log(this.config, `Failed to create an entry ${eUid} ${formatError(error)}`, 'error');
|
|
332
|
+
log(this.config, `Failed to create an entry '${eUid}' ${formatError(error)} Title of the failed entry: '${entries[eUid].title}'`, 'error');
|
|
333
333
|
}
|
|
334
334
|
self.createdEntriesWOUid.push({
|
|
335
335
|
content_type: ctUid,
|
|
@@ -342,7 +342,7 @@ module.exports = class ImportEntries {
|
|
|
342
342
|
}
|
|
343
343
|
// TODO: if status code: 422, check the reason
|
|
344
344
|
// 429 for rate limit
|
|
345
|
-
log(this.config, `Failed to create an entry ${eUid} ${formatError(error)}`, 'error');
|
|
345
|
+
log(this.config, `Failed to create an entry '${eUid}' ${formatError(error)}. Title of the failed entry: '${entries[eUid].title}'`, 'error');
|
|
346
346
|
self.fails.push({
|
|
347
347
|
content_type: ctUid,
|
|
348
348
|
locale: lang,
|
|
@@ -394,7 +394,13 @@ module.exports = class ImportEntries {
|
|
|
394
394
|
return resolve();
|
|
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) || '{}').entry) === null || _b === void 0 ? void 0 : _b.title;
|
|
399
|
+
addlogs(this.config, chalk.red("Failed to create entries in '" +
|
|
400
|
+
lang +
|
|
401
|
+
"' language. " +
|
|
402
|
+
'Title of the failed entry: ' +
|
|
403
|
+
`'${title || ''}'`), 'error');
|
|
398
404
|
return reject(error);
|
|
399
405
|
});
|
|
400
406
|
});
|
|
@@ -473,7 +479,7 @@ module.exports = class ImportEntries {
|
|
|
473
479
|
return _entry;
|
|
474
480
|
}
|
|
475
481
|
catch (error) {
|
|
476
|
-
addlogs(this.config, `Failed to update the entry ${uid} references while reposting ${formatError(error)}`, 'error');
|
|
482
|
+
addlogs(this.config, `Failed to update the entry '${uid}' references while reposting ${formatError(error)}`, 'error');
|
|
477
483
|
}
|
|
478
484
|
});
|
|
479
485
|
log(this.config, 'Starting the reposting process for entries');
|
|
@@ -544,7 +550,7 @@ module.exports = class ImportEntries {
|
|
|
544
550
|
})
|
|
545
551
|
.catch((error) => {
|
|
546
552
|
// error while updating entries with references
|
|
547
|
-
addlogs(this.config, `Failed re-post entries of content type ${ctUid} locale ${lang}`, 'error');
|
|
553
|
+
addlogs(this.config, `Failed re-post entries of content type '${ctUid}' locale '${lang}'`, 'error');
|
|
548
554
|
addlogs(this.config, formatError(error), 'error');
|
|
549
555
|
// throw error;
|
|
550
556
|
});
|
|
@@ -620,7 +626,7 @@ module.exports = class ImportEntries {
|
|
|
620
626
|
})
|
|
621
627
|
.catch((_error) => {
|
|
622
628
|
addlogs(this.config, formatError(_error), 'error');
|
|
623
|
-
reject(`Failed suppress content type ${schema.uid} reference fields`);
|
|
629
|
+
reject(`Failed suppress content type '${schema.uid}' reference fields`);
|
|
624
630
|
});
|
|
625
631
|
// update 5 content types at a time
|
|
626
632
|
}, {
|
|
@@ -984,6 +990,7 @@ module.exports = class ImportEntries {
|
|
|
984
990
|
break;
|
|
985
991
|
}
|
|
986
992
|
case 'json': {
|
|
993
|
+
const structuredPTag = '{"type":"p","attrs":{},"children":[{"text":""}]}';
|
|
987
994
|
if (entry[element.uid] && element.field_metadata.rich_text_type) {
|
|
988
995
|
if (element.multiple) {
|
|
989
996
|
entry[element.uid] = entry[element.uid].map((jsonRteData) => {
|
|
@@ -991,6 +998,9 @@ module.exports = class ImportEntries {
|
|
|
991
998
|
let entryReferences = jsonRteData.children.filter((e) => this.doEntryReferencesExist(e));
|
|
992
999
|
if (entryReferences.length > 0) {
|
|
993
1000
|
jsonRteData.children = jsonRteData.children.filter((e) => !this.doEntryReferencesExist(e));
|
|
1001
|
+
if (jsonRteData.children.length === 0) {
|
|
1002
|
+
jsonRteData.children.push(JSON.parse(structuredPTag));
|
|
1003
|
+
}
|
|
994
1004
|
return jsonRteData; // return jsonRteData without entry references
|
|
995
1005
|
}
|
|
996
1006
|
else {
|
|
@@ -1002,6 +1012,9 @@ module.exports = class ImportEntries {
|
|
|
1002
1012
|
let entryReferences = entry[element.uid].children.filter((e) => this.doEntryReferencesExist(e));
|
|
1003
1013
|
if (entryReferences.length > 0) {
|
|
1004
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
|
+
}
|
|
1005
1018
|
}
|
|
1006
1019
|
}
|
|
1007
1020
|
}
|
|
@@ -67,7 +67,7 @@ module.exports = class ImportEnvironments {
|
|
|
67
67
|
}
|
|
68
68
|
else {
|
|
69
69
|
// the environment has already been created
|
|
70
|
-
log(config, `The environment ${env.name} already exists. Skipping it to avoid duplicates!`, 'success');
|
|
70
|
+
log(config, `The environment '${env.name}' already exists. Skipping it to avoid duplicates!`, 'success');
|
|
71
71
|
}
|
|
72
72
|
}, { concurrency: self.fetchConcurrency })
|
|
73
73
|
.then(function () {
|
|
@@ -59,7 +59,7 @@ module.exports = class ImportExtensions {
|
|
|
59
59
|
log(self.config, `Extension '${ext.title}' already exists`, 'error');
|
|
60
60
|
}
|
|
61
61
|
else {
|
|
62
|
-
log(self.config, "Extension: '" + ext.title + "' failed to
|
|
62
|
+
log(self.config, "Extension: '" + ext.title + "' failed to import" + formatError(error), 'error');
|
|
63
63
|
}
|
|
64
64
|
});
|
|
65
65
|
}
|
|
@@ -71,13 +71,14 @@ 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;
|
|
74
75
|
let error = JSON.parse(err.message);
|
|
75
76
|
if (error.errors.title) {
|
|
76
77
|
// eslint-disable-next-line no-undef
|
|
77
78
|
log(self.config, `Globalfield '${snip.uid} already exists'`, 'error');
|
|
78
79
|
}
|
|
79
80
|
else {
|
|
80
|
-
log(self.config, chalk.red(`Globalfield failed to import ${formatError(error)}`), 'error');
|
|
81
|
+
log(self.config, chalk.red(`Globalfield '${snip.title}' failed to import. ${formatError(error)}`), 'error');
|
|
81
82
|
}
|
|
82
83
|
self.fails.push(snip);
|
|
83
84
|
});
|
|
@@ -96,10 +97,7 @@ module.exports = class ImportGlobalFields {
|
|
|
96
97
|
return resolve();
|
|
97
98
|
})
|
|
98
99
|
.catch(function (err) {
|
|
99
|
-
// error while importing globalfields
|
|
100
100
|
let error = JSON.parse(err);
|
|
101
|
-
// error while importing globalfields
|
|
102
|
-
log(self.config, err, 'error');
|
|
103
101
|
fileHelper.writeFileSync(globalfieldsFailsPath, self.fails);
|
|
104
102
|
log(self.config, `Globalfields import failed. ${formatError(err)}`, 'error');
|
|
105
103
|
return reject(error);
|
|
@@ -3,7 +3,7 @@ declare class ImportMarketplaceApps {
|
|
|
3
3
|
constructor(importConfig: any, stackAPIClient: any);
|
|
4
4
|
client: any;
|
|
5
5
|
httpClient: any;
|
|
6
|
-
|
|
6
|
+
appOriginalName: any;
|
|
7
7
|
appUidMapping: {};
|
|
8
8
|
appNameMapping: {};
|
|
9
9
|
marketplaceApps: any[];
|
|
@@ -42,10 +42,12 @@ declare class ImportMarketplaceApps {
|
|
|
42
42
|
getAppName(name: any, appSuffix?: number): any;
|
|
43
43
|
/**
|
|
44
44
|
* @method installApps
|
|
45
|
-
*
|
|
46
|
-
* @
|
|
45
|
+
*
|
|
46
|
+
* @param {Record<string, any>} app
|
|
47
|
+
* @param {Record<string, any>[]} installedApps
|
|
48
|
+
* @returns {Promise<void>}
|
|
47
49
|
*/
|
|
48
|
-
installApps(app:
|
|
50
|
+
installApps(app: Record<string, any>, installedApps: Record<string, any>[]): Promise<void>;
|
|
49
51
|
makeRedirectUrlCall(response: any, appName: any): Promise<void>;
|
|
50
52
|
ifAppAlreadyExist(app: any, currentStackApp: any): Promise<any>;
|
|
51
53
|
confirmToCloseProcess(installation: any): Promise<void>;
|
|
@@ -62,7 +62,7 @@ module.exports = class ImportMarketplaceApps {
|
|
|
62
62
|
.catch((error) => {
|
|
63
63
|
console.log(error);
|
|
64
64
|
});
|
|
65
|
-
if (tempStackData
|
|
65
|
+
if (tempStackData === null || tempStackData === void 0 ? void 0 : tempStackData.org_uid) {
|
|
66
66
|
this.config.org_uid = tempStackData.org_uid;
|
|
67
67
|
}
|
|
68
68
|
}
|
|
@@ -131,25 +131,26 @@ module.exports = class ImportMarketplaceApps {
|
|
|
131
131
|
});
|
|
132
132
|
}
|
|
133
133
|
async generateUidMapper() {
|
|
134
|
+
var _a, _b;
|
|
134
135
|
const listOfNewMeta = [];
|
|
135
136
|
const listOfOldMeta = [];
|
|
136
|
-
const
|
|
137
|
-
const allInstalledApps = await getAllStackSpecificApps(this.developerHubBaseUrl, this.httpClient, this.config);
|
|
137
|
+
const extensionUidMap = {};
|
|
138
|
+
const allInstalledApps = (await getAllStackSpecificApps(this.developerHubBaseUrl, this.httpClient, this.config)) || [];
|
|
138
139
|
for (const app of this.marketplaceApps) {
|
|
139
|
-
listOfOldMeta.push(..._.map(app
|
|
140
|
+
listOfOldMeta.push(..._.map((_a = app === null || app === void 0 ? void 0 : app.ui_location) === null || _a === void 0 ? void 0 : _a.locations, 'meta').flat());
|
|
140
141
|
}
|
|
141
142
|
for (const app of allInstalledApps) {
|
|
142
|
-
listOfNewMeta.push(..._.map(app
|
|
143
|
+
listOfNewMeta.push(..._.map((_b = app === null || app === void 0 ? void 0 : app.ui_location) === null || _b === void 0 ? void 0 : _b.locations, 'meta').flat());
|
|
143
144
|
}
|
|
144
145
|
for (const { extension_uid, name, path, uid, data_type } of _.filter(listOfOldMeta, 'name')) {
|
|
145
146
|
const meta = _.find(listOfNewMeta, { name, path }) ||
|
|
146
147
|
_.find(listOfNewMeta, { name: this.appNameMapping[name], path }) ||
|
|
147
148
|
_.find(listOfNewMeta, { name, uid, data_type });
|
|
148
149
|
if (meta) {
|
|
149
|
-
|
|
150
|
+
extensionUidMap[extension_uid] = meta.extension_uid;
|
|
150
151
|
}
|
|
151
152
|
}
|
|
152
|
-
return
|
|
153
|
+
return extensionUidMap;
|
|
153
154
|
}
|
|
154
155
|
/**
|
|
155
156
|
* @method handleAllPrivateAppsCreationProcess
|
|
@@ -166,10 +167,10 @@ module.exports = class ImportMarketplaceApps {
|
|
|
166
167
|
for (let app of privateApps) {
|
|
167
168
|
// NOTE keys can be passed to install new app in the developer hub
|
|
168
169
|
app.manifest = _.pick(app.manifest, ['uid', 'name', 'description', 'icon', 'target_type', 'webhook', 'oauth']);
|
|
169
|
-
this.
|
|
170
|
+
this.appOriginalName = app.manifest.name;
|
|
170
171
|
await this.createPrivateApps(Object.assign({ oauth: app.oauth, webhook: app.webhook, ui_location: app.ui_location }, app.manifest));
|
|
171
172
|
}
|
|
172
|
-
this.
|
|
173
|
+
this.appOriginalName = undefined;
|
|
173
174
|
}
|
|
174
175
|
async getConfirmationToCreateApps(privateApps) {
|
|
175
176
|
if (!this.config.forceStopMarketplaceAppsPrompt) {
|
|
@@ -184,7 +185,8 @@ module.exports = class ImportMarketplaceApps {
|
|
|
184
185
|
}
|
|
185
186
|
}
|
|
186
187
|
async createPrivateApps(app, uidCleaned = false, appSuffix = 1) {
|
|
187
|
-
|
|
188
|
+
var _a;
|
|
189
|
+
let locations = (_a = app === null || app === void 0 ? void 0 : app.ui_location) === null || _a === void 0 ? void 0 : _a.locations;
|
|
188
190
|
if (!uidCleaned && !_.isEmpty(locations)) {
|
|
189
191
|
app.ui_location.locations = this.updateManifestUILocations(locations, 'uid');
|
|
190
192
|
}
|
|
@@ -223,7 +225,7 @@ module.exports = class ImportMarketplaceApps {
|
|
|
223
225
|
// NOTE new app installation
|
|
224
226
|
log(this.config, `${response.name} app created successfully.!`, 'success');
|
|
225
227
|
this.appUidMapping[app.uid] = response.uid;
|
|
226
|
-
this.appNameMapping[this.
|
|
228
|
+
this.appNameMapping[this.appOriginalName] = response.name;
|
|
227
229
|
}
|
|
228
230
|
}
|
|
229
231
|
async handleNameConflict(app, appSuffix) {
|
|
@@ -254,8 +256,8 @@ module.exports = class ImportMarketplaceApps {
|
|
|
254
256
|
location.meta = _.map(location.meta, (meta) => {
|
|
255
257
|
if (meta.name) {
|
|
256
258
|
const name = `${_.first(_.split(meta.name, '◈'))}◈${appSuffix}`;
|
|
257
|
-
if (!this.appNameMapping[this.
|
|
258
|
-
this.appNameMapping[this.
|
|
259
|
+
if (!this.appNameMapping[this.appOriginalName]) {
|
|
260
|
+
this.appNameMapping[this.appOriginalName] = name;
|
|
259
261
|
}
|
|
260
262
|
meta.name = name;
|
|
261
263
|
}
|
|
@@ -274,8 +276,10 @@ module.exports = class ImportMarketplaceApps {
|
|
|
274
276
|
}
|
|
275
277
|
/**
|
|
276
278
|
* @method installApps
|
|
277
|
-
*
|
|
278
|
-
* @
|
|
279
|
+
*
|
|
280
|
+
* @param {Record<string, any>} app
|
|
281
|
+
* @param {Record<string, any>[]} installedApps
|
|
282
|
+
* @returns {Promise<void>}
|
|
279
283
|
*/
|
|
280
284
|
async installApps(app, installedApps) {
|
|
281
285
|
let updateParam;
|
|
@@ -101,13 +101,13 @@ module.exports = class importWorkflows {
|
|
|
101
101
|
.catch(function (error) {
|
|
102
102
|
self.fails.push(workflow);
|
|
103
103
|
if (error.errors.name) {
|
|
104
|
-
log(self.config, `workflow ${workflow.name} already exist`, 'error');
|
|
104
|
+
log(self.config, `workflow '${workflow.name}' already exist`, 'error');
|
|
105
105
|
}
|
|
106
106
|
else if (error.errors['workflow_stages.0.users']) {
|
|
107
107
|
log(self.config, "Failed to import Workflows as you've specified certain roles in the Stage transition and access rules section. We currently don't import roles to the stack.", 'error');
|
|
108
108
|
}
|
|
109
109
|
else {
|
|
110
|
-
log(self.config, `
|
|
110
|
+
log(self.config, `Workflow '${workflow.name}' failed.`, 'error');
|
|
111
111
|
}
|
|
112
112
|
});
|
|
113
113
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import Bluebird from 'bluebird';
|
|
2
2
|
import { ImportConfig } from 'src/types';
|
|
3
3
|
export declare const uploadAssetHelper: (config: ImportConfig, req: any, fsPath: string, RETRY?: number) => Bluebird<unknown>;
|
|
4
|
-
export declare const lookupAssets: (data:
|
|
4
|
+
export declare const lookupAssets: (data: Record<string, any>, mappedAssetUids: Record<string, any>, mappedAssetUrls: Record<string, any>, assetUidMapperPath: string, installedExtensions: Record<string, any>[]) => any;
|
|
@@ -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
|
}
|
|
@@ -3,12 +3,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
const path = tslib_1.__importStar(require("path"));
|
|
5
5
|
const ncp_1 = tslib_1.__importDefault(require("ncp"));
|
|
6
|
+
const index_1 = require("./index");
|
|
6
7
|
function setupBackupDir(importConfig) {
|
|
7
8
|
return new Promise(async (resolve, reject) => {
|
|
8
9
|
if (importConfig.hasOwnProperty('useBackedupDir')) {
|
|
9
10
|
return resolve(importConfig.useBackedupDir);
|
|
10
11
|
}
|
|
11
|
-
|
|
12
|
+
//NOTE: If the backup folder's directory is provided, create it at that location; otherwise, the default path (working directory).
|
|
13
|
+
let backupDirPath = path.join(process.cwd(), '_backup_' + Math.floor(Math.random() * 1000));
|
|
14
|
+
if (importConfig.createBackupDir) {
|
|
15
|
+
if (index_1.fileHelper.fileExistsSync(importConfig.createBackupDir)) {
|
|
16
|
+
index_1.fileHelper.removeDirSync(importConfig.createBackupDir);
|
|
17
|
+
}
|
|
18
|
+
index_1.fileHelper.makeDirectory(importConfig.createBackupDir);
|
|
19
|
+
backupDirPath = importConfig.createBackupDir;
|
|
20
|
+
}
|
|
12
21
|
const limit = importConfig.backupConcurrency || 16;
|
|
13
22
|
if (path.isAbsolute(importConfig.contentDir)) {
|
|
14
23
|
return (0, ncp_1.default)(importConfig.contentDir, backupDirPath, { limit }, (error) => {
|
|
@@ -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
|
};
|