@ds-sfdc/sfparty 1.0.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.
@@ -0,0 +1,75 @@
1
+ const options = {
2
+ type: {
3
+ demand: false,
4
+ alias: 'type',
5
+ description: 'type of metadata to $1',
6
+ demandOption: false,
7
+ type: 'string',
8
+ },
9
+ format: {
10
+ demand: true,
11
+ alias: 'format',
12
+ default: 'yaml',
13
+ description: 'type of output',
14
+ demandOption: true,
15
+ type: 'string',
16
+ },
17
+ name: {
18
+ alias: 'n',
19
+ description: 'name of metadata file to $1',
20
+ demandOption: false,
21
+ type: 'string',
22
+ },
23
+ source: {
24
+ demand: false,
25
+ alias: 's',
26
+ description: 'package directory path specified in sfdx-project.json',
27
+ type: 'string',
28
+ },
29
+ target: {
30
+ demand: false,
31
+ alias: 't',
32
+ description: 'target path to directory to create yaml/json files',
33
+ type: 'string',
34
+ }
35
+ }
36
+
37
+ function getOptions(type) {
38
+ let optionObj = {... options}
39
+ Object.keys(optionObj).forEach(key => {
40
+ Object.keys(optionObj[key]).forEach(subKey => {
41
+ if (typeof optionObj[key][subKey] == 'string') {
42
+ optionObj[key][subKey] = optionObj[key][subKey].replaceAll('$1', type)
43
+ }
44
+ })
45
+ })
46
+
47
+ return optionObj
48
+ }
49
+
50
+ export const splitOptions = getOptions('split')
51
+ export const combineOptions = getOptions('combine')
52
+
53
+ const examples = [
54
+ ['$0 $1'],
55
+ ['$0 $1 --type=profile'],
56
+ ['$0 $1 --type="profile,label"'],
57
+ ['$0 $1 --type=permset --name="Permission Set Name"'],
58
+ ['--source=packageDir --target=dir/dir'],
59
+ ['name portion of file: [name].profile-meta.xml'],
60
+ ['Example: --name="Admin" for Admin.profile-meta.xml'],
61
+ ['\nCommands not supporting name or all parameters:'],
62
+ ['$0 $1 --type=label'],
63
+ ]
64
+
65
+ function getExamples(type) {
66
+ let exArr = [...examples]
67
+ exArr.forEach(arrItem => {
68
+ arrItem[0] = arrItem[0].replaceAll('$1', type)
69
+ })
70
+
71
+ return exArr
72
+ }
73
+
74
+ export const splitExamples = getExamples('split')
75
+ export const combineExamples = getExamples('combine')
@@ -8,6 +8,12 @@ import * as xml2js from 'xml2js'
8
8
  import * as fileUtils from '../lib/fileUtils.js'
9
9
 
10
10
  const spinner = cliSpinners['dots']
11
+ const processed = {
12
+ total: 0,
13
+ errors: 0,
14
+ current: 0,
15
+ type: undefined,
16
+ }
11
17
 
