@contentstack/cli-cm-import 1.5.11 → 1.7.0

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 (126) hide show
  1. package/README.md +6 -14
  2. package/bin/dev +17 -0
  3. package/bin/dev.cmd +3 -0
  4. package/bin/run +6 -0
  5. package/bin/run.cmd +3 -0
  6. package/lib/commands/cm/stacks/import.d.ts +10 -0
  7. package/lib/commands/cm/stacks/import.js +111 -0
  8. package/lib/config/index.d.ts +3 -0
  9. package/lib/config/index.js +395 -0
  10. package/lib/import/index.d.ts +1 -0
  11. package/lib/import/index.js +8 -0
  12. package/lib/import/module-importer.d.ts +13 -0
  13. package/lib/import/module-importer.js +70 -0
  14. package/lib/import/modules/assets.d.ts +63 -0
  15. package/lib/import/modules/assets.js +265 -0
  16. package/lib/import/modules/base-class.d.ts +70 -0
  17. package/lib/import/modules/base-class.js +218 -0
  18. package/lib/import/modules/content-types.d.ts +56 -0
  19. package/lib/import/modules/content-types.js +186 -0
  20. package/lib/import/modules/custom-roles.d.ts +37 -0
  21. package/lib/import/modules/custom-roles.js +171 -0
  22. package/lib/import/modules/environments.d.ts +27 -0
  23. package/lib/import/modules/environments.js +106 -0
  24. package/lib/import/modules/extensions.d.ts +27 -0
  25. package/lib/import/modules/extensions.js +106 -0
  26. package/lib/import/modules/global-fields.d.ts +34 -0
  27. package/lib/import/modules/global-fields.js +99 -0
  28. package/lib/import/modules/index.d.ts +2 -0
  29. package/lib/import/modules/index.js +19 -0
  30. package/lib/import/modules/labels.d.ts +34 -0
  31. package/lib/import/modules/labels.js +171 -0
  32. package/lib/import/modules/locales.d.ts +31 -0
  33. package/lib/import/modules/locales.js +144 -0
  34. package/lib/import/modules/marketplace-apps.d.ts +51 -0
  35. package/lib/import/modules/marketplace-apps.js +297 -0
  36. package/lib/import/modules/webhooks.d.ts +27 -0
  37. package/lib/import/modules/webhooks.js +110 -0
  38. package/lib/import/modules-js/assets.d.ts +33 -0
  39. package/lib/import/modules-js/assets.js +415 -0
  40. package/lib/import/modules-js/content-types.d.ts +33 -0
  41. package/lib/import/modules-js/content-types.js +176 -0
  42. package/lib/import/modules-js/custom-roles.d.ts +15 -0
  43. package/lib/import/modules-js/custom-roles.js +143 -0
  44. package/lib/import/modules-js/entries.d.ts +54 -0
  45. package/lib/import/modules-js/entries.js +1260 -0
  46. package/lib/import/modules-js/environments.d.ts +13 -0
  47. package/lib/import/modules-js/environments.js +85 -0
  48. package/lib/import/modules-js/extensions.d.ts +18 -0
  49. package/lib/import/modules-js/extensions.js +86 -0
  50. package/lib/import/modules-js/global-fields.d.ts +13 -0
  51. package/lib/import/modules-js/global-fields.js +109 -0
  52. package/lib/import/modules-js/index.d.ts +1 -0
  53. package/lib/import/modules-js/index.js +33 -0
  54. package/lib/import/modules-js/labels.d.ts +20 -0
  55. package/lib/import/modules-js/labels.js +148 -0
  56. package/lib/import/modules-js/locales.d.ts +24 -0
  57. package/lib/import/modules-js/locales.js +196 -0
  58. package/lib/import/modules-js/marketplace-apps.d.ts +60 -0
  59. package/lib/import/modules-js/marketplace-apps.js +409 -0
  60. package/lib/import/modules-js/webhooks.d.ts +17 -0
  61. package/lib/import/modules-js/webhooks.js +85 -0
  62. package/lib/import/modules-js/workflows.d.ts +18 -0
  63. package/lib/import/modules-js/workflows.js +132 -0
  64. package/lib/types/default-config.d.ts +143 -0
  65. package/lib/types/default-config.js +2 -0
  66. package/lib/types/import-config.d.ts +52 -0
  67. package/lib/types/import-config.js +2 -0
  68. package/lib/types/index.d.ts +63 -0
  69. package/lib/types/index.js +4 -0
  70. package/lib/utils/asset-helper.d.ts +4 -0
  71. package/lib/utils/asset-helper.js +387 -0
  72. package/lib/utils/backup-handler.d.ts +2 -0
  73. package/lib/utils/backup-handler.js +31 -0
  74. package/lib/utils/common-helper.d.ts +20 -0
  75. package/lib/utils/common-helper.js +244 -0
  76. package/lib/utils/content-type-helper.d.ts +51 -0
  77. package/lib/utils/content-type-helper.js +145 -0
  78. package/lib/utils/entries-helper.d.ts +4 -0
  79. package/lib/utils/entries-helper.js +252 -0
  80. package/lib/utils/extension-helper.d.ts +5 -0
  81. package/lib/utils/extension-helper.js +84 -0
  82. package/lib/utils/file-helper.d.ts +14 -0
  83. package/lib/utils/file-helper.js +140 -0
  84. package/lib/utils/import-config-handler.d.ts +3 -0
  85. package/lib/utils/import-config-handler.js +73 -0
  86. package/lib/utils/index.d.ts +12 -0
  87. package/lib/utils/index.js +39 -0
  88. package/lib/utils/interactive.d.ts +7 -0
  89. package/lib/utils/interactive.js +88 -0
  90. package/lib/utils/logger.d.ts +8 -0
  91. package/lib/utils/logger.js +154 -0
  92. package/lib/utils/login-handler.d.ts +8 -0
  93. package/lib/utils/login-handler.js +53 -0
  94. package/lib/utils/marketplace-app-helper.d.ts +16 -0
  95. package/lib/utils/marketplace-app-helper.js +143 -0
  96. package/messages/index.json +1 -7
  97. package/oclif.manifest.json +2 -2
  98. package/package.json +46 -20
  99. package/src/app.js +0 -217
  100. package/src/commands/cm/stacks/import.js +0 -161
  101. package/src/config/default.js +0 -352
  102. package/src/lib/import/assets.js +0 -495
  103. package/src/lib/import/content-types.js +0 -201
  104. package/src/lib/import/custom-roles.js +0 -169
  105. package/src/lib/import/entries.js +0 -1495
  106. package/src/lib/import/environments.js +0 -106
  107. package/src/lib/import/extensions.js +0 -108
  108. package/src/lib/import/global-fields.js +0 -135
  109. package/src/lib/import/labels.js +0 -175
  110. package/src/lib/import/locales.js +0 -216
  111. package/src/lib/import/marketplace-apps.js +0 -542
  112. package/src/lib/import/webhooks.js +0 -113
  113. package/src/lib/import/workflows.js +0 -166
  114. package/src/lib/util/extensionsUidReplace.js +0 -67
  115. package/src/lib/util/fs.js +0 -124
  116. package/src/lib/util/import-flags.js +0 -187
  117. package/src/lib/util/index.js +0 -222
  118. package/src/lib/util/log.js +0 -144
  119. package/src/lib/util/login.js +0 -58
  120. package/src/lib/util/lookupReplaceAssets.js +0 -366
  121. package/src/lib/util/lookupReplaceEntries.js +0 -250
  122. package/src/lib/util/marketplace-app-helper.js +0 -31
  123. package/src/lib/util/removeReferenceFields.js +0 -59
  124. package/src/lib/util/schemaTemplate.js +0 -38
  125. package/src/lib/util/supress-mandatory-fields.js +0 -34
  126. package/src/lib/util/upload.js +0 -56
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const pick_1 = tslib_1.__importDefault(require("lodash/pick"));
5
+ const last_1 = tslib_1.__importDefault(require("lodash/last"));
6
+ const chunk_1 = tslib_1.__importDefault(require("lodash/chunk"));
7
+ const isEmpty_1 = tslib_1.__importDefault(require("lodash/isEmpty"));
8
+ const entries_1 = tslib_1.__importDefault(require("lodash/entries"));
9
+ const isEqual_1 = tslib_1.__importDefault(require("lodash/isEqual"));
10
+ const omit_1 = tslib_1.__importDefault(require("lodash/omit"));
11
+ const utils_1 = require("../../utils");
12
+ class BaseClass {
13
+ constructor({ importConfig, stackAPIClient }) {
14
+ this.client = stackAPIClient;
15
+ this.importConfig = importConfig;
16
+ this.modulesConfig = importConfig.modules;
17
+ }
18
+ get stack() {
19
+ return this.client;
20
+ }
21
+ /**
22
+ * @method delay
23
+ * @param {number} ms number
24
+ * @returns {Promise} Promise<void>
25
+ */
26
+ delay(ms) {
27
+ /* eslint-disable no-promise-executor-return */
28
+ return new Promise((resolve) => setTimeout(resolve, ms <= 0 ? 0 : ms));
29
+ }
30
+ /**
31
+ * @method makeConcurrentCall
32
+ * @param {Record<string, any>} env EnvType
33
+ * @param {CustomPromiseHandler} promisifyHandler CustomPromiseHandler
34
+ * @param {boolean} logBatchCompletionMsg boolean
35
+ * @returns {Promise} Promise<void>
36
+ */
37
+ makeConcurrentCall(env, promisifyHandler, logBatchCompletionMsg = true) {
38
+ const { apiParams, apiContent, processName, indexerCount, currentIndexer, concurrencyLimit = this.importConfig.modules.apiConcurrency, } = env;
39
+ /* eslint-disable no-async-promise-executor */
40
+ return new Promise(async (resolve) => {
41
+ let batchNo = 0;
42
+ let isLastRequest = false;
43
+ const batches = (0, chunk_1.default)(apiContent, concurrencyLimit);
44
+ /* eslint-disable no-promise-executor-return */
45
+ if ((0, isEmpty_1.default)(batches))
46
+ return resolve();
47
+ for (const [batchIndex, batch] of (0, entries_1.default)(batches)) {
48
+ batchNo += 1;
49
+ const allPromise = [];
50
+ const start = Date.now();
51
+ for (const [index, element] of (0, entries_1.default)(batch)) {
52
+ let promise = Promise.resolve();
53
+ isLastRequest = (0, isEqual_1.default)((0, last_1.default)(batch), element) && (0, isEqual_1.default)((0, last_1.default)(batches), batch);
54
+ if (promisifyHandler instanceof Function) {
55
+ promise = promisifyHandler({
56
+ apiParams,
57
+ isLastRequest,
58
+ element,
59
+ index: Number(index),
60
+ batchIndex: Number(batchIndex),
61
+ });
62
+ }
63
+ else if (apiParams) {
64
+ apiParams.apiData = element;
65
+ promise = this.makeAPICall(apiParams, isLastRequest);
66
+ }
67
+ allPromise.push(promise);
68
+ }
69
+ /* eslint-disable no-await-in-loop */
70
+ await Promise.allSettled(allPromise);
71
+ /* eslint-disable no-await-in-loop */
72
+ await this.logMsgAndWaitIfRequired(processName, start, batches.length, batchNo, logBatchCompletionMsg, indexerCount, currentIndexer);
73
+ if (isLastRequest)
74
+ resolve();
75
+ }
76
+ });
77
+ }
78
+ /**
79
+ * @method logMsgAndWaitIfRequired
80
+ * @param {string} processName string
81
+ * @param {number} start number
82
+ * @param {number} batchNo - number
83
+ * @returns {Promise} Promise<void>
84
+ */
85
+ async logMsgAndWaitIfRequired(processName, start, totelBatches, batchNo, logBatchCompletionMsg = true, indexerCount, currentIndexer) {
86
+ const end = Date.now();
87
+ const exeTime = end - start;
88
+ if (logBatchCompletionMsg) {
89
+ let batchMsg = '';
90
+ // info: Batch No. 20 of import assets is complete
91
+ if (currentIndexer)
92
+ batchMsg += `Current chunk processing is (${currentIndexer}/${indexerCount})`;
93
+ (0, utils_1.log)(this.importConfig, `Batch No. (${batchNo}/${totelBatches}) of ${processName} is complete. ${batchMsg}`, 'success');
94
+ }
95
+ if (this.importConfig.modules.assets.displayExecutionTime) {
96
+ console.log(`Time taken to execute: ${exeTime} milliseconds; wait time: ${exeTime < 1000 ? 1000 - exeTime : 0} milliseconds`);
97
+ }
98
+ if (exeTime < 1000)
99
+ await this.delay(1000 - exeTime);
100
+ }
101
+ /**
102
+ * @method makeAPICall
103
+ * @param {Record<string, any>} apiOptions - Api related params
104
+ * @param {Record<string, any>} isLastRequest - Boolean
105
+ * @return {Promise} Promise<void>
106
+ */
107
+ makeAPICall(apiOptions, isLastRequest = false) {
108
+ if (apiOptions.serializeData instanceof Function) {
109
+ apiOptions = apiOptions.serializeData(apiOptions);
110
+ }
111
+ const { uid, entity, reject, resolve, apiData, additionalInfo, includeParamOnCompletion } = apiOptions;
112
+ const onSuccess = (response) => resolve({
113
+ response,
114
+ isLastRequest,
115
+ additionalInfo,
116
+ apiData: includeParamOnCompletion ? apiData : undefined,
117
+ });
118
+ const onReject = (error) => reject({
119
+ error,
120
+ isLastRequest,
121
+ additionalInfo,
122
+ apiData: includeParamOnCompletion ? apiData : undefined,
123
+ });
124
+ switch (entity) {
125
+ case 'create-assets-folder':
126
+ return this.stack
127
+ .asset()
128
+ .folder()
129
+ .create({ asset: (0, pick_1.default)(apiData, this.modulesConfig.assets.folderValidKeys) })
130
+ .then(onSuccess)
131
+ .catch(onReject);
132
+ case 'create-assets':
133
+ return this.stack
134
+ .asset()
135
+ .create((0, pick_1.default)(apiData, [...this.modulesConfig.assets.validKeys, 'upload']))
136
+ .then(onSuccess)
137
+ .catch(onReject);
138
+ case 'replace-assets':
139
+ return this.stack
140
+ .asset(uid)
141
+ .replace((0, pick_1.default)(apiData, [...this.modulesConfig.assets.validKeys, 'upload']))
142
+ .then(onSuccess)
143
+ .catch(onReject);
144
+ case 'publish-assets':
145
+ return this.stack
146
+ .asset(uid)
147
+ .publish((0, pick_1.default)(apiData, ['publishDetails']))
148
+ .then(onSuccess)
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);
156
+ case 'create-locale':
157
+ return this.stack
158
+ .locale()
159
+ .create({ locale: (0, pick_1.default)(apiData, ['name', 'code']) })
160
+ .then(onSuccess)
161
+ .catch(onReject);
162
+ case 'update-locale':
163
+ return this.stack
164
+ .locale(apiData.code)
165
+ .update({ locale: (0, pick_1.default)(apiData, [...this.modulesConfig.locales.requiredKeys]) })
166
+ .then(onSuccess)
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);
213
+ default:
214
+ return Promise.resolve();
215
+ }
216
+ }
217
+ }
218
+ exports.default = BaseClass;
@@ -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;