@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 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.0 linux-x64 node-v18.17.1
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 isEmpty_1 = tslib_1.__importDefault(require("lodash/isEmpty"));
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) || "{}").entry) === null || _b === void 0 ? void 0 : _b.title;
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 || ""}'`), 'error');
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
@@ -14,5 +14,6 @@ declare class importWorkflows {
14
14
  stackAPIClient: any;
15
15
  start(): Promise<any>;
16
16
  workflows: any;
17
+ updateNextAvailableStagesUid(workflow: any, newWorkflowStages: any, oldWorkflowStages: any): any;
17
18
  }
18
19
  import Promise = require("bluebird");
@@ -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 { isEmpty, merge } = require('lodash');
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
- assetUrls.push(markdownMatch[0]);
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' || schema[i].reference_to) {
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
- return true;
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
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.8.0",
2
+ "version": "1.8.2",
3
3
  "commands": {
4
4
  "cm:stacks:import": {
5
5
  "id": "cm:stacks:import",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@contentstack/cli-cm-import",
3
3
  "description": "Contentstack CLI plugin to import content into stack",
4
- "version": "1.8.0",
4
+ "version": "1.8.2",
5
5
  "author": "Contentstack",
6
6
  "bugs": "https://github.com/contentstack/cli/issues",
7
7
  "dependencies": {