12
18
  export class Combine {
13
19
  #type = undefined
@@ -33,6 +39,7 @@ export class Combine {
33
39
  this.targetDir = config.targetDir
34
40
  this.metaDir = config.metaDir
35
41
  this.sequence = config.sequence
42
+ this.total = config.total
36
43
  }
37
44
 
38
45
  get metadataDefinition() {
@@ -102,13 +109,19 @@ export class Combine {
102
109
  })
103
110
 
104
111
  function getXML(that) {
112
+ if (processed.type != that.#root) {
113
+ processed.current = 0
114
+ processed.type = that.#root
115
+ }
116
+ processed.current++
117
+
105
118
  that.#startTime = process.hrtime.bigint()
106
- that.#spinnerMessage = `[%1] of ${global.processed.total} - ${that.#root}: [%4]${chalk.yellowBright('[%5]')}[%2][%3]`
119
+ that.#spinnerMessage = `[%1] of ${that.total} - ${that.#root}: [%4]${chalk.yellowBright('[%5]')}[%2][%3]`
107
120
 
108
121
  that.#types.forEach(key => {
109
122
  // display message
110
123
  logUpdate(that.#spinnerMessage
111
- .replace('[%1]', that.sequence.toString().padStart(global.processed.total.toString().length, ' '))
124
+ .replace('[%1]', that.sequence.toString().padStart(that.total.toString().length, ' '))
112
125
  .replace('[%2]', `\n${chalk.magentaBright(nextFrame(that))} ${key}`)
113
126
  .replace('[%3]', `${that.#errorMessage}`)
114
127
  .replace('[%4]', `${global.icons.working} `)
@@ -157,7 +170,7 @@ export class Combine {
157
170
  // iterate over fileList
158
171
  fileList.forEach((file, index) => {
159
172
  logUpdate(that.#spinnerMessage
160
- .replace('[%1]', that.sequence.toString().padStart(global.processed.total.toString().length, ' '))
173
+ .replace('[%1]', that.sequence.toString().padStart(that.total.toString().length, ' '))
161
174
  .replace('[%2]', `\n${chalk.magentaBright(nextFrame(that))} ${key} - ${index + 1} of ${fileList.length} - ${chalk.magentaBright(file)}`)
162
175
  .replace('[%3]', `${that.#errorMessage}`)
163
176
  .replace('[%4]', `${global.icons.working} `)
@@ -292,7 +305,7 @@ export class Combine {
292
305
  let stateIcon = (that.#errorMessage == '') ? global.icons.success : global.icons.fail
293
306
 
294
307
  logUpdate(that.#spinnerMessage
295
- .replace('[%1]', that.sequence.toString().padStart(global.processed.total.toString().length, ' '))
308
+ .replace('[%1]', that.sequence.toString().padStart(that.total.toString().length, ' '))
296
309
  .replace('[%2]', `. Processed in ${durationMessage}.`)
297
310
  .replace('[%3]', `${that.#errorMessage}`)
298
311
  .replace('[%4]', `${stateIcon} `)
@@ -12,6 +12,12 @@ import cliSpinners from 'cli-spinners'
12
12
  import * as fileUtils from '../lib/fileUtils.js'
13
13
 
14
14
  const spinner = cliSpinners['dots']
15
+ const processed = {
16
+ total: 0,
17
+ errors: 0,
18
+ current: 0,
19
+ type: undefined,
20
+ }
15
21
 
16
22
  export class Split {
17
23
  #type = undefined
@@ -33,6 +39,7 @@ export class Split {
33
39
  this.targetDir = config.targetDir
34
40
  this.metaFilePath = config.metaFilePath
35
41
  this.sequence = config.sequence
42
+ this.total = config.total
36
43
  }
37
44
 
38
45
  get metadataDefinition() {
@@ -117,13 +124,18 @@ export class Split {
117
124
  })
118
125
 
119
126
  function processJSON(that, json, baseDir) {
120
- that.#spinnerMessage = `[%1] of ${global.processed.total} - Workflow: [%4]${chalk.yellowBright(that.#fileName.shortName)}[%2][%3]`
127
+ that.#spinnerMessage = `[%1] of ${that.total} - ${that.#root}: [%4]${chalk.yellowBright(that.#fileName.shortName)}[%2][%3]`
121
128
 
122
129
  let targetDir = baseDir
130
+ if (processed.type != that.#root) {
131
+ processed.current = 0
132
+ processed.type = that.#root
133
+ }
134
+ processed.current++
123
135
  Object.keys(json).forEach(key => {
124
- that.sequence = global.processed.current
136
+ that.sequence = processed.current
125
137
  logUpdate(that.#spinnerMessage
126
- .replace('[%1]', that.sequence.toString().padStart(global.processed.total.toString().length, ' '))
138
+ .replace('[%1]', that.sequence.toString().padStart(that.total.toString().length, ' '))
127
139
  .replace('[%2]', `\n${chalk.magentaBright(nextFrame(that))} ${key}`)
128
140
  .replace('[%3]', `${that.#errorMessage}`)
129
141
  .replace('[%4]', `${global.icons.working} `)
@@ -157,9 +169,9 @@ export class Split {
157
169
  }
158
170
  mainInfo.main.name = that.#fileName.shortName
159
171
  that.metadataDefinition.main.forEach(key => {
160
- that.sequence = global.processed.current
172
+ that.sequence = processed.current
161
173
  logUpdate(that.#spinnerMessage
162
- .replace('[%1]', that.sequence.toString().padStart(global.processed.total.toString().length, ' '))
174
+ .replace('[%1]', that.sequence.toString().padStart(that.total.toString().length, ' '))
163
175
  .replace('[%2]', `\n${chalk.magentaBright(nextFrame(that))} ${key}`)
164
176
  .replace('[%3]', `${that.#errorMessage}`)
165
177
  .replace('[%4]', `${global.icons.working} `)
@@ -182,7 +194,7 @@ export class Split {
182
194
  let durationMessage = `${executionTime.seconds}.${executionTime.milliseconds}s`
183
195
  let stateIcon = (that.#errorMessage == '') ? global.icons.success : global.icons.fail
184
196
  logUpdate(that.#spinnerMessage
185
- .replace('[%1]', that.sequence.toString().padStart(global.processed.total.toString().length, ' '))
197
+ .replace('[%1]', that.sequence.toString().padStart(that.total.toString().length, ' '))
186
198
  .replace('[%2]', `. Processed in ${durationMessage}.`)
187
199
  .replace('[%3]', `${that.#errorMessage}`)
188
200
  .replace('[%4]', `${stateIcon} `)
@@ -1,286 +0,0 @@
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 '../../meta/PermissionSets.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.icons.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.icons.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.icons.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.icons.success : global.icons.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
- }