@contentstack/cli-cm-import 1.0.0 → 1.1.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/oclif.manifest.json +1 -1
- package/package.json +4 -4
- package/src/app.js +3 -3
- package/src/config/default.js +18 -2
- package/src/lib/import/assets.js +8 -10
- package/src/lib/import/content-types.js +22 -7
- package/src/lib/import/custom-roles.js +159 -0
- package/src/lib/import/entries.js +31 -11
- package/src/lib/import/global-fields.js +16 -3
- package/src/lib/import/locales.js +1 -0
- package/src/lib/import/marketplace-apps.js +440 -0
- package/src/lib/import/workflows.js +41 -11
- package/src/lib/util/extensionsUidReplace.js +18 -3
- package/src/lib/util/lookupReplaceAssets.js +99 -8
- package/src/lib/util/lookupReplaceEntries.js +43 -42
- package/src/lib/util/marketplace-app-helper.js +38 -0
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Contentstack Export
|
|
3
|
+
* Copyright (c) 2019 Contentstack LLC
|
|
4
|
+
* MIT Licensed
|
|
5
|
+
*/
|
|
6
|
+
const fs = require('fs')
|
|
7
|
+
const _ = require('lodash')
|
|
8
|
+
const path = require('path')
|
|
9
|
+
const chalk = require('chalk')
|
|
10
|
+
const mkdirp = require('mkdirp')
|
|
11
|
+
const { cliux, configHandler, HttpClient, NodeCrypto } = require('@contentstack/cli-utilities')
|
|
12
|
+
|
|
13
|
+
let config = require('../../config/default')
|
|
14
|
+
const { addlogs: log } = require('../util/log')
|
|
15
|
+
const { readFile, writeFile } = require('../util/fs')
|
|
16
|
+
const sdk = require('../util/contentstack-management-sdk')
|
|
17
|
+
const { getInstalledExtensions } = require('../util/marketplace-app-helper')
|
|
18
|
+
|
|
19
|
+
let client
|
|
20
|
+
const marketplaceAppConfig = config.modules.marketplace_apps
|
|
21
|
+
|
|
22
|
+
function importMarketplaceApps() {
|
|
23
|
+
this.marketplaceApps = []
|
|
24
|
+
this.marketplaceAppsUid = []
|
|
25
|
+
this.developerHuBaseUrl = null
|
|
26
|
+
this.marketplaceAppFolderPath = ''
|
|
27
|
+
|
|
28
|
+
this.start = async (credentialConfig) => {
|
|
29
|
+
config = credentialConfig
|
|
30
|
+
client = sdk.Client(config)
|
|
31
|
+
this.developerHuBaseUrl = await this.getDeveloperHubUrl()
|
|
32
|
+
this.marketplaceAppFolderPath = path.resolve(config.data, marketplaceAppConfig.dirName)
|
|
33
|
+
this.marketplaceApps = _.uniqBy(
|
|
34
|
+
readFile(path.resolve(this.marketplaceAppFolderPath, marketplaceAppConfig.fileName)),
|
|
35
|
+
'app_uid'
|
|
36
|
+
)
|
|
37
|
+
this.marketplaceAppsUid = _.map(this.marketplaceApps, 'uid')
|
|
38
|
+
|
|
39
|
+
if (!config.auth_token && !_.isEmpty(this.marketplaceApps)) {
|
|
40
|
+
cliux.print('WARNING!!! To import Marketplace apps, you must be logged in. Please check csdx auth:login --help to log in', { color: 'yellow' })
|
|
41
|
+
return Promise.resolve()
|
|
42
|
+
} else if(_.isEmpty(this.marketplaceApps)) {
|
|
43
|
+
return Promise.resolve()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
await this.getOrgUid()
|
|
47
|
+
return this.handleInstallationProcess()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @method getOrgUid
|
|
52
|
+
* @returns {Void}
|
|
53
|
+
*/
|
|
54
|
+
this.getOrgUid = async () => {
|
|
55
|
+
// NOTE get org uid
|
|
56
|
+
if (config.auth_token) {
|
|
57
|
+
const stack = await client
|
|
58
|
+
.stack({ api_key: config.target_stack, authtoken: config.auth_token })
|
|
59
|
+
.fetch()
|
|
60
|
+
.catch((error) => {
|
|
61
|
+
console.log(error)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
if (stack && stack.org_uid) {
|
|
65
|
+
config.org_uid = stack.org_uid
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @method handleInstallationProcess
|
|
72
|
+
* @returns {Promise<void>}
|
|
73
|
+
*/
|
|
74
|
+
this.handleInstallationProcess = async () => {
|
|
75
|
+
const self = this
|
|
76
|
+
const headers = {
|
|
77
|
+
authtoken: config.auth_token,
|
|
78
|
+
organization_uid: config.org_uid
|
|
79
|
+
}
|
|
80
|
+
const httpClient = new HttpClient().headers(headers)
|
|
81
|
+
const nodeCrypto = new NodeCrypto()
|
|
82
|
+
|
|
83
|
+
// NOTE install all private apps which is not available for stack.
|
|
84
|
+
await this.handleAllPrivateAppsCreationProcess({ httpClient })
|
|
85
|
+
const installedExtensions = await getInstalledExtensions(config)
|
|
86
|
+
|
|
87
|
+
// NOTE after private app installation, refetch marketplace apps from file
|
|
88
|
+
const marketplaceAppsFromFile = readFile(path.resolve(this.marketplaceAppFolderPath, marketplaceAppConfig.fileName))
|
|
89
|
+
this.marketplaceApps = _.filter(marketplaceAppsFromFile, ({ uid }) => _.includes(this.marketplaceAppsUid, uid))
|
|
90
|
+
|
|
91
|
+
log(config, 'Starting marketplace app installation', 'success')
|
|
92
|
+
|
|
93
|
+
for (let app of self.marketplaceApps) {
|
|
94
|
+
await self.installApps({ app, installedExtensions, httpClient, nodeCrypto })
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// NOTE get all the extension again after all apps installed (To manage uid mapping in content type, entries)
|
|
98
|
+
const extensions = await getInstalledExtensions(config)
|
|
99
|
+
const mapperFolderPath = path.join(config.data, 'mapper', 'marketplace_apps')
|
|
100
|
+
|
|
101
|
+
if (!fs.existsSync(mapperFolderPath)) {
|
|
102
|
+
mkdirp.sync(mapperFolderPath)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const appUidMapperPath = path.join(mapperFolderPath, 'marketplace-apps.json')
|
|
106
|
+
const installedExt = _.map(
|
|
107
|
+
extensions,
|
|
108
|
+
(row) => _.pick(row, ['uid', 'title', 'type', 'app_uid', 'app_installation_uid'])
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
writeFile(appUidMapperPath, installedExt)
|
|
112
|
+
|
|
113
|
+
return Promise.resolve()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @method handleAllPrivateAppsCreationProcess
|
|
118
|
+
* @param {Object} options
|
|
119
|
+
* @returns {Promise<void>}
|
|
120
|
+
*/
|
|
121
|
+
this.handleAllPrivateAppsCreationProcess = async (options) => {
|
|
122
|
+
const self = this
|
|
123
|
+
const { httpClient } = options
|
|
124
|
+
const listOfExportedPrivateApps = _.filter(self.marketplaceApps, { visibility: 'private' })
|
|
125
|
+
|
|
126
|
+
if (_.isEmpty(listOfExportedPrivateApps)) {
|
|
127
|
+
return Promise.resolve()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// NOTE get list of developer-hub installed apps (private)
|
|
131
|
+
const installedDeveloperHubApps = await httpClient.get(`${config.extensionHost}/apps-api/apps/`)
|
|
132
|
+
.then(({ data: { data } }) => data)
|
|
133
|
+
.catch(err => {
|
|
134
|
+
console.log(err)
|
|
135
|
+
}) || []
|
|
136
|
+
const listOfNotInstalledPrivateApps = _.filter(
|
|
137
|
+
listOfExportedPrivateApps,
|
|
138
|
+
(app) => !_.includes(_.map(installedDeveloperHubApps, 'uid'), app.app_uid)
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
if (!_.isEmpty(listOfNotInstalledPrivateApps)) {
|
|
142
|
+
const confirmation = await cliux.confirm(
|
|
143
|
+
chalk.yellow(`WARNING!!! The listed apps are private apps that are not available in the destination stack: \n\n${_.map(listOfNotInstalledPrivateApps, ({ manifest: { name } }, index) => `${String(index + 1)}) ${name}`).join('\n')}\n\nWould you like to re-create the private app and then proceed with the installation? (y/n)`)
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
if (!confirmation) {
|
|
147
|
+
const continueProcess = await cliux.confirm(
|
|
148
|
+
chalk.yellow(`WARNING!!! Canceling the app re-creation may break the content type and entry import. Would you like to proceed? (y/n)`)
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
if (continueProcess) {
|
|
152
|
+
return resolve()
|
|
153
|
+
} else {
|
|
154
|
+
process.exit()
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
log(config, 'Starting developer hub private apps re-creation', 'success')
|
|
160
|
+
|
|
161
|
+
for (let app of listOfNotInstalledPrivateApps) {
|
|
162
|
+
await self.createAllPrivateAppsInDeveloperHub({ app, httpClient })
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return Promise.resolve()
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @method removeUidFromManifestUILocations
|
|
170
|
+
* @param {Array<Object>} locations
|
|
171
|
+
* @returns {Array<Object>}
|
|
172
|
+
*/
|
|
173
|
+
this.removeUidFromManifestUILocations = (locations) => {
|
|
174
|
+
return _.map(locations, (location) => {
|
|
175
|
+
if (location.meta) {
|
|
176
|
+
location.meta = _.map(
|
|
177
|
+
location.meta,
|
|
178
|
+
(meta) => _.omit(meta, ['uid'])
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return location
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @method createAllPrivateAppsInDeveloperHub
|
|
188
|
+
* @param {Object} options
|
|
189
|
+
* @returns {Promise<void>}
|
|
190
|
+
*/
|
|
191
|
+
this.createAllPrivateAppsInDeveloperHub = async (options, uidCleaned = false) => {
|
|
192
|
+
const self = this
|
|
193
|
+
const { app, httpClient } = options
|
|
194
|
+
|
|
195
|
+
return new Promise((resolve) => {
|
|
196
|
+
if (
|
|
197
|
+
!uidCleaned &&
|
|
198
|
+
app.manifest.ui_location &&
|
|
199
|
+
!_.isEmpty(app.manifest.ui_location.locations)
|
|
200
|
+
) {
|
|
201
|
+
app.manifest.ui_location.locations = this.removeUidFromManifestUILocations(app.manifest.ui_location.locations)
|
|
202
|
+
}
|
|
203
|
+
httpClient.post(
|
|
204
|
+
`${config.extensionHost}/apps-api/apps`,
|
|
205
|
+
app.manifest
|
|
206
|
+
).then(async ({ data: result }) => {
|
|
207
|
+
const { name } = app.manifest
|
|
208
|
+
const { data, error, message } = result || {}
|
|
209
|
+
|
|
210
|
+
if (error) {
|
|
211
|
+
log(config, message, 'error')
|
|
212
|
+
|
|
213
|
+
if (_.toLower(error) === 'conflict') {
|
|
214
|
+
const appName = await cliux.inquire({
|
|
215
|
+
type: 'input',
|
|
216
|
+
name: 'name',
|
|
217
|
+
default: `Copy of ${app.manifest.name}`,
|
|
218
|
+
validate: this.validateAppName,
|
|
219
|
+
message: `${message}. Enter a new name to create an app.?`,
|
|
220
|
+
})
|
|
221
|
+
app.manifest.name = appName
|
|
222
|
+
|
|
223
|
+
await self.createAllPrivateAppsInDeveloperHub({ app, httpClient }, true)
|
|
224
|
+
.then(resolve)
|
|
225
|
+
.catch(resolve)
|
|
226
|
+
} else {
|
|
227
|
+
const confirmation = await cliux.confirm(
|
|
228
|
+
chalk.yellow('WARNING!!! The above error may have an impact if the failed app is referenced in entries/content type. Would you like to proceed? (y/n)')
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
if (confirmation) {
|
|
232
|
+
resolve()
|
|
233
|
+
} else {
|
|
234
|
+
process.exit()
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
} else if (data) { // NOTE new app installation
|
|
238
|
+
log(config, `${name} app created successfully.!`, 'success')
|
|
239
|
+
this.updatePrivateAppUid(app, data, app.manifest.name)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
resolve()
|
|
243
|
+
}).catch(error => {
|
|
244
|
+
if (error && (error.message || error.error_message)) {
|
|
245
|
+
log(config, (error && (error.message || error.error_message)), 'error')
|
|
246
|
+
} else {
|
|
247
|
+
log(config, 'Something went wrong.!', 'error')
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
resolve()
|
|
251
|
+
})
|
|
252
|
+
})
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* @method updatePrivateAppUid
|
|
257
|
+
* @param {Object} app
|
|
258
|
+
* @param {Object} data
|
|
259
|
+
*/
|
|
260
|
+
this.updatePrivateAppUid = (app, data, appName) => {
|
|
261
|
+
const self = this
|
|
262
|
+
const allMarketplaceApps = readFile(path.resolve(self.marketplaceAppFolderPath, marketplaceAppConfig.fileName))
|
|
263
|
+
const index = _.findIndex(
|
|
264
|
+
allMarketplaceApps,
|
|
265
|
+
{ uid: app.uid, visibility: 'private' }
|
|
266
|
+
)
|
|
267
|
+
if (index > -1) {
|
|
268
|
+
allMarketplaceApps[index] = {
|
|
269
|
+
...allMarketplaceApps[index],
|
|
270
|
+
title: data.name,
|
|
271
|
+
app_uid: data.uid,
|
|
272
|
+
old_title: allMarketplaceApps[index].title,
|
|
273
|
+
previous_data: [
|
|
274
|
+
...(allMarketplaceApps[index].old_data || []),
|
|
275
|
+
{ [`v${(allMarketplaceApps[index].old_data || []).length}`]: allMarketplaceApps[index] }
|
|
276
|
+
]
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// NOTE Update app name
|
|
280
|
+
allMarketplaceApps[index].manifest.name = appName
|
|
281
|
+
|
|
282
|
+
writeFile(
|
|
283
|
+
path.join(self.marketplaceAppFolderPath, marketplaceAppConfig.fileName),
|
|
284
|
+
allMarketplaceApps
|
|
285
|
+
)
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* @method installApps
|
|
291
|
+
* @param {Object} options
|
|
292
|
+
* @returns {Void}
|
|
293
|
+
*/
|
|
294
|
+
this.installApps = (options) => {
|
|
295
|
+
const self = this
|
|
296
|
+
const { app, installedExtensions, httpClient, nodeCrypto } = options
|
|
297
|
+
|
|
298
|
+
return new Promise((resolve, reject) => {
|
|
299
|
+
httpClient.post(
|
|
300
|
+
`${self.developerHuBaseUrl}/apps/${app.app_uid}/install`,
|
|
301
|
+
{ target_type: 'stack', target_uid: config.target_stack }
|
|
302
|
+
).then(async ({ data: result }) => {
|
|
303
|
+
let updateParam
|
|
304
|
+
const { title } = app
|
|
305
|
+
const { data, error, message, error_code, error_message } = result
|
|
306
|
+
|
|
307
|
+
if (error || error_code) { // NOTE if already installed copy only config data
|
|
308
|
+
log(config, `${message || error_message} - ${title}`, 'success')
|
|
309
|
+
const ext = _.find(installedExtensions, { app_uid: app.app_uid })
|
|
310
|
+
|
|
311
|
+
if (ext) {
|
|
312
|
+
if (!_.isEmpty(app.configuration) || !_.isEmpty(app.server_configuration)) {
|
|
313
|
+
cliux.print(
|
|
314
|
+
`WARNING!!! The ${title} app already exists and it may have its own configuration. But the current app you install has its own configuration which is used internally to manage content.`,
|
|
315
|
+
{ color: 'yellow' }
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
const configOption = await cliux.inquire({
|
|
319
|
+
choices: [
|
|
320
|
+
'Update it with the new configuration.',
|
|
321
|
+
'Do not update the configuration (WARNING!!! If you do not update the configuration, there may be some issues with the content which you import).',
|
|
322
|
+
'Exit'
|
|
323
|
+
],
|
|
324
|
+
type: 'list',
|
|
325
|
+
name: 'value',
|
|
326
|
+
message: 'Choose the option to proceed'
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
if (configOption === 'Exit') {
|
|
330
|
+
process.exit()
|
|
331
|
+
} else if (configOption === 'Update it with the new configuration.') {
|
|
332
|
+
updateParam = {
|
|
333
|
+
app,
|
|
334
|
+
nodeCrypto,
|
|
335
|
+
httpClient,
|
|
336
|
+
data: { ...ext, installation_uid: ext.app_installation_uid }
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
} else {
|
|
341
|
+
cliux.print(`WARNING!!! ${message || error_message}`, { color: 'yellow' })
|
|
342
|
+
const confirmation = await cliux.confirm(
|
|
343
|
+
chalk.yellow('WARNING!!! The above error may have an impact if the failed app is referenced in entries/content type. Would you like to proceed? (y/n)')
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
if (!confirmation) {
|
|
347
|
+
process.exit()
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
} else if (data) { // NOTE new app installation
|
|
351
|
+
log(config, `${title} app installed successfully.!`, 'success')
|
|
352
|
+
updateParam = { data, app, nodeCrypto, httpClient }
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (updateParam) {
|
|
356
|
+
self.updateAppsConfig(updateParam)
|
|
357
|
+
.then(resolve)
|
|
358
|
+
.catch(reject)
|
|
359
|
+
} else {
|
|
360
|
+
resolve()
|
|
361
|
+
}
|
|
362
|
+
}).catch(error => {
|
|
363
|
+
if (error && (error.message || error.error_message)) {
|
|
364
|
+
log(config, (error && (error.message || error.error_message)), 'error')
|
|
365
|
+
} else {
|
|
366
|
+
log(config, 'Something went wrong.!', 'error')
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
reject()
|
|
370
|
+
})
|
|
371
|
+
})
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* @method updateAppsConfig
|
|
376
|
+
* @param {Object<{ data, app, httpClient, nodeCrypto }>} param
|
|
377
|
+
* @returns {Promise<void>}
|
|
378
|
+
*/
|
|
379
|
+
this.updateAppsConfig = ({ data, app, httpClient, nodeCrypto }) => {
|
|
380
|
+
return new Promise((resolve, reject) => {
|
|
381
|
+
const payload = {}
|
|
382
|
+
const { title, configuration, server_configuration } = app
|
|
383
|
+
|
|
384
|
+
if (!_.isEmpty(configuration)) {
|
|
385
|
+
payload['configuration'] = nodeCrypto.decrypt(configuration)
|
|
386
|
+
}
|
|
387
|
+
if (!_.isEmpty(server_configuration)) {
|
|
388
|
+
payload['server_configuration'] = nodeCrypto.decrypt(server_configuration)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (_.isEmpty(data) || _.isEmpty(payload) || !data.installation_uid) {
|
|
392
|
+
resolve()
|
|
393
|
+
} else {
|
|
394
|
+
httpClient.put(`${this.developerHuBaseUrl}/installations/${data.installation_uid}`, payload)
|
|
395
|
+
.then(() => {
|
|
396
|
+
log(config, `${title} app config updated successfully.!`, 'success')
|
|
397
|
+
}).then(resolve)
|
|
398
|
+
.catch(error => {
|
|
399
|
+
if (error && (error.message || error.error_message)) {
|
|
400
|
+
log(config, (error && (error.message || error.error_message)), 'error')
|
|
401
|
+
} else {
|
|
402
|
+
log(config, 'Something went wrong.!', 'error')
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
reject()
|
|
406
|
+
})
|
|
407
|
+
}
|
|
408
|
+
})
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
this.validateAppName = (name) => {
|
|
412
|
+
if (name.length < 3 || name.length > 20) {
|
|
413
|
+
return 'The app name should be within 3-20 characters long.';
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
this.getDeveloperHubUrl = async () => {
|
|
420
|
+
const { cma, name } = configHandler.get('region') || {}
|
|
421
|
+
let developerHubBaseUrl = config.developerHubUrls[cma]
|
|
422
|
+
|
|
423
|
+
if (!developerHubBaseUrl) {
|
|
424
|
+
developerHubBaseUrl = await cliux.inquire({
|
|
425
|
+
type: 'input',
|
|
426
|
+
name: 'name',
|
|
427
|
+
validate: (url) => {
|
|
428
|
+
if (!url) return 'Developer-hub URL can\'t be empty.'
|
|
429
|
+
|
|
430
|
+
return true
|
|
431
|
+
},
|
|
432
|
+
message: `Enter the developer-hub base URL for the ${name} region - `,
|
|
433
|
+
})
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return developerHubBaseUrl.startsWith('http') ? developerHubBaseUrl : `https://${developerHubBaseUrl}`
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
module.exports = new importMarketplaceApps()
|
|
@@ -57,21 +57,51 @@ importWorkflows.prototype = {
|
|
|
57
57
|
self.workflowsUids = Object.keys(self.workflows);
|
|
58
58
|
return Promise.map(
|
|
59
59
|
self.workflowsUids,
|
|
60
|
-
function (workflowUid) {
|
|
60
|
+
async function (workflowUid) {
|
|
61
61
|
let workflow = self.workflows[workflowUid];
|
|
62
62
|
|
|
63
63
|
if (!self.workflowUidMapper.hasOwnProperty(workflowUid)) {
|
|
64
|
-
const workflowStages = workflow.workflow_stages
|
|
65
|
-
|
|
64
|
+
const workflowStages = workflow.workflow_stages;
|
|
65
|
+
const roleNameMap = {};
|
|
66
|
+
const roles = await client.stack({ api_key: config.target_stack, management_token: config.management_token }).role().fetchAll();
|
|
67
|
+
for (const role of roles.items) {
|
|
68
|
+
roleNameMap[role.name] = role.uid;
|
|
69
|
+
}
|
|
70
|
+
for (let i = 0; i < workflowStages.length; i++) {
|
|
71
|
+
const stage = workflowStages[i];
|
|
66
72
|
if (
|
|
67
|
-
stage.SYS_ACL.users.uids.length
|
|
73
|
+
stage.SYS_ACL.users.uids.length &&
|
|
68
74
|
stage.SYS_ACL.users.uids[0] !== '$all'
|
|
69
75
|
) {
|
|
70
76
|
stage.SYS_ACL.users.uids = ['$all'];
|
|
71
77
|
}
|
|
72
78
|
|
|
73
|
-
if (stage.SYS_ACL.roles.uids.length
|
|
74
|
-
|
|
79
|
+
if (stage.SYS_ACL.roles.uids.length) {
|
|
80
|
+
try {
|
|
81
|
+
for (let i = 0; i < stage.SYS_ACL.roles.uids.length; i++) {
|
|
82
|
+
const roleData = stage.SYS_ACL.roles.uids[i];
|
|
83
|
+
if (!roleNameMap[roleData.name]) {
|
|
84
|
+
// rules.branch is required to create custom roles.
|
|
85
|
+
const branchRuleExists = roleData.rules.find(rule => rule.module === 'branch');
|
|
86
|
+
if (!branchRuleExists) {
|
|
87
|
+
roleData.rules.push({
|
|
88
|
+
module: 'branch',
|
|
89
|
+
branches: ['main'],
|
|
90
|
+
acl: { read: true }
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const role = await client.stack({ api_key: config.target_stack, management_token: config.management_token }).role().create({ role: roleData });
|
|
95
|
+
stage.SYS_ACL.roles.uids[i] = role.uid;
|
|
96
|
+
roleNameMap[roleData.name] = role.uid;
|
|
97
|
+
} else {
|
|
98
|
+
stage.SYS_ACL.roles.uids[i] = roleNameMap[roleData.name];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
addlogs(config, chalk.red('Error while importing workflows roles. ' + error && error.message), 'error');
|
|
103
|
+
return reject({ message: 'Error while importing workflows roles' });
|
|
104
|
+
}
|
|
75
105
|
}
|
|
76
106
|
}
|
|
77
107
|
|
|
@@ -79,15 +109,15 @@ importWorkflows.prototype = {
|
|
|
79
109
|
addlogs(config, chalk.yellow('We are skipping import of `Workflow superuser(s)` from workflow'), 'info');
|
|
80
110
|
delete workflow.admin_users;
|
|
81
111
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
workflow
|
|
85
|
-
}
|
|
112
|
+
// One branch is required to create workflow.
|
|
113
|
+
if (!workflow.branches) {
|
|
114
|
+
workflow.branches = ['main'];
|
|
115
|
+
}
|
|
86
116
|
|
|
87
117
|
return client
|
|
88
118
|
.stack({ api_key: config.target_stack, management_token: config.management_token })
|
|
89
119
|
.workflow()
|
|
90
|
-
.create(
|
|
120
|
+
.create({ workflow })
|
|
91
121
|
.then(function (response) {
|
|
92
122
|
self.workflowUidMapper[workflowUid] = response;
|
|
93
123
|
helper.writeFile(workflowUidMapperPath, self.workflowUidMapper);
|
|
@@ -6,23 +6,25 @@
|
|
|
6
6
|
|
|
7
7
|
// eslint-disable-next-line unicorn/filename-case
|
|
8
8
|
let path = require('path');
|
|
9
|
+
const _ = require('lodash')
|
|
9
10
|
let helper = require('./fs');
|
|
10
11
|
let util = require('../util');
|
|
11
12
|
let config = util.getConfig();
|
|
12
13
|
let extensionPath = path.resolve(config.data, 'mapper/extensions', 'uid-mapping.json');
|
|
13
14
|
let globalfieldsPath = path.resolve(config.data, 'mapper/globalfields', 'uid-mapping.json');
|
|
15
|
+
const marketplaceAppPath = path.resolve(config.data, 'marketplace_apps', 'marketplace_apps.json');
|
|
14
16
|
|
|
15
17
|
// eslint-disable-next-line camelcase
|
|
16
|
-
let extension_uid_Replace = (module.exports = function (schema, preserveStackVersion) {
|
|
18
|
+
let extension_uid_Replace = (module.exports = function (schema, preserveStackVersion, installedExtensions) {
|
|
17
19
|
for (let i in schema) {
|
|
18
20
|
if (schema[i].data_type === 'group') {
|
|
19
|
-
extension_uid_Replace(schema[i].schema, preserveStackVersion);
|
|
21
|
+
extension_uid_Replace(schema[i].schema, preserveStackVersion, installedExtensions);
|
|
20
22
|
} else if (schema[i].data_type === 'blocks') {
|
|
21
23
|
for (let block in schema[i].blocks) {
|
|
22
24
|
if (schema[i].blocks[block].hasOwnProperty('reference_to')) {
|
|
23
25
|
delete schema[i].blocks[block].schema;
|
|
24
26
|
} else {
|
|
25
|
-
extension_uid_Replace(schema[i].blocks[block].schema, preserveStackVersion);
|
|
27
|
+
extension_uid_Replace(schema[i].blocks[block].schema, preserveStackVersion, installedExtensions);
|
|
26
28
|
}
|
|
27
29
|
}
|
|
28
30
|
} else if (
|
|
@@ -47,6 +49,19 @@ let extension_uid_Replace = (module.exports = function (schema, preserveStackVer
|
|
|
47
49
|
if (data && data.hasOwnProperty(extension_key_value)) {
|
|
48
50
|
// eslint-disable-next-line camelcase
|
|
49
51
|
schema[i].extension_uid = data[extension_key_value];
|
|
52
|
+
} else if (schema[i].field_metadata && schema[i].field_metadata.extension) {
|
|
53
|
+
if (installedExtensions) {
|
|
54
|
+
const marketplaceApps = helper.readFile(marketplaceAppPath);
|
|
55
|
+
const oldExt = _.find(marketplaceApps, { uid: schema[i].extension_uid })
|
|
56
|
+
|
|
57
|
+
if (oldExt) {
|
|
58
|
+
const ext = _.find(installedExtensions, { type: 'field', title: oldExt.title, app_uid: oldExt.app_uid })
|
|
59
|
+
|
|
60
|
+
if (ext) {
|
|
61
|
+
schema[i].extension_uid = ext.uid
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
50
65
|
}
|
|
51
66
|
} else if (schema[i].data_type === 'json' && schema[i].hasOwnProperty('plugins') && schema[i].plugins.length > 0) {
|
|
52
67
|
const newPluginUidsArray = [];
|