@ds-sfdc/sfparty 0.0.0 → 1.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/.husky/commit-msg +4 -0
- package/commitlint.config.cjs +1 -0
- package/package.json +10 -7
- package/{index.js → src/index.js} +125 -186
- package/{lib → src/lib}/fileUtils.js +18 -10
- package/{lib → src/lib}/permset/combine.js +5 -5
- package/{lib → src/lib}/permset/split.js +4 -4
- package/{lib → src/lib}/profile/combine.js +5 -5
- package/{lib → src/lib}/profile/split.js +1 -1
- package/{lib/label/definition.js → src/meta/CustomLabels.js} +12 -1
- package/{lib/permset/definition.js → src/meta/PermissionSets.js} +10 -17
- package/{lib/profile/definition.js → src/meta/Profiles.js} +32 -5
- package/{lib/workflow/definition.js → src/meta/Workflows.js} +1 -0
- package/{lib/workflow → src/party}/combine.js +100 -42
- package/{lib/workflow → src/party}/split.js +124 -69
- package/lib/label/combine.js +0 -203
- package/lib/label/split.js +0 -213
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
export const
|
|
1
|
+
export const metadataDefinition = {
|
|
2
2
|
metaUrl: 'https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_customlabels.htm',
|
|
3
|
+
directory: 'labels',
|
|
4
|
+
filetype: 'labels',
|
|
5
|
+
root: 'CustomLabels',
|
|
6
|
+
main: [
|
|
7
|
+
'$',
|
|
8
|
+
],
|
|
9
|
+
singleFiles: [
|
|
10
|
+
],
|
|
3
11
|
directories: [
|
|
4
12
|
'labels',
|
|
5
13
|
],
|
|
@@ -9,4 +17,7 @@ export const labelDefinition = {
|
|
|
9
17
|
keyOrder: {
|
|
10
18
|
labels: ['fullName', 'shortDescription', 'categories', 'protected', 'language', 'value'],
|
|
11
19
|
},
|
|
20
|
+
xmlOrder: {
|
|
21
|
+
labels: ['fullName'],
|
|
22
|
+
}
|
|
12
23
|
}
|
|
@@ -1,43 +1,41 @@
|
|
|
1
|
-
export const
|
|
1
|
+
export const metadataDefinition = {
|
|
2
2
|
metaUrl: 'https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_permissionset.htm',
|
|
3
|
+
directory: 'permissionsets',
|
|
4
|
+
filetype: 'permissionset',
|
|
5
|
+
root: 'PermissionSet',
|
|
3
6
|
main: [
|
|
4
7
|
'label',
|
|
5
8
|
'description',
|
|
6
9
|
'custom',
|
|
7
10
|
'hasActivationRequired',
|
|
8
11
|
'license',
|
|
9
|
-
'userLicense', // Replaced by license
|
|
12
|
+
'userLicense', // Replaced by license
|
|
13
|
+
'$',
|
|
10
14
|
],
|
|
11
15
|
singleFiles: [
|
|
12
16
|
'applicationVisibilities',
|
|
13
|
-
// 'categoryGroupVisibilities', // PROFILE ONLY
|
|
14
17
|
'classAccesses',
|
|
15
18
|
'customMetadataTypeAccesses',
|
|
16
19
|
'customPermissions',
|
|
17
20
|
'customSettingAccesses',
|
|
18
21
|
'externalDataSourceAccesses',
|
|
19
22
|
'flowAccesses',
|
|
20
|
-
// 'layoutAssignments', // PROFILE ONLY
|
|
21
|
-
// 'loginHours', // PROFILE ONLY
|
|
22
|
-
// 'loginIpRanges', // PROFILE ONLY
|
|
23
23
|
'pageAccesses',
|
|
24
|
-
// 'profileActionOverrides', // PROFILE ONLY
|
|
25
24
|
'tabSettings',
|
|
26
|
-
// 'tabVisibilities', // PROFILE ONLY
|
|
27
25
|
'userPermissions',
|
|
28
26
|
],
|
|
29
27
|
directories: [
|
|
30
28
|
'fieldPermissions',
|
|
31
|
-
// 'loginFlows', // PROFILE ONLY
|
|
32
29
|
'objectPermissions',
|
|
33
30
|
'recordTypeVisibilities',
|
|
34
31
|
],
|
|
35
|
-
|
|
36
|
-
'
|
|
32
|
+
splitObjects: [
|
|
33
|
+
'fieldPermissions',
|
|
34
|
+
'objectPermissions',
|
|
35
|
+
'recordTypeVisibilities',
|
|
37
36
|
],
|
|
38
37
|
sortKeys: {
|
|
39
38
|
'applicationVisibilities': 'application',
|
|
40
|
-
// 'categoryGroupVisibilities': 'dataCategoryGroup', // PROFILE ONLY
|
|
41
39
|
'classAccesses': 'apexClass',
|
|
42
40
|
'customMetadataTypeAccesses': 'name',
|
|
43
41
|
'customPermissions': 'name',
|
|
@@ -45,15 +43,10 @@ export const permsetDefinition = {
|
|
|
45
43
|
'externalDataSourceAccesses': 'externalDataSource',
|
|
46
44
|
'fieldPermissions': 'field',
|
|
47
45
|
'flowAccesses': 'flow',
|
|
48
|
-
// 'layoutAssignments': 'layout', // PROFILE ONLY
|
|
49
|
-
// 'loginFlows': 'friendlyName', // PROFILE ONLY
|
|
50
|
-
// 'loginIpRanges': 'startAddress', // PROFILE ONLY
|
|
51
46
|
'objectPermissions': 'object',
|
|
52
47
|
'pageAccesses': 'apexPage',
|
|
53
|
-
// 'profileActionOverrides': 'pageOrSobjectType', // PROFILE ONLY
|
|
54
48
|
'recordTypeVisibilities': 'recordType',
|
|
55
49
|
'tabSettings': 'tab',
|
|
56
|
-
// 'tabVisibilities': 'tab', // PROFILE ONLY
|
|
57
50
|
'userPermissions': 'name',
|
|
58
51
|
},
|
|
59
52
|
keyOrder: {
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
export const
|
|
1
|
+
export const metadataDefinition = {
|
|
2
2
|
metaUrl: 'https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_profile.htm',
|
|
3
|
+
directory: 'profiles',
|
|
4
|
+
filetype: 'profile',
|
|
5
|
+
root: 'Profile',
|
|
3
6
|
main: [
|
|
7
|
+
'fullName',
|
|
4
8
|
'custom',
|
|
5
9
|
'description',
|
|
6
|
-
'fullName',
|
|
7
10
|
'userLicense',
|
|
11
|
+
'$',
|
|
8
12
|
],
|
|
9
13
|
singleFiles: [
|
|
10
14
|
'applicationVisibilities',
|
|
@@ -29,8 +33,10 @@ export const profileDefinition = {
|
|
|
29
33
|
'objectPermissions',
|
|
30
34
|
'recordTypeVisibilities',
|
|
31
35
|
],
|
|
32
|
-
|
|
33
|
-
'
|
|
36
|
+
splitObjects: [
|
|
37
|
+
'fieldPermissions',
|
|
38
|
+
'objectPermissions',
|
|
39
|
+
'recordTypeVisibilities',
|
|
34
40
|
],
|
|
35
41
|
sortKeys: {
|
|
36
42
|
'applicationVisibilities': 'application',
|
|
@@ -51,5 +57,26 @@ export const profileDefinition = {
|
|
|
51
57
|
'recordTypeVisibilities': 'recordType',
|
|
52
58
|
'tabVisibilities': 'tab',
|
|
53
59
|
'userPermissions': 'name',
|
|
54
|
-
}
|
|
60
|
+
},
|
|
61
|
+
keyOrder: {
|
|
62
|
+
applicationVisibilities: ['application', 'visible'],
|
|
63
|
+
categoryGroupVisibilities: ['dataCategoryGroup'], // TODO
|
|
64
|
+
classAccesses: ['apexClass', 'enabled'],
|
|
65
|
+
customMetadataTypeAccesses: ['name', 'enabled'],
|
|
66
|
+
customPermissions: ['name', 'enabled'],
|
|
67
|
+
customSettingAccesses: ['name', 'enabled'],
|
|
68
|
+
externalDataSourceAccesses: ['externalDataSource', 'enabled'],
|
|
69
|
+
fieldPermissions: ['field', 'editable', 'readable'],
|
|
70
|
+
flowAccesses: ['flow', 'enabled'],
|
|
71
|
+
layoutAssignments: ['layout'], // TODO
|
|
72
|
+
loginFlows: ['friendlyName'], // TODO
|
|
73
|
+
loginIpRanges: ['startAddress'], // TODO
|
|
74
|
+
objectPermissions: ['object', 'allowCreate', 'allowRead', 'allowEdit', 'allowDelete', 'viewAllRecords', 'modifyAllRecords'],
|
|
75
|
+
pageAccesses: ['apexPage', 'enabled'],
|
|
76
|
+
profileActionOverrides: ['pageOrSobjectType'], // TODO
|
|
77
|
+
recordTypeVisibilities: ['recordType', 'visible'],
|
|
78
|
+
tabVisibilities: ['tab', 'visibility'],
|
|
79
|
+
userPermissions: ['name', 'enabled'],
|
|
80
|
+
},
|
|
81
|
+
xmlFirst: 'fullName'
|
|
55
82
|
}
|
|
@@ -3,8 +3,9 @@ import logUpdate from 'log-update'
|
|
|
3
3
|
import chalk from 'chalk'
|
|
4
4
|
import convertHrtime from 'convert-hrtime'
|
|
5
5
|
import cliSpinners from 'cli-spinners'
|
|
6
|
+
import os from 'node:os'
|
|
6
7
|
import * as xml2js from 'xml2js'
|
|
7
|
-
import * as fileUtils from '../fileUtils.js'
|
|
8
|
+
import * as fileUtils from '../lib/fileUtils.js'
|
|
8
9
|
|
|
9
10
|
const spinner = cliSpinners['dots']
|
|
10
11
|
|
|
@@ -73,18 +74,18 @@ export class Combine {
|
|
|
73
74
|
return new Promise((resolve, reject) => {
|
|
74
75
|
const that = this
|
|
75
76
|
if (!fileUtils.directoryExists(that.sourceDir)) reject(`Path does not exist: ${that.sourceDir}`)
|
|
77
|
+
let types = ['directories', 'singleFiles', 'main']
|
|
78
|
+
types.forEach(type => {
|
|
79
|
+
if (that.metadataDefinition[type] !== undefined) {
|
|
80
|
+
that.#types = that.#types.concat(that.metadataDefinition[type])
|
|
81
|
+
}
|
|
82
|
+
})
|
|
76
83
|
|
|
77
|
-
if (that.metadataDefinition.directories !== undefined) {
|
|
78
|
-
that.#types = that.#types.concat(that.metadataDefinition.directories)
|
|
79
|
-
}
|
|
80
|
-
if (that.metadataDefinition.singleFiles !== undefined) {
|
|
81
|
-
that.#types = that.#types.concat(that.metadataDefinition.singleFiles)
|
|
82
|
-
}
|
|
83
|
-
if (that.metadataDefinition.main !== undefined) {
|
|
84
|
-
that.#types = that.#types.concat(that.metadataDefinition.main)
|
|
85
|
-
}
|
|
86
84
|
that.#types.sort((a, b) => {
|
|
87
85
|
if (a == '$') return -1
|
|
86
|
+
if (that.metadataDefinition.xmlFirst !== undefined) {
|
|
87
|
+
if (a == that.metadataDefinition.xmlFirst) return -1
|
|
88
|
+
}
|
|
88
89
|
if (a < b) return -1
|
|
89
90
|
if (a > b) return 1
|
|
90
91
|
return 0
|
|
@@ -110,7 +111,7 @@ export class Combine {
|
|
|
110
111
|
.replace('[%1]', that.sequence.toString().padStart(global.processed.total.toString().length, ' '))
|
|
111
112
|
.replace('[%2]', `\n${chalk.magentaBright(nextFrame(that))} ${key}`)
|
|
112
113
|
.replace('[%3]', `${that.#errorMessage}`)
|
|
113
|
-
.replace('[%4]', `${global.
|
|
114
|
+
.replace('[%4]', `${global.icons.working} `)
|
|
114
115
|
.replace('[%5]', `${that.#fileName.shortName} `)
|
|
115
116
|
)
|
|
116
117
|
|
|
@@ -121,9 +122,12 @@ export class Combine {
|
|
|
121
122
|
shortName: 'Main',
|
|
122
123
|
fullName: path.join(that.sourceDir, that.metaDir, `main.${global.format}`),
|
|
123
124
|
}
|
|
124
|
-
processFile(that, key, fileObj)
|
|
125
|
+
processFile(that, key, fileObj, 'main')
|
|
126
|
+
if (that.#json.$ === undefined) {
|
|
127
|
+
that.#json.$ = { xmlns: 'https://soap.sforce.com/2006/04/metadata' }
|
|
128
|
+
}
|
|
125
129
|
} else if (that.metadataDefinition.singleFiles.includes(key)) {
|
|
126
|
-
|
|
130
|
+
processSingleFile(that, key)
|
|
127
131
|
} else if (that.metadataDefinition.directories.includes(key)) {
|
|
128
132
|
processDirectory(that, key)
|
|
129
133
|
} else {
|
|
@@ -132,6 +136,15 @@ export class Combine {
|
|
|
132
136
|
})
|
|
133
137
|
}
|
|
134
138
|
|
|
139
|
+
function processSingleFile(that, key) {
|
|
140
|
+
const fileObj = {
|
|
141
|
+
shortName: key,
|
|
142
|
+
fullName: path.join(that.sourceDir, that.metaDir, key + `.${global.format}`),
|
|
143
|
+
}
|
|
144
|
+
processFile(that, key, fileObj)
|
|
145
|
+
|
|
146
|
+
}
|
|
147
|
+
|
|
135
148
|
function processDirectory(that, key) {
|
|
136
149
|
// Process the directory sourceDir/metaDir/key
|
|
137
150
|
const currentDir = path.join(that.sourceDir, that.metaDir, key)
|
|
@@ -147,7 +160,7 @@ export class Combine {
|
|
|
147
160
|
.replace('[%1]', that.sequence.toString().padStart(global.processed.total.toString().length, ' '))
|
|
148
161
|
.replace('[%2]', `\n${chalk.magentaBright(nextFrame(that))} ${key} - ${index + 1} of ${fileList.length} - ${chalk.magentaBright(file)}`)
|
|
149
162
|
.replace('[%3]', `${that.#errorMessage}`)
|
|
150
|
-
.replace('[%4]', `${global.
|
|
163
|
+
.replace('[%4]', `${global.icons.working} `)
|
|
151
164
|
.replace('[%5]', `${that.#fileName.shortName} `)
|
|
152
165
|
)
|
|
153
166
|
|
|
@@ -162,38 +175,88 @@ export class Combine {
|
|
|
162
175
|
return true
|
|
163
176
|
}
|
|
164
177
|
|
|
165
|
-
function processFile(that, key, fileObj = undefined) {
|
|
178
|
+
function processFile(that, key, fileObj = undefined, rootKey = undefined) {
|
|
166
179
|
if (
|
|
167
180
|
fileObj === undefined ||
|
|
168
181
|
typeof fileObj != 'object' ||
|
|
169
182
|
fileObj.shortName === undefined ||
|
|
170
183
|
fileObj.fullName === undefined
|
|
171
184
|
) {
|
|
172
|
-
that.#errorMessage += `\n${global.
|
|
185
|
+
that.#errorMessage += `\n${global.icons.warn} Invalid file information passed ${chalk.redBright(fileObj)}`
|
|
173
186
|
return false
|
|
174
187
|
}
|
|
175
188
|
|
|
176
|
-
if (fileUtils.fileExists(fileObj.fullName)) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
189
|
+
if (!fileUtils.fileExists(fileObj.fullName)) {
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let result = fileUtils.readPartFile(fileObj.fullName)
|
|
194
|
+
|
|
195
|
+
// if split by object we need to add object back to values
|
|
196
|
+
if (that.metadataDefinition.splitObjects !== undefined && that.metadataDefinition.splitObjects.includes(key)) {
|
|
197
|
+
result = hydrateObject(that, result, key, fileObj)
|
|
198
|
+
}
|
|
199
|
+
result = sortAndArrange(that, result, key)
|
|
200
|
+
|
|
201
|
+
if (Array.isArray(that.#json[key])) {
|
|
202
|
+
try {
|
|
203
|
+
if (Array.isArray(result[key])) {
|
|
204
|
+
result[key].forEach(arrItem => {
|
|
205
|
+
that.#json[key].push(arrItem)
|
|
206
|
+
})
|
|
207
|
+
} else {
|
|
208
|
+
that.#json[key].push(result[key])
|
|
209
|
+
}
|
|
210
|
+
} catch (error) {
|
|
211
|
+
throw error
|
|
212
|
+
}
|
|
213
|
+
} else {
|
|
214
|
+
try {
|
|
215
|
+
that.#json[key] = (rootKey !== undefined) ? result[rootKey][key] : result[key]
|
|
216
|
+
} catch (error) {
|
|
217
|
+
let test = { key: key, rootKey: rootKey, json: result }
|
|
218
|
+
throw error
|
|
183
219
|
}
|
|
184
220
|
}
|
|
185
221
|
|
|
186
|
-
updateFileStats(that, fileUtils.fileInfo(fileObj.fullName).stats)
|
|
222
|
+
updateFileStats(that, fileObj.fullName, fileUtils.fileInfo(fileObj.fullName).stats)
|
|
187
223
|
// genericXML(that, key, fileObj.fullName)
|
|
188
224
|
}
|
|
189
225
|
|
|
190
|
-
function
|
|
191
|
-
|
|
192
|
-
|
|
226
|
+
function hydrateObject(that, json, key, fileObj) {
|
|
227
|
+
const sortKey = that.metadataDefinition.sortKeys[key]
|
|
228
|
+
let object = json.object
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
json[key].forEach((arrItem) => {
|
|
232
|
+
arrItem[sortKey] = `${object}.${arrItem[sortKey]}`.replace('.undefined', '')
|
|
233
|
+
|
|
234
|
+
// add object key if previously existed
|
|
235
|
+
if (that.metadataDefinition.keyOrder[key] !== undefined && that.metadataDefinition.keyOrder[key].includes('order')) {
|
|
236
|
+
arrItem.object = object
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
// delete object key that we added to the part file
|
|
241
|
+
delete json.object
|
|
242
|
+
} catch (error) {
|
|
243
|
+
throw error
|
|
193
244
|
}
|
|
194
245
|
|
|
195
|
-
|
|
196
|
-
|
|
246
|
+
return json
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function updateFileStats(that, fileName, stats) {
|
|
250
|
+
try {
|
|
251
|
+
if (that.#fileStats.atime === undefined || stats.atime > that.#fileStats.atime) {
|
|
252
|
+
that.#fileStats.atime = stats.atime
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (that.#fileStats.mtime === undefined || stats.mtime > that.#fileStats.mtime) {
|
|
256
|
+
that.#fileStats.mtime = stats.mtime
|
|
257
|
+
}
|
|
258
|
+
} catch (error) {
|
|
259
|
+
throw error
|
|
197
260
|
}
|
|
198
261
|
}
|
|
199
262
|
|
|
@@ -226,7 +289,7 @@ export class Combine {
|
|
|
226
289
|
function finishMessage(that) {
|
|
227
290
|
let executionTime = getTimeDiff(BigInt(that.#startTime))
|
|
228
291
|
let durationMessage = `${executionTime.seconds}.${executionTime.milliseconds}s`
|
|
229
|
-
let stateIcon = (that.#errorMessage == '') ? global.
|
|
292
|
+
let stateIcon = (that.#errorMessage == '') ? global.icons.success : global.icons.fail
|
|
230
293
|
|
|
231
294
|
logUpdate(that.#spinnerMessage
|
|
232
295
|
.replace('[%1]', that.sequence.toString().padStart(global.processed.total.toString().length, ' '))
|
|
@@ -250,7 +313,7 @@ export class Combine {
|
|
|
250
313
|
}
|
|
251
314
|
|
|
252
315
|
function sortJSON(json, key) {
|
|
253
|
-
if (Array.isArray(json)) {
|
|
316
|
+
if (Array.isArray(json) && key !== undefined) {
|
|
254
317
|
json.sort((a, b) => {
|
|
255
318
|
if (a[key] < b[key]) return -1
|
|
256
319
|
if (a[key] > b[key]) return 1
|
|
@@ -263,15 +326,17 @@ function sortJSON(json, key) {
|
|
|
263
326
|
function sortAndArrange(that, json, key = undefined, topLevel = true) {
|
|
264
327
|
// sort and order keys
|
|
265
328
|
const sortKey = that.metadataDefinition.sortKeys[key]
|
|
266
|
-
|
|
329
|
+
|
|
267
330
|
json = arrangeKeys(that, json, key)
|
|
268
331
|
json = sortJSON(json, sortKey)
|
|
269
332
|
|
|
270
333
|
Object.keys(json).forEach((subKey, index, thisObj) => {
|
|
271
334
|
if (typeof json[subKey] == 'object') {
|
|
272
335
|
if (!Array.isArray(json[subKey])) {
|
|
273
|
-
|
|
274
|
-
|
|
336
|
+
if (Object.keys(json[subKey]).length > 1) {
|
|
337
|
+
// call recursively on object
|
|
338
|
+
json[subKey] = sortAndArrange(that, json[subKey], subKey, false)
|
|
339
|
+
}
|
|
275
340
|
} else {
|
|
276
341
|
// iterate array for objects
|
|
277
342
|
json[subKey].forEach((arrItem, index) => {
|
|
@@ -284,14 +349,7 @@ function sortAndArrange(that, json, key = undefined, topLevel = true) {
|
|
|
284
349
|
}
|
|
285
350
|
})
|
|
286
351
|
|
|
287
|
-
|
|
288
|
-
if (topLevel) {
|
|
289
|
-
jsonResult[key] = json
|
|
290
|
-
} else {
|
|
291
|
-
jsonResult = json
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
return jsonResult
|
|
352
|
+
return json
|
|
295
353
|
}
|
|
296
354
|
|
|
297
355
|
function arrangeKeys(that, json, key = undefined) {
|