@contentstack/cli-cm-import 1.6.0 → 1.7.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.
Files changed (42) hide show
  1. package/README.md +1 -1
  2. package/lib/commands/cm/stacks/import.js +1 -1
  3. package/lib/config/index.js +24 -1
  4. package/lib/import/modules/base-class.d.ts +3 -2
  5. package/lib/import/modules/base-class.js +53 -0
  6. package/lib/import/modules/content-types.d.ts +56 -0
  7. package/lib/import/modules/content-types.js +186 -0
  8. package/lib/import/modules/custom-roles.d.ts +37 -0
  9. package/lib/import/modules/custom-roles.js +171 -0
  10. package/lib/import/modules/environments.d.ts +27 -0
  11. package/lib/import/modules/environments.js +106 -0
  12. package/lib/import/modules/extensions.d.ts +27 -0
  13. package/lib/import/modules/extensions.js +106 -0
  14. package/lib/import/modules/global-fields.d.ts +34 -0
  15. package/lib/import/modules/global-fields.js +99 -0
  16. package/lib/import/modules/labels.d.ts +34 -0
  17. package/lib/import/modules/labels.js +171 -0
  18. package/lib/import/modules/locales.js +0 -8
  19. package/lib/import/modules/marketplace-apps.d.ts +51 -0
  20. package/lib/import/modules/marketplace-apps.js +309 -0
  21. package/lib/import/modules/webhooks.d.ts +27 -0
  22. package/lib/import/modules/webhooks.js +110 -0
  23. package/lib/import/modules-js/custom-roles.js +2 -0
  24. package/lib/import/modules-js/entries.js +8 -2
  25. package/lib/import/modules-js/extensions.d.ts +1 -0
  26. package/lib/import/modules-js/marketplace-apps.js +11 -11
  27. package/lib/types/default-config.d.ts +13 -0
  28. package/lib/types/import-config.d.ts +1 -0
  29. package/lib/types/index.d.ts +33 -0
  30. package/lib/types/index.js +2 -0
  31. package/lib/utils/content-type-helper.d.ts +3 -1
  32. package/lib/utils/content-type-helper.js +3 -1
  33. package/lib/utils/extension-helper.d.ts +0 -5
  34. package/lib/utils/extension-helper.js +23 -11
  35. package/lib/utils/index.d.ts +1 -1
  36. package/lib/utils/index.js +11 -1
  37. package/lib/utils/interactive.d.ts +5 -0
  38. package/lib/utils/interactive.js +66 -1
  39. package/lib/utils/marketplace-app-helper.d.ts +13 -1
  40. package/lib/utils/marketplace-app-helper.js +127 -15
  41. package/oclif.manifest.json +1 -1
  42. 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.6.0 linux-x64 node-v18.16.1
50
+ @contentstack/cli-cm-import/1.7.1 linux-x64 node-v18.17.0
51
51
  $ csdx --help [COMMAND]
52
52
  USAGE
53
53
  $ csdx COMMAND
@@ -25,7 +25,7 @@ class ImportCommand extends cli_command_1.Command {
25
25
  }
26
26
  catch (error) {
27
27
  (0, utils_1.log)({ data: contentDir }, `Failed to import stack content - ${(0, utils_1.formatError)(error)}`, 'error');
28
- (0, utils_1.log)({ data: contentDir }, `The log has been stored at ${{ data: contentDir } ? node_path_1.default.join(contentDir, 'logs', 'import') : node_path_1.default.join(__dirname, 'logs')}`, 'info');
28
+ (0, utils_1.log)({ data: contentDir }, `The log has been stored at ${{ data: contentDir } ? node_path_1.default.join(contentDir || __dirname, 'logs', 'import') : node_path_1.default.join(__dirname, 'logs')}`, 'info');
29
29
  }
30
30
  }
31
31
  }
