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