@contentstack/cli-cm-export 1.5.9 → 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 (113) hide show
  1. package/README.md +2 -2
  2. package/lib/commands/cm/stacks/export.d.ts +10 -0
  3. package/lib/commands/cm/stacks/export.js +108 -0
  4. package/lib/config/index.d.ts +3 -0
  5. package/lib/config/index.js +403 -0
  6. package/lib/export/index.d.ts +1 -0
  7. package/lib/export/index.js +8 -0
  8. package/lib/export/module-exporter.d.ts +15 -0
  9. package/lib/export/module-exporter.js +93 -0
  10. package/lib/export/modules/assets.d.ts +54 -0
  11. package/lib/export/modules/assets.js +303 -0
  12. package/lib/export/modules/base-class.d.ts +48 -0
  13. package/lib/export/modules/base-class.js +117 -0
  14. package/lib/export/modules/content-types.d.ts +15 -0
  15. package/lib/export/modules/content-types.js +72 -0
  16. package/lib/export/modules/custom-roles.d.ts +16 -0
  17. package/lib/export/modules/custom-roles.js +81 -0
  18. package/lib/export/modules/environments.d.ts +12 -0
  19. package/lib/export/modules/environments.js +64 -0
  20. package/lib/export/modules/extensions.d.ts +12 -0
  21. package/lib/export/modules/extensions.js +64 -0
  22. package/lib/export/modules/global-fields.d.ts +14 -0
  23. package/lib/export/modules/global-fields.js +59 -0
  24. package/lib/export/modules/index.d.ts +3 -0
  25. package/lib/export/modules/index.js +38 -0
  26. package/lib/export/modules/labels.d.ts +12 -0
  27. package/lib/export/modules/labels.js +64 -0
  28. package/lib/export/modules/locales.d.ts +16 -0
  29. package/lib/export/modules/locales.js +68 -0
  30. package/lib/export/modules/marketplace-apps.d.ts +18 -0
  31. package/lib/export/modules/marketplace-apps.js +119 -0
  32. package/lib/export/modules/webhooks.d.ts +12 -0
  33. package/lib/export/modules/webhooks.js +63 -0
  34. package/lib/export/modules-js/assets.d.ts +43 -0
  35. package/lib/export/modules-js/assets.js +391 -0
  36. package/lib/export/modules-js/content-types.d.ts +21 -0
  37. package/lib/export/modules-js/content-types.js +75 -0
  38. package/lib/export/modules-js/custom-roles.d.ts +21 -0
  39. package/lib/export/modules-js/custom-roles.js +76 -0
  40. package/lib/export/modules-js/entries.d.ts +18 -0
  41. package/lib/export/modules-js/entries.js +141 -0
  42. package/lib/export/modules-js/environments.d.ts +16 -0
  43. package/lib/export/modules-js/environments.js +62 -0
  44. package/lib/export/modules-js/extensions.d.ts +18 -0
  45. package/lib/export/modules-js/extensions.js +57 -0
  46. package/lib/export/modules-js/global-fields.d.ts +22 -0
  47. package/lib/export/modules-js/global-fields.js +107 -0
  48. package/lib/export/modules-js/index.d.ts +2 -0
  49. package/lib/export/modules-js/index.js +31 -0
  50. package/lib/export/modules-js/labels.d.ts +14 -0
  51. package/lib/export/modules-js/labels.js +56 -0
  52. package/lib/export/modules-js/locales.d.ts +23 -0
  53. package/lib/export/modules-js/locales.js +67 -0
  54. package/lib/export/modules-js/marketplace-apps.d.ts +20 -0
  55. package/lib/export/modules-js/marketplace-apps.js +125 -0
  56. package/lib/export/modules-js/stack.d.ts +18 -0
  57. package/lib/export/modules-js/stack.js +91 -0
  58. package/lib/export/modules-js/webhooks.d.ts +18 -0
  59. package/lib/export/modules-js/webhooks.js +60 -0
  60. package/lib/export/modules-js/workflows.d.ts +16 -0
  61. package/lib/export/modules-js/workflows.js +89 -0
  62. package/lib/types/default-config.d.ts +158 -0
  63. package/lib/types/default-config.js +2 -0
  64. package/lib/types/export-config.d.ts +36 -0
  65. package/lib/types/export-config.js +2 -0
  66. package/lib/types/index.d.ts +88 -0
  67. package/lib/types/index.js +2 -0
  68. package/lib/utils/basic-login.d.ts +8 -0
  69. package/lib/utils/basic-login.js +45 -0
  70. package/lib/utils/common-helper.d.ts +11 -0
  71. package/lib/utils/common-helper.js +78 -0
  72. package/lib/utils/export-config-handler.d.ts +3 -0
  73. package/lib/utils/export-config-handler.js +72 -0
  74. package/lib/utils/file-helper.d.ts +14 -0
  75. package/lib/utils/file-helper.js +120 -0
  76. package/lib/utils/index.d.ts +10 -0
  77. package/lib/utils/index.js +21 -0
  78. package/lib/utils/interactive.d.ts +7 -0
  79. package/lib/utils/interactive.js +84 -0
  80. package/lib/utils/logger.d.ts +8 -0
  81. package/lib/utils/logger.js +154 -0
  82. package/lib/utils/marketplace-app-helper.d.ts +11 -0
  83. package/lib/utils/marketplace-app-helper.js +55 -0
  84. package/lib/utils/setup-branches.d.ts +3 -0
  85. package/lib/utils/setup-branches.js +49 -0
  86. package/lib/utils/setup-export-dir.d.ts +2 -0
  87. package/lib/utils/setup-export-dir.js +12 -0
  88. package/messages/index.json +1 -7
  89. package/oclif.manifest.json +1 -1
  90. package/package.json +38 -23
  91. package/src/app.js +0 -156
  92. package/src/commands/cm/stacks/export.js +0 -202
  93. package/src/config/default.js +0 -360
  94. package/src/lib/export/assets.js +0 -454
  95. package/src/lib/export/content-types.js +0 -90
  96. package/src/lib/export/custom-roles.js +0 -92
  97. package/src/lib/export/entries.js +0 -200
  98. package/src/lib/export/environments.js +0 -74
  99. package/src/lib/export/extensions.js +0 -69
  100. package/src/lib/export/global-fields.js +0 -122
  101. package/src/lib/export/labels.js +0 -67
  102. package/src/lib/export/locales.js +0 -72
  103. package/src/lib/export/marketplace-apps.js +0 -187
  104. package/src/lib/export/stack.js +0 -98
  105. package/src/lib/export/webhooks.js +0 -76
  106. package/src/lib/export/workflows.js +0 -106
  107. package/src/lib/util/export-flags.js +0 -193
  108. package/src/lib/util/helper.js +0 -113
  109. package/src/lib/util/index.js +0 -80
  110. package/src/lib/util/log.js +0 -161
  111. package/src/lib/util/login.js +0 -79
  112. package/src/lib/util/marketplace-app-helper.js +0 -24
  113. package/src/lib/util/setup-branches.js +0 -56
