@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.
@@ -1,5 +1,13 @@
1
- export const labelDefinition = {
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 permsetDefinition = {
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
- ignore: [
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 profileDefinition = {
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
- ignore: [
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
  }
@@ -1,5 +1,6 @@
1
1
  export const metadataDefinition = {
2
2
  metaUrl: 'https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_workflow.htm',
3
+ directory: 'workflows',
3
4
  filetype: 'workflow',
4
5
  root: 'Workflow',
5
6
  main: [
@@ -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.statusLevel.working} `)
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
- // TODO process single file
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.statusLevel.working} `)
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.statusLevel.warn} Invalid file information passed ${chalk.redBright(fileObj)}`
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
- let result = fileUtils.readPartFile(fileObj.fullName)
178
- result = sortAndArrange(that, result, key)
179
- if (Array.isArray(that.#json[key])) {
180
- that.#json[key].push(result[key])
181
- } else {
182
- that.#json[key] = result[key][key][key]
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 updateFileStats(that, stats) {
191
- if (that.#fileStats.atime === undefined || stats.atime > that.#fileStats.atime) {
192
- that.#fileStats.atime = stats.atime
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
- if (that.#fileStats.mtime === undefined || stats.mtime > that.#fileStats.mtime) {
196
- that.#fileStats.mtime = stats.mtime
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.statusLevel.success : global.statusLevel.fail
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
- let jsonResult = {}
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
- // call recursively on object
274
- json[subKey] = sortAndArrange(that, json[subKey], subKey)
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
- // we need to include the key into the json for the top-level only
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) {