@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.
- 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 +403 -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/content-types.d.ts +15 -0
- package/lib/export/modules/content-types.js +72 -0
- package/lib/export/modules/custom-roles.d.ts +16 -0
- package/lib/export/modules/custom-roles.js +81 -0
- package/lib/export/modules/environments.d.ts +12 -0
- package/lib/export/modules/environments.js +64 -0
- package/lib/export/modules/extensions.d.ts +12 -0
- package/lib/export/modules/extensions.js +64 -0
- package/lib/export/modules/global-fields.d.ts +14 -0
- package/lib/export/modules/global-fields.js +59 -0
- package/lib/export/modules/index.d.ts +3 -0
- package/lib/export/modules/index.js +38 -0
- package/lib/export/modules/labels.d.ts +12 -0
- package/lib/export/modules/labels.js +64 -0
- package/lib/export/modules/locales.d.ts +16 -0
- package/lib/export/modules/locales.js +68 -0
- package/lib/export/modules/marketplace-apps.d.ts +18 -0
- package/lib/export/modules/marketplace-apps.js +119 -0
- package/lib/export/modules/webhooks.d.ts +12 -0
- package/lib/export/modules/webhooks.js +63 -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 +2 -0
- package/lib/export/modules-js/index.js +31 -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 +20 -0
- package/lib/export/modules-js/marketplace-apps.js +125 -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 +36 -0
- package/lib/types/export-config.js +2 -0
- package/lib/types/index.d.ts +88 -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 +7 -0
- package/lib/utils/interactive.js +84 -0
- package/lib/utils/logger.d.ts +8 -0
- package/lib/utils/logger.js +154 -0
- package/lib/utils/marketplace-app-helper.d.ts +11 -0
- package/lib/utils/marketplace-app-helper.js +55 -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 +38 -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,12 @@
|
|
|
1
|
+
import BaseClass from './base-class';
|
|
2
|
+
import { ModuleClassParams } from '../../types';
|
|
3
|
+
export default class ExportWebhooks extends BaseClass {
|
|
4
|
+
private webhooks;
|
|
5
|
+
private webhookConfig;
|
|
6
|
+
webhooksFolderPath: string;
|
|
7
|
+
private qs;
|
|
8
|
+
constructor({ exportConfig, stackAPIClient }: ModuleClassParams);
|
|
9
|
+
start(): Promise<void>;
|
|
10
|
+
getWebhooks(skip?: number): Promise<void>;
|
|
11
|
+
sanitizeAttribs(webhooks: Record<string, string>[]): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const omit_1 = tslib_1.__importDefault(require("lodash/omit"));
|
|
5
|
+
const isEmpty_1 = tslib_1.__importDefault(require("lodash/isEmpty"));
|
|
6
|
+
const node_path_1 = require("node:path");
|
|
7
|
+
const config_1 = tslib_1.__importDefault(require("../../config"));
|
|
8
|
+
const base_class_1 = tslib_1.__importDefault(require("./base-class"));
|
|
9
|
+
const utils_1 = require("../../utils");
|
|
10
|
+
class ExportWebhooks extends base_class_1.default {
|
|
11
|
+
constructor({ exportConfig, stackAPIClient }) {
|
|
12
|
+
super({ exportConfig, stackAPIClient });
|
|
13
|
+
this.webhooks = {};
|
|
14
|
+
this.webhookConfig = config_1.default.modules.webhooks;
|
|
15
|
+
this.qs = { include_count: true, asc: 'updated_at' };
|
|
16
|
+
}
|
|
17
|
+
async start() {
|
|
18
|
+
(0, utils_1.log)(this.exportConfig, 'Starting webhooks export', 'info');
|
|
19
|
+
this.webhooksFolderPath = (0, node_path_1.resolve)(this.exportConfig.data, this.exportConfig.branchName || '', this.webhookConfig.dirName);
|
|
20
|
+
await utils_1.fsUtil.makeDirectory(this.webhooksFolderPath);
|
|
21
|
+
await this.getWebhooks();
|
|
22
|
+
if (this.webhooks === undefined || (0, isEmpty_1.default)(this.webhooks)) {
|
|
23
|
+
(0, utils_1.log)(this.exportConfig, 'No webhooks found', 'info');
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
utils_1.fsUtil.writeFile((0, node_path_1.resolve)(this.webhooksFolderPath, this.webhookConfig.fileName), this.webhooks);
|
|
27
|
+
(0, utils_1.log)(this.exportConfig, 'All the webhooks have been exported successfully!', 'success');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async getWebhooks(skip = 0) {
|
|
31
|
+
if (skip) {
|
|
32
|
+
this.qs.skip = skip;
|
|
33
|
+
}
|
|
34
|
+
await this.stack
|
|
35
|
+
.webhook()
|
|
36
|
+
.fetchAll(this.qs)
|
|
37
|
+
.then(async (data) => {
|
|
38
|
+
const { items, count } = data;
|
|
39
|
+
if (items === null || items === void 0 ? void 0 : items.length) {
|
|
40
|
+
this.sanitizeAttribs(items);
|
|
41
|
+
skip += this.webhookConfig.limit || 100;
|
|
42
|
+
if (skip >= count) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
return await this.getWebhooks(skip);
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
.catch(({ error }) => {
|
|
49
|
+
(0, utils_1.log)(this.exportConfig, `Failed to export webhooks.${(0, utils_1.formatError)(error)}`, 'error');
|
|
50
|
+
(0, utils_1.log)(this.exportConfig, error, 'error');
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
sanitizeAttribs(webhooks) {
|
|
54
|
+
var _a;
|
|
55
|
+
for (let index = 0; index < (webhooks === null || webhooks === void 0 ? void 0 : webhooks.length); index++) {
|
|
56
|
+
const webhookUid = webhooks[index].uid;
|
|
57
|
+
const webhookName = (_a = webhooks[index]) === null || _a === void 0 ? void 0 : _a.name;
|
|
58
|
+
this.webhooks[webhookUid] = (0, omit_1.default)(webhooks[index], ['SYS_ACL']);
|
|
59
|
+
(0, utils_1.log)(this.exportConfig, `'${webhookName}' webhook was exported successfully`, 'success');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.default = ExportWebhooks;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export = ExportAssets;
|
|
2
|
+
declare class ExportAssets {
|
|
3
|
+
constructor(exportConfig: any, stackAPIClient: any);
|
|
4
|
+
config: any;
|
|
5
|
+
bLimit: number;
|
|
6
|
+
vLimit: any;
|
|
7
|
+
invalidKeys: string[];
|
|
8
|
+
folderJSONPath: any;
|
|
9
|
+
folderData: any[];
|
|
10
|
+
assetsFolderPath: any;
|
|
11
|
+
assetContents: {};
|
|
12
|
+
httpClient: HttpClient;
|
|
13
|
+
assetConfig: {
|
|
14
|
+
dirName: string;
|
|
15
|
+
fileName: string;
|
|
16
|
+
batchLimit: number;
|
|
17
|
+
host: string;
|
|
18
|
+
invalidKeys: string[];
|
|
19
|
+
chunkFileSize: number;
|
|
20
|
+
downloadLimit: number;
|
|
21
|
+
fetchConcurrency: number;
|
|
22
|
+
assetsMetaKeys: string[];
|
|
23
|
+
securedAssets: boolean;
|
|
24
|
+
displayExecutionTime: boolean;
|
|
25
|
+
enableDownloadStatus: boolean;
|
|
26
|
+
includeVersionedAssets: boolean;
|
|
27
|
+
dependencies?: import("../../types").Modules[];
|
|
28
|
+
};
|
|
29
|
+
stackAPIClient: any;
|
|
30
|
+
assetDownloadRetry: {};
|
|
31
|
+
assetDownloadRetryLimit: number;
|
|
32
|
+
start(): any;
|
|
33
|
+
assetContentsFile: string;
|
|
34
|
+
exportFolders(): any;
|
|
35
|
+
getFolderJSON(skip: any, fCount: any): any;
|
|
36
|
+
getAssetCount(folder: any): any;
|
|
37
|
+
getAssetJSON(skip: any): any;
|
|
38
|
+
getVersionedAssetJSON(uid: any, version: any, bucket: any): any;
|
|
39
|
+
downloadAsset(asset: any): any;
|
|
40
|
+
getFolders(): any;
|
|
41
|
+
getFolderDetails(skip: any, tCount: any): any;
|
|
42
|
+
}
|
|
43
|
+
import { HttpClient } from "@contentstack/cli-utilities/lib/http-client/client";
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Contentstack Export
|
|
3
|
+
* Copyright (c) 2019 Contentstack LLC
|
|
4
|
+
* MIT Licensed
|
|
5
|
+
*/
|
|
6
|
+
const mkdirp = require('mkdirp');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const Promise = require('bluebird');
|
|
10
|
+
const _ = require('lodash');
|
|
11
|
+
const chalk = require('chalk');
|
|
12
|
+
const progress = require('progress-stream');
|
|
13
|
+
const { HttpClient, configHandler } = require('@contentstack/cli-utilities');
|
|
14
|
+
const { fileHelper, log, formatError } = require('../../utils');
|
|
15
|
+
let { default: config } = require('../../config');
|
|
16
|
+
module.exports = class ExportAssets {
|
|
17
|
+
constructor(exportConfig, stackAPIClient) {
|
|
18
|
+
this.folderData = [];
|
|
19
|
+
this.assetContents = {};
|
|
20
|
+
this.httpClient = HttpClient.create();
|
|
21
|
+
this.assetConfig = config.modules.assets;
|
|
22
|
+
this.stackAPIClient = stackAPIClient;
|
|
23
|
+
this.config = _.merge(config, exportConfig);
|
|
24
|
+
this.folderData = [];
|
|
25
|
+
this.assetContents = {};
|
|
26
|
+
this.assetDownloadRetry = {};
|
|
27
|
+
this.assetDownloadRetryLimit = 3;
|
|
28
|
+
this.invalidKeys = this.assetConfig.invalidKeys;
|
|
29
|
+
this.bLimit = this.assetConfig.batchLimit || 15;
|
|
30
|
+
this.vLimit = this.assetConfig.downloadLimit || this.config.fetchConcurrency || 3;
|
|
31
|
+
}
|
|
32
|
+
start() {
|
|
33
|
+
const self = this;
|
|
34
|
+
this.assetsFolderPath = path.resolve(this.config.data, this.config.branchName || '', this.assetConfig.dirName);
|
|
35
|
+
this.assetContentsFile = path.resolve(this.assetsFolderPath, 'assets.json');
|
|
36
|
+
this.folderJSONPath = path.resolve(this.assetsFolderPath, 'folders.json');
|
|
37
|
+
log(this.config, 'Starting assets export', 'success');
|
|
38
|
+
// Create asset folder
|
|
39
|
+
mkdirp.sync(this.assetsFolderPath);
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
// TBD: getting all the assets should have optimized
|
|
42
|
+
return self
|
|
43
|
+
.getAssetCount()
|
|
44
|
+
.then((count) => {
|
|
45
|
+
const assetBatches = [];
|
|
46
|
+
if (typeof count !== 'number' || count === 0) {
|
|
47
|
+
log(self.config, 'No assets found', 'success');
|
|
48
|
+
return resolve();
|
|
49
|
+
}
|
|
50
|
+
for (let i = 0; i <= count; i += self.bLimit) {
|
|
51
|
+
assetBatches.push(i);
|
|
52
|
+
}
|
|
53
|
+
return Promise.map(assetBatches, (batch) => {
|
|
54
|
+
return self
|
|
55
|
+
.getAssetJSON(batch)
|
|
56
|
+
.then((assetsJSON) => {
|
|
57
|
+
return Promise.map(assetsJSON, (assetJSON) => {
|
|
58
|
+
if (self.assetConfig.downloadVersionAssets) {
|
|
59
|
+
return self
|
|
60
|
+
.getVersionedAssetJSON(assetJSON.uid, assetJSON._version)
|
|
61
|
+
.then(() => {
|
|
62
|
+
self.assetContents[assetJSON.uid] = assetJSON;
|
|
63
|
+
})
|
|
64
|
+
.catch((error) => {
|
|
65
|
+
log(self.config, `Asset '${assetJSON.uid}' failed to download.\n ${formatError(error)}`, 'error');
|
|
66
|
+
log(self.config, error, 'error');
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
return self
|
|
71
|
+
.downloadAsset(assetJSON)
|
|
72
|
+
.then(() => {
|
|
73
|
+
self.assetContents[assetJSON.uid] = assetJSON;
|
|
74
|
+
})
|
|
75
|
+
.catch((err) => {
|
|
76
|
+
log(self.config, `Asset '${assetJSON.uid}' download failed. ${formatError(err)}`, 'error');
|
|
77
|
+
return err;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}, { concurrency: self.vLimit })
|
|
81
|
+
.then(() => {
|
|
82
|
+
log(self.config, 'Batch no ' + (batch + 1) + ' of assets is complete', 'success');
|
|
83
|
+
// fileHelper.writeFileSync(this.assetContentsFile, self.assetContents)
|
|
84
|
+
})
|
|
85
|
+
.catch((error) => {
|
|
86
|
+
log(self.config, `Asset batch ${batch + 1} failed to download`, 'error');
|
|
87
|
+
log(self.config, formatError(error), 'error');
|
|
88
|
+
log(self.config, error, 'error');
|
|
89
|
+
});
|
|
90
|
+
})
|
|
91
|
+
.catch((error) => {
|
|
92
|
+
log(self.config, error, 'error');
|
|
93
|
+
reject(error);
|
|
94
|
+
});
|
|
95
|
+
}, { concurrency: self.assetConfig.concurrencyLimit || 1 })
|
|
96
|
+
.then(() => {
|
|
97
|
+
fileHelper.writeFileSync(self.assetContentsFile, self.assetContents);
|
|
98
|
+
return self
|
|
99
|
+
.exportFolders()
|
|
100
|
+
.then(() => {
|
|
101
|
+
log(self.config, chalk.green('Asset export completed successfully'), 'success');
|
|
102
|
+
return resolve();
|
|
103
|
+
})
|
|
104
|
+
.catch((error) => {
|
|
105
|
+
log(self.config, error, 'error');
|
|
106
|
+
reject(error);
|
|
107
|
+
});
|
|
108
|
+
})
|
|
109
|
+
.catch((error) => {
|
|
110
|
+
fileHelper.writeFileSync(self.assetContentsFile, self.assetContents);
|
|
111
|
+
log(self.config, `Asset export failed. ${formatError(error)}`, 'error');
|
|
112
|
+
log(self.config, error, 'error');
|
|
113
|
+
reject(error);
|
|
114
|
+
});
|
|
115
|
+
})
|
|
116
|
+
.catch((error) => {
|
|
117
|
+
log(self.config, error, 'error');
|
|
118
|
+
reject(error);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
exportFolders() {
|
|
123
|
+
const self = this;
|
|
124
|
+
return new Promise((resolve, reject) => {
|
|
125
|
+
return self
|
|
126
|
+
.getAssetCount(true)
|
|
127
|
+
.then((fCount) => {
|
|
128
|
+
if (fCount === 0) {
|
|
129
|
+
log(self.config, 'No folders were found in the stack!', 'success');
|
|
130
|
+
return resolve();
|
|
131
|
+
}
|
|
132
|
+
return self
|
|
133
|
+
.getFolderJSON(0, fCount)
|
|
134
|
+
.then(() => {
|
|
135
|
+
// asset folders have been successfully exported
|
|
136
|
+
log(self.config, 'Asset-folders have been successfully exported!', 'success');
|
|
137
|
+
return resolve();
|
|
138
|
+
})
|
|
139
|
+
.catch((error) => {
|
|
140
|
+
log(self.config, `Error while exporting asset-folders!\n ${formatError(error)}`, 'error');
|
|
141
|
+
return reject(error);
|
|
142
|
+
});
|
|
143
|
+
})
|
|
144
|
+
.catch((error) => {
|
|
145
|
+
log(self.config, error, 'error');
|
|
146
|
+
// error while fetching asset folder count
|
|
147
|
+
return reject(error);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
getFolderJSON(skip, fCount) {
|
|
152
|
+
const self = this;
|
|
153
|
+
return new Promise((resolve, reject) => {
|
|
154
|
+
if (typeof skip !== 'number') {
|
|
155
|
+
skip = 0;
|
|
156
|
+
}
|
|
157
|
+
if (skip >= fCount) {
|
|
158
|
+
fileHelper.writeFileSync(self.folderJSONPath, self.folderData);
|
|
159
|
+
return resolve();
|
|
160
|
+
}
|
|
161
|
+
const queryRequestObj = {
|
|
162
|
+
skip,
|
|
163
|
+
include_folders: true,
|
|
164
|
+
query: { is_dir: true },
|
|
165
|
+
};
|
|
166
|
+
self.stackAPIClient
|
|
167
|
+
.asset()
|
|
168
|
+
.query(queryRequestObj)
|
|
169
|
+
.find()
|
|
170
|
+
.then((response) => {
|
|
171
|
+
skip += 100;
|
|
172
|
+
self.folderData.push(...response.items);
|
|
173
|
+
return self.getFolderJSON(skip, fCount).then(resolve).catch(reject);
|
|
174
|
+
})
|
|
175
|
+
.catch((error) => reject(error));
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
getAssetCount(folder) {
|
|
179
|
+
const self = this;
|
|
180
|
+
return new Promise((resolve, reject) => {
|
|
181
|
+
if (folder && typeof folder === 'boolean') {
|
|
182
|
+
const queryOptions = {
|
|
183
|
+
skip: 99999990,
|
|
184
|
+
include_count: true,
|
|
185
|
+
include_folders: true,
|
|
186
|
+
query: { is_dir: true },
|
|
187
|
+
};
|
|
188
|
+
self.stackAPIClient
|
|
189
|
+
.asset()
|
|
190
|
+
.query(queryOptions)
|
|
191
|
+
.find()
|
|
192
|
+
.then((asset) => {
|
|
193
|
+
return resolve(asset.count);
|
|
194
|
+
})
|
|
195
|
+
.catch((error) => {
|
|
196
|
+
log(self.config, error, 'error');
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
const queryOptions = { skip: 99999990, include_count: true };
|
|
201
|
+
self.stackAPIClient
|
|
202
|
+
.asset()
|
|
203
|
+
.query(queryOptions)
|
|
204
|
+
.find()
|
|
205
|
+
.then(({ count }) => resolve(count))
|
|
206
|
+
.catch((error) => {
|
|
207
|
+
log(self.config, error, 'error');
|
|
208
|
+
reject(error);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
getAssetJSON(skip) {
|
|
214
|
+
const self = this;
|
|
215
|
+
return new Promise((resolve, reject) => {
|
|
216
|
+
if (typeof skip !== 'number') {
|
|
217
|
+
skip = 0;
|
|
218
|
+
}
|
|
219
|
+
const queryRequestObj = {
|
|
220
|
+
skip: skip,
|
|
221
|
+
limit: self.bLimit,
|
|
222
|
+
include_publish_details: true,
|
|
223
|
+
except: {
|
|
224
|
+
BASE: self.invalidKeys,
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
self.stackAPIClient
|
|
228
|
+
.asset()
|
|
229
|
+
.query(queryRequestObj)
|
|
230
|
+
.find()
|
|
231
|
+
.then(({ items }) => resolve(items))
|
|
232
|
+
.catch((error) => {
|
|
233
|
+
log(self.config, error, 'error');
|
|
234
|
+
return reject();
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
getVersionedAssetJSON(uid, version, bucket) {
|
|
239
|
+
const self = this;
|
|
240
|
+
const assetVersionInfo = bucket || [];
|
|
241
|
+
return new Promise((resolve, reject) => {
|
|
242
|
+
if (self.assetDownloadRetry[uid + version] > self.assetDownloadRetryLimit) {
|
|
243
|
+
console.log('Reached max', self.assetDownloadRetry[uid + version]);
|
|
244
|
+
return reject(new Error('Asset Max download retry limit exceeded! ' + uid));
|
|
245
|
+
}
|
|
246
|
+
if (version <= 0) {
|
|
247
|
+
const assetVersionInfoFile = path.resolve(self.assetsFolderPath, uid, '_contentstack_' + uid + '.json');
|
|
248
|
+
fileHelper.writeFileSync(assetVersionInfoFile, assetVersionInfo);
|
|
249
|
+
return resolve();
|
|
250
|
+
}
|
|
251
|
+
const queryrequestOption = {
|
|
252
|
+
version: version,
|
|
253
|
+
include_publish_details: true,
|
|
254
|
+
except: {
|
|
255
|
+
BASE: self.invalidKeys,
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
self.stackAPIClient
|
|
259
|
+
.asset(uid)
|
|
260
|
+
.fetch(queryrequestOption)
|
|
261
|
+
.then((versionedAssetJSONResponse) => {
|
|
262
|
+
self
|
|
263
|
+
.downloadAsset(versionedAssetJSONResponse)
|
|
264
|
+
.then(() => {
|
|
265
|
+
assetVersionInfo.splice(0, 0, versionedAssetJSONResponse);
|
|
266
|
+
// Remove duplicates
|
|
267
|
+
assetVersionInfo = _.uniqWith(assetVersionInfo, _.isEqual);
|
|
268
|
+
self.getVersionedAssetJSON(uid, --version, assetVersionInfo).then(resolve).catch(reject);
|
|
269
|
+
})
|
|
270
|
+
.catch(reject);
|
|
271
|
+
})
|
|
272
|
+
.catch((error) => {
|
|
273
|
+
log(self.config, error, 'error');
|
|
274
|
+
if (error.status === 408) {
|
|
275
|
+
console.log('retrying', uid);
|
|
276
|
+
// retrying when timeout
|
|
277
|
+
self.assetDownloadRetry[uid + version]
|
|
278
|
+
? ++self.assetDownloadRetry[uid + version]
|
|
279
|
+
: (self.assetDownloadRetry[uid + version] = 1);
|
|
280
|
+
return self.getVersionedAssetJSON(uid, version, assetVersionInfo).then(resolve).catch(reject);
|
|
281
|
+
}
|
|
282
|
+
reject(error);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
downloadAsset(asset) {
|
|
287
|
+
const self = this;
|
|
288
|
+
return new Promise(async (resolve, reject) => {
|
|
289
|
+
const assetFolderPath = path.resolve(self.assetsFolderPath, asset.uid);
|
|
290
|
+
const assetFilePath = path.resolve(assetFolderPath, asset.filename);
|
|
291
|
+
if (fs.existsSync(assetFilePath)) {
|
|
292
|
+
log(self.config, 'Skipping download of { title: ' + asset.filename + ', uid: ' + asset.uid + ' }, as they already exist', 'success');
|
|
293
|
+
return resolve();
|
|
294
|
+
}
|
|
295
|
+
self.assetStream = {
|
|
296
|
+
url: self.config.securedAssets ? `${asset.url}?authtoken=${configHandler.get('authtoken')}` : asset.url,
|
|
297
|
+
};
|
|
298
|
+
fileHelper.makeDirectory(assetFolderPath);
|
|
299
|
+
const assetFileStream = fs.createWriteStream(assetFilePath);
|
|
300
|
+
self.assetStream.url = encodeURI(self.assetStream.url);
|
|
301
|
+
self.httpClient
|
|
302
|
+
.options({ responseType: 'stream' })
|
|
303
|
+
.get(self.assetStream.url)
|
|
304
|
+
.then(({ data: assetStreamRequest }) => {
|
|
305
|
+
if (self.assetConfig.enableDownloadStatus) {
|
|
306
|
+
const str = progress({
|
|
307
|
+
time: 5000,
|
|
308
|
+
length: assetStreamRequest.headers['content-length'],
|
|
309
|
+
});
|
|
310
|
+
str.on('progress', (progressData) => {
|
|
311
|
+
console.log(`${asset.filename}: ${Math.round(progressData.percentage)}%`);
|
|
312
|
+
});
|
|
313
|
+
assetStreamRequest.pipe(str).pipe(assetFileStream);
|
|
314
|
+
}
|
|
315
|
+
assetStreamRequest.pipe(assetFileStream);
|
|
316
|
+
})
|
|
317
|
+
.catch((error) => {
|
|
318
|
+
log(self.config, error, 'error');
|
|
319
|
+
reject(error);
|
|
320
|
+
});
|
|
321
|
+
assetFileStream
|
|
322
|
+
.on('close', function () {
|
|
323
|
+
log(self.config, 'Downloaded ' + asset.filename + ': ' + asset.uid + ' successfully!', 'success');
|
|
324
|
+
return resolve();
|
|
325
|
+
})
|
|
326
|
+
.on('error', (error) => {
|
|
327
|
+
log(self.config, `Download ${asset.filename}: ${asset.uid} failed!`, 'error');
|
|
328
|
+
log(self.config, error, 'error');
|
|
329
|
+
reject(error);
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
getFolders() {
|
|
334
|
+
const self = this;
|
|
335
|
+
return new Promise((resolve, reject) => {
|
|
336
|
+
return self
|
|
337
|
+
.getAssetCount(true)
|
|
338
|
+
.then((count) => {
|
|
339
|
+
if (count === 0) {
|
|
340
|
+
log(self.config, 'No folders were found in the stack', 'success');
|
|
341
|
+
return resolve();
|
|
342
|
+
}
|
|
343
|
+
return self
|
|
344
|
+
.getFolderDetails(0, count)
|
|
345
|
+
.then(function () {
|
|
346
|
+
log(self.config, chalk.green('Exported asset-folders successfully!'), 'success');
|
|
347
|
+
return resolve();
|
|
348
|
+
})
|
|
349
|
+
.catch(function (error) {
|
|
350
|
+
log(self.config, error, 'error');
|
|
351
|
+
reject(error);
|
|
352
|
+
});
|
|
353
|
+
})
|
|
354
|
+
.catch(function (error) {
|
|
355
|
+
log(self.config, error, 'error');
|
|
356
|
+
reject(error);
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
getFolderDetails(skip, tCount) {
|
|
361
|
+
const self = this;
|
|
362
|
+
return new Promise((resolve, reject) => {
|
|
363
|
+
if (typeof skip !== 'number') {
|
|
364
|
+
skip = 0;
|
|
365
|
+
}
|
|
366
|
+
if (skip > tCount) {
|
|
367
|
+
fileHelper.writeFileSync(self.folderJSONPath, self.folderContents);
|
|
368
|
+
return resolve();
|
|
369
|
+
}
|
|
370
|
+
const queryRequestObj = {
|
|
371
|
+
skip: skip,
|
|
372
|
+
include_folders: true,
|
|
373
|
+
query: { is_dir: true },
|
|
374
|
+
};
|
|
375
|
+
self.stackAPIClient
|
|
376
|
+
.asset()
|
|
377
|
+
.query(queryRequestObj)
|
|
378
|
+
.find()
|
|
379
|
+
.then((folderDetailsResponse) => {
|
|
380
|
+
for (let i in folderDetailsResponse.items) {
|
|
381
|
+
self.folderContents.push(folderDetailsResponse.items[i]);
|
|
382
|
+
}
|
|
383
|
+
skip += 100;
|
|
384
|
+
return self.getFolderDetails(skip, tCount).then(resolve).catch(reject);
|
|
385
|
+
})
|
|
386
|
+
.catch((error) => {
|
|
387
|
+
log(self.config, error, 'error');
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export = ContentTypesExport;
|
|
2
|
+
declare class ContentTypesExport {
|
|
3
|
+
constructor(exportConfig: any, stackAPIClient: any);
|
|
4
|
+
stackAPIClient: any;
|
|
5
|
+
exportConfig: any;
|
|
6
|
+
contentTypesConfig: any;
|
|
7
|
+
qs: {
|
|
8
|
+
include_count: boolean;
|
|
9
|
+
asc: string;
|
|
10
|
+
limit: any;
|
|
11
|
+
include_global_field_schema: boolean;
|
|
12
|
+
};
|
|
13
|
+
contentTypesPath: string;
|
|
14
|
+
contentTypes: any[];
|
|
15
|
+
fetchConcurrency: any;
|
|
16
|
+
writeConcurrency: any;
|
|
17
|
+
start(): Promise<void>;
|
|
18
|
+
getContentTypes(skip?: number): any;
|
|
19
|
+
sanitizeAttribs(contentTypes: any): any[];
|
|
20
|
+
writeContentTypes(contentTypes: any): Promise<any>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const { fileHelper, executeTask, formatError, log } = require('../../utils');
|
|
4
|
+
class ContentTypesExport {
|
|
5
|
+
constructor(exportConfig, stackAPIClient) {
|
|
6
|
+
this.stackAPIClient = stackAPIClient;
|
|
7
|
+
this.exportConfig = exportConfig;
|
|
8
|
+
this.contentTypesConfig = exportConfig.modules.content_types;
|
|
9
|
+
this.qs = {
|
|
10
|
+
include_count: true,
|
|
11
|
+
asc: 'updated_at',
|
|
12
|
+
limit: this.contentTypesConfig.limit,
|
|
13
|
+
include_global_field_schema: true,
|
|
14
|
+
};
|
|
15
|
+
// If content type id is provided then use it as part of query
|
|
16
|
+
if (Array.isArray(this.exportConfig.contentTypes) && this.exportConfig.length > 0) {
|
|
17
|
+
this.qs.uid = { $in: this.exportConfig.contentTypes };
|
|
18
|
+
}
|
|
19
|
+
this.contentTypesPath = path.resolve(exportConfig.data, exportConfig.branchName || '', this.contentTypesConfig.dirName);
|
|
20
|
+
this.contentTypes = [];
|
|
21
|
+
this.fetchConcurrency = this.contentTypesConfig.fetchConcurrency || this.exportConfig.fetchConcurrency;
|
|
22
|
+
this.writeConcurrency = this.contentTypesConfig.writeConcurrency || this.exportConfig.writeConcurrency;
|
|
23
|
+
}
|
|
24
|
+
async start() {
|
|
25
|
+
try {
|
|
26
|
+
log(this.exportConfig, 'Starting content type export', 'success');
|
|
27
|
+
await fileHelper.makeDirectory(this.contentTypesPath);
|
|
28
|
+
await this.getContentTypes();
|
|
29
|
+
await this.writeContentTypes(this.contentTypes);
|
|
30
|
+
log(this.exportConfig, chalk.green('Content type(s) exported successfully'), 'success');
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
log(this.exportConfig, `Failed to export content types ${formatError(error)}`, 'error');
|
|
34
|
+
throw new Error('Failed to export content types');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async getContentTypes(skip = 0) {
|
|
38
|
+
if (skip) {
|
|
39
|
+
this.qs.skip = skip;
|
|
40
|
+
}
|
|
41
|
+
const contentTypeSearchResponse = await this.stackAPIClient.contentType().query(this.qs).find();
|
|
42
|
+
if (Array.isArray(contentTypeSearchResponse.items) && contentTypeSearchResponse.items.length > 0) {
|
|
43
|
+
let updatedContentTypes = this.sanitizeAttribs(contentTypeSearchResponse.items);
|
|
44
|
+
this.contentTypes.push(...updatedContentTypes);
|
|
45
|
+
skip += this.contentTypesConfig.limit;
|
|
46
|
+
if (skip > contentTypeSearchResponse.count) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
return await this.getContentTypes(skip);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
log(this.exportConfig, 'No content types returned for the given query', 'info');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
sanitizeAttribs(contentTypes) {
|
|
56
|
+
let updatedContentTypes = [];
|
|
57
|
+
contentTypes.forEach((contentType) => {
|
|
58
|
+
for (let key in contentType) {
|
|
59
|
+
if (this.contentTypesConfig.validKeys.indexOf(key) === -1) {
|
|
60
|
+
delete contentType[key];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
updatedContentTypes.push(contentType);
|
|
64
|
+
});
|
|
65
|
+
return updatedContentTypes;
|
|
66
|
+
}
|
|
67
|
+
async writeContentTypes(contentTypes) {
|
|
68
|
+
function write(contentType) {
|
|
69
|
+
return fileHelper.writeFile(path.join(this.contentTypesPath, `${contentType.uid === 'schema' ? 'schema|1' : contentType.uid}.json`), contentType);
|
|
70
|
+
}
|
|
71
|
+
await executeTask(contentTypes, write.bind(this), { concurrency: this.writeConcurrency });
|
|
72
|
+
return fileHelper.writeFile(path.join(this.contentTypesPath, 'schema.json'), contentTypes);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
module.exports = ContentTypesExport;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export = ExportCustomRoles;
|
|
2
|
+
declare class ExportCustomRoles {
|
|
3
|
+
constructor(exportConfig: any, stackAPIClient: any);
|
|
4
|
+
roles: {};
|
|
5
|
+
customRoles: {};
|
|
6
|
+
EXISTING_ROLES: {
|
|
7
|
+
Admin: number;
|
|
8
|
+
Developer: number;
|
|
9
|
+
'Content Manager': number;
|
|
10
|
+
};
|
|
11
|
+
rolesConfig: {
|
|
12
|
+
dirName: string;
|
|
13
|
+
fileName: string;
|
|
14
|
+
customRolesLocalesFileName: string;
|
|
15
|
+
dependencies?: import("../../types").Modules[];
|
|
16
|
+
};
|
|
17
|
+
config: any;
|
|
18
|
+
stackAPIClient: any;
|
|
19
|
+
start(): Promise<void>;
|
|
20
|
+
getCustomRolesLocales(customRoles: any, customRolesLocalesFilepath: any, stackAPIClient: any, config: any): Promise<void>;
|
|
21
|
+
}
|