@ds-sfdc/sfparty 0.0.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/LICENSE.md +28 -0
- package/README.md +18 -0
- package/index.js +535 -0
- package/lib/fileUtils.js +165 -0
- package/lib/label/combine.js +203 -0
- package/lib/label/definition.js +12 -0
- package/lib/label/split.js +213 -0
- package/lib/permset/combine.js +286 -0
- package/lib/permset/definition.js +74 -0
- package/lib/permset/split.js +287 -0
- package/lib/profile/combine.js +309 -0
- package/lib/profile/definition.js +55 -0
- package/lib/profile/split.js +983 -0
- package/lib/workflow/combine.js +330 -0
- package/lib/workflow/definition.js +55 -0
- package/lib/workflow/split.js +278 -0
- package/nodemon.json +4 -0
- package/package.json +51 -0
- package/sfdx-project.json +11 -0
- package/tests/root.spec.js +14 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023, Tim Paulaskas
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# @ds-sfdc/sfparty
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@ds-sfdc/sfparty) [](https://npmjs.org/package/@ds-sfdc/sfparty) [](https://github.com/TimPaulaskasDS/sfparty/blob/main/LICENSE.md)
|
|
4
|
+
|
|
5
|
+
## Using the template
|
|
6
|
+
|
|
7
|
+
This tool will split Salesforce metadata XML files into YAML parts (or JSON).
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm i @ds-sfdc/sfparty
|
|
13
|
+
```
|
|
14
|
+
## Commands
|
|
15
|
+
|
|
16
|
+
<!-- commands -->
|
|
17
|
+
|
|
18
|
+
<!-- commandsstop -->
|
package/index.js
ADDED
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict'
|
|
3
|
+
import { readFileSync } from 'fs'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
import yargs from 'yargs'
|
|
6
|
+
import { hideBin } from 'yargs/helpers'
|
|
7
|
+
import winston from 'winston'
|
|
8
|
+
import chalk from 'chalk'
|
|
9
|
+
import convertHrtime from 'convert-hrtime'
|
|
10
|
+
import * as fileUtils from './lib/fileUtils.js'
|
|
11
|
+
import * as profileSplit from './lib/profile/split.js'
|
|
12
|
+
import * as profileCombine from './lib/profile/combine.js'
|
|
13
|
+
import * as permSetSplit from './lib/permset/split.js'
|
|
14
|
+
import * as permSetCombine from './lib/permset/combine.js'
|
|
15
|
+
import * as labelSplit from './lib/label/split.js'
|
|
16
|
+
import * as labelCombine from './lib/label/combine.js'
|
|
17
|
+
import * as workflowSplit from './lib/workflow/split.js'
|
|
18
|
+
import * as workflowCombine from './lib/workflow/combine.js'
|
|
19
|
+
import * as workflowDefinition from './lib/workflow/definition.js'
|
|
20
|
+
|
|
21
|
+
const startTime = process.hrtime.bigint()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
global.logger = winston.createLogger({
|
|
25
|
+
levels: winston.config.syslog.levels,
|
|
26
|
+
format: winston.format.cli(),
|
|
27
|
+
defaultMeta: { service: 'dstools', method: 'profile' },
|
|
28
|
+
transports: [
|
|
29
|
+
new winston.transports.Console(),
|
|
30
|
+
],
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
global.processed = {
|
|
34
|
+
total: 0,
|
|
35
|
+
errors: 0,
|
|
36
|
+
current: 1,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
global.statusLevel = {
|
|
40
|
+
"warn": '🔕',
|
|
41
|
+
"success": chalk.greenBright('✔'),
|
|
42
|
+
"fail": '❗',
|
|
43
|
+
"working": '⏳'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getRootPath(packageDir) {
|
|
47
|
+
let rootPath = fileUtils.find('sfdx-project.json')
|
|
48
|
+
let defaultDir
|
|
49
|
+
if (rootPath) {
|
|
50
|
+
global.__basedir = fileUtils.fileInfo(rootPath).dirname
|
|
51
|
+
let packageJSON = JSON.parse(readFileSync(rootPath))
|
|
52
|
+
if (Array.isArray(packageJSON.packageDirectories)) {
|
|
53
|
+
packageJSON.packageDirectories.every(directory => {
|
|
54
|
+
if (directory.default || packageJSON.packageDirectories.length == 1) defaultDir = directory.path
|
|
55
|
+
if (directory == packageDir) {
|
|
56
|
+
defaultDir = directory
|
|
57
|
+
return false
|
|
58
|
+
}
|
|
59
|
+
return true
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
global.logger.error('Could not determine base path of Salesforce source directory. No sfdx-project.json found. Please specify a source path or execute from Salesforce project directory.')
|
|
64
|
+
process.exit(1)
|
|
65
|
+
}
|
|
66
|
+
if (packageDir && packageDir != defaultDir) {
|
|
67
|
+
global.logger.error('Could not find directory in sfdx-project.json. Please specify a package directory path from the sfdx-project.json file.')
|
|
68
|
+
process.exit(1)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return defaultDir
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let errorMessage = chalk.red('Please specify the action of ' + chalk.whiteBright.bgRedBright('split') + ' or ' + chalk.whiteBright.bgRedBright('combine') + '.')
|
|
75
|
+
|
|
76
|
+
yargs(hideBin(process.argv))
|
|
77
|
+
.alias('h', 'help')
|
|
78
|
+
.command({
|
|
79
|
+
command: '[split]',
|
|
80
|
+
alias: 'split',
|
|
81
|
+
description: 'splits metadata xml to json files',
|
|
82
|
+
builder: (yargs) => {
|
|
83
|
+
yargs
|
|
84
|
+
.example([
|
|
85
|
+
['$0 split --type=profile --all'],
|
|
86
|
+
['$0 split --type=permset --name="Permission Set Name"'],
|
|
87
|
+
['--source=packageDir --target=dir/dir'],
|
|
88
|
+
['name portion of file: [name].profile-meta.xml'],
|
|
89
|
+
['Example: --name="Admin" for Admin.profile-meta.xml'],
|
|
90
|
+
['\nCommands not supporting name or all parameters:'],
|
|
91
|
+
['$0 split --type=label'],
|
|
92
|
+
])
|
|
93
|
+
.options({
|
|
94
|
+
type: {
|
|
95
|
+
demand: true,
|
|
96
|
+
alias: 'type',
|
|
97
|
+
description: 'type of metadata to split',
|
|
98
|
+
demandOption: true,
|
|
99
|
+
type: 'string',
|
|
100
|
+
},
|
|
101
|
+
format: {
|
|
102
|
+
demand: true,
|
|
103
|
+
alias: 'format',
|
|
104
|
+
default: 'yaml',
|
|
105
|
+
description: 'type of output',
|
|
106
|
+
demandOption: true,
|
|
107
|
+
type: 'string',
|
|
108
|
+
},
|
|
109
|
+
name: {
|
|
110
|
+
alias: 'n',
|
|
111
|
+
description: 'name of metadata file to split',
|
|
112
|
+
demandOption: false,
|
|
113
|
+
type: 'string',
|
|
114
|
+
},
|
|
115
|
+
all: {
|
|
116
|
+
alias: 'a',
|
|
117
|
+
description: 'all metadata files of type will be split',
|
|
118
|
+
demandOption: false,
|
|
119
|
+
type: 'boolean',
|
|
120
|
+
},
|
|
121
|
+
source: {
|
|
122
|
+
demand: false,
|
|
123
|
+
alias: 's',
|
|
124
|
+
description: 'package directory path specified in sfdx-project.json',
|
|
125
|
+
type: 'string',
|
|
126
|
+
},
|
|
127
|
+
target: {
|
|
128
|
+
demand: false,
|
|
129
|
+
alias: 't',
|
|
130
|
+
description: 'target path to directory to create json files',
|
|
131
|
+
type: 'string',
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
.choices('type', ['label', 'permset', 'profile', 'workflow'])
|
|
135
|
+
.choices('format', ['json', 'yaml'])
|
|
136
|
+
.check((argv, options) => {
|
|
137
|
+
const name = argv.name
|
|
138
|
+
const all = argv.all
|
|
139
|
+
|
|
140
|
+
switch (argv.type) {
|
|
141
|
+
case 'profile':
|
|
142
|
+
case 'permset':
|
|
143
|
+
case 'workflow':
|
|
144
|
+
if ((typeof name != 'undefined' || name == '') && (typeof all != 'undefined' && all)) {
|
|
145
|
+
throw new Error(chalk.redBright('You cannot specify ' + chalk.whiteBright.bgRedBright('--name') + ' and ' + chalk.whiteBright.bgRedBright('--all') + ' at the same time.'))
|
|
146
|
+
} else if (typeof name == 'undefined' && (typeof all == 'undefined' || !all)) {
|
|
147
|
+
throw new Error(chalk.redBright('You must specify the ' + chalk.whiteBright.bgRedBright('--name') + ' parameter or use the ' + chalk.whiteBright.bgRedBright('--all') + ' switch.'))
|
|
148
|
+
} else {
|
|
149
|
+
return true // tell Yargs that the arguments passed the check
|
|
150
|
+
}
|
|
151
|
+
case 'label':
|
|
152
|
+
if ((typeof name != 'undefined' && name != '') || (typeof all != 'undefined' && all)) {
|
|
153
|
+
throw new Error(chalk.redBright('You cannot specify ' + chalk.whiteBright.bgRedBright('--name') + ' or ' + chalk.whiteBright.bgRedBright('--all') + ' when using label.'))
|
|
154
|
+
}
|
|
155
|
+
return true
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
},
|
|
159
|
+
handler: (argv) => {
|
|
160
|
+
let metaExtension
|
|
161
|
+
const fileList = []
|
|
162
|
+
let type = argv.type
|
|
163
|
+
global.format = argv.format
|
|
164
|
+
switch (type) {
|
|
165
|
+
case 'profile':
|
|
166
|
+
type = 'profiles'
|
|
167
|
+
metaExtension = '.profile-meta.xml'
|
|
168
|
+
break
|
|
169
|
+
case 'permset':
|
|
170
|
+
type = 'permissionsets'
|
|
171
|
+
metaExtension = '.permissionset-meta.xml'
|
|
172
|
+
break
|
|
173
|
+
case 'label':
|
|
174
|
+
type = 'labels'
|
|
175
|
+
metaExtension = '.labels-meta.xml'
|
|
176
|
+
break
|
|
177
|
+
case 'workflow':
|
|
178
|
+
type = 'workflows'
|
|
179
|
+
metaExtension = '.workflow-meta.xml'
|
|
180
|
+
break
|
|
181
|
+
default:
|
|
182
|
+
global.logger.error('Metadata type not supported: ' + type)
|
|
183
|
+
process.exit(1)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
let sourceDir = argv.source || ''
|
|
187
|
+
let targetDir = argv.target || ''
|
|
188
|
+
let name = argv.name
|
|
189
|
+
let all = argv.all
|
|
190
|
+
let packageDir = getRootPath(sourceDir)
|
|
191
|
+
|
|
192
|
+
if (type == 'labels') {
|
|
193
|
+
name = 'CustomLabels'
|
|
194
|
+
}
|
|
195
|
+
sourceDir = path.join(global.__basedir, packageDir, 'main', 'default', type)
|
|
196
|
+
if (targetDir == '') {
|
|
197
|
+
targetDir = path.join(global.__basedir, packageDir + '-party', 'main', 'default', type)
|
|
198
|
+
} else {
|
|
199
|
+
targetDir = path.join(targetDir, 'main', 'default', type)
|
|
200
|
+
}
|
|
201
|
+
let metaDirPath = sourceDir
|
|
202
|
+
console.log(`${chalk.bgBlackBright('Source path:')} ${sourceDir}`)
|
|
203
|
+
console.log(`${chalk.bgBlackBright('Target path:')} ${targetDir}`)
|
|
204
|
+
console.log()
|
|
205
|
+
|
|
206
|
+
if (!all) {
|
|
207
|
+
let metaFilePath = path.join(metaDirPath, name)
|
|
208
|
+
if (!fileUtils.fileExists(metaFilePath)) {
|
|
209
|
+
name += metaExtension
|
|
210
|
+
metaFilePath = path.join(metaDirPath, name)
|
|
211
|
+
if (!fileUtils.fileExists(metaFilePath)) {
|
|
212
|
+
global.logger.error('File not found: ' + metaFilePath)
|
|
213
|
+
process.exit(1)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
fileList.push(name)
|
|
217
|
+
} else {
|
|
218
|
+
if (fileUtils.directoryExists(sourceDir)) {
|
|
219
|
+
fileUtils.getFiles(sourceDir, metaExtension).forEach(file => {
|
|
220
|
+
fileList.push(file)
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
global.processed.total = fileList.length
|
|
226
|
+
console.log(`Splitting a total of ${fileList.length} file(s)`)
|
|
227
|
+
console.log()
|
|
228
|
+
const promList = []
|
|
229
|
+
fileList.forEach(metaFile => {
|
|
230
|
+
switch (type) {
|
|
231
|
+
case 'profiles':
|
|
232
|
+
const profile = new profileSplit.Profile({
|
|
233
|
+
sourceDir: sourceDir,
|
|
234
|
+
targetDir: targetDir,
|
|
235
|
+
metaFilePath: path.join(sourceDir, metaFile),
|
|
236
|
+
sequence: promList.length + 1,
|
|
237
|
+
})
|
|
238
|
+
const profProm = profile.split()
|
|
239
|
+
promList.push(profProm)
|
|
240
|
+
profProm.then(() => {
|
|
241
|
+
global.processed.current++
|
|
242
|
+
})
|
|
243
|
+
break
|
|
244
|
+
case 'permissionsets':
|
|
245
|
+
const permSet = new permSetSplit.Permset({
|
|
246
|
+
sourceDir: sourceDir,
|
|
247
|
+
targetDir: targetDir,
|
|
248
|
+
metaFilePath: path.join(sourceDir, metaFile),
|
|
249
|
+
sequence: promList.length + 1,
|
|
250
|
+
})
|
|
251
|
+
const permProm = permSet.split()
|
|
252
|
+
promList.push(permProm)
|
|
253
|
+
permProm.then(() => {
|
|
254
|
+
global.processed.current++
|
|
255
|
+
})
|
|
256
|
+
break
|
|
257
|
+
case 'labels':
|
|
258
|
+
const label = new labelSplit.CustomLabel({
|
|
259
|
+
sourceDir: sourceDir,
|
|
260
|
+
targetDir: targetDir,
|
|
261
|
+
metaFilePath: path.join(sourceDir, metaFile),
|
|
262
|
+
sequence: promList.length + 1,
|
|
263
|
+
})
|
|
264
|
+
const labelProm = label.split()
|
|
265
|
+
promList.push(labelProm)
|
|
266
|
+
labelProm.then(() => {
|
|
267
|
+
global.processed.current++
|
|
268
|
+
})
|
|
269
|
+
break
|
|
270
|
+
case 'workflows':
|
|
271
|
+
const workflow = new workflowSplit.Split({
|
|
272
|
+
metadataDefinition: workflowDefinition.metadataDefinition,
|
|
273
|
+
sourceDir: sourceDir,
|
|
274
|
+
targetDir: targetDir,
|
|
275
|
+
metaFilePath: path.join(sourceDir, metaFile),
|
|
276
|
+
sequence: promList.length + 1,
|
|
277
|
+
})
|
|
278
|
+
const workflowProm = workflow.split()
|
|
279
|
+
promList.push(workflowProm)
|
|
280
|
+
workflowProm.then((resolve, reject) => {
|
|
281
|
+
if (resolve == false) {
|
|
282
|
+
global.processed.errors++
|
|
283
|
+
global.processed.current--
|
|
284
|
+
} else {
|
|
285
|
+
global.processed.current++
|
|
286
|
+
}
|
|
287
|
+
})
|
|
288
|
+
break
|
|
289
|
+
}
|
|
290
|
+
})
|
|
291
|
+
Promise.allSettled(promList).then((results) => {
|
|
292
|
+
let message = `Split ${chalk.bgBlackBright(global.processed.current)} file(s) ${(global.processed.errors > 0) ? 'with ' + chalk.bgBlackBright.red(global.processed.errors) + ' error(s) ' : ''}in `
|
|
293
|
+
displayMessage(startTime, message)
|
|
294
|
+
})
|
|
295
|
+
}
|
|
296
|
+
})
|
|
297
|
+
.command({
|
|
298
|
+
command: '[combine]',
|
|
299
|
+
alias: 'combine',
|
|
300
|
+
description: 'combines json files into metadata xml',
|
|
301
|
+
builder: (yargs) => {
|
|
302
|
+
yargs
|
|
303
|
+
.example([
|
|
304
|
+
['$0 combine --type=profile --all'],
|
|
305
|
+
['$0 combine --type=permset --name="Permission Set Name"'],
|
|
306
|
+
['--source=packageDir --target=dir/dir'],
|
|
307
|
+
['name portion of file: [name].profile-meta.xml'],
|
|
308
|
+
['Example: --name="Admin" for Admin.profile-meta.xml'],
|
|
309
|
+
['\nCommands not supporting name or all parameters:'],
|
|
310
|
+
['$0 combine --type=label'],])
|
|
311
|
+
.options({
|
|
312
|
+
type: {
|
|
313
|
+
demand: true,
|
|
314
|
+
alias: 'type',
|
|
315
|
+
description: 'type of metadata to combine',
|
|
316
|
+
demandOption: true,
|
|
317
|
+
type: 'string',
|
|
318
|
+
},
|
|
319
|
+
format: {
|
|
320
|
+
demand: true,
|
|
321
|
+
alias: 'format',
|
|
322
|
+
default: 'yaml',
|
|
323
|
+
description: 'type of output',
|
|
324
|
+
demandOption: true,
|
|
325
|
+
type: 'string',
|
|
326
|
+
},
|
|
327
|
+
name: {
|
|
328
|
+
alias: 'n',
|
|
329
|
+
description: 'name of metadata file to combine',
|
|
330
|
+
demandOption: false,
|
|
331
|
+
type: 'string',
|
|
332
|
+
},
|
|
333
|
+
all: {
|
|
334
|
+
alias: 'a',
|
|
335
|
+
description: 'all json files of type will be combined',
|
|
336
|
+
demandOption: false,
|
|
337
|
+
type: 'boolean',
|
|
338
|
+
},
|
|
339
|
+
source: {
|
|
340
|
+
demand: false,
|
|
341
|
+
alias: 's',
|
|
342
|
+
description: 'package directory path specified in sfdx-project.json',
|
|
343
|
+
type: 'string',
|
|
344
|
+
},
|
|
345
|
+
target: {
|
|
346
|
+
demand: false,
|
|
347
|
+
alias: 't',
|
|
348
|
+
description: 'target path to directory to create xml files',
|
|
349
|
+
type: 'string',
|
|
350
|
+
}
|
|
351
|
+
})
|
|
352
|
+
.choices('type', ['label', 'permset', 'profile', 'workflow'])
|
|
353
|
+
.choices('format', ['json', 'yaml'])
|
|
354
|
+
.check((argv, options) => {
|
|
355
|
+
const name = argv.name
|
|
356
|
+
const all = argv.all
|
|
357
|
+
|
|
358
|
+
switch (argv.type) {
|
|
359
|
+
case 'profile':
|
|
360
|
+
case 'permset':
|
|
361
|
+
case 'workflow':
|
|
362
|
+
if ((typeof name != 'undefined' || name == '') && (typeof all != 'undefined' && all)) {
|
|
363
|
+
throw new Error(chalk.redBright('You cannot specify ' + chalk.whiteBright.bgRedBright('--name') + ' and ' + chalk.whiteBright.bgRedBright('--all') + ' at the same time.'))
|
|
364
|
+
} else if (typeof name == 'undefined' && (typeof all == 'undefined' || !all)) {
|
|
365
|
+
throw new Error(chalk.redBright('You must specify the ' + chalk.whiteBright.bgRedBright('--name') + ' parameter or use the ' + chalk.whiteBright.bgRedBright('--all') + ' switch.'))
|
|
366
|
+
} else {
|
|
367
|
+
return true // tell Yargs that the arguments passed the check
|
|
368
|
+
}
|
|
369
|
+
case 'label':
|
|
370
|
+
if ((typeof name != 'undefined' && name != '') || (typeof all != 'undefined' && all)) {
|
|
371
|
+
throw new Error(chalk.redBright('You cannot specify ' + chalk.whiteBright.bgRedBright('--name') + ' or ' + chalk.whiteBright.bgRedBright('--all') + ' when using label.'))
|
|
372
|
+
}
|
|
373
|
+
return true
|
|
374
|
+
}
|
|
375
|
+
})
|
|
376
|
+
},
|
|
377
|
+
handler: (argv) => {
|
|
378
|
+
let processList = []
|
|
379
|
+
global.format = argv.format
|
|
380
|
+
let type = argv.type
|
|
381
|
+
switch (type) {
|
|
382
|
+
case 'profile':
|
|
383
|
+
type = 'profiles'
|
|
384
|
+
break
|
|
385
|
+
case 'permset':
|
|
386
|
+
type = 'permissionsets'
|
|
387
|
+
break
|
|
388
|
+
case 'label':
|
|
389
|
+
type = 'labels'
|
|
390
|
+
break
|
|
391
|
+
case 'workflow':
|
|
392
|
+
type = 'workflows'
|
|
393
|
+
break
|
|
394
|
+
default:
|
|
395
|
+
global.logger.error('Metadata type not supported: ' + type)
|
|
396
|
+
process.exit(1)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
let sourceDir = argv.source || ''
|
|
400
|
+
let targetDir = argv.target || ''
|
|
401
|
+
let name = argv.name
|
|
402
|
+
let all = argv.all
|
|
403
|
+
let packageDir = getRootPath(sourceDir)
|
|
404
|
+
|
|
405
|
+
sourceDir = path.join(global.__basedir, packageDir + '-party', 'main', 'default', type)
|
|
406
|
+
if (targetDir == '') {
|
|
407
|
+
targetDir = path.join(global.__basedir, packageDir, 'main', 'default', type)
|
|
408
|
+
} else {
|
|
409
|
+
targetDir = path.join(targetDir, 'main', 'default', type)
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
console.log(`${chalk.bgBlackBright('Source path:')} ${sourceDir}`)
|
|
413
|
+
console.log(`${chalk.bgBlackBright('Target path:')} ${targetDir}`)
|
|
414
|
+
console.log()
|
|
415
|
+
|
|
416
|
+
if (type == 'labels') {
|
|
417
|
+
processList = fileUtils.getFiles(sourceDir, `.${global.format}`)
|
|
418
|
+
} else if (!all) {
|
|
419
|
+
let metaDirPath = path.join(sourceDir, name)
|
|
420
|
+
if (!fileUtils.directoryExists(metaDirPath)) {
|
|
421
|
+
global.logger.error('Directory not found: ' + metaDirPath)
|
|
422
|
+
process.exit(1)
|
|
423
|
+
}
|
|
424
|
+
processList.push(name)
|
|
425
|
+
} else {
|
|
426
|
+
processList = fileUtils.getDirectories(sourceDir)
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
global.processed.total = processList.length
|
|
430
|
+
console.log(`Combining a total of ${global.processed.total} file(s)`)
|
|
431
|
+
console.log()
|
|
432
|
+
|
|
433
|
+
const promList = []
|
|
434
|
+
if (type != 'labels') {
|
|
435
|
+
processList.forEach(metaDir => {
|
|
436
|
+
if (type == 'profiles') {
|
|
437
|
+
const profile = new profileCombine.Profile({
|
|
438
|
+
sourceDir: sourceDir,
|
|
439
|
+
targetDir: targetDir,
|
|
440
|
+
metaDir: metaDir,
|
|
441
|
+
sequence: promList.length + 1,
|
|
442
|
+
})
|
|
443
|
+
const profProm = profile.combine()
|
|
444
|
+
promList.push(profProm)
|
|
445
|
+
profProm.then(() => {
|
|
446
|
+
global.processed.current++
|
|
447
|
+
})
|
|
448
|
+
} else if (type == 'permissionsets') {
|
|
449
|
+
const permSet = new permSetCombine.Permset({
|
|
450
|
+
sourceDir: sourceDir,
|
|
451
|
+
targetDir: targetDir,
|
|
452
|
+
metaDir: metaDir,
|
|
453
|
+
sequence: promList.length + 1,
|
|
454
|
+
})
|
|
455
|
+
const permProm = permSet.combine()
|
|
456
|
+
promList.push(permProm)
|
|
457
|
+
permProm.then(() => {
|
|
458
|
+
global.processed.current++
|
|
459
|
+
})
|
|
460
|
+
} else if (type == 'workflows') {
|
|
461
|
+
const workflow = new workflowCombine.Combine({
|
|
462
|
+
metadataDefinition: workflowDefinition.metadataDefinition,
|
|
463
|
+
sourceDir: sourceDir,
|
|
464
|
+
targetDir: targetDir,
|
|
465
|
+
metaDir: metaDir,
|
|
466
|
+
sequence: promList.length + 1,
|
|
467
|
+
})
|
|
468
|
+
const workflowProm = workflow.combine()
|
|
469
|
+
promList.push(workflowProm)
|
|
470
|
+
workflowProm.then((resolve, reject) => {
|
|
471
|
+
global.processed.current++
|
|
472
|
+
})
|
|
473
|
+
}
|
|
474
|
+
})
|
|
475
|
+
} else if (type == 'labels') {
|
|
476
|
+
const label = new labelCombine.CustomLabel({
|
|
477
|
+
sourceDir: sourceDir,
|
|
478
|
+
targetDir: targetDir,
|
|
479
|
+
metaDir: sourceDir, // use sourceDir
|
|
480
|
+
processList: processList,
|
|
481
|
+
})
|
|
482
|
+
const labelProm = label.combine()
|
|
483
|
+
promList.push(labelProm)
|
|
484
|
+
labelProm.then(() => {
|
|
485
|
+
global.processed.current++
|
|
486
|
+
})
|
|
487
|
+
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
Promise.allSettled(promList).then(([result]) => {
|
|
491
|
+
if (result !== undefined && result.status == 'fulfilled') {
|
|
492
|
+
let message = `Combined ${chalk.bgBlackBright(global.processed.total)} file(s) in `
|
|
493
|
+
displayMessage(startTime, message)
|
|
494
|
+
} else {
|
|
495
|
+
global.logger.error((result !== undefined) ? result.reason : 'metadata error')
|
|
496
|
+
}
|
|
497
|
+
})
|
|
498
|
+
}
|
|
499
|
+
})
|
|
500
|
+
.demandCommand(1, errorMessage)
|
|
501
|
+
.example([
|
|
502
|
+
['$0 split --type=profile --all'],
|
|
503
|
+
['$0 split --type=profile --name="Profile Name"'],
|
|
504
|
+
['$0 combine --type=permset --all'],
|
|
505
|
+
['$0 combine --type=permset --name="Permission Set Name"'],
|
|
506
|
+
])
|
|
507
|
+
.help()
|
|
508
|
+
.argv
|
|
509
|
+
.parse
|
|
510
|
+
|
|
511
|
+
function displayMessage(startTime, message) {
|
|
512
|
+
const diff = process.hrtime.bigint() - BigInt(startTime)
|
|
513
|
+
let durationMessage
|
|
514
|
+
let executionTime = convertHrtime(diff);
|
|
515
|
+
let minutes = Math.floor((executionTime.seconds + Math.round(executionTime.milliseconds / 100000)) / 60)
|
|
516
|
+
let seconds = Math.round((executionTime.seconds + Math.round(executionTime.milliseconds / 100000)) % 60)
|
|
517
|
+
if (minutes == 0 && seconds == 0) {
|
|
518
|
+
durationMessage = message + chalk.magentaBright(`<1s`)
|
|
519
|
+
} else if (minutes > 0) {
|
|
520
|
+
durationMessage = message + chalk.magentaBright(`${minutes}m ${seconds}s`)
|
|
521
|
+
} else {
|
|
522
|
+
durationMessage = message + chalk.magentaBright(`${seconds}s`)
|
|
523
|
+
}
|
|
524
|
+
console.log('\n' + durationMessage)
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
let callAmount = 0;
|
|
528
|
+
process.on('SIGINT', function () {
|
|
529
|
+
if (callAmount < 1) {
|
|
530
|
+
console.log(`✅ Received abort command`);
|
|
531
|
+
process.exit(1)
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
callAmount++;
|
|
535
|
+
})
|