@@ -0,0 +1,54 @@
1
+ import { ModuleClassParams } from '../../types';
2
+ import BaseClass from './base-class';
3
+ export default class ExportAssets extends BaseClass {
4
+ private assetsRootPath;
5
+ assetConfig: {
6
+ dirName: string;
7
+ fileName: string;
8
+ batchLimit: number;
9
+ host: string;
10
+ invalidKeys: string[];
11
+ chunkFileSize: number;
12
+ downloadLimit: number;
13
+ fetchConcurrency: number;
14
+ assetsMetaKeys: string[];
15
+ securedAssets: boolean;
16
+ displayExecutionTime: boolean;
17
+ enableDownloadStatus: boolean;
18
+ includeVersionedAssets: boolean;
19
+ dependencies?: import("../../types").Modules[];
20
+ };
21
+ private assetsFolder;
22
+ versionedAssets: Record<string, unknown>[];
23
+ constructor({ exportConfig, stackAPIClient }: ModuleClassParams);
24
+ get commonQueryParam(): Record<string, unknown>;
25
+ start(): Promise<void>;
26
+ /**
27
+ * @method getAssetsFolders
28
+ * @param {number} totalCount number
29
+ * @returns Promise<any|void>
30
+ */
31
+ getAssetsFolders(totalCount: number | void): Promise<Promise<void> | void>;
32
+ /**
33
+ * @method getAssets
34
+ * @param totalCount number
35
+ * @returns Promise<void>
36
+ */
37
+ getAssets(totalCount: number | void): Promise<any | void>;
38
+ /**
39
+ * @method getVersionedAssets
40
+ * @returns Promise<any|void>
41
+ */
42
+ getVersionedAssets(): Promise<any | void>;
43
+ /**
44
+ * @method getAssetsCount
45
+ * @param isDir boolean
46
+ * @returns Promise<number|undefined>
47
+ */
48
+ getAssetsCount(isDir?: boolean): Promise<number | void>;
49
+ /**
50
+ * @method downloadAssets
51
+ * @returns Promise<any|void>
52
+ */
53
+ downloadAssets(): Promise<any | void>;
54
+ }
@@ -0,0 +1,303 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const map_1 = tslib_1.__importDefault(require("lodash/map"));
5
+ const chunk_1 = tslib_1.__importDefault(require("lodash/chunk"));
6
+ const first_1 = tslib_1.__importDefault(require("lodash/first"));
7
+ const merge_1 = tslib_1.__importDefault(require("lodash/merge"));
8
+ const filter_1 = tslib_1.__importDefault(require("lodash/filter"));
9
+ const uniqBy_1 = tslib_1.__importDefault(require("lodash/uniqBy"));
10
+ const values_1 = tslib_1.__importDefault(require("lodash/values"));
11
+ const entries_1 = tslib_1.__importDefault(require("lodash/entries"));
12
+ const isEmpty_1 = tslib_1.__importDefault(require("lodash/isEmpty"));
13
+ const includes_1 = tslib_1.__importDefault(require("lodash/includes"));
14
+ const progress_stream_1 = tslib_1.__importDefault(require("progress-stream"));
15
+ const node_fs_1 = require("node:fs");
16
+ const node_path_1 = require("node:path");
17
+ const cli_utilities_1 = require("@contentstack/cli-utilities");
18
+ const config_1 = tslib_1.__importDefault(require("../../config"));
19
+ const utils_1 = require("../../utils");
20
+ const base_class_1 = tslib_1.__importDefault(require("./base-class"));
21
+ class ExportAssets extends base_class_1.default {
22
+ constructor({ exportConfig, stackAPIClient }) {
23
+ super({ exportConfig, stackAPIClient });
24
+ this.assetConfig = config_1.default.modules.assets;
25
+ this.assetsFolder = [];
26
+ this.versionedAssets = [];
27
+ }
28
+ get commonQueryParam() {
29
+ return {
30
+ skip: 0,
31
+ asc: 'created_at',
32
+ include_count: false,
33
+ };
34
+ }
35
+ async start() {
36
+ this.assetsRootPath = (0, node_path_1.resolve)(this.exportConfig.data, this.exportConfig.branchName || '', this.assetConfig.dirName);
37
+ // NOTE step 1: Get assets and it's folder count in parallel
38
+ const [assetsCount, assetsFolderCount] = await Promise.all([this.getAssetsCount(), this.getAssetsCount(true)]);
39
+ // NOTE step 2: Get assets and it's folder data in parallel
40
+ await Promise.all([this.getAssetsFolders(assetsFolderCount), this.getAssets(assetsCount)]);
41
+ // NOTE step 3: Get versioned assets
42
+ if (!(0, isEmpty_1.default)(this.versionedAssets) && this.assetConfig.includeVersionedAssets) {
43
+ await this.getVersionedAssets();
44
+ }
45
+ // NOTE step 4: Download all assets
46
+ await this.downloadAssets();
47
+ (0, utils_1.log)(this.exportConfig, 'Assets exported successfully.!', 'info');
48
+ }
49
+ /**
50
+ * @method getAssetsFolders
51
+ * @param {number} totalCount number
52
+ * @returns Promise<any|void>
53
+ */
54
+ getAssetsFolders(totalCount) {
55
+ if (!totalCount)
56
+ return Promise.resolve();
57
+ const queryParam = Object.assign(Object.assign({}, this.commonQueryParam), { query: { is_dir: true } });
58
+ const onSuccess = ({ response: { items } }) => {
59
+ if (!(0, isEmpty_1.default)(items))
60
+ this.assetsFolder.push(...items);
61
+ };
62
+ const onReject = ({ error }) => {
63
+ (0, utils_1.log)(this.exportConfig, 'Export asset folder query failed', 'error');
64
+ (0, utils_1.log)(this.exportConfig, error, 'error');
65
+ };
66
+ return this.makeConcurrentCall({
67
+ totalCount,
68
+ apiParams: {
69
+ queryParam,
70
+ module: 'assets',
71
+ reject: onReject,
72
+ resolve: onSuccess,
73
+ },
74
+ module: 'assets folders',
75
+ concurrencyLimit: this.assetConfig.fetchConcurrency,
76
+ }).then(() => {
77
+ if (!(0, isEmpty_1.default)(this.assetsFolder)) {
78
+ new cli_utilities_1.FsUtility({ basePath: this.assetsRootPath }).writeFile((0, node_path_1.resolve)(this.assetsRootPath, 'folders.json'), this.assetsFolder);
79
+ }
80
+ (0, utils_1.log)(this.exportConfig, 'Assets folder Exported successfully.!', 'info');
81
+ });
82
+ }
83
+ /**
84
+ * @method getAssets
85
+ * @param totalCount number
86
+ * @returns Promise<void>
87
+ */
88
+ getAssets(totalCount) {
89
+ if (!totalCount)
90
+ return Promise.resolve();
91
+ let fs;
92
+ let metaHandler;
93
+ const queryParam = Object.assign(Object.assign({}, this.commonQueryParam), { include_publish_details: true, except: { BASE: this.assetConfig.invalidKeys } });
94
+ if (this.assetConfig.includeVersionedAssets) {
95
+ const customHandler = (array) => {
96
+ const versionAssets = (0, filter_1.default)(array, ({ _version }) => _version > 1);
97
+ if (!(0, isEmpty_1.default)(versionAssets)) {
98
+ this.versionedAssets.push(...(0, map_1.default)(versionAssets, ({ uid, _version }) => ({
99
+ [uid]: _version,
100
+ })));
101
+ }
102
+ };
103
+ metaHandler = customHandler;
104
+ }
105
+ const onReject = ({ error }) => {
106
+ (0, utils_1.log)(this.exportConfig, 'Export asset query failed', 'error');
107
+ (0, utils_1.log)(this.exportConfig, error.message, 'error');
108
+ };
109
+ const onSuccess = ({ response: { items } }) => {
110
+ if (!fs && !(0, isEmpty_1.default)(items)) {
111
+ fs = new cli_utilities_1.FsUtility({
112
+ metaHandler,
113
+ moduleName: 'assets',
114
+ indexFileName: 'assets.json',
115
+ basePath: this.assetsRootPath,
116
+ chunkFileSize: this.assetConfig.chunkFileSize,
117
+ metaPickKeys: (0, merge_1.default)(['uid', 'url', 'filename'], this.assetConfig.assetsMetaKeys),
118
+ });
119
+ }
120
+ if (!(0, isEmpty_1.default)(items))
121
+ fs === null || fs === void 0 ? void 0 : fs.writeIntoFile(items, { mapKeyVal: true });
122
+ };
123
+ return this.makeConcurrentCall({
124
+ module: 'assets',
125
+ totalCount,
126
+ apiParams: {
127
+ queryParam,
128
+ module: 'assets',
129
+ reject: onReject,
130
+ resolve: onSuccess,
131
+ },
132
+ concurrencyLimit: this.assetConfig.fetchConcurrency,
133
+ }).then(() => {
134
+ fs === null || fs === void 0 ? void 0 : fs.completeFile(true);
135
+ (0, utils_1.log)(this.exportConfig, 'Assets metadata exported successfully.!', 'info');
136
+ });
137
+ }
138
+ /**
139
+ * @method getVersionedAssets
140
+ * @returns Promise<any|void>
141
+ */
142
+ getVersionedAssets() {
143
+ let fs;
144
+ const queryParam = Object.assign(Object.assign({}, this.commonQueryParam), { include_publish_details: true, except: { BASE: this.assetConfig.invalidKeys } });
145
+ const versionedAssets = (0, map_1.default)(this.versionedAssets, (element) => {
146
+ const batch = [];
147
+ const [uid, version] = (0, first_1.default)((0, entries_1.default)(element));
148
+ for (let index = 1; index < version; index++) {
149
+ batch.push({ [uid]: index });
150
+ }
151
+ return batch;
152
+ }).flat();
153
+ const apiBatches = (0, chunk_1.default)(versionedAssets, this.assetConfig.fetchConcurrency);
154
+ const promisifyHandler = (input) => {
155
+ const { index, batchIndex, apiParams, isLastRequest } = input;
156
+ const batch = apiBatches[batchIndex][index];
157
+ const [uid, version] = (0, first_1.default)((0, entries_1.default)(batch));
158
+ if (apiParams === null || apiParams === void 0 ? void 0 : apiParams.queryParam) {
159
+ apiParams.uid = uid;
160
+ apiParams.queryParam.version = version;
161
+ return this.makeAPICall(apiParams, isLastRequest);
162
+ }
163
+ return Promise.resolve();
164
+ };
165
+ const onSuccess = ({ response }) => {
166
+ if (!fs && !(0, isEmpty_1.default)(response)) {
167
+ fs = new cli_utilities_1.FsUtility({
168
+ moduleName: 'assets',
169
+ indexFileName: 'versioned-assets.json',
170
+ chunkFileSize: this.assetConfig.chunkFileSize,
171
+ basePath: (0, node_path_1.resolve)(this.assetsRootPath, 'versions'),
172
+ metaPickKeys: (0, merge_1.default)(['uid', 'url', 'filename', '_version'], this.assetConfig.assetsMetaKeys),
173
+ });
174
+ }
175
+ if (!(0, isEmpty_1.default)(response))
176
+ fs === null || fs === void 0 ? void 0 : fs.writeIntoFile([response], {
177
+ mapKeyVal: true,
178
+ keyName: ['uid', '_version'],
179
+ });
180
+ };
181
+ const onReject = ({ error }) => {
182
+ (0, utils_1.log)(this.exportConfig, 'Export versioned asset query failed', 'error');
183
+ (0, utils_1.log)(this.exportConfig, error, 'error');
184
+ };
185
+ return this.makeConcurrentCall({
186
+ apiBatches,
187
+ apiParams: {
188
+ queryParam,
189
+ module: 'asset',
190
+ reject: onReject,
191
+ resolve: onSuccess,
192
+ },
193
+ module: 'versioned assets',
194
+ totalCount: versionedAssets.length,
195
+ concurrencyLimit: this.assetConfig.fetchConcurrency,
196
+ }, promisifyHandler).then(() => {
197
+ fs === null || fs === void 0 ? void 0 : fs.completeFile(true);
198
+ (0, utils_1.log)(this.exportConfig, 'Assets folder Exported successfully.!', 'info');
199
+ });
200
+ }
201
+ /**
202
+ * @method getAssetsCount
203
+ * @param isDir boolean
204
+ * @returns Promise<number|undefined>
205
+ */
206
+ getAssetsCount(isDir = false) {
207
+ const queryParam = Object.assign(Object.assign({ limit: 1 }, this.commonQueryParam), { skip: 10 ** 100 });
208
+ if (isDir)
209
+ queryParam.query = { is_dir: true };
210
+ return this.stack
211
+ .asset()
212
+ .query(queryParam)
213
+ .count()
214
+ .then(({ assets }) => assets)
215
+ .catch((error) => {
216
+ (0, utils_1.log)(this.exportConfig, 'Get count query failed', 'error');
217
+ (0, utils_1.log)(this.exportConfig, error, 'error');
218
+ });
219
+ }
220
+ /**
221
+ * @method downloadAssets
222
+ * @returns Promise<any|void>
223
+ */
224
+ async downloadAssets() {
225
+ const fs = new cli_utilities_1.FsUtility({
226
+ fileExt: 'json',
227
+ createDirIfNotExist: false,
228
+ basePath: this.assetsRootPath,
229
+ });
230
+ const assetsMetaData = fs.getPlainMeta();
231
+ let listOfAssets = (0, values_1.default)(assetsMetaData).flat();
232
+ if (this.assetConfig.includeVersionedAssets) {
233
+ const versionedAssetsMetaData = fs.getPlainMeta((0, node_path_1.resolve)(this.assetsRootPath, 'versions', 'metadata.json'));
234
+ listOfAssets.push(...(0, values_1.default)(versionedAssetsMetaData).flat());
235
+ }
236
+ listOfAssets = (0, uniqBy_1.default)(listOfAssets, 'url');
237
+ const apiBatches = (0, chunk_1.default)(listOfAssets, this.assetConfig.downloadLimit);
238
+ const downloadedAssetsDirs = await (0, cli_utilities_1.getDirectories)((0, node_path_1.resolve)(this.assetsRootPath, 'files'));
239
+ const onSuccess = ({ response: { data }, additionalInfo }) => {
240
+ const { asset } = additionalInfo;
241
+ const assetFolderPath = (0, node_path_1.resolve)(this.assetsRootPath, 'files', asset.uid);
242
+ const assetFilePath = (0, node_path_1.resolve)(assetFolderPath, asset.filename);
243
+ if (!(0, includes_1.default)(downloadedAssetsDirs, asset.uid)) {
244
+ fs.createFolderIfNotExist(assetFolderPath);
245
+ }
246
+ const assetWriterStream = (0, node_fs_1.createWriteStream)(assetFilePath);
247
+ assetWriterStream.on('error', (error) => {
248
+ (0, utils_1.log)(this.exportConfig, `Downloaded failed ${asset.filename}: ${asset.uid}!`, 'error');
249
+ (0, utils_1.log)(this.exportConfig, error, 'error');
250
+ });
251
+ /**
252
+ * NOTE if pipe not working as expected add the following code below to fix the issue
253
+ * https://oramind.com/using-streams-efficiently-in-nodejs/
254
+ * import * as stream from "stream";
255
+ * import { promisify } from "util";
256
+ * const finished = promisify(stream.finished);
257
+ * await finished(assetWriterStream);
258
+ */
259
+ if (this.assetConfig.enableDownloadStatus) {
260
+ const str = (0, progress_stream_1.default)({
261
+ time: 5000,
262
+ length: data.headers['content-length'],
263
+ });
264
+ str.on('progress', function (progressData) {
265
+ console.log(`${asset.filename}: ${Math.round(progressData.percentage)}%`);
266
+ });
267
+ data.pipe(str).pipe(assetWriterStream);
268
+ }
269
+ else {
270
+ data.pipe(assetWriterStream);
271
+ }
272
+ (0, utils_1.log)(this.exportConfig, `Downloaded ${asset.filename}: ${asset.uid} successfully!`, 'success');
273
+ };
274
+ const onReject = ({ error, additionalInfo }) => {
275
+ const { asset } = additionalInfo;
276
+ (0, utils_1.log)(this.exportConfig, `Downloaded failed ${asset.filename}: ${asset.uid}!`, 'error');
277
+ (0, utils_1.log)(this.exportConfig, error, 'error');
278
+ };
279
+ const promisifyHandler = (input) => {
280
+ const { index, batchIndex } = input;
281
+ const asset = apiBatches[batchIndex][index];
282
+ const url = this.assetConfig.securedAssets
283
+ ? `${asset.url}?authtoken=${cli_utilities_1.configHandler.get('authtoken')}`
284
+ : asset.url;
285
+ return this.makeAPICall({
286
+ reject: onReject,
287
+ resolve: onSuccess,
288
+ url: encodeURI(url),
289
+ module: 'download-asset',
290
+ additionalInfo: { asset },
291
+ });
292
+ };
293
+ return this.makeConcurrentCall({
294
+ apiBatches,
295
+ module: 'assets download',
296
+ totalCount: listOfAssets.length,
297
+ concurrencyLimit: this.assetConfig.downloadLimit,
298
+ }, promisifyHandler).then(() => {
299
+ (0, utils_1.log)(this.exportConfig, 'Assets download completed successfully.!', 'info');
300
+ });
301
+ }
302
+ }
303
+ exports.default = ExportAssets;
@@ -0,0 +1,48 @@
1
+ import { ExportConfig, ModuleClassParams } from '../../types';
2
+ export type ApiOptions = {
3
+ uid?: string;
4
+ url?: string;
5
+ module: ApiModuleType;
6
+ queryParam?: Record<any, any>;
7
+ resolve: (value: any) => void;
8
+ reject: (error: any) => void;
9
+ additionalInfo?: Record<any, any>;
10
+ };
11
+ export type EnvType = {
12
+ module: string;
13
+ totalCount: number;
14
+ apiBatches?: number[];
15
+ concurrencyLimit: number;
16
+ apiParams?: ApiOptions;
17
+ };
18
+ export type CustomPromiseHandlerInput = {
19
+ index: number;
20
+ batchIndex: number;
21
+ apiParams?: ApiOptions;
22
+ isLastRequest: boolean;
23
+ };
24
+ export type CustomPromiseHandler = (input: CustomPromiseHandlerInput) => Promise<any>;
25
+ export type ApiModuleType = 'stack' | 'asset' | 'assets' | 'entry' | 'entries' | 'content-type' | 'content-types' | 'stacks' | 'download-asset';
26
+ export default abstract class BaseClass {
27
+ readonly client: any;
28
+ exportConfig: ExportConfig;
29
+ constructor({ exportConfig, stackAPIClient }: Omit<ModuleClassParams, 'moduleName'>);
30
+ get stack(): any;
31
+ delay(ms: number): Promise<void>;
32
+ makeConcurrentCall(env: EnvType, promisifyHandler?: CustomPromiseHandler): Promise<void>;
33
+ /**
34
+ * @method logMsgAndWaitIfRequired
35
+ * @param module string
36
+ * @param start number
37
+ * @param batchNo number
38
+ * @returns Promise<void>
39
+ */
40
+ logMsgAndWaitIfRequired(module: string, start: number, batchNo: number): Promise<void>;
41
+ /**
42
+ * @method makeAPICall
43
+ * @param {Record<string, any>} options - Api related params
44
+ * @param {Record<string, any>} isLastRequest - Boolean
45
+ * @returns Promise<any>
46
+ */
47
+ makeAPICall({ module: moduleName, reject, resolve, url, uid, additionalInfo, queryParam }: ApiOptions, isLastRequest?: boolean): Promise<any>;
48
+ }
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const map_1 = tslib_1.__importDefault(require("lodash/map"));
5
+ const fill_1 = tslib_1.__importDefault(require("lodash/fill"));
6
+ const last_1 = tslib_1.__importDefault(require("lodash/last"));
7
+ const chunk_1 = tslib_1.__importDefault(require("lodash/chunk"));
8
+ const isEmpty_1 = tslib_1.__importDefault(require("lodash/isEmpty"));
9
+ const entries_1 = tslib_1.__importDefault(require("lodash/entries"));
10
+ const isEqual_1 = tslib_1.__importDefault(require("lodash/isEqual"));
11
+ const utils_1 = require("../../utils");
12
+ class BaseClass {
13
+ constructor({ exportConfig, stackAPIClient }) {
14
+ this.client = stackAPIClient;
15
+ this.exportConfig = exportConfig;
16
+ }
17
+ get stack() {
18
+ return this.client;
19
+ }
20
+ delay(ms) {
21
+ /* eslint-disable no-promise-executor-return */
22
+ return new Promise((resolve) => setTimeout(resolve, ms <= 0 ? 0 : ms));
23
+ }
24
+ makeConcurrentCall(env, promisifyHandler) {
25
+ const { module, apiBatches, totalCount, apiParams, concurrencyLimit } = env;
26
+ /* eslint-disable no-async-promise-executor */
27
+ return new Promise(async (resolve) => {
28
+ let batchNo = 0;
29
+ let isLastRequest = false;
30
+ const batch = (0, fill_1.default)(Array.from({ length: Number.parseInt(String(totalCount / 100), 10) }), 100);
31
+ if (totalCount % 100)
32
+ batch.push(100);
33
+ const batches = apiBatches ||
34
+ (0, chunk_1.default)((0, map_1.default)(batch, (skip, i) => skip * i), concurrencyLimit);
35
+ /* eslint-disable no-promise-executor-return */
36
+ if ((0, isEmpty_1.default)(batches))
37
+ return resolve();
38
+ for (const [batchIndex, batch] of (0, entries_1.default)(batches)) {
39
+ batchNo += 1;
40
+ const allPromise = [];
41
+ const start = Date.now();
42
+ for (const [index, element] of (0, entries_1.default)(batch)) {
43
+ let promise;
44
+ isLastRequest = (0, isEqual_1.default)((0, last_1.default)(batch), element) && (0, isEqual_1.default)((0, last_1.default)(batches), batch);
45
+ if (promisifyHandler instanceof Function) {
46
+ promise = promisifyHandler({
47
+ apiParams,
48
+ isLastRequest,
49
+ index: Number(index),
50
+ batchIndex: Number(batchIndex),
51
+ });
52
+ }
53
+ else if (apiParams === null || apiParams === void 0 ? void 0 : apiParams.queryParam) {
54
+ apiParams.queryParam.skip = element;
55
+ promise = this.makeAPICall(apiParams, isLastRequest);
56
+ }
57
+ allPromise.push(promise);
58
+ }
59
+ /* eslint-disable no-await-in-loop */
60
+ await Promise.allSettled(allPromise);
61
+ /* eslint-disable no-await-in-loop */
62
+ await this.logMsgAndWaitIfRequired(module, start, batchNo);
63
+ if (isLastRequest)
64
+ resolve();
65
+ }
66
+ });
67
+ }
68
+ /**
69
+ * @method logMsgAndWaitIfRequired
70
+ * @param module string
71
+ * @param start number
72
+ * @param batchNo number
73
+ * @returns Promise<void>
74
+ */
75
+ async logMsgAndWaitIfRequired(module, start, batchNo) {
76
+ const end = Date.now();
77
+ const exeTime = end - start;
78
+ (0, utils_1.log)(this.exportConfig, `Batch No. ${batchNo} of ${module} is complete.`, 'success');
79
+ if (this.exportConfig.modules.assets.displayExecutionTime) {
80
+ console.log(`Time taken to execute: ${exeTime} milliseconds; wait time: ${exeTime < 1000 ? 1000 - exeTime : 0} milliseconds`);
81
+ }
82
+ if (exeTime < 1000)
83
+ await this.delay(1000 - exeTime);
84
+ }
85
+ /**
86
+ * @method makeAPICall
87
+ * @param {Record<string, any>} options - Api related params
88
+ * @param {Record<string, any>} isLastRequest - Boolean
89
+ * @returns Promise<any>
90
+ */
91
+ makeAPICall({ module: moduleName, reject, resolve, url = '', uid = '', additionalInfo, queryParam = {} }, isLastRequest = false) {
92
+ switch (moduleName) {
93
+ case 'asset':
94
+ return this.stack
95
+ .asset(uid)
96
+ .fetch(queryParam)
97
+ .then((response) => resolve({ response, isLastRequest, additionalInfo }))
98
+ .catch((error) => reject({ error, isLastRequest, additionalInfo }));
99
+ case 'assets':
100
+ return this.stack
101
+ .asset()
102
+ .query(queryParam)
103
+ .find()
104
+ .then((response) => resolve({ response, isLastRequest, additionalInfo }))
105
+ .catch((error) => reject({ error, isLastRequest, additionalInfo }));
106
+ case 'download-asset':
107
+ return this.stack
108
+ .asset()
109
+ .download({ url, responseType: 'stream' })
110
+ .then((response) => resolve({ response, isLastRequest, additionalInfo }))
111
+ .catch((error) => reject({ error, isLastRequest, additionalInfo }));
112
+ default:
113
+ return Promise.resolve();
114
+ }
115
+ }
116
+ }
117
+ exports.default = BaseClass;
@@ -0,0 +1,15 @@
1
+ import { ExportConfig, ModuleClassParams } from '../../types';
2
+ import BaseClass from './base-class';
3
+ export default class ContentTypesExport extends BaseClass {
4
+ private stackAPIClient;
5
+ exportConfig: ExportConfig;
6
+ private qs;
7
+ private contentTypesConfig;
8
+ private contentTypesDirPath;
9
+ private contentTypes;
10
+ constructor({ exportConfig, stackAPIClient }: ModuleClassParams);
11
+ start(): Promise<void>;
12
+ getContentTypes(skip?: number): Promise<any>;
13
+ sanitizeAttribs(contentTypes: Record<string, unknown>[]): Record<string, unknown>[];
14
+ writeContentTypes(contentTypes: Record<string, unknown>[]): Promise<void>;
15
+ }
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const path = tslib_1.__importStar(require("path"));
5
+ const utils_1 = require("../../utils");
6
+ const base_class_1 = tslib_1.__importDefault(require("./base-class"));
7
+ class ContentTypesExport extends base_class_1.default {
8
+ constructor({ exportConfig, stackAPIClient }) {
9
+ super({ exportConfig, stackAPIClient });
10
+ this.stackAPIClient = stackAPIClient;
11
+ this.contentTypesConfig = exportConfig.modules['content-types'];
12
+ this.qs = {
13
+ include_count: true,
14
+ asc: 'updated_at',
15
+ limit: this.contentTypesConfig.limit,
16
+ include_global_field_schema: true,
17
+ };
18
+ this.contentTypesDirPath = path.resolve(exportConfig.data, exportConfig.branchName || '', this.contentTypesConfig.dirName);
19
+ this.contentTypes = [];
20
+ }
21
+ async start() {
22
+ try {
23
+ (0, utils_1.log)(this.exportConfig, 'Starting content type export', 'success');
24
+ await utils_1.fsUtil.makeDirectory(this.contentTypesDirPath);
25
+ await this.getContentTypes();
26
+ await this.writeContentTypes(this.contentTypes);
27
+ (0, utils_1.log)(this.exportConfig, 'Content type(s) exported successfully', 'success');
28
+ }
29
+ catch (error) {
30
+ (0, utils_1.log)(this.exportConfig, `Failed to export content types ${(0, utils_1.formatError)(error)}`, 'error');
31
+ throw new Error('Failed to export content types');
32
+ }
33
+ }
34
+ async getContentTypes(skip = 0) {
35
+ if (skip) {
36
+ this.qs.skip = skip;
37
+ }
38
+ const contentTypeSearchResponse = await this.stackAPIClient.contentType().query(this.qs).find();
39
+ if (Array.isArray(contentTypeSearchResponse.items) && contentTypeSearchResponse.items.length > 0) {
40
+ let updatedContentTypes = this.sanitizeAttribs(contentTypeSearchResponse.items);
41
+ this.contentTypes.push(...updatedContentTypes);
42
+ skip += this.contentTypesConfig.limit || 100;
43
+ if (skip >= contentTypeSearchResponse.count) {
44
+ return;
45
+ }
46
+ return await this.getContentTypes(skip);
47
+ }
48
+ else {
49
+ (0, utils_1.log)(this.exportConfig, 'No content types returned for the given query', 'info');
50
+ }
51
+ }
52
+ sanitizeAttribs(contentTypes) {
53
+ let updatedContentTypes = [];
54
+ contentTypes.forEach((contentType) => {
55
+ for (let key in contentType) {
56
+ if (this.contentTypesConfig.validKeys.indexOf(key) === -1) {
57
+ delete contentType[key];
58
+ }
59
+ }
60
+ updatedContentTypes.push(contentType);
61
+ });
62
+ return updatedContentTypes;
63
+ }
64
+ async writeContentTypes(contentTypes) {
65
+ function write(contentType) {
66
+ return utils_1.fsUtil.writeFile(path.join(this.contentTypesDirPath, `${contentType.uid === 'schema' ? 'schema|1' : contentType.uid}.json`), contentType);
67
+ }
68
+ await (0, utils_1.executeTask)(contentTypes, write.bind(this), { concurrency: this.exportConfig.writeConcurrency });
69
+ return utils_1.fsUtil.writeFile(path.join(this.contentTypesDirPath, 'schema.json'), contentTypes);
70
+ }
71
+ }
72
+ exports.default = ContentTypesExport;
@@ -0,0 +1,16 @@
1
+ import BaseClass from './base-class';
2
+ import { ModuleClassParams } from '../../types';
3
+ export default class ExportCustomRoles extends BaseClass {
4
+ private customRoles;
5
+ private existingRoles;
6
+ private customRolesConfig;
7
+ private sourceLocalesMap;
8
+ private localesMap;
9
+ rolesFolderPath: string;
10
+ customRolesLocalesFilepath: string;
11
+ constructor({ exportConfig, stackAPIClient }: ModuleClassParams);
12
+ start(): Promise<void>;
13
+ getCustomRoles(): Promise<void>;
14
+ getLocales(): Promise<void>;
15
+ getCustomRolesLocales(): Promise<void>;
16
+ }