@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
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import os from 'os'
|
|
3
|
+
import { readFileSync, writeFileSync, utimesSync } from 'fs'
|
|
4
|
+
import logUpdate from 'log-update'
|
|
5
|
+
import chalk from 'chalk'
|
|
6
|
+
import convertHrtime from 'convert-hrtime'
|
|
7
|
+
import cliSpinners from 'cli-spinners'
|
|
8
|
+
import * as fileUtils from '../fileUtils.js'
|
|
9
|
+
import { permsetDefinition } from './definition.js'
|
|
10
|
+
import * as xml2js from 'xml2js'
|
|
11
|
+
import * as yaml from 'js-yaml'
|
|
12
|
+
|
|
13
|
+
const spinner = cliSpinners['dots']
|
|
14
|
+
|
|
15
|
+
export class Permset {
|
|
16
|
+
#xml = ''
|
|
17
|
+
#types = []
|
|
18
|
+
#spinnerMessage = ''
|
|
19
|
+
#index = 0
|
|
20
|
+
#startTime = 0
|
|
21
|
+
#fileName = ''
|
|
22
|
+
#errorMessage = ''
|
|
23
|
+
#fileStats
|
|
24
|
+
|
|
25
|
+
constructor(config) {
|
|
26
|
+
this.sourceDir = config.sourceDir
|
|
27
|
+
this.targetDir = config.targetDir
|
|
28
|
+
this.metaDir = config.metaDir
|
|
29
|
+
this.sequence = config.sequence
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
combine() {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const that = this
|
|
35
|
+
if (!fileUtils.directoryExists(path.join(that.sourceDir, that.metaDir))) reject(that.metaDir)
|
|
36
|
+
|
|
37
|
+
that.metaDir = fileUtils.getDirectories(that.sourceDir).find(element => element.toLowerCase() == that.metaDir.toLowerCase())
|
|
38
|
+
|
|
39
|
+
that.#xml = `<?xml version="1.0" encoding="UTF-8"?>${os.EOL}`
|
|
40
|
+
that.#xml += `<PermissionSet xmlns="https://soap.sforce.com/2006/04/metadata">${os.EOL}`
|
|
41
|
+
|
|
42
|
+
permsetDefinition.main.forEach(key => { that.#types.push(key) })
|
|
43
|
+
permsetDefinition.singleFiles.forEach(key => { that.#types.push(key) })
|
|
44
|
+
permsetDefinition.directories.forEach(key => { that.#types.push(key) })
|
|
45
|
+
that.#types.sort()
|
|
46
|
+
|
|
47
|
+
setFileName(that)
|
|
48
|
+
processPermSet(that)
|
|
49
|
+
|
|
50
|
+
saveXML(that)
|
|
51
|
+
resolve(that.metaDir)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
function setFileName(that) {
|
|
55
|
+
const fileName = path.join(that.sourceDir, that.metaDir, `main.${global.format}`)
|
|
56
|
+
if (fileUtils.fileExists(fileName)) {
|
|
57
|
+
const data = readFileSync(fileName, { encoding: 'utf8', flag: 'r' })
|
|
58
|
+
const result = (global.format == 'yaml') ? yaml.load(data) : JSON.parse(data)
|
|
59
|
+
that.#fileStats = fileUtils.fileInfo(fileName).stats
|
|
60
|
+
that.#fileName = path.join(that.targetDir, result.name + '.permissionset-meta.xml')
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function processPermSet(that) {
|
|
65
|
+
that.#startTime = process.hrtime.bigint()
|
|
66
|
+
that.#spinnerMessage = `[%1] of ${global.processed.total} - Permission Set: [%4]${chalk.yellowBright(that.metaDir)}[%2][%3]`
|
|
67
|
+
logUpdate(that.#spinnerMessage
|
|
68
|
+
.replace('[%1]', that.sequence.toString().padStart(global.processed.total.toString().length, ' '))
|
|
69
|
+
.replace('[%2]', '')
|
|
70
|
+
.replace('[%3]', '')
|
|
71
|
+
.replace('[%4]', '')
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
that.#types.forEach(key => {
|
|
75
|
+
let myLocation
|
|
76
|
+
if (permsetDefinition.main.includes(key)) {
|
|
77
|
+
myLocation = 'main'
|
|
78
|
+
} else if (permsetDefinition.directories.includes(key)) {
|
|
79
|
+
myLocation = 'directory'
|
|
80
|
+
} else if (permsetDefinition.singleFiles.includes(key)) {
|
|
81
|
+
myLocation = 'file'
|
|
82
|
+
}
|
|
83
|
+
logUpdate(that.#spinnerMessage
|
|
84
|
+
.replace('[%1]', that.sequence.toString().padStart(global.processed.total.toString().length, ' '))
|
|
85
|
+
.replace('[%2]', `\n${chalk.magentaBright(nextFrame(that))} ${key}`)
|
|
86
|
+
.replace('[%3]', `${that.#errorMessage}`)
|
|
87
|
+
.replace('[%4]', `${global.statusLevel.working} `)
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
switch (myLocation) {
|
|
91
|
+
case 'main':
|
|
92
|
+
processMain(that, key)
|
|
93
|
+
break
|
|
94
|
+
case 'file':
|
|
95
|
+
processFile(that, key)
|
|
96
|
+
break
|
|
97
|
+
case 'directory':
|
|
98
|
+
processDirectory(that, key)
|
|
99
|
+
break
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function processMain(that, key) {
|
|
105
|
+
const fileName = path.join(that.sourceDir, that.metaDir, `main.${global.format}`)
|
|
106
|
+
if (fileUtils.fileExists(fileName)) {
|
|
107
|
+
const data = readFileSync(fileName, { encoding: 'utf8', flag: 'r' })
|
|
108
|
+
const result = (global.format == 'yaml') ? yaml.load(data) : JSON.parse(data)
|
|
109
|
+
if (result[key] !== undefined) that.#xml += `\t<${key}>${result[key]}</${key}>${os.EOL}`
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function processFile(that, key) {
|
|
114
|
+
const fileName = path.join(that.sourceDir, that.metaDir, `${key}.${global.format}`)
|
|
115
|
+
if (fileUtils.fileExists(fileName)) {
|
|
116
|
+
if (permsetDefinition.singleFiles.includes(key)) {
|
|
117
|
+
genericXML(that, key)
|
|
118
|
+
} else {
|
|
119
|
+
that.#errorMessage += `\n${global.statusLevel.warn} Not processed: ${key}`
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function processDirectory(that, key) {
|
|
125
|
+
switch (key) {
|
|
126
|
+
case 'objectPermissions':
|
|
127
|
+
objectPermissions(that, key)
|
|
128
|
+
break
|
|
129
|
+
default:
|
|
130
|
+
if (permsetDefinition.directories.includes(key)) {
|
|
131
|
+
genericDirectoryXML(that, key)
|
|
132
|
+
break
|
|
133
|
+
} else {
|
|
134
|
+
that.#errorMessage += `\n${global.statusLevel.warn} Not processed: ${key}`
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function getTimeDiff(startTime, endTime = process.hrtime.bigint()) {
|
|
141
|
+
const diff = BigInt(endTime) - BigInt(startTime)
|
|
142
|
+
let executionTime = convertHrtime(diff)
|
|
143
|
+
executionTime.seconds = Math.round(executionTime.seconds)
|
|
144
|
+
executionTime.milliseconds = Math.round(executionTime.milliseconds / 1000)
|
|
145
|
+
if (executionTime.milliseconds == 0 && executionTime.nanoseconds > 0) executionTime.milliseconds = 1
|
|
146
|
+
return executionTime
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function saveXML(that) {
|
|
150
|
+
fileUtils.createDirectory(that.targetDir)
|
|
151
|
+
that.#xml += '</PermissionSet>\n'
|
|
152
|
+
writeFileSync(that.#fileName, that.#xml)
|
|
153
|
+
utimesSync(that.#fileName, that.#fileStats.atime, that.#fileStats.mtime)
|
|
154
|
+
|
|
155
|
+
let executionTime = getTimeDiff(BigInt(that.#startTime))
|
|
156
|
+
let durationMessage = `${executionTime.seconds}.${executionTime.milliseconds}s`
|
|
157
|
+
let stateIcon = (that.#errorMessage == '') ? global.statusLevel.success : global.statusLevel.fail
|
|
158
|
+
logUpdate(that.#spinnerMessage
|
|
159
|
+
.replace('[%1]', that.sequence.toString().padStart(global.processed.total.toString().length, ' '))
|
|
160
|
+
.replace('[%2]', `. Processed in ${durationMessage}.`)
|
|
161
|
+
.replace('[%3]', `${that.#errorMessage}`)
|
|
162
|
+
.replace('[%4]', `${stateIcon} `)
|
|
163
|
+
)
|
|
164
|
+
logUpdate.done()
|
|
165
|
+
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function nextFrame(that) {
|
|
169
|
+
return spinner.frames[that.#index = ++that.#index % spinner.frames.length]
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function genericXML(that, key) {
|
|
173
|
+
const fileName = path.join(that.sourceDir, that.metaDir, `${key}.${global.format}`)
|
|
174
|
+
const builder = new xml2js.Builder({ cdata: false, headless: true, rootName: key })
|
|
175
|
+
|
|
176
|
+
if (fileUtils.fileExists(fileName)) {
|
|
177
|
+
const data = readFileSync(fileName, { encoding: 'utf8', flag: 'r' })
|
|
178
|
+
const result = (global.format == 'yaml') ? yaml.load(data) : JSON.parse(data)
|
|
179
|
+
result[key] = sortJSONKeys(sortJSON(result[key], permsetDefinition.sortKeys[key]))
|
|
180
|
+
result[key].forEach(element => {
|
|
181
|
+
that.#xml += `\t<${key}>${os.EOL}`
|
|
182
|
+
Object.keys(element).forEach(tag => {
|
|
183
|
+
let xml
|
|
184
|
+
try {
|
|
185
|
+
xml = builder.buildObject(element[tag]).replace(`<${key}>`, '').replace(`</${key}>`, '')
|
|
186
|
+
that.#xml += `\t\t<${tag}>${xml}</${tag}>${os.EOL}`
|
|
187
|
+
} catch (error) {
|
|
188
|
+
global.logger.error(error)
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
that.#xml += `\t</${key}>${os.EOL}`
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function genericDirectoryXML(that, key) {
|
|
197
|
+
let dirPath = path.join(that.sourceDir, that.metaDir, key)
|
|
198
|
+
if (!fileUtils.directoryExists(dirPath)) return
|
|
199
|
+
|
|
200
|
+
let fileList = fileUtils.getFiles(dirPath, `.${global.format}`).sort()
|
|
201
|
+
fileList.forEach(fileName => {
|
|
202
|
+
const data = readFileSync(path.join(dirPath, fileName), { encoding: 'utf8', flag: 'r' })
|
|
203
|
+
const result = (global.format == 'yaml') ? yaml.load(data) : JSON.parse(data)
|
|
204
|
+
const object = result.object
|
|
205
|
+
result[key] = sortJSONKeys(sortJSON(result[key], permsetDefinition.sortKeys[key]))
|
|
206
|
+
result[key].forEach(element => {
|
|
207
|
+
that.#xml += `\t<${key}>${os.EOL}`
|
|
208
|
+
Object.keys(element).forEach(tag => {
|
|
209
|
+
if (tag == permsetDefinition.sortKeys[key] && object) {
|
|
210
|
+
that.#xml += `\t\t<${tag}>${object}.${element[tag]}</${tag}>${os.EOL}`
|
|
211
|
+
} else {
|
|
212
|
+
that.#xml += `\t\t<${tag}>${element[tag]}</${tag}>${os.EOL}`
|
|
213
|
+
}
|
|
214
|
+
})
|
|
215
|
+
that.#xml += `\t</${key}>${os.EOL}`
|
|
216
|
+
})
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function objectPermissions(that, key) {
|
|
221
|
+
let dirPath = path.join(that.sourceDir, that.metaDir, key)
|
|
222
|
+
if (!fileUtils.directoryExists(dirPath)) return
|
|
223
|
+
|
|
224
|
+
let fileList = fileUtils.getFiles(dirPath, `.${global.format}`).sort((a, b) => a.localeCompare(b))
|
|
225
|
+
fileList.forEach(fileName => {
|
|
226
|
+
const data = readFileSync(path.join(dirPath, fileName), { encoding: 'utf8', flag: 'r' })
|
|
227
|
+
const result = (global.format == 'yaml') ? yaml.load(data) : JSON.parse(data)
|
|
228
|
+
result[key]['object'] = result.object
|
|
229
|
+
result[key] = sortJSONKeys(result[key])
|
|
230
|
+
that.#xml += `\t<${key}>${os.EOL}`
|
|
231
|
+
Object.keys(result[key]).forEach(element => {
|
|
232
|
+
that.#xml += `\t\t<${element}>${result[key][element]}</${element}>${os.EOL}`
|
|
233
|
+
})
|
|
234
|
+
that.#xml += `\t</${key}>${os.EOL}`
|
|
235
|
+
})
|
|
236
|
+
}
|
|
237
|
+
// end of functions
|
|
238
|
+
// end of combine
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// end of class
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function sortJSON(json, key) {
|
|
245
|
+
if (Array.isArray(json)) {
|
|
246
|
+
json.sort((a, b) => {
|
|
247
|
+
if (a[key] < b[key]) return -1
|
|
248
|
+
if (a[key] > b[key]) return 1
|
|
249
|
+
return 0
|
|
250
|
+
})
|
|
251
|
+
}
|
|
252
|
+
return json
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function sortJSONKeys(json) {
|
|
256
|
+
// sort json keys alphabetically
|
|
257
|
+
if (Array.isArray(json)) {
|
|
258
|
+
json.forEach(function (part, index) {
|
|
259
|
+
this[index] = Object.keys(this[index])
|
|
260
|
+
.sort((a, b) => {
|
|
261
|
+
if (a < b) return -1
|
|
262
|
+
if (a > b) return 1
|
|
263
|
+
return 0
|
|
264
|
+
})
|
|
265
|
+
.reduce((accumulator, key) => {
|
|
266
|
+
accumulator[key] = this[index][key]
|
|
267
|
+
|
|
268
|
+
return accumulator
|
|
269
|
+
}, {})
|
|
270
|
+
}, json)
|
|
271
|
+
|
|
272
|
+
} else {
|
|
273
|
+
json = Object.keys(json)
|
|
274
|
+
.sort((a, b) => {
|
|
275
|
+
if (a < b) return -1
|
|
276
|
+
if (a > b) return 1
|
|
277
|
+
return 0
|
|
278
|
+
})
|
|
279
|
+
.reduce((accumulator, key) => {
|
|
280
|
+
accumulator[key] = json[key]
|
|
281
|
+
|
|
282
|
+
return accumulator
|
|
283
|
+
}, {})
|
|
284
|
+
}
|
|
285
|
+
return json
|
|
286
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export const permsetDefinition = {
|
|
2
|
+
metaUrl: 'https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_permissionset.htm',
|
|
3
|
+
main: [
|
|
4
|
+
'label',
|
|
5
|
+
'description',
|
|
6
|
+
'custom',
|
|
7
|
+
'hasActivationRequired',
|
|
8
|
+
'license',
|
|
9
|
+
'userLicense', // Replaced by license
|
|
10
|
+
],
|
|
11
|
+
singleFiles: [
|
|
12
|
+
'applicationVisibilities',
|
|
13
|
+
// 'categoryGroupVisibilities', // PROFILE ONLY
|
|
14
|
+
'classAccesses',
|
|
15
|
+
'customMetadataTypeAccesses',
|
|
16
|
+
'customPermissions',
|
|
17
|
+
'customSettingAccesses',
|
|
18
|
+
'externalDataSourceAccesses',
|
|
19
|
+
'flowAccesses',
|
|
20
|
+
// 'layoutAssignments', // PROFILE ONLY
|
|
21
|
+
// 'loginHours', // PROFILE ONLY
|
|
22
|
+
// 'loginIpRanges', // PROFILE ONLY
|
|
23
|
+
'pageAccesses',
|
|
24
|
+
// 'profileActionOverrides', // PROFILE ONLY
|
|
25
|
+
'tabSettings',
|
|
26
|
+
// 'tabVisibilities', // PROFILE ONLY
|
|
27
|
+
'userPermissions',
|
|
28
|
+
],
|
|
29
|
+
directories: [
|
|
30
|
+
'fieldPermissions',
|
|
31
|
+
// 'loginFlows', // PROFILE ONLY
|
|
32
|
+
'objectPermissions',
|
|
33
|
+
'recordTypeVisibilities',
|
|
34
|
+
],
|
|
35
|
+
ignore: [
|
|
36
|
+
'$',
|
|
37
|
+
],
|
|
38
|
+
sortKeys: {
|
|
39
|
+
'applicationVisibilities': 'application',
|
|
40
|
+
// 'categoryGroupVisibilities': 'dataCategoryGroup', // PROFILE ONLY
|
|
41
|
+
'classAccesses': 'apexClass',
|
|
42
|
+
'customMetadataTypeAccesses': 'name',
|
|
43
|
+
'customPermissions': 'name',
|
|
44
|
+
'customSettingAccesses': 'name',
|
|
45
|
+
'externalDataSourceAccesses': 'externalDataSource',
|
|
46
|
+
'fieldPermissions': 'field',
|
|
47
|
+
'flowAccesses': 'flow',
|
|
48
|
+
// 'layoutAssignments': 'layout', // PROFILE ONLY
|
|
49
|
+
// 'loginFlows': 'friendlyName', // PROFILE ONLY
|
|
50
|
+
// 'loginIpRanges': 'startAddress', // PROFILE ONLY
|
|
51
|
+
'objectPermissions': 'object',
|
|
52
|
+
'pageAccesses': 'apexPage',
|
|
53
|
+
// 'profileActionOverrides': 'pageOrSobjectType', // PROFILE ONLY
|
|
54
|
+
'recordTypeVisibilities': 'recordType',
|
|
55
|
+
'tabSettings': 'tab',
|
|
56
|
+
// 'tabVisibilities': 'tab', // PROFILE ONLY
|
|
57
|
+
'userPermissions': 'name',
|
|
58
|
+
},
|
|
59
|
+
keyOrder: {
|
|
60
|
+
applicationVisibilities: ['application', 'visible'],
|
|
61
|
+
classAccesses: ['apexClass', 'enabled'],
|
|
62
|
+
customMetadataTypeAccesses: ['name', 'enabled'],
|
|
63
|
+
customPermissions: ['name', 'enabled'],
|
|
64
|
+
customSettingAccesses: ['name', 'enabled'],
|
|
65
|
+
externalDataSourceAccesses: ['externalDataSource', 'enabled'],
|
|
66
|
+
fieldPermissions: ['field', 'editable', 'readable'],
|
|
67
|
+
flowAccesses: ['flow', 'enabled'],
|
|
68
|
+
objectPermissions: ['object', 'allowCreate', 'allowRead', 'allowEdit', 'allowDelete', 'viewAllRecords', 'modifyAllRecords'],
|
|
69
|
+
pageAccesses: ['apexPage', 'enabled'],
|
|
70
|
+
recordTypeVisibilities: ['recordType', 'visible'],
|
|
71
|
+
tabSettings: ['tab', 'visibility'],
|
|
72
|
+
userPermissions: ['name', 'enabled'],
|
|
73
|
+
},
|
|
74
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import fs from 'fs'
|
|
5
|
+
import os from 'os'
|
|
6
|
+
import { readFile } from 'fs'
|
|
7
|
+
import { Parser } from 'xml2js'
|
|
8
|
+
import logUpdate from 'log-update'
|
|
9
|
+
import chalk from 'chalk'
|
|
10
|
+
import convertHrtime from 'convert-hrtime'
|
|
11
|
+
import cliSpinners from 'cli-spinners'
|
|
12
|
+
import * as yaml from 'js-yaml'
|
|
13
|
+
import * as fileUtils from '../fileUtils.js'
|
|
14
|
+
import { permsetDefinition } from './definition.js'
|
|
15
|
+
|
|
16
|
+
const spinner = cliSpinners['dots']
|
|
17
|
+
|
|
18
|
+
export class Permset {
|
|
19
|
+
#fileName = {
|
|
20
|
+
'fullName': undefined,
|
|
21
|
+
'shortName': undefined,
|
|
22
|
+
}
|
|
23
|
+
#json
|
|
24
|
+
#errorMessage = ''
|
|
25
|
+
#index = 0
|
|
26
|
+
#startTime = 0
|
|
27
|
+
#spinnerMessage = ''
|
|
28
|
+
|
|
29
|
+
constructor(config) {
|
|
30
|
+
this.sourceDir = config.sourceDir
|
|
31
|
+
this.targetDir = config.targetDir
|
|
32
|
+
this.metaFilePath = config.metaFilePath
|
|
33
|
+
this.sequence = config.sequence
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get metaFilePath() {
|
|
37
|
+
return this._metaFilePath
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
set metaFilePath(value) {
|
|
41
|
+
value = value.trim()
|
|
42
|
+
if (value === '') {
|
|
43
|
+
throw 'The file path cannot be empty'
|
|
44
|
+
}
|
|
45
|
+
this._metaFilePath = value
|
|
46
|
+
this.#fileName.fullName = fileUtils.fileInfo(value).filename
|
|
47
|
+
this.#fileName.shortName = fileUtils.fileInfo(value).filename.replace('.permissionset-meta.xml', '')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
split() {
|
|
51
|
+
const that = this
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
|
|
54
|
+
if (!that.#fileName || !that.sourceDir || !that.targetDir || !that.metaFilePath) {
|
|
55
|
+
global.logger.error('Invalid information passed to split')
|
|
56
|
+
process.exit(1)
|
|
57
|
+
}
|
|
58
|
+
if (!fileUtils.fileExists(that.metaFilePath)) {
|
|
59
|
+
global.logger.error(`file not found: ${that.metaFilePath}`)
|
|
60
|
+
process.exit(1)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
that.targetDir = path.join(that.targetDir, that.#fileName.shortName)
|
|
64
|
+
let parser = new Parser()
|
|
65
|
+
const getJSON = new Promise((resolve, reject) => {
|
|
66
|
+
readFile(that.metaFilePath, function (err, data) {
|
|
67
|
+
parser.parseString(data, function (err, result) {
|
|
68
|
+
if (result) {
|
|
69
|
+
resolve(result)
|
|
70
|
+
} else {
|
|
71
|
+
global.logger.error(`error converting xml to json: ${that.metaFilePath}`)
|
|
72
|
+
process.exit(1)
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
getJSON.then((result) => {
|
|
78
|
+
// modify the json to remove unwanted arrays
|
|
79
|
+
delete result.PermissionSet['$']
|
|
80
|
+
let jsonString = JSON.stringify(result, (name, value) => {
|
|
81
|
+
if (name == '' || !isNaN(name) || permsetDefinition.directories.includes(name) || permsetDefinition.singleFiles.includes(name)) {
|
|
82
|
+
return value
|
|
83
|
+
} else {
|
|
84
|
+
return xml2json(value)
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
that.#json = JSON.parse(jsonString)
|
|
88
|
+
|
|
89
|
+
Object.keys(that.#json.PermissionSet).forEach(key => {
|
|
90
|
+
const keyOrder = permsetDefinition.keyOrder[key]
|
|
91
|
+
const sortKey = permsetDefinition.sortKeys[key]
|
|
92
|
+
|
|
93
|
+
if (Array.isArray(that.#json.PermissionSet[key])) {
|
|
94
|
+
// sort json to order by sortKey
|
|
95
|
+
that.#json.PermissionSet[key].sort((a, b) => {
|
|
96
|
+
if (a[sortKey] < b[sortKey]) {
|
|
97
|
+
return -1;
|
|
98
|
+
}
|
|
99
|
+
if (a[sortKey] > b[sortKey]) {
|
|
100
|
+
return 1;
|
|
101
|
+
}
|
|
102
|
+
return 0;
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
// sort json keys in specified order
|
|
106
|
+
that.#json.PermissionSet[key].forEach(function (part, index) {
|
|
107
|
+
this[index] = Object.keys(this[index])
|
|
108
|
+
.sort((a, b) => {
|
|
109
|
+
if (keyOrder.indexOf(a) < keyOrder.indexOf(b)) return -1
|
|
110
|
+
if (keyOrder.indexOf(a) > keyOrder.indexOf(b)) return 1
|
|
111
|
+
return 0
|
|
112
|
+
})
|
|
113
|
+
.reduce((accumulator, key) => {
|
|
114
|
+
accumulator[key] = this[index][key]
|
|
115
|
+
return accumulator
|
|
116
|
+
}, {})
|
|
117
|
+
}, that.#json.PermissionSet[key])
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
processFile(that)
|
|
122
|
+
completeFile(that)
|
|
123
|
+
resolve(true)
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
function nextFrame(that) {
|
|
128
|
+
return spinner.frames[that.#index = ++that.#index % spinner.frames.length]
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function completeFile(that) {
|
|
132
|
+
let executionTime = getTimeDiff(BigInt(that.#startTime))
|
|
133
|
+
let durationMessage = `${executionTime.seconds}.${executionTime.milliseconds}s`
|
|
134
|
+
let stateIcon = (that.#errorMessage == '') ? global.statusLevel.success : global.statusLevel.fail
|
|
135
|
+
logUpdate(that.#spinnerMessage
|
|
136
|
+
.replace('[%1]', that.sequence.toString().padStart(global.processed.total.toString().length, ' '))
|
|
137
|
+
.replace('[%2]', `. Processed in ${durationMessage}.`)
|
|
138
|
+
.replace('[%3]', `${that.#errorMessage}`)
|
|
139
|
+
.replace('[%4]', `${stateIcon} `)
|
|
140
|
+
)
|
|
141
|
+
logUpdate.done()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function processFile(that) {
|
|
145
|
+
that.#startTime = process.hrtime.bigint()
|
|
146
|
+
that.#spinnerMessage = `[%1] of ${global.processed.total} - Permission Set: [%4]${chalk.yellowBright(that.#fileName.shortName)}[%2][%3]`
|
|
147
|
+
|
|
148
|
+
fileUtils.deleteDirectory(that.targetDir, true) // recursive delete existing directory
|
|
149
|
+
fileUtils.createDirectory(that.targetDir) // create directory
|
|
150
|
+
|
|
151
|
+
Main(that)
|
|
152
|
+
|
|
153
|
+
Object.keys(that.#json.PermissionSet).forEach(key => {
|
|
154
|
+
that.sequence = global.processed.current
|
|
155
|
+
logUpdate(that.#spinnerMessage
|
|
156
|
+
.replace('[%1]', that.sequence.toString().padStart(global.processed.total.toString().length, ' '))
|
|
157
|
+
.replace('[%2]', `\n${chalk.magentaBright(nextFrame(that))} ${key}`)
|
|
158
|
+
.replace('[%3]', `${that.#errorMessage}`)
|
|
159
|
+
.replace('[%4]', `${global.statusLevel.working} `)
|
|
160
|
+
)
|
|
161
|
+
if (permsetDefinition.directories.includes(key)) {
|
|
162
|
+
processDirectory(that, key)
|
|
163
|
+
} else if (permsetDefinition.singleFiles.includes(key)) {
|
|
164
|
+
singleFile(that, key)
|
|
165
|
+
} else {
|
|
166
|
+
if (!permsetDefinition.ignore.includes(key) && !permsetDefinition.main.includes(key)) {
|
|
167
|
+
that.#errorMessage += `\n${global.statusLevel.warn} Not processed: ${key}`
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
return true
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function Main(that) {
|
|
176
|
+
let fileName = path.join(that.targetDir, `main.${global.format}`)
|
|
177
|
+
let mainInfo = {}
|
|
178
|
+
mainInfo.name = that.#fileName.shortName
|
|
179
|
+
permsetDefinition.main.forEach(key => {
|
|
180
|
+
if (that.#json.PermissionSet[key] !== undefined) {
|
|
181
|
+
mainInfo[key] = that.#json.PermissionSet[key]
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
let jsonString = JSON.stringify(mainInfo, null, '\t')
|
|
186
|
+
switch (global.format) {
|
|
187
|
+
case 'json':
|
|
188
|
+
fs.writeFileSync(fileName, jsonString)
|
|
189
|
+
break
|
|
190
|
+
case 'yaml':
|
|
191
|
+
let doc = yaml.dump(JSON.parse(jsonString))
|
|
192
|
+
fs.writeFileSync(fileName, doc)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function processDirectory(that, key) {
|
|
197
|
+
const objects = {}
|
|
198
|
+
const myKey = permsetDefinition.sortKeys[key]
|
|
199
|
+
const hasObject = that.#json.PermissionSet[key][0][myKey].split('.').length == 2
|
|
200
|
+
fileUtils.createDirectory(path.join(that.targetDir, key)) // create directory
|
|
201
|
+
|
|
202
|
+
// populate objects with data per object
|
|
203
|
+
if (hasObject) {
|
|
204
|
+
that.#json.PermissionSet[key].forEach(element => {
|
|
205
|
+
let [object] = element[myKey].toString().split('.')
|
|
206
|
+
if (objects[object] === undefined) {
|
|
207
|
+
objects[object] = {
|
|
208
|
+
object: object
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (objects[object][key] === undefined) {
|
|
212
|
+
objects[object][key] = []
|
|
213
|
+
}
|
|
214
|
+
element[myKey] = element[myKey].replace(`${object}.`, '')
|
|
215
|
+
objects[object][key].push(element)
|
|
216
|
+
})
|
|
217
|
+
} else {
|
|
218
|
+
that.#json.PermissionSet[key].forEach(element => {
|
|
219
|
+
let object = element[myKey]
|
|
220
|
+
if (objects[object] === undefined) {
|
|
221
|
+
objects[object] = {
|
|
222
|
+
object: object
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (objects[object][key] === undefined) {
|
|
226
|
+
objects[object][key] = {}
|
|
227
|
+
}
|
|
228
|
+
delete element[myKey]
|
|
229
|
+
|
|
230
|
+
Object.keys(element).forEach(elemKey => {
|
|
231
|
+
objects[object][key][elemKey] = element[elemKey]
|
|
232
|
+
})
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
Object.keys(objects).forEach(object => {
|
|
237
|
+
let fileName = path.join(that.targetDir, key, `${object}.${global.format}`)
|
|
238
|
+
|
|
239
|
+
let jsonString = JSON.stringify(objects[object], null, '\t')
|
|
240
|
+
switch (global.format) {
|
|
241
|
+
case 'json':
|
|
242
|
+
fs.writeFileSync(fileName, jsonString)
|
|
243
|
+
break
|
|
244
|
+
case 'yaml':
|
|
245
|
+
let doc = yaml.dump(JSON.parse(jsonString))
|
|
246
|
+
fs.writeFileSync(fileName, doc)
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function singleFile(that, key) {
|
|
252
|
+
let fileName = path.join(that.targetDir, `${key}.${global.format}`)
|
|
253
|
+
let currentJSON = {}
|
|
254
|
+
currentJSON[key] = that.#json.PermissionSet[key]
|
|
255
|
+
|
|
256
|
+
let jsonString = JSON.stringify(currentJSON, null, '\t')
|
|
257
|
+
switch (global.format) {
|
|
258
|
+
case 'json':
|
|
259
|
+
fs.writeFileSync(fileName, jsonString)
|
|
260
|
+
break
|
|
261
|
+
case 'yaml':
|
|
262
|
+
let doc = yaml.dump(JSON.parse(jsonString))
|
|
263
|
+
fs.writeFileSync(fileName, doc)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function getTimeDiff(startTime, endTime = process.hrtime.bigint()) {
|
|
270
|
+
const diff = BigInt(endTime) - BigInt(startTime)
|
|
271
|
+
let executionTime = convertHrtime(diff)
|
|
272
|
+
executionTime.seconds = Math.round(executionTime.seconds)
|
|
273
|
+
executionTime.milliseconds = Math.round(executionTime.milliseconds / 1000)
|
|
274
|
+
if (executionTime.milliseconds == 0 && executionTime.nanoseconds > 0) executionTime.milliseconds = 1
|
|
275
|
+
return executionTime
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function xml2json(currentValue) {
|
|
279
|
+
if (Array.isArray(currentValue)) {
|
|
280
|
+
if (currentValue.length == 1) {
|
|
281
|
+
currentValue = currentValue[0].toString().trim()
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (currentValue == 'true') currentValue = true
|
|
285
|
+
if (currentValue == 'false') currentValue = false
|
|
286
|
+
return currentValue
|
|
287
|
+
}
|