@contentstack/cli-cm-export 1.5.9 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/lib/commands/cm/stacks/export.d.ts +10 -0
- package/lib/commands/cm/stacks/export.js +108 -0
- package/lib/config/index.d.ts +3 -0
- package/lib/config/index.js +393 -0
- package/lib/export/index.d.ts +1 -0
- package/lib/export/index.js +8 -0
- package/lib/export/module-exporter.d.ts +15 -0
- package/lib/export/module-exporter.js +93 -0
- package/lib/export/modules/assets.d.ts +54 -0
- package/lib/export/modules/assets.js +303 -0
- package/lib/export/modules/base-class.d.ts +48 -0
- package/lib/export/modules/base-class.js +117 -0
- package/lib/export/modules/index.d.ts +3 -0
- package/lib/export/modules/index.js +38 -0
- package/lib/export/modules/locales.d.ts +16 -0
- package/lib/export/modules/locales.js +68 -0
- package/lib/export/modules-js/assets.d.ts +43 -0
- package/lib/export/modules-js/assets.js +391 -0
- package/lib/export/modules-js/content-types.d.ts +21 -0
- package/lib/export/modules-js/content-types.js +75 -0
- package/lib/export/modules-js/custom-roles.d.ts +21 -0
- package/lib/export/modules-js/custom-roles.js +76 -0
- package/lib/export/modules-js/entries.d.ts +18 -0
- package/lib/export/modules-js/entries.js +141 -0
- package/lib/export/modules-js/environments.d.ts +16 -0
- package/lib/export/modules-js/environments.js +62 -0
- package/lib/export/modules-js/extensions.d.ts +18 -0
- package/lib/export/modules-js/extensions.js +57 -0
- package/lib/export/modules-js/global-fields.d.ts +22 -0
- package/lib/export/modules-js/global-fields.js +107 -0
- package/lib/export/modules-js/index.d.ts +1 -0
- package/lib/export/modules-js/index.js +33 -0
- package/lib/export/modules-js/labels.d.ts +14 -0
- package/lib/export/modules-js/labels.js +56 -0
- package/lib/export/modules-js/locales.d.ts +23 -0
- package/lib/export/modules-js/locales.js +67 -0
- package/lib/export/modules-js/marketplace-apps.d.ts +21 -0
- package/lib/export/modules-js/marketplace-apps.js +144 -0
- package/lib/export/modules-js/stack.d.ts +18 -0
- package/lib/export/modules-js/stack.js +91 -0
- package/lib/export/modules-js/webhooks.d.ts +18 -0
- package/lib/export/modules-js/webhooks.js +60 -0
- package/lib/export/modules-js/workflows.d.ts +16 -0
- package/lib/export/modules-js/workflows.js +89 -0
- package/lib/types/default-config.d.ts +158 -0
- package/lib/types/default-config.js +2 -0
- package/lib/types/export-config.d.ts +34 -0
- package/lib/types/export-config.js +2 -0
- package/lib/types/index.d.ts +45 -0
- package/lib/types/index.js +2 -0
- package/lib/utils/basic-login.d.ts +8 -0
- package/lib/utils/basic-login.js +45 -0
- package/lib/utils/common-helper.d.ts +11 -0
- package/lib/utils/common-helper.js +78 -0
- package/lib/utils/export-config-handler.d.ts +3 -0
- package/lib/utils/export-config-handler.js +72 -0
- package/lib/utils/file-helper.d.ts +14 -0
- package/lib/utils/file-helper.js +120 -0
- package/lib/utils/index.d.ts +10 -0
- package/lib/utils/index.js +21 -0
- package/lib/utils/interactive.d.ts +6 -0
- package/lib/utils/interactive.js +71 -0
- package/lib/utils/logger.d.ts +8 -0
- package/lib/utils/logger.js +154 -0
- package/lib/utils/marketplace-app-helper.d.ts +1 -0
- package/lib/utils/marketplace-app-helper.js +23 -0
- package/lib/utils/setup-branches.d.ts +3 -0
- package/lib/utils/setup-branches.js +49 -0
- package/lib/utils/setup-export-dir.d.ts +2 -0
- package/lib/utils/setup-export-dir.js +12 -0
- package/messages/index.json +1 -7
- package/oclif.manifest.json +1 -1
- package/package.json +37 -23
- package/src/app.js +0 -156
- package/src/commands/cm/stacks/export.js +0 -202
- package/src/config/default.js +0 -360
- package/src/lib/export/assets.js +0 -454
- package/src/lib/export/content-types.js +0 -90
- package/src/lib/export/custom-roles.js +0 -92
- package/src/lib/export/entries.js +0 -200
- package/src/lib/export/environments.js +0 -74
- package/src/lib/export/extensions.js +0 -69
- package/src/lib/export/global-fields.js +0 -122
- package/src/lib/export/labels.js +0 -67
- package/src/lib/export/locales.js +0 -72
- package/src/lib/export/marketplace-apps.js +0 -187
- package/src/lib/export/stack.js +0 -98
- package/src/lib/export/webhooks.js +0 -76
- package/src/lib/export/workflows.js +0 -106
- package/src/lib/util/export-flags.js +0 -193
- package/src/lib/util/helper.js +0 -113
- package/src/lib/util/index.js +0 -80
- package/src/lib/util/log.js +0 -161
- package/src/lib/util/login.js +0 -79
- package/src/lib/util/marketplace-app-helper.js +0 -24
- 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,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
var _a;
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.ExportAssets = void 0;
|
|
31
|
+
async function startModuleExport(modulePayload) {
|
|
32
|
+
const { default: ModuleRunner } = await (_a = `./${modulePayload.moduleName}`, Promise.resolve().then(() => __importStar(require(_a))));
|
|
33
|
+
const moduleRunner = new ModuleRunner(modulePayload);
|
|
34
|
+
return moduleRunner.start();
|
|
35
|
+
}
|
|
36
|
+
exports.default = startModuleExport;
|
|
37
|
+
var assets_1 = require("./assets");
|
|
38
|
+
Object.defineProperty(exports, "ExportAssets", { enumerable: true, get: function () { return __importDefault(assets_1).default; } });
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ExportConfig, ModuleClassParams } from '../../types';
|
|
2
|
+
import BaseClass from './base-class';
|
|
3
|
+
export default class LocaleExport extends BaseClass {
|
|
4
|
+
private stackAPIClient;
|
|
5
|
+
exportConfig: ExportConfig;
|
|
6
|
+
private masterLocaleConfig;
|
|
7
|
+
private qs;
|
|
8
|
+
private localeConfig;
|
|
9
|
+
private localesPath;
|
|
10
|
+
private masterLocale;
|
|
11
|
+
private locales;
|
|
12
|
+
constructor({ exportConfig, stackAPIClient }: ModuleClassParams);
|
|
13
|
+
start(): Promise<void>;
|
|
14
|
+
getLocales(skip?: number): Promise<any>;
|
|
15
|
+
sanitizeAttribs(locales: Record<string, string>[]): void;
|
|
16
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
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 LocaleExport extends base_class_1.default {
|
|
8
|
+
constructor({ exportConfig, stackAPIClient }) {
|
|
9
|
+
super({ exportConfig, stackAPIClient });
|
|
10
|
+
this.stackAPIClient = stackAPIClient;
|
|
11
|
+
this.localeConfig = exportConfig.modules.locales;
|
|
12
|
+
this.masterLocaleConfig = exportConfig.modules.masterLocale;
|
|
13
|
+
this.qs = {
|
|
14
|
+
include_count: true,
|
|
15
|
+
asc: 'updated_at',
|
|
16
|
+
only: {
|
|
17
|
+
BASE: this.localeConfig.requiredKeys,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
this.localesPath = path.resolve(exportConfig.data, exportConfig.branchName || '', this.localeConfig.dirName);
|
|
21
|
+
this.locales = {};
|
|
22
|
+
this.masterLocale = {};
|
|
23
|
+
}
|
|
24
|
+
async start() {
|
|
25
|
+
try {
|
|
26
|
+
(0, utils_1.log)(this.exportConfig, 'Starting locale export', 'success');
|
|
27
|
+
await utils_1.fsUtil.makeDirectory(this.localesPath);
|
|
28
|
+
await this.getLocales();
|
|
29
|
+
utils_1.fsUtil.writeFile(path.join(this.localesPath, this.localeConfig.fileName), this.locales);
|
|
30
|
+
utils_1.fsUtil.writeFile(path.join(this.localesPath, this.masterLocaleConfig.fileName), this.masterLocale);
|
|
31
|
+
(0, utils_1.log)(this.exportConfig, 'Completed locale export', 'success');
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
(0, utils_1.log)(this.exportConfig, `Failed to export locales. ${(0, utils_1.formatError)(error)}`, 'error');
|
|
35
|
+
throw new Error('Failed to export locales');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async getLocales(skip = 0) {
|
|
39
|
+
if (skip) {
|
|
40
|
+
this.qs.skip = skip;
|
|
41
|
+
}
|
|
42
|
+
let localesFetchResponse = await this.stackAPIClient.locale().query(this.qs).find();
|
|
43
|
+
if (Array.isArray(localesFetchResponse.items) && localesFetchResponse.items.length > 0) {
|
|
44
|
+
this.sanitizeAttribs(localesFetchResponse.items);
|
|
45
|
+
skip += this.localeConfig.limit || 100;
|
|
46
|
+
if (skip > localesFetchResponse.count) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
return await this.getLocales(skip);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
sanitizeAttribs(locales) {
|
|
53
|
+
locales.forEach((locale) => {
|
|
54
|
+
for (let key in locale) {
|
|
55
|
+
if (this.localeConfig.requiredKeys.indexOf(key) === -1) {
|
|
56
|
+
delete locale[key];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (locale.code === this.exportConfig.master_locale.code) {
|
|
60
|
+
this.masterLocale[locale.uid] = locale;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
this.locales[locale.uid] = locale;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.default = LocaleExport;
|