@@ -62,6 +62,7 @@ const config = {
62
62
  extensions: {
63
63
  dirName: 'extensions',
64
64
  fileName: 'extensions.json',
65
+ validKeys: ['data_type', 'srcdoc', 'title', 'type', 'mutiple', 'config'],
65
66
  },
66
67
  webhooks: {
67
68
  dirName: 'webhooks',
@@ -108,6 +109,12 @@ const config = {
108
109
  validKeys: ['title', 'uid', 'schema', 'options', 'singleton', 'description'],
109
110
  limit: 100,
110
111
  },
112
+ 'content-types': {
113
+ dirName: 'content_types',
114
+ fileName: 'content_types.json',
115
+ validKeys: ['title', 'uid', 'schema', 'options', 'singleton', 'description'],
116
+ limit: 100,
117
+ },
111
118
  entries: {
112
119
  dirName: 'entries',
113
120
  fileName: 'entries.json',
@@ -121,6 +128,12 @@ const config = {
121
128
  validKeys: ['title', 'uid', 'schema', 'options', 'singleton', 'description'],
122
129
  limit: 100,
123
130
  },
131
+ 'global-fields': {
132
+ dirName: 'global_fields',
133
+ fileName: 'globalfields.json',
134
+ validKeys: ['title', 'uid', 'schema', 'options', 'singleton', 'description'],
135
+ limit: 100,
136
+ },
124
137
  stack: {
125
138
  dirName: 'stack',
126
139
  fileName: 'stack.json',
@@ -354,7 +367,17 @@ const config = {
354
367
  stacks: '/stacks/',
355
368
  labels: '/labels/',
356
369
  },
357
- updatedModules: ['assets', 'locales'],
370
+ updatedModules: [
371
+ 'assets',
372
+ 'extensions',
373
+ 'locales',
374
+ 'marketplace-apps',
375
+ 'labels',
376
+ 'global-fields',
377
+ 'content-types',
378
+ 'webhooks',
379
+ 'custom-roles',
380
+ ],
358
381
  rateLimit: 5,
359
382
  preserveStackVersion: false,
360
383
  entriesPublish: true,
@@ -3,12 +3,12 @@ import { ImportConfig, ModuleClassParams } from '../../types';
3
3
  export type AdditionalKeys = {
4
4
  backupDir: string;
5
5
  };
6
- export type ApiModuleType = 'create-assets' | 'replace-assets' | 'publish-assets' | 'create-assets-folder' | 'create-locale' | 'update-locale';
6
+ export type ApiModuleType = 'create-assets' | 'replace-assets' | 'publish-assets' | 'create-assets-folder' | 'create-extensions' | 'create-locale' | 'update-locale' | 'create-gfs' | 'create-cts' | 'update-cts' | 'update-gfs' | 'create-environments' | 'create-labels' | 'update-labels' | 'create-webhooks' | 'create-workflows' | 'create-custom-role';
7
7
  export type ApiOptions = {
8
8
  uid?: string;
9
9
  url?: string;
10
10
  entity: ApiModuleType;
11
- apiData?: Record<any, any>;
11
+ apiData?: Record<any, any> | any;
12
12
  resolve: (value: any) => void;
13
13
  reject: (error: any) => void;
14
14
  additionalInfo?: Record<any, any>;
@@ -27,6 +27,7 @@ export type EnvType = {
27
27
  export type CustomPromiseHandlerInput = {
28
28
  index: number;
29
29
  batchIndex: number;
30
+ element?: Record<string, unknown>;
30
31
  apiParams?: ApiOptions;
31
32
  isLastRequest: boolean;
32
33
  };
@@ -7,6 +7,7 @@ const chunk_1 = tslib_1.__importDefault(require("lodash/chunk"));
7
7
  const isEmpty_1 = tslib_1.__importDefault(require("lodash/isEmpty"));
8
8
  const entries_1 = tslib_1.__importDefault(require("lodash/entries"));
9
9
  const isEqual_1 = tslib_1.__importDefault(require("lodash/isEqual"));
10
+ const omit_1 = tslib_1.__importDefault(require("lodash/omit"));
10
11
  const utils_1 = require("../../utils");
11
12
  class BaseClass {
12
13
  constructor({ importConfig, stackAPIClient }) {
@@ -54,6 +55,7 @@ class BaseClass {
54
55
  promise = promisifyHandler({
55
56
  apiParams,
56
57
  isLastRequest,
58
+ element,
57
59
  index: Number(index),
58
60
  batchIndex: Number(batchIndex),
59
61
  });
@@ -145,6 +147,12 @@ class BaseClass {
145
147
  .publish((0, pick_1.default)(apiData, ['publishDetails']))
146
148
  .then(onSuccess)
147
149
  .catch(onReject);
150
+ case 'create-extensions':
151
+ return this.stack
152
+ .extension()
153
+ .create({ extension: (0, omit_1.default)(apiData, ['uid']) })
154
+ .then(onSuccess)
155
+ .catch(onReject);
148
156
  case 'create-locale':
149
157
  return this.stack
150
158
  .locale()
@@ -157,6 +165,51 @@ class BaseClass {
157
165
  .update({ locale: (0, pick_1.default)(apiData, [...this.modulesConfig.locales.requiredKeys]) })
158
166
  .then(onSuccess)
159
167
  .catch(onReject);
168
+ case 'create-cts':
169
+ return this.stack.contentType().create(apiData).then(onSuccess).catch(onReject);
170
+ case 'update-cts':
171
+ return apiData.update().then(onSuccess).catch(onReject);
172
+ case 'update-gfs':
173
+ return apiData.update().then(onSuccess).catch(onReject);
174
+ case 'create-environments':
175
+ // return this.stack
176
+ // .environment()
177
+ // .create({ environment: omit(apiData, ['uid']) as EnvironmentData })
178
+ // .then(onSuccess)
179
+ // .catch(onReject);
180
+ case 'create-labels':
181
+ return this.stack
182
+ .label()
183
+ .create({ label: (0, omit_1.default)(apiData, ['uid']) })
184
+ .then(onSuccess)
185
+ .catch(onReject);
186
+ case 'update-labels':
187
+ return this.stack
188
+ .label(apiData.uid)
189
+ .fetch()
190
+ .then(async (response) => {
191
+ response.parent = apiData.parent;
192
+ await response.update().then(onSuccess).catch(onReject);
193
+ })
194
+ .catch(onReject);
195
+ case 'create-webhooks':
196
+ return this.stack
197
+ .webhook()
198
+ .create({ webhook: (0, omit_1.default)(apiData, ['uid']) })
199
+ .then(onSuccess)
200
+ .catch(onReject);
201
+ case 'create-workflows':
202
+ return this.stack
203
+ .workflow()
204
+ .create({ workflow: apiData })
205
+ .then(onSuccess)
206
+ .catch(onReject);
207
+ case 'create-custom-role':
208
+ return this.stack
209
+ .role()
210
+ .create({ role: apiData })
211
+ .then(onSuccess)
212
+ .catch(onReject);
160
213
  default:
161
214
  return Promise.resolve();
162
215
  }
@@ -0,0 +1,56 @@
1
+ /*!
2
+ * Contentstack Import
3
+ * Copyright (c) 2019 Contentstack LLC
4
+ * MIT Licensed
5
+ */
6
+ import { ModuleClassParams } from '../../types';
7
+ import BaseClass, { ApiOptions } from './base-class';
8
+ export default class ContentTypesImport extends BaseClass {
9
+ private cTsMapperPath;
10
+ private cTsFolderPath;
11
+ private cTsFailsPath;
12
+ private cTsSuccessPath;
13
+ private gFsPendingPath;
14
+ private pendingGFs;
15
+ private createdGFs;
16
+ private gFsFolderPath;
17
+ private gFsMapperFolderPath;
18
+ private gFs;
19
+ private failedCTs;
20
+ private createdCTs;
21
+ private cTs;
22
+ private cTsUidMapper;
23
+ private config;
24
+ private stackAPIClient;
25
+ private marketplaceAppMapperPath;
26
+ private reqConcurrency;
27
+ private ignoredFilesInContentTypesFolder;
28
+ private titleToUIdMap;
29
+ private fieldRules;
30
+ private installedExtensions;
31
+ private cTsConfig;
32
+ private gFsConfig;
33
+ constructor({ importConfig, stackAPIClient }: ModuleClassParams);
34
+ start(): Promise<any>;
35
+ seedCTs(): Promise<any>;
36
+ /**
37
+ * @method serializeCTs
38
+ * @param {ApiOptions} apiOptions ApiOptions
39
+ * @returns {ApiOptions} ApiOptions
40
+ */
41
+ serializeCTs(apiOptions: ApiOptions): ApiOptions;
42
+ updateCTs(): Promise<any>;
43
+ /**
44
+ * @method serializeUpdateCTs
45
+ * @param {ApiOptions} apiOptions ApiOptions
46
+ * @returns {ApiOptions} ApiOptions
47
+ */
48
+ serializeUpdateCTs(apiOptions: ApiOptions): ApiOptions;
49
+ updatePendingGFs(): Promise<any>;
50
+ /**
51
+ * @method serializeUpdateGFs
52
+ * @param {ApiOptions} apiOptions ApiOptions
53
+ * @returns {ApiOptions} ApiOptions
54
+ */
55
+ serializeUpdateGFs(apiOptions: ApiOptions): ApiOptions;
56
+ }
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ /* eslint-disable no-prototype-builtins */
3
+ /*!
4
+ * Contentstack Import
5
+ * Copyright (c) 2019 Contentstack LLC
6
+ * MIT Licensed
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ const tslib_1 = require("tslib");
10
+ const path = tslib_1.__importStar(require("path"));
11
+ const lodash_1 = require("lodash");
12
+ const utils_1 = require("../../utils");
13
+ const base_class_1 = tslib_1.__importDefault(require("./base-class"));
14
+ class ContentTypesImport extends base_class_1.default {
15
+ constructor({ importConfig, stackAPIClient }) {
16
+ super({ importConfig, stackAPIClient });
17
+ this.cTsConfig = importConfig.modules['content-types'];
18
+ this.gFsConfig = importConfig.modules['global-fields'];
19
+ this.reqConcurrency = this.cTsConfig.writeConcurrency || this.importConfig.writeConcurrency;
20
+ this.cTsFolderPath = path.join(this.importConfig.data, this.cTsConfig.dirName);
21
+ this.cTsMapperPath = path.join(this.importConfig.data, 'mapper', 'content_types');
22
+ this.cTsSuccessPath = path.join(this.cTsMapperPath, 'success.json');
23
+ this.gFsFolderPath = path.resolve(this.importConfig.data, this.gFsConfig.dirName);
24
+ this.gFsMapperFolderPath = path.join(importConfig.data, 'mapper', 'global_fields', 'success.json');
25
+ this.gFsPendingPath = path.join(importConfig.data, 'mapper', 'global_fields', 'pending_global_fields.js');
26
+ this.marketplaceAppMapperPath = path.join(this.importConfig.data, 'mapper', 'marketplace_apps', 'uid-mapping.json');
27
+ this.ignoredFilesInContentTypesFolder = new Map([
28
+ ['__master.json', 'true'],
29
+ ['__priority.json', 'true'],
30
+ ['schema.json', 'true'],
31
+ ['.DS_Store', 'true'],
32
+ ]);
33
+ this.cTs = [];
34
+ this.createdCTs = [];
35
+ this.titleToUIdMap = new Map();
36
+ this.fieldRules = [];
37
+ this.gFs = [];
38
+ this.createdGFs = [];
39
+ this.pendingGFs = [];
40
+ }
41
+ async start() {
42
+ /**
43
+ * read content type, check if it is necessary to read the entire dir
44
+ * Seed content types
45
+ * Update content types, lookup extension.
46
+ * Update pending global fields
47
+ * write field rules
48
+ */
49
+ this.cTs = utils_1.fsUtil.readFile(path.join(this.cTsFolderPath, 'schema.json'));
50
+ if (!this.cTs || (0, lodash_1.isEmpty)(this.cTs)) {
51
+ (0, utils_1.log)(this.importConfig, 'No content type found to import', 'info');
52
+ return;
53
+ }
54
+ await utils_1.fsUtil.makeDirectory(this.cTsMapperPath);
55
+ this.installedExtensions = ((await utils_1.fsUtil.readFile(this.marketplaceAppMapperPath)) || { extension_uid: {} }).extension_uid;
56
+ await this.seedCTs();
57
+ (0, utils_1.log)(this.importConfig, 'Created content types', 'success');
58
+ await this.updateCTs();
59
+ (0, utils_1.log)(this.importConfig, 'Updated content types with references', 'success');
60
+ if (this.fieldRules.length > 0) {
61
+ await utils_1.fsUtil.writeFile(path.join(this.cTsFolderPath, 'field_rules_uid.json'), this.fieldRules);
62
+ }
63
+ await this.updatePendingGFs().catch((error) => {
64
+ (0, utils_1.log)(this.importConfig, `Error while updating pending global field ${(0, utils_1.formatError)(error)}`, 'error');
65
+ });
66
+ (0, utils_1.log)(this.importConfig, 'Updated pending global fields with content type with references', 'success');
67
+ (0, utils_1.log)(this.importConfig, 'Content types have been imported successfully!', 'success');
68
+ }
69
+ async seedCTs() {
70
+ const onSuccess = ({ response: globalField, apiData: { content_type: { uid = null } = {} } = {} }) => {
71
+ (0, utils_1.log)(this.importConfig, `${uid} content type seeded`, 'info');
72
+ };
73
+ const onReject = ({ error, apiData: { content_type: { uid = null } = {} } = {} }) => {
74
+ if (error.errorCode === 115 && (error.errors.uid || error.errors.title)) {
75
+ (0, utils_1.log)(this.importConfig, `${uid} content type already exist`, 'info');
76
+ }
77
+ else {
78
+ (0, utils_1.log)(this.importConfig, (0, utils_1.formatError)(error), 'error');
79
+ process.exit(1);
80
+ }
81
+ };
82
+ return await this.makeConcurrentCall({
83
+ processName: 'Import content types',
84
+ apiContent: this.cTs,
85
+ apiParams: {
86
+ serializeData: this.serializeCTs.bind(this),
87
+ reject: onReject.bind(this),
88
+ resolve: onSuccess.bind(this),
89
+ entity: 'create-cts',
90
+ includeParamOnCompletion: true,
91
+ },
92
+ concurrencyLimit: this.reqConcurrency,
93
+ });
94
+ }
95
+ /**
96
+ * @method serializeCTs
97
+ * @param {ApiOptions} apiOptions ApiOptions
98
+ * @returns {ApiOptions} ApiOptions
99
+ */
100
+ serializeCTs(apiOptions) {
101
+ const { apiData: contentType } = apiOptions;
102
+ const updatedCT = (0, lodash_1.cloneDeep)(utils_1.schemaTemplate);
103
+ updatedCT.content_type.uid = contentType.uid;
104
+ updatedCT.content_type.title = contentType.title;
105
+ apiOptions.apiData = updatedCT;
106
+ return apiOptions;
107
+ }
108
+ async updateCTs() {
109
+ const onSuccess = ({ response: contentType, apiData: { uid } }) => {
110
+ (0, utils_1.log)(this.importConfig, `${uid} updated with references`, 'success');
111
+ };
112
+ const onReject = ({ error, apiData: { uid } }) => {
113
+ (0, utils_1.log)(this.importConfig, (0, utils_1.formatError)(error), 'error');
114
+ throw new Error(`Content type '${uid}' update error`);
115
+ };
116
+ return await this.makeConcurrentCall({
117
+ processName: 'Update content types',
118
+ apiContent: this.cTs,
119
+ apiParams: {
120
+ serializeData: this.serializeUpdateCTs.bind(this),
121
+ reject: onReject.bind(this),
122
+ resolve: onSuccess.bind(this),
123
+ entity: 'update-cts',
124
+ includeParamOnCompletion: true,
125
+ },
126
+ concurrencyLimit: this.reqConcurrency,
127
+ });
128
+ }
129
+ /**
130
+ * @method serializeUpdateCTs
131
+ * @param {ApiOptions} apiOptions ApiOptions
132
+ * @returns {ApiOptions} ApiOptions
133
+ */
134
+ serializeUpdateCTs(apiOptions) {
135
+ const { apiData: contentType } = apiOptions;
136
+ if (contentType.field_rules) {
137
+ this.fieldRules.push(contentType.uid);
138
+ delete contentType.field_rules;
139
+ }
140
+ (0, utils_1.lookupExtension)(this.importConfig, contentType.schema, this.importConfig.preserveStackVersion, this.installedExtensions);
141
+ const contentTypePayload = this.stack.contentType(contentType.uid);
142
+ Object.assign(contentTypePayload, (0, lodash_1.cloneDeep)(contentType));
143
+ apiOptions.apiData = contentTypePayload;
144
+ return apiOptions;
145
+ }
146
+ async updatePendingGFs() {
147
+ this.pendingGFs = utils_1.fsUtil.readFile(this.gFsPendingPath);
148
+ this.gFs = utils_1.fsUtil.readFile(path.resolve(this.gFsFolderPath, this.gFsConfig.fileName));
149
+ const onSuccess = ({ response: globalField, apiData: { uid } = undefined }) => {
150
+ (0, utils_1.log)(this.importConfig, `Updated the global field ${uid} with content type references`, 'info');
151
+ };
152
+ const onReject = ({ error, apiData: { uid } = undefined }) => {
153
+ (0, utils_1.log)(this.importConfig, `failed to update the global field ${uid} ${(0, utils_1.formatError)(error)}`, 'error');
154
+ };
155
+ return await this.makeConcurrentCall({
156
+ processName: 'Update pending global fields',
157
+ apiContent: (0, lodash_1.map)(this.pendingGFs, (uid) => {
158
+ return { uid };
159
+ }),
160
+ apiParams: {
161
+ serializeData: this.serializeUpdateGFs.bind(this),
162
+ reject: onReject.bind(this),
163
+ resolve: onSuccess.bind(this),
164
+ entity: 'update-gfs',
165
+ includeParamOnCompletion: true,
166
+ },
167
+ concurrencyLimit: this.reqConcurrency,
168
+ });
169
+ }
170
+ /**
171
+ * @method serializeUpdateGFs
172
+ * @param {ApiOptions} apiOptions ApiOptions
173
+ * @returns {ApiOptions} ApiOptions
174
+ */
175
+ serializeUpdateGFs(apiOptions) {
176
+ const { apiData: { uid }, } = apiOptions;
177
+ const globalField = (0, lodash_1.find)(this.gFs, { uid });
178
+ (0, utils_1.lookupExtension)(this.importConfig, globalField.schema, this.importConfig.preserveStackVersion, this.installedExtensions);
179
+ apiOptions.apiData = globalField;
180
+ const globalFieldPayload = this.stack.globalField(uid);
181
+ Object.assign(globalFieldPayload, (0, lodash_1.cloneDeep)(globalField));
182
+ apiOptions.apiData = globalFieldPayload;
183
+ return apiOptions;
184
+ }
185
+ }
186
+ exports.default = ContentTypesImport;
@@ -0,0 +1,37 @@
1
+ import BaseClass, { ApiOptions } from './base-class';
2
+ import { ModuleClassParams } from '../../types';
3
+ export default class ImportCustomRoles extends BaseClass {
4
+ private customRolesMapperPath;
5
+ private customRolesFolderPath;
6
+ private customRolesUidMapperPath;
7
+ private envUidMapperFolderPath;
8
+ private entriesUidMapperFolderPath;
9
+ private createdCustomRolesPath;
10
+ private customRolesFailsPath;
11
+ private customRolesConfig;
12
+ private customRoles;
13
+ private customRolesLocales;
14
+ private customRolesUidMapper;
15
+ private createdCustomRoles;
16
+ private failedCustomRoles;
17
+ private environmentsUidMap;
18
+ private entriesUidMap;
19
+ private localesUidMap;
20
+ targetLocalesMap: Record<string, unknown>;
21
+ sourceLocalesMap: Record<string, unknown>;
22
+ constructor({ importConfig, stackAPIClient }: ModuleClassParams);
23
+ /**
24
+ * @method start
25
+ * @returns {Promise<void>} Promise<void>
26
+ */
27
+ start(): Promise<void>;
28
+ getLocalesUidMap(): Promise<void>;
29
+ importCustomRoles(): Promise<string>;
30
+ /**
31
+ * @method serializeWebhooks
32
+ * @param {ApiOptions} apiOptions ApiOptions
33
+ * @returns {ApiOptions} ApiOptions
34
+ */
35
+ serializeWebhooks(apiOptions: ApiOptions): ApiOptions;
36
+ getTransformUidsFactory: (rule: Record<string, any>) => Record<string, any>;
37
+ }
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const isEmpty_1 = tslib_1.__importDefault(require("lodash/isEmpty"));
5
+ const values_1 = tslib_1.__importDefault(require("lodash/values"));
6
+ const node_path_1 = require("node:path");
7
+ const config_1 = tslib_1.__importDefault(require("../../config"));
8
+ const utils_1 = require("../../utils");
9
+ const base_class_1 = tslib_1.__importDefault(require("./base-class"));
10
+ const lodash_1 = require("lodash");
11
+ class ImportCustomRoles extends base_class_1.default {
12
+ constructor({ importConfig, stackAPIClient }) {
13
+ super({ importConfig, stackAPIClient });
14
+ this.getTransformUidsFactory = (rule) => {
15
+ if (rule.module === 'environment') {
16
+ rule.environments = (0, lodash_1.map)(rule.environments, (env) => this.environmentsUidMap[env]);
17
+ }
18
+ else if (rule.module === 'locale') {
19
+ rule.locales = (0, lodash_1.map)(rule.locales, (locale) => this.localesUidMap[locale]);
20
+ }
21
+ else if (rule.module === 'entry') {
22
+ rule.entries = (0, lodash_1.map)(rule.entries, (entry) => this.entriesUidMap[entry]);
23
+ }
24
+ return rule;
25
+ };
26
+ this.customRolesConfig = config_1.default.modules.customRoles;
27
+ this.customRolesMapperPath = (0, node_path_1.join)(this.importConfig.backupDir, 'mapper', 'custom-roles');
28
+ this.customRolesFolderPath = (0, node_path_1.join)(this.importConfig.backupDir, this.customRolesConfig.dirName);
29
+ this.customRolesUidMapperPath = (0, node_path_1.join)(this.customRolesMapperPath, 'uid-mapping.json');
30
+ this.envUidMapperFolderPath = (0, node_path_1.join)(this.importConfig.backupDir, 'mapper', 'environments');
31
+ this.entriesUidMapperFolderPath = (0, node_path_1.join)(this.importConfig.backupDir, 'mapper', 'entries');
32
+ this.createdCustomRolesPath = (0, node_path_1.join)(this.customRolesMapperPath, 'success.json');
33
+ this.customRolesFailsPath = (0, node_path_1.join)(this.customRolesMapperPath, 'fails.json');
34
+ this.customRoles = {};
35
+ this.failedCustomRoles = [];
36
+ this.createdCustomRoles = [];
37
+ this.customRolesUidMapper = {};
38
+ this.customRolesLocales = {};
39
+ this.environmentsUidMap = {};
40
+ this.entriesUidMap = {};
41
+ this.localesUidMap = {};
42
+ }
43
+ /**
44
+ * @method start
45
+ * @returns {Promise<void>} Promise<void>
46
+ */
47
+ async start() {
48
+ var _a, _b;
49
+ (0, utils_1.log)(this.importConfig, 'Migrating custom-roles', 'info');
50
+ //Step1 check folder exists or not
51
+ if (utils_1.fileHelper.fileExistsSync(this.customRolesFolderPath)) {
52
+ this.customRoles = utils_1.fsUtil.readFile((0, node_path_1.join)(this.customRolesFolderPath, this.customRolesConfig.fileName), true);
53
+ this.customRolesLocales = utils_1.fsUtil.readFile((0, node_path_1.join)(this.customRolesFolderPath, this.customRolesConfig.customRolesLocalesFileName), true);
54
+ }
55
+ else {
56
+ (0, utils_1.log)(this.importConfig, `No such file or directory - '${this.customRolesFolderPath}'`, 'error');
57
+ return;
58
+ }
59
+ //create webhooks in mapper directory
60
+ await utils_1.fsUtil.makeDirectory(this.customRolesMapperPath);
61
+ this.customRolesUidMapper = utils_1.fileHelper.fileExistsSync(this.customRolesUidMapperPath)
62
+ ? utils_1.fsUtil.readFile((0, node_path_1.join)(this.customRolesUidMapperPath), true)
63
+ : {};
64
+ this.environmentsUidMap = utils_1.fileHelper.fileExistsSync(this.envUidMapperFolderPath)
65
+ ? utils_1.fsUtil.readFile((0, node_path_1.join)(this.envUidMapperFolderPath, 'uid-mapping.json'), true)
66
+ : {};
67
+ this.entriesUidMap = utils_1.fileHelper.fileExistsSync(this.entriesUidMapperFolderPath)
68
+ ? utils_1.fsUtil.readFile((0, node_path_1.join)(this.entriesUidMapperFolderPath, 'uid-mapping.json'), true)
69
+ : {};
70
+ //source and target stack locale map
71
+ await this.getLocalesUidMap();
72
+ await this.importCustomRoles();
73
+ if ((_a = this.createdCustomRoles) === null || _a === void 0 ? void 0 : _a.length) {
74
+ utils_1.fsUtil.writeFile(this.createdCustomRolesPath, this.createdCustomRoles);
75
+ }
76
+ if ((_b = this.failedCustomRoles) === null || _b === void 0 ? void 0 : _b.length) {
77
+ utils_1.fsUtil.writeFile(this.customRolesFailsPath, this.failedCustomRoles);
78
+ }
79
+ (0, utils_1.log)(this.importConfig, 'Custom roles have been imported successfully!', 'success');
80
+ }
81
+ async getLocalesUidMap() {
82
+ const { items } = await this.stack
83
+ .locale()
84
+ .query()
85
+ .find()
86
+ .then((data) => data)
87
+ .catch((error) => (0, utils_1.log)(this.importConfig, `Failed to fetch locale.${(0, utils_1.formatError)(error)}`, 'error'));
88
+ this.targetLocalesMap = {};
89
+ this.sourceLocalesMap = {};
90
+ (0, lodash_1.forEach)(items, (locale) => {
91
+ this.targetLocalesMap[locale.code] = locale.uid;
92
+ });
93
+ for (const key in this.customRolesLocales) {
94
+ const sourceLocales = this.customRolesLocales[key];
95
+ this.sourceLocalesMap[sourceLocales.code] = key;
96
+ }
97
+ for (const key in this.sourceLocalesMap) {
98
+ const sourceLocaleKey = this.sourceLocalesMap[key];
99
+ this.localesUidMap[sourceLocaleKey] = this.targetLocalesMap[key];
100
+ }
101
+ }
102
+ async importCustomRoles() {
103
+ if (this.customRoles === undefined || (0, isEmpty_1.default)(this.customRoles)) {
104
+ (0, utils_1.log)(this.importConfig, 'No custom-roles found', 'info');
105
+ return (0, node_path_1.resolve)();
106
+ }
107
+ const apiContent = (0, values_1.default)(this.customRoles);
108
+ const onSuccess = ({ response, apiData: { uid, name } = { uid: null, name: '' } }) => {
109
+ this.createdCustomRoles.push(response);
110
+ this.customRolesUidMapper[uid] = response.uid;
111
+ (0, utils_1.log)(this.importConfig, `custom-role '${name}' imported successfully`, 'success');
112
+ utils_1.fsUtil.writeFile(this.customRolesUidMapperPath, this.customRolesUidMapper);
113
+ };
114
+ const onReject = ({ error, apiData }) => {
115
+ var _a;
116
+ const err = (error === null || error === void 0 ? void 0 : error.message) ? JSON.parse(error.message) : error;
117
+ const { name } = apiData;
118
+ if ((_a = err === null || err === void 0 ? void 0 : err.errors) === null || _a === void 0 ? void 0 : _a.name) {
119
+ (0, utils_1.log)(this.importConfig, `custom-role '${name}' already exists`, 'info');
120
+ }
121
+ else {
122
+ this.failedCustomRoles.push(apiData);
123
+ (0, utils_1.log)(this.importConfig, `custom-role '${name}' failed to be import.${(0, utils_1.formatError)(error)}`, 'error');
124
+ (0, utils_1.log)(this.importConfig, (0, utils_1.formatError)(error), 'error');
125
+ }
126
+ };
127
+ await this.makeConcurrentCall({
128
+ apiContent,
129
+ processName: 'create custom role',
130
+ apiParams: {
131
+ serializeData: this.serializeWebhooks.bind(this),
132
+ reject: onReject.bind(this),
133
+ resolve: onSuccess.bind(this),
134
+ entity: 'create-custom-role',
135
+ includeParamOnCompletion: true,
136
+ },
137
+ concurrencyLimit: config_1.default.fetchConcurrency || 1,
138
+ }, undefined, false);
139
+ }
140
+ /**
141
+ * @method serializeWebhooks
142
+ * @param {ApiOptions} apiOptions ApiOptions
143
+ * @returns {ApiOptions} ApiOptions
144
+ */
145
+ serializeWebhooks(apiOptions) {
146
+ const { apiData: customRole } = apiOptions;
147
+ if (this.customRolesUidMapper.hasOwnProperty(customRole.uid)) {
148
+ (0, utils_1.log)(this.importConfig, `custom-role '${customRole.name}' already exists. Skipping it to avoid duplicates!`, 'info');
149
+ apiOptions.entity = undefined;
150
+ }
151
+ else {
152
+ let branchRuleExists = false;
153
+ (0, lodash_1.forEach)(customRole.rules, (rule) => {
154
+ rule = this.getTransformUidsFactory(rule);
155
+ // rules.branch is required to create custom roles.
156
+ if (rule.module === 'branch')
157
+ branchRuleExists = true;
158
+ });
159
+ if (!branchRuleExists) {
160
+ customRole.rules.push({
161
+ module: 'branch',
162
+ branches: ['main'],
163
+ acl: { read: true },
164
+ });
165
+ }
166
+ apiOptions.apiData = customRole;
167
+ }
168
+ return apiOptions;
169
+ }
170
+ }
171
+ exports.default = ImportCustomRoles;
@@ -0,0 +1,27 @@
1
+ import BaseClass, { ApiOptions } from './base-class';
2
+ import { ModuleClassParams } from '../../types';
3
+ export default class ImportEnvironments extends BaseClass {
4
+ private mapperDirPath;
5
+ private environmentsFolderPath;
6
+ private envUidMapperPath;
7
+ private envSuccessPath;
8
+ private envFailsPath;
9
+ private environmentsConfig;
10
+ private environments;
11
+ private envUidMapper;
12
+ private envSuccess;
13
+ private envFailed;
14
+ constructor({ importConfig, stackAPIClient }: ModuleClassParams);
15
+ /**
16
+ * @method start
17
+ * @returns {Promise<void>} Promise<void>
18
+ */
19
+ start(): Promise<void>;
20
+ importEnvironments(): Promise<string>;
21
+ /**
22
+ * @method serializeEnvironments
23
+ * @param {ApiOptions} apiOptions ApiOptions
24
+ * @returns {ApiOptions} ApiOptions
25
+ */
26
+ serializeEnvironments(apiOptions: ApiOptions): ApiOptions;
27
+ }