mcdev 3.1.1 → 4.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.
Files changed (135) hide show
  1. package/.eslintrc.json +67 -7
  2. package/.github/ISSUE_TEMPLATE/bug.yml +5 -1
  3. package/.github/ISSUE_TEMPLATE/task.md +1 -1
  4. package/.github/PULL_REQUEST_TEMPLATE.md +5 -3
  5. package/.github/dependabot.yml +14 -0
  6. package/.github/workflows/code-analysis.yml +57 -0
  7. package/.husky/commit-msg +10 -0
  8. package/.husky/post-checkout +5 -0
  9. package/.husky/pre-commit +2 -1
  10. package/.prettierrc +8 -0
  11. package/.vscode/settings.json +1 -1
  12. package/LICENSE +2 -2
  13. package/README.md +134 -45
  14. package/boilerplate/config.json +5 -11
  15. package/boilerplate/files/.prettierrc +8 -0
  16. package/boilerplate/files/.vscode/extensions.json +0 -1
  17. package/boilerplate/files/.vscode/settings.json +28 -2
  18. package/boilerplate/files/README.md +2 -2
  19. package/boilerplate/forcedUpdates.json +10 -0
  20. package/boilerplate/npm-dependencies.json +5 -5
  21. package/docs/dist/documentation.md +2795 -1724
  22. package/jsconfig.json +1 -1
  23. package/lib/Builder.js +166 -75
  24. package/lib/Deployer.js +244 -96
  25. package/lib/MetadataTypeDefinitions.js +2 -0
  26. package/lib/MetadataTypeInfo.js +2 -0
  27. package/lib/Retriever.js +61 -84
  28. package/lib/cli.js +133 -25
  29. package/lib/index.js +242 -563
  30. package/lib/metadataTypes/AccountUser.js +101 -95
  31. package/lib/metadataTypes/Asset.js +677 -248
  32. package/lib/metadataTypes/AttributeGroup.js +23 -12
  33. package/lib/metadataTypes/Automation.js +456 -357
  34. package/lib/metadataTypes/Campaign.js +33 -93
  35. package/lib/metadataTypes/ContentArea.js +31 -11
  36. package/lib/metadataTypes/DataExtension.js +391 -376
  37. package/lib/metadataTypes/DataExtensionField.js +131 -54
  38. package/lib/metadataTypes/DataExtensionTemplate.js +22 -4
  39. package/lib/metadataTypes/DataExtract.js +67 -50
  40. package/lib/metadataTypes/DataExtractType.js +14 -8
  41. package/lib/metadataTypes/Discovery.js +21 -16
  42. package/lib/metadataTypes/Email.js +32 -12
  43. package/lib/metadataTypes/EmailSendDefinition.js +85 -80
  44. package/lib/metadataTypes/EventDefinition.js +69 -47
  45. package/lib/metadataTypes/FileTransfer.js +78 -54
  46. package/lib/metadataTypes/Filter.js +11 -4
  47. package/lib/metadataTypes/Folder.js +149 -117
  48. package/lib/metadataTypes/FtpLocation.js +14 -8
  49. package/lib/metadataTypes/ImportFile.js +69 -69
  50. package/lib/metadataTypes/Interaction.js +19 -4
  51. package/lib/metadataTypes/List.js +54 -13
  52. package/lib/metadataTypes/MetadataType.js +687 -479
  53. package/lib/metadataTypes/MobileCode.js +46 -0
  54. package/lib/metadataTypes/MobileKeyword.js +114 -0
  55. package/lib/metadataTypes/Query.js +204 -103
  56. package/lib/metadataTypes/Role.js +76 -61
  57. package/lib/metadataTypes/Script.js +146 -82
  58. package/lib/metadataTypes/SetDefinition.js +20 -8
  59. package/lib/metadataTypes/TriggeredSendDefinition.js +78 -58
  60. package/lib/metadataTypes/definitions/Asset.definition.js +21 -10
  61. package/lib/metadataTypes/definitions/AttributeGroup.definition.js +12 -0
  62. package/lib/metadataTypes/definitions/Automation.definition.js +10 -5
  63. package/lib/metadataTypes/definitions/Campaign.definition.js +44 -1
  64. package/lib/metadataTypes/definitions/DataExtension.definition.js +4 -0
  65. package/lib/metadataTypes/definitions/DataExtensionTemplate.definition.js +6 -0
  66. package/lib/metadataTypes/definitions/DataExtract.definition.js +18 -14
  67. package/lib/metadataTypes/definitions/Discovery.definition.js +12 -0
  68. package/lib/metadataTypes/definitions/EmailSendDefinition.definition.js +4 -0
  69. package/lib/metadataTypes/definitions/EventDefinition.definition.js +22 -0
  70. package/lib/metadataTypes/definitions/FileTransfer.definition.js +4 -0
  71. package/lib/metadataTypes/definitions/Filter.definition.js +4 -0
  72. package/lib/metadataTypes/definitions/Folder.definition.js +6 -0
  73. package/lib/metadataTypes/definitions/FtpLocation.definition.js +4 -0
  74. package/lib/metadataTypes/definitions/ImportFile.definition.js +10 -5
  75. package/lib/metadataTypes/definitions/Interaction.definition.js +4 -0
  76. package/lib/metadataTypes/definitions/MobileCode.definition.js +163 -0
  77. package/lib/metadataTypes/definitions/MobileKeyword.definition.js +253 -0
  78. package/lib/metadataTypes/definitions/Query.definition.js +4 -0
  79. package/lib/metadataTypes/definitions/Role.definition.js +5 -0
  80. package/lib/metadataTypes/definitions/Script.definition.js +4 -0
  81. package/lib/metadataTypes/definitions/SetDefinition.definition.js +28 -0
  82. package/lib/metadataTypes/definitions/TriggeredSendDefinition.definition.js +4 -0
  83. package/lib/retrieveChangelog.js +7 -6
  84. package/lib/util/auth.js +117 -0
  85. package/lib/util/businessUnit.js +55 -66
  86. package/lib/util/cache.js +194 -0
  87. package/lib/util/cli.js +90 -116
  88. package/lib/util/config.js +302 -0
  89. package/lib/util/devops.js +240 -50
  90. package/lib/util/file.js +120 -191
  91. package/lib/util/init.config.js +195 -69
  92. package/lib/util/init.git.js +45 -50
  93. package/lib/util/init.js +72 -59
  94. package/lib/util/init.npm.js +48 -39
  95. package/lib/util/util.js +280 -564
  96. package/package.json +44 -33
  97. package/test/dataExtension.test.js +152 -0
  98. package/test/mockRoot/.mcdev-auth.json +8 -0
  99. package/test/mockRoot/.mcdevrc.json +67 -0
  100. package/test/mockRoot/deploy/testInstance/testBU/dataExtension/childBU_dataextension_test.dataExtension-meta.json +39 -0
  101. package/test/mockRoot/deploy/testInstance/testBU/dataExtension/testDataExtension.dataExtension-meta.json +23 -0
  102. package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.json +11 -0
  103. package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.sql +4 -0
  104. package/test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.json +11 -0
  105. package/test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.sql +4 -0
  106. package/test/query.test.js +149 -0
  107. package/test/resourceFactory.js +142 -0
  108. package/test/resources/1111111/dataFolder/retrieve-response.xml +43 -0
  109. package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json +18 -0
  110. package/test/resources/9999999/automation/v1/queries/get-response.json +24 -0
  111. package/test/resources/9999999/automation/v1/queries/post-response.json +18 -0
  112. package/test/resources/9999999/dataExtension/build-expected.json +51 -0
  113. package/test/resources/9999999/dataExtension/create-expected.json +23 -0
  114. package/test/resources/9999999/dataExtension/create-response.xml +54 -0
  115. package/test/resources/9999999/dataExtension/retrieve-expected.json +51 -0
  116. package/test/resources/9999999/dataExtension/retrieve-response.xml +47 -0
  117. package/test/resources/9999999/dataExtension/template-expected.json +51 -0
  118. package/test/resources/9999999/dataExtension/update-expected.json +55 -0
  119. package/test/resources/9999999/dataExtension/update-response.xml +52 -0
  120. package/test/resources/9999999/dataExtensionField/retrieve-response.xml +93 -0
  121. package/test/resources/9999999/dataExtensionTemplate/retrieve-response.xml +303 -0
  122. package/test/resources/9999999/dataFolder/retrieve-response.xml +65 -0
  123. package/test/resources/9999999/query/build-expected.json +8 -0
  124. package/test/resources/9999999/query/get-expected.json +11 -0
  125. package/test/resources/9999999/query/patch-expected.json +11 -0
  126. package/test/resources/9999999/query/post-expected.json +11 -0
  127. package/test/resources/9999999/query/template-expected.json +8 -0
  128. package/test/resources/auth.json +32 -0
  129. package/test/resources/rest404-response.json +5 -0
  130. package/test/resources/retrieve-response.xml +21 -0
  131. package/test/utils.js +107 -0
  132. package/types/mcdev.d.js +301 -0
  133. package/CHANGELOG.md +0 -126
  134. package/PULL_REQUEST_TEMPLATE.md +0 -19
  135. package/test/util/file.js +0 -51
package/lib/index.js CHANGED
@@ -1,7 +1,10 @@
1
1
  'use strict';
2
2
 
3
+ const TYPE = require('../types/mcdev.d');
3
4
  const Util = require('./util/util');
5
+ const auth = require('./util/auth');
4
6
  const File = require('./util/file');
7
+ const config = require('./util/config');
5
8
  const Init = require('./util/init');
6
9
  const InitGit = require('./util/init.git');
7
10
  const Cli = require('./util/cli');
@@ -12,307 +15,133 @@ const Deployer = require('./Deployer');
12
15
  const MetadataTypeInfo = require('./MetadataTypeInfo');
13
16
  const MetadataTypeDefinitions = require('./MetadataTypeDefinitions');
14
17
  const Retriever = require('./Retriever');
15
- const inquirer = require('inquirer');
16
- let properties;
18
+ const cache = require('./util/cache');
17
19
 
18
20
  /**
19
21
  * main class
20
22
  */
21
23
  class Mcdev {
22
24
  /**
23
- * handler for 'mcdev createDeltaPkg
24
- * @param {Object} argv yargs parameters
25
- * @param {String} [argv.range] git commit range
26
- into deploy directory
27
- * @param {String} [argv.filter] filter file paths that start with any
28
- * @param {Boolean} [argv.skipInteraction] allows to skip interactive wizard
29
- * @returns {void}
30
- */
31
- static async createDeltaPkg(argv) {
32
- this._setLoggingLevel(argv);
33
- properties = properties || File.loadConfigFile();
34
- if (!Util.checkProperties(properties)) {
35
- return null;
36
- }
37
- // get source market and source BU from config
38
- if (argv.filter) {
39
- return DevOps.createDeltaPkg(properties, argv.range, true, argv.filter);
40
- } else {
41
- // If no custom filter was provided, use deployment marketLists & templating
42
-
43
- // check if sourceTargetMapping is valid
44
- if (
45
- !properties.options.deployment.sourceTargetMapping ||
46
- !Object.keys(properties.options.deployment.sourceTargetMapping).length
47
- ) {
48
- Util.logger.error('Bad configuration of options.deployment.sourceTargetMapping');
49
- return;
50
- }
51
- const sourceMarketListArr = Object.keys(
52
- properties.options.deployment.sourceTargetMapping
53
- );
54
-
55
- for (const sourceML of sourceMarketListArr) {
56
- // check if sourceTargetMapping has valid values
57
- // #1 check source marketlist
58
- try {
59
- Builder.verifyMarketList(sourceML, properties);
60
- // remove potentially existing "description"-entry
61
- delete properties.marketList[sourceML].description;
62
-
63
- const sourceMarketBuArr = Object.keys(properties.marketList[sourceML]);
64
- if (sourceMarketBuArr.length !== 1) {
65
- throw new Error('Only 1 BU is allowed per source marketList');
66
- }
67
- if ('string' !== typeof properties.marketList[sourceML][sourceMarketBuArr[0]]) {
68
- throw new Error('Only 1 market per BU is allowed per source marketList');
69
- }
70
- } catch (ex) {
71
- Util.logger.error('Deployment Source: ' + ex.message);
72
- return;
73
- }
74
- // #2 check corresponding target marketList
75
- let targetML;
76
- try {
77
- targetML = properties.options.deployment.sourceTargetMapping[sourceML];
78
- if ('string' !== typeof targetML) {
79
- throw new Error(
80
- 'Please define one target marketList per source in deployment.sourceTargetMapping (No arrays allowed)'
81
- );
82
- }
83
- Builder.verifyMarketList(targetML, properties);
84
- // remove potentially existing "description"-entry
85
- delete properties.marketList[targetML].description;
86
- } catch (ex) {
87
- Util.logger.error('Deployment Target: ' + ex.message);
88
- }
89
- }
90
- // all good let's loop a second time for actual execution
91
- for (const sourceMlName of sourceMarketListArr) {
92
- const targetMlName =
93
- properties.options.deployment.sourceTargetMapping[sourceMlName];
94
- const sourceBU = Object.keys(properties.marketList[sourceMlName])[0];
95
- const sourceMarket = Object.values(properties.marketList[sourceMlName])[0];
96
-
97
- const delta = await DevOps.createDeltaPkg(properties, argv.range, false, sourceBU);
98
- // If only chaing templating and buildDefinition if required
99
- if (!delta || delta.length === 0) {
100
- // info/error messages was printed by DevOps.createDeltaPkg() already
101
- return;
102
- }
103
- Util.logger.info('=============');
104
-
105
- // Put files into maps. One map with BU -> type -> file (for retrieveAsTemplate)
106
- // Other map only with type -> file (for buildDefinitionBulk)
107
- const buTypeDelta = {};
108
- const typeDelta = {};
109
- delta
110
- // Only template/build files that were added/updated/moved. no deletions
111
- // ! doesn't work for folder, because their name parsing doesnt work at the moment
112
- .filter((file) => file.gitAction !== 'delete' && file.name)
113
- .forEach((file) => {
114
- const buPath = `${file._credential}/${file._businessUnit}`;
115
- if (!buTypeDelta[buPath]) {
116
- buTypeDelta[buPath] = {};
117
- }
118
- if (!buTypeDelta[buPath][file.type]) {
119
- buTypeDelta[buPath][file.type] = [];
120
- }
121
- buTypeDelta[buPath][file.type].push(file.name);
122
-
123
- if (!typeDelta[file.type]) {
124
- typeDelta[file.type] = [];
125
- }
126
- typeDelta[file.type].push(file.name);
127
- });
128
-
129
- // Run retrieve as template for each business unit for each type
130
- Util.logger.info('Retrieve template from Git delta');
131
- // ! needs to be for (.. in ..) loop so that it gets executed in series
132
- for (const bu in buTypeDelta) {
133
- for (const type in buTypeDelta[bu]) {
134
- Util.logger.info(
135
- `⚡ mcdev rt ${bu} ${type} "${buTypeDelta[bu][type].join(
136
- ','
137
- )}" ${sourceMarket}`
138
- );
139
- await this.retrieveAsTemplate(
140
- bu,
141
- type,
142
- buTypeDelta[bu][type].join(','),
143
- sourceMarket
144
- );
145
- }
146
- }
147
-
148
- // Run build definitions bulk for each type
149
- Util.logger.info(`- ✔️ Templates created`);
150
- Util.logger.info('=============');
151
- Util.logger.info('Build definitions from delta templates');
152
- if (
153
- properties.directories.templateBuilds == properties.directories.deploy ||
154
- (Array.isArray(properties.directories.templateBuilds) &&
155
- properties.directories.templateBuilds.includes(
156
- properties.directories.deploy
157
- ))
158
- ) {
159
- let responses;
160
- if (!argv.skipInteraction) {
161
- // deploy folder is in targets for definition creation
162
- // recommend to purge their content first
163
- const questions = [
164
- {
165
- type: 'confirm',
166
- name: 'isPurgeDeployFolder',
167
- message:
168
- 'Do you want to empty the deploy folder (ensures no files from previous deployments remain)?',
169
- default: true,
170
- },
171
- ];
172
- responses = await new Promise((resolve) => {
173
- inquirer.prompt(questions).then((answers) => {
174
- resolve(answers);
175
- });
176
- });
177
- }
178
- if (argv.skipInteraction || responses.isPurgeDeployFolders) {
179
- // Clear output folder structure for selected sub-type
180
- File.removeSync(File.normalizePath([properties.directories.deploy]));
181
- }
182
- }
183
- const bdPromises = [];
184
- for (const type in typeDelta) {
185
- Util.logger.info(
186
- `⚡ mcdev bdb ${targetMlName} ${type} "${typeDelta[type].join(',')}"`
187
- );
188
- // omitting "await" to speed up creation
189
- bdPromises.push(
190
- this.buildDefinitionBulk(targetMlName, type, typeDelta[type].join(','))
191
- );
192
- }
193
- await Promise.all(bdPromises);
194
- Util.logger.info(`- ✔️ Deploy defintions created`);
195
- if (
196
- properties.directories.templateBuilds == properties.directories.deploy ||
197
- (Array.isArray(properties.directories.templateBuilds) &&
198
- properties.directories.templateBuilds.includes(
199
- properties.directories.deploy
200
- ))
201
- ) {
202
- Util.logger.info(`You can now run deploy on the prepared BUs`);
203
- } else {
204
- Util.logger.info(
205
- `Your templated defintions are now ready to be copied into the deploy folder. Hint: You can have this auto-copied if you adjust directories.templateBuilds in your config.`
206
- );
207
- }
208
- }
209
- }
25
+ * helper method to use unattended mode when including mcdev as a package
26
+ *
27
+ * @param {boolean | TYPE.skipInteraction} [skipInteraction] signals what to insert automatically for things usually asked via wizard
28
+ * @returns {void}
29
+ */
30
+ static setSkipInteraction(skipInteraction) {
31
+ Util.skipInteraction = skipInteraction;
210
32
  }
211
-
212
33
  /**
213
34
  * configures what is displayed in the console
35
+ *
214
36
  * @param {object} argv list of command line parameters given by user
215
- * @param {Boolean} [argv.silent] only errors printed to CLI
216
- * @param {Boolean} [argv.verbose] chatty user CLI output
217
- * @param {Boolean} [argv.debug] enables developer output & features
37
+ * @param {boolean} [argv.silent] only errors printed to CLI
38
+ * @param {boolean} [argv.verbose] chatty user CLI output
39
+ * @param {boolean} [argv.debug] enables developer output & features
218
40
  * @returns {void}
219
41
  */
220
- static _setLoggingLevel(argv) {
221
- if (argv.silent) {
222
- // only errors printed to CLI
223
- Util.logger.level = 'error';
224
- Util.loggerTransports.console.level = 'error';
225
- } else if (argv.verbose) {
226
- // chatty user cli logs
227
- Util.logger.level = 'verbose';
228
- Util.loggerTransports.console.level = 'verbose';
229
- } else {
230
- // default user cli logs
231
- // TODO to be switched to "warn" when cli-process is integrated
232
- Util.logger.level = 'info';
233
- Util.loggerTransports.console.level = 'info';
234
- }
235
- if (argv.debug) {
236
- // enables developer output & features. no change to actual logs
237
- Util.logger.level = 'debug';
42
+ static setLoggingLevel(argv) {
43
+ Util.setLoggingLevel(argv);
44
+ }
45
+ /**
46
+ * handler for 'mcdev createDeltaPkg
47
+ *
48
+ * @param {object} argv yargs parameters
49
+ * @param {string} [argv.range] git commit range
50
+ into deploy directory
51
+ * @param {string} [argv.filter] filter file paths that start with any
52
+ * @param {TYPE.skipInteraction} [argv.skipInteraction] allows to skip interactive wizard
53
+ * @returns {Promise.<TYPE.DeltaPkgItem[]>} list of changed items
54
+ */
55
+ static async createDeltaPkg(argv) {
56
+ Util.logger.info('Create Delta Package ::');
57
+ Mcdev.setSkipInteraction(argv.skipInteraction);
58
+ const properties = await config.getProperties();
59
+ if (!(await config.checkProperties(properties))) {
60
+ return null;
238
61
  }
62
+
63
+ return argv.filter
64
+ ? // get source market and source BU from config
65
+ DevOps.getDeltaList(properties, argv.range, true, argv.filter)
66
+ : // If no custom filter was provided, use deployment marketLists & templating
67
+ DevOps.buildDeltaDefinitions(properties, argv.range, argv.skipInteraction);
239
68
  }
240
69
 
241
70
  /**
242
71
  * @returns {Promise} .
243
72
  */
244
73
  static async selectTypes() {
245
- properties = properties || File.loadConfigFile();
246
- if (!Util.checkProperties(properties)) {
74
+ const properties = await config.getProperties();
75
+ if (!(await config.checkProperties(properties))) {
247
76
  return null;
248
77
  }
249
78
  await Cli.selectTypes(properties);
250
79
  }
251
80
  /**
252
- * @returns {Promise} .
81
+ * @returns {void}
253
82
  */
254
83
  static explainTypes() {
255
84
  Cli.explainTypes();
256
85
  }
257
86
  /**
258
- * @param {Boolean|Object} [skipInteraction] signals what to insert automatically for things usually asked via wizard
259
- * @returns {Promise} .
87
+ * @param {boolean | TYPE.skipInteraction} [skipInteraction] signals what to insert automatically for things usually asked via wizard
88
+ * @returns {Promise.<boolean>} success flag
260
89
  */
261
90
  static async upgrade(skipInteraction) {
262
- properties = properties || File.loadConfigFile();
91
+ Mcdev.setSkipInteraction(skipInteraction);
92
+ const properties = await config.getProperties();
263
93
  if (!properties) {
264
94
  Util.logger.error('No config found. Please run mcdev init');
265
- return;
95
+ return false;
266
96
  }
267
97
  if ((await InitGit.initGitRepo(skipInteraction)).status === 'error') {
268
- return;
98
+ return false;
269
99
  }
270
100
 
271
- Init.upgradeProject(properties, false);
101
+ return Init.upgradeProject(properties, false);
272
102
  }
273
103
 
274
104
  /**
275
105
  * Retrieve all metadata from the specified business unit into the local file system.
276
- * @param {String} businessUnit references credentials from properties.json
277
- * @param {String} [selectedType] limit retrieval to given metadata type
106
+ *
107
+ * @param {string} businessUnit references credentials from properties.json
108
+ * @param {string[]} [selectedTypesArr] limit retrieval to given metadata type
109
+ * @param {string[]} [keys] limit retrieval to given metadata key
278
110
  * @param {boolean} [changelogOnly] skip saving, only create json in memory
279
- * @returns {Promise<Object>} -
111
+ * @returns {Promise.<object>} -
280
112
  */
281
- static async retrieve(businessUnit, selectedType, changelogOnly) {
113
+ static async retrieve(businessUnit, selectedTypesArr, keys, changelogOnly) {
282
114
  Util.logger.info('mcdev:: Retrieve');
283
- properties = properties || File.loadConfigFile();
284
- if (!Util.checkProperties(properties)) {
115
+ const properties = await config.getProperties();
116
+ if (!(await config.checkProperties(properties))) {
285
117
  // return null here to avoid seeing 2 error messages for the same issue
286
118
  return null;
287
119
  }
288
- const [type, subType] = selectedType ? selectedType.split('-') : [];
289
- if (type && !MetadataTypeInfo[type]) {
290
- Util.logger.error(`:: '${type}' is not a valid metadata type`);
291
- return;
292
- } else if (
293
- type &&
294
- subType &&
295
- (!MetadataTypeInfo[type] || !MetadataTypeDefinitions[type].subTypes.includes(subType))
296
- ) {
297
- Util.logger.error(`:: '${selectedType}' is not a valid metadata type`);
298
- return;
120
+
121
+ // assume a list was passed in and check each entry's validity
122
+ if (selectedTypesArr) {
123
+ for (const selectedType of selectedTypesArr) {
124
+ if (!Util._isValidType(selectedType)) {
125
+ return;
126
+ }
127
+ }
299
128
  }
300
129
 
301
130
  if (businessUnit === '*') {
302
- Util.logger.info('\n:: Retrieving all BUs for all credentials');
131
+ Util.logger.info('\n :: Retrieving all BUs for all credentials');
303
132
  let counter_credTotal = 0;
304
133
  for (const cred in properties.credentials) {
305
- Util.logger.info(`\n:: Retrieving all BUs for ${cred}`);
134
+ Util.logger.info(`\n :: Retrieving all BUs for ${cred}`);
306
135
  let counter_credBu = 0;
307
136
  for (const bu in properties.credentials[cred].businessUnits) {
308
- await this._retrieveBU(cred, bu, selectedType);
137
+ await this._retrieveBU(cred, bu, selectedTypesArr, keys);
309
138
  counter_credBu++;
310
139
  Util.restartLogger();
311
140
  }
312
141
  counter_credTotal += counter_credBu;
313
- Util.logger.info(`\n:: ${counter_credBu} BUs for ${cred}\n`);
142
+ Util.logger.info(`\n :: ${counter_credBu} BUs for ${cred}\n`);
314
143
  }
315
- Util.logger.info(`\n:: ${counter_credTotal} BUs in total\n`);
144
+ Util.logger.info(`\n :: ${counter_credTotal} BUs in total\n`);
316
145
  } else {
317
146
  let [cred, bu] = businessUnit ? businessUnit.split('/') : [null, null];
318
147
  // to allow all-BU via user selection we need to run this here already
@@ -336,20 +165,21 @@ class Mcdev {
336
165
  }
337
166
 
338
167
  if (bu === '*' && properties.credentials && properties.credentials[cred]) {
339
- Util.logger.info(`\n:: Retrieving all BUs for ${cred}`);
168
+ Util.logger.info(`\n :: Retrieving all BUs for ${cred}`);
340
169
  let counter_credBu = 0;
341
170
  for (const bu in properties.credentials[cred].businessUnits) {
342
- await this._retrieveBU(cred, bu, selectedType);
171
+ await this._retrieveBU(cred, bu, selectedTypesArr, keys);
343
172
  counter_credBu++;
344
173
  Util.restartLogger();
345
174
  }
346
- Util.logger.info(`\n:: ${counter_credBu} BUs for ${cred}\n`);
175
+ Util.logger.info(`\n :: ${counter_credBu} BUs for ${cred}\n`);
347
176
  } else {
348
177
  // retrieve a single BU; return
349
178
  const retrieveChangelog = await this._retrieveBU(
350
179
  cred,
351
180
  bu,
352
- selectedType,
181
+ selectedTypesArr,
182
+ keys,
353
183
  changelogOnly
354
184
  );
355
185
  if (changelogOnly) {
@@ -361,14 +191,20 @@ class Mcdev {
361
191
  }
362
192
  /**
363
193
  * helper for retrieve()
364
- * @param {String} cred name of Credential
365
- * @param {String} bu name of BU
366
- * @param {String} [selectedType] limit retrieval to given metadata type/subtype
194
+ *
195
+ * @private
196
+ * @param {string} cred name of Credential
197
+ * @param {string} bu name of BU
198
+ * @param {string[]} [selectedTypesArr] limit retrieval to given metadata type/subtype
199
+ * @param {string[]} [keys] limit retrieval to given metadata key
367
200
  * @param {boolean} [changelogOnly] skip saving, only create json in memory
368
- * @returns {Promise<Object>} ensure that BUs are worked on sequentially
201
+ * @returns {Promise.<object>} ensure that BUs are worked on sequentially
369
202
  */
370
- static async _retrieveBU(cred, bu, selectedType, changelogOnly) {
371
- properties = properties || File.loadConfigFile();
203
+ static async _retrieveBU(cred, bu, selectedTypesArr, keys, changelogOnly) {
204
+ const properties = await config.getProperties();
205
+ if (!(await config.checkProperties(properties))) {
206
+ return null;
207
+ }
372
208
  const buObject = await Cli.getCredentialObject(
373
209
  properties,
374
210
  cred !== null ? cred + '/' + bu : null,
@@ -376,216 +212,101 @@ class Mcdev {
376
212
  true
377
213
  );
378
214
  if (buObject !== null) {
215
+ cache.initCache(buObject);
379
216
  cred = buObject.credential;
380
217
  bu = buObject.businessUnit;
381
- Util.logger.info(`\n:: Retrieving ${cred}/${bu}\n`);
382
- let retrieveTypesArr;
383
- const [type, subType] = selectedType ? selectedType.split('-') : [];
384
- if (
385
- type &&
386
- subType &&
387
- MetadataTypeInfo[type] &&
388
- MetadataTypeDefinitions[type].subTypes.includes(subType)
389
- ) {
390
- // Clear output folder structure for selected sub-type
391
- File.removeSync(
392
- File.normalizePath([properties.directories.retrieve, cred, bu, type, subType])
393
- );
394
- retrieveTypesArr = [selectedType];
395
- } else if (type && MetadataTypeInfo[type]) {
396
- // Clear output folder structure for selected type
397
- File.removeSync(
398
- File.normalizePath([properties.directories.retrieve, cred, bu, type])
399
- );
400
- retrieveTypesArr = [type];
401
- } else {
218
+ Util.logger.info(`\n :: Retrieving ${cred}/${bu}\n`);
219
+ const retrieveTypesArr = [];
220
+ if (selectedTypesArr) {
221
+ for (const selectedType of selectedTypesArr) {
222
+ const [type, subType] = selectedType ? selectedType.split('-') : [];
223
+ const removePathArr = [properties.directories.retrieve, cred, bu, type];
224
+ if (
225
+ type &&
226
+ subType &&
227
+ MetadataTypeInfo[type] &&
228
+ MetadataTypeDefinitions[type].subTypes.includes(subType)
229
+ ) {
230
+ // Clear output folder structure for selected sub-type
231
+ removePathArr.push(subType);
232
+ retrieveTypesArr.push(selectedType);
233
+ } else if (type && MetadataTypeInfo[type]) {
234
+ // Clear output folder structure for selected type
235
+ retrieveTypesArr.push(type);
236
+ }
237
+ if (!keys) {
238
+ // dont delete directories if we are just re-retrieving a single file
239
+ await File.remove(File.normalizePath(removePathArr));
240
+ }
241
+ }
242
+ }
243
+ if (!retrieveTypesArr.length) {
244
+ // assume no type was given and config settings are used instead:
402
245
  // Clear output folder structure
403
246
  File.removeSync(File.normalizePath([properties.directories.retrieve, cred, bu]));
404
- // assume no type was given and config settings are used instead:
405
247
  // removes subtypes and removes duplicates
406
- retrieveTypesArr = [
407
- ...new Set(properties.metaDataTypes.retrieve.map((type) => type.split('-')[0])),
408
- ];
409
- }
410
- let client;
411
- try {
412
- client = await Util.getETClient(buObject);
413
- } catch (ex) {
414
- Util.logger.error(ex.message);
415
- return;
248
+ retrieveTypesArr.push(
249
+ ...new Set(properties.metaDataTypes.retrieve.map((type) => type.split('-')[0]))
250
+ );
416
251
  }
417
- const retriever = new Retriever(properties, buObject, client);
252
+ const retriever = new Retriever(properties, buObject);
418
253
 
419
254
  try {
420
255
  // await is required or the calls end up conflicting
421
256
  const retrieveChangelog = await retriever.retrieve(
422
257
  retrieveTypesArr,
423
- null,
258
+ keys,
424
259
  null,
425
260
  changelogOnly
426
261
  );
427
262
  if (changelogOnly) {
428
263
  return retrieveChangelog;
429
264
  }
430
- if (properties.options.documentOnRetrieve) {
431
- // todo: find the underlying async issue that makes this wait necessary
432
- await new Promise((resolve) => {
433
- setTimeout(() => resolve('done!'), 1000);
434
- });
435
- await this.badKeys(`${cred}/${bu}`);
436
- }
437
265
  } catch (ex) {
438
- Util.logger.error('mcdev.retrieve failed: ' + ex.message);
439
- Util.logger.debug(ex.stack);
440
- if (Util.logger.level === 'debug') {
441
- console.log(ex.stack);
442
- }
443
- }
444
- }
445
- }
446
- /**
447
- * helper for deploy()
448
- * @param {String} cred name of Credential
449
- * @param {String} bu name of BU
450
- * @param {String} [type] limit deployment to given metadata type
451
- * @returns {Promise} ensure that BUs are worked on sequentially
452
- */
453
- static async _deployBU(cred, bu, type) {
454
- const buPath = `${cred}/${bu}`;
455
- Util.logger.info(`::Deploying ${buPath}`);
456
- properties = properties || File.loadConfigFile();
457
- const buObject = await Cli.getCredentialObject(properties, buPath, null, true);
458
- if (buObject !== null) {
459
- let client;
460
- try {
461
- client = await Util.getETClient(buObject);
462
- } catch (ex) {
463
- Util.logger.error(ex.message);
464
- return;
465
- }
466
- const deployer = new Deployer(properties, buObject, client, type);
467
- try {
468
- // await is required or the calls end up conflicting
469
- await deployer.deploy();
470
- } catch (ex) {
471
- Util.logger.error('mcdev.deploy failed: ' + ex.message);
472
- Util.logger.debug(ex.stack);
473
- if (Util.logger.level === 'debug') {
474
- console.log(ex.stack);
475
- }
266
+ Util.logger.errorStack(ex, 'mcdev.retrieve failed');
476
267
  }
477
268
  }
478
269
  }
479
270
 
480
271
  /**
481
272
  * Deploys all metadata located in the 'deploy' directory to the specified business unit
482
- * @param {String} businessUnit references credentials from properties.json
483
- * @param {String} [selectedType] limit deployment to given metadata type
484
- * @returns {Promise<void>} -
273
+ *
274
+ * @param {string} businessUnit references credentials from properties.json
275
+ * @param {string[]} [selectedTypesArr] limit deployment to given metadata type
276
+ * @param {string[]} [keyArr] limit deployment to given metadata keys
277
+ * @param {boolean} [fromRetrieve] optionally deploy whats defined via selectedTypesArr + keyArr directly from retrieve folder instead of from deploy folder
278
+ * @returns {Promise.<void>} -
485
279
  */
486
- static async deploy(businessUnit, selectedType) {
487
- Util.logger.info('mcdev:: Deploy');
488
- properties = properties || File.loadConfigFile();
489
-
490
- const [type, subType] = selectedType ? selectedType.split('-') : [];
491
- if (type && !MetadataTypeInfo[type]) {
492
- Util.logger.error(`:: '${type}' is not a valid metadata type`);
493
- return;
494
- } else if (
495
- type &&
496
- subType &&
497
- (!MetadataTypeInfo[type] || !MetadataTypeDefinitions[type].subTypes.includes(subType))
498
- ) {
499
- Util.logger.error(`:: '${selectedType}' is not a valid metadata type`);
500
- return;
501
- }
502
- let counter_credBu = 0;
503
- if (businessUnit === '*') {
504
- // all credentials and all BUs shall be deployed to
505
- const deployFolders = await File.readDirectories(
506
- properties.directories.deploy,
507
- 2,
508
- false
509
- );
510
- for (const buPath of deployFolders.filter((r) => r.includes('/'))) {
511
- const [cred, bu] = buPath.split('/');
512
- await this._deployBU(cred, bu, type);
513
- counter_credBu++;
514
- Util.logger.info('');
515
- Util.restartLogger();
516
- }
517
- } else {
518
- // anything but "*" passed in
519
- let [cred, bu] = businessUnit ? businessUnit.split('/') : [null, null];
520
-
521
- // to allow all-BU via user selection we need to run this here already
522
- if (
523
- properties.credentials &&
524
- (!properties.credentials[cred] ||
525
- (bu !== '*' && properties.credentials[cred].businessUnits[bu]))
526
- ) {
527
- const buObject = await Cli.getCredentialObject(
528
- properties,
529
- cred !== null ? cred + '/' + bu : null,
530
- null,
531
- true
532
- );
533
- if (buObject !== null) {
534
- cred = buObject.credential;
535
- bu = buObject.businessUnit;
536
- } else {
537
- return;
538
- }
539
- }
540
-
541
- if (bu === '*' && properties.credentials && properties.credentials[cred]) {
542
- // valid credential given and -all- BUs targeted
543
- Util.logger.info(`\n:: Deploying all BUs for ${cred}`);
544
- let counter_credBu = 0;
545
- // for (const bu in properties.credentials[cred].businessUnits) {
546
- const deployFolders = await File.readDirectories(
547
- File.normalizePath([properties.directories.deploy, cred]),
548
- 1,
549
- false
550
- );
551
- for (const buPath of deployFolders) {
552
- await this._deployBU(cred, buPath, type);
553
- counter_credBu++;
554
- Util.logger.info('');
555
- Util.restartLogger();
556
- }
557
- Util.logger.info(`\n:: ${counter_credBu} BUs for ${cred}\n`);
558
- } else {
559
- // either bad credential or specific BU or no BU given
560
- await this._deployBU(cred, bu, type);
561
- counter_credBu++;
562
- }
563
- }
564
- if (counter_credBu !== 0) {
565
- Util.logger.info(`\n:: Deployed ${counter_credBu} BUs\n`);
566
- }
280
+ static async deploy(businessUnit, selectedTypesArr, keyArr, fromRetrieve = false) {
281
+ return Deployer.deploy(businessUnit, selectedTypesArr, keyArr, fromRetrieve);
567
282
  }
568
283
 
569
284
  /**
570
285
  * Creates template file for properties.json
286
+ *
571
287
  * @param {string} [credentialsName] identifying name of the installed package / project
572
- * @param {Boolean|Object} [skipInteraction] signals what to insert automatically for things usually asked via wizard
573
- * @returns {Promise<void>} -
288
+ * @param {boolean | TYPE.skipInteraction} [skipInteraction] signals what to insert automatically for things usually asked via wizard
289
+ * @returns {Promise.<void>} -
574
290
  */
575
291
  static async initProject(credentialsName, skipInteraction) {
576
292
  Util.logger.info('mcdev:: Setting up project');
577
- properties = properties || File.loadConfigFile(!!credentialsName);
293
+ Mcdev.setSkipInteraction(skipInteraction);
294
+ const properties = await config.getProperties(!!credentialsName);
578
295
  await Init.initProject(properties, credentialsName, skipInteraction);
579
296
  }
580
297
 
581
298
  /**
582
299
  * Refreshes BU names and ID's from MC instance
300
+ *
583
301
  * @param {string} credentialsName identifying name of the installed package / project
584
- * @returns {Promise<void>} -
302
+ * @returns {Promise.<void>} -
585
303
  */
586
304
  static async findBUs(credentialsName) {
587
305
  Util.logger.info('mcdev:: Load BUs');
588
- properties = properties || File.loadConfigFile();
306
+ const properties = await config.getProperties();
307
+ if (!(await config.checkProperties(properties))) {
308
+ return null;
309
+ }
589
310
  const buObject = await Cli.getCredentialObject(properties, credentialsName, true);
590
311
  if (buObject !== null) {
591
312
  BuHelper.refreshBUProperties(properties, buObject.credential);
@@ -595,19 +316,22 @@ class Mcdev {
595
316
  /**
596
317
  * Creates docs for supported metadata types in Markdown and/or HTML format
597
318
  *
598
- * @param {String} businessUnit references credentials from properties.json
599
- * @param {String} type metadata type
600
- * @returns {Promise<void>} -
319
+ * @param {string} businessUnit references credentials from properties.json
320
+ * @param {string} type metadata type
321
+ * @returns {Promise.<void>} -
601
322
  */
602
323
  static async document(businessUnit, type) {
603
324
  Util.logger.info('mcdev:: Document');
604
- properties = properties || File.loadConfigFile();
325
+ const properties = await config.getProperties();
326
+ if (!(await config.checkProperties(properties))) {
327
+ return null;
328
+ }
605
329
  if (type && !MetadataTypeInfo[type]) {
606
330
  Util.logger.error(`:: '${type}' is not a valid metadata type`);
607
331
  return;
608
332
  }
609
333
  try {
610
- const parentBUOnlyTypes = ['role'];
334
+ const parentBUOnlyTypes = ['accountUser', 'role'];
611
335
  const buObject = await Cli.getCredentialObject(
612
336
  properties,
613
337
  parentBUOnlyTypes.includes(type) ? businessUnit.split('/')[0] : businessUnit,
@@ -629,20 +353,29 @@ class Mcdev {
629
353
  /**
630
354
  * Creates docs for supported metadata types in Markdown and/or HTML format
631
355
  *
632
- * @param {String} businessUnit references credentials from properties.json
633
- * @param {String} type supported metadata type
634
- * @param {String} customerKey Identifier of data extension
635
- * @returns {Promise<void>} -
356
+ * @param {string} businessUnit references credentials from properties.json
357
+ * @param {string} type supported metadata type
358
+ * @param {string} customerKey Identifier of data extension
359
+ * @returns {Promise.<void>} -
636
360
  */
637
361
  static async deleteByKey(businessUnit, type, customerKey) {
638
362
  Util.logger.info('mcdev:: delete');
639
- properties = properties || File.loadConfigFile();
363
+ const properties = await config.getProperties();
364
+ if (!(await config.checkProperties(properties))) {
365
+ return null;
366
+ }
640
367
  const buObject = await Cli.getCredentialObject(properties, businessUnit);
641
368
  if (buObject !== null) {
642
369
  if ('string' !== typeof type) {
643
370
  Util.logger.error('mcdev.delete failed: Bad metadata type passed in');
644
371
  return;
645
372
  }
373
+ try {
374
+ MetadataTypeInfo[type].client = auth.getSDK(buObject);
375
+ } catch (ex) {
376
+ Util.logger.error(ex.message);
377
+ return;
378
+ }
646
379
  try {
647
380
  MetadataTypeInfo[type].properties = properties;
648
381
  MetadataTypeInfo[type].deleteByKey(buObject, customerKey);
@@ -654,11 +387,15 @@ class Mcdev {
654
387
 
655
388
  /**
656
389
  * Converts metadata to legacy format. Output is saved in 'converted' directory
657
- * @param {String} businessUnit references credentials from properties.json
658
- * @returns {Promise<void>} -
390
+ *
391
+ * @param {string} businessUnit references credentials from properties.json
392
+ * @returns {Promise.<void>} -
659
393
  */
660
394
  static async badKeys(businessUnit) {
661
- properties = properties || File.loadConfigFile();
395
+ const properties = await config.getProperties();
396
+ if (!(await config.checkProperties(properties))) {
397
+ return null;
398
+ }
662
399
  const buObject = await Cli.getCredentialObject(properties, businessUnit);
663
400
  if (buObject !== null) {
664
401
  Util.logger.info('Gathering list of Name<>External Key mismatches (bad keys)');
@@ -669,29 +406,23 @@ class Mcdev {
669
406
  buObject.businessUnit,
670
407
  ])
671
408
  );
672
- const docPath = File.filterIllegalPathChars(
673
- properties.directories.badKeys + buObject.credential
674
- );
409
+ const docPath = File.normalizePath([
410
+ properties.directories.docs,
411
+ 'badKeys',
412
+ buObject.credential,
413
+ ]);
675
414
  const filename = File.normalizePath([
676
415
  docPath,
677
416
  File.filterIllegalFilenames(buObject.businessUnit) + '.badKeys.md',
678
417
  ]);
679
- if (!File.existsSync(docPath)) {
680
- File.mkdirpSync(docPath);
681
- } else if (File.existsSync(filename)) {
418
+ await File.ensureDir(docPath);
419
+ if (await File.pathExistsSync(filename)) {
682
420
  File.removeSync(filename);
683
421
  }
684
422
 
685
- const regex = RegExp('(\\w+-){4}\\w+');
686
- let metadata;
687
- if (File.existsSync(retrieveDir)) {
688
- metadata = Deployer.readBUMetadata(retrieveDir, null, true);
689
- } else {
690
- Util.logger.warn(
691
- `Looks like ${retrieveDir} does not exist. If there was no metadata retrieved this is expected, in other cases re-run retrieve to attempt to fix this issue`
692
- );
693
- return;
694
- }
423
+ const regex = new RegExp('(\\w+-){4}\\w+');
424
+ await File.ensureDir(retrieveDir);
425
+ const metadata = Deployer.readBUMetadata(retrieveDir, null, true);
695
426
  let output = '# List of Metadata with Name-Key mismatches\n';
696
427
  for (const metadataType in metadata) {
697
428
  let listEntries = '';
@@ -726,27 +457,23 @@ class Mcdev {
726
457
 
727
458
  /**
728
459
  * Retrieve a specific metadata file and templatise.
729
- * @param {String} businessUnit references credentials from properties.json
730
- * @param {String} selectedType supported metadata type
731
- * @param {String} name name of the metadata
732
- * @param {String} market market which should be used to revert template
733
- * @returns {Promise<void>} -
460
+ *
461
+ * @param {string} businessUnit references credentials from properties.json
462
+ * @param {string} selectedType supported metadata type
463
+ * @param {string[]} name name of the metadata
464
+ * @param {string} market market which should be used to revert template
465
+ * @returns {Promise.<TYPE.MultiMetadataTypeList>} -
734
466
  */
735
467
  static async retrieveAsTemplate(businessUnit, selectedType, name, market) {
736
468
  Util.logger.info('mcdev:: Retrieve as Template');
737
- properties = properties || File.loadConfigFile();
738
- const [type, subType] = selectedType ? selectedType.split('-') : [];
739
- if (type && !MetadataTypeInfo[type]) {
740
- Util.logger.error(`:: '${type}' is not a valid metadata type`);
741
- return;
742
- } else if (
743
- type &&
744
- subType &&
745
- (!MetadataTypeInfo[type] || !MetadataTypeDefinitions[type].subTypes.includes(subType))
746
- ) {
747
- Util.logger.error(`:: '${selectedType}' is not a valid metadata type`);
469
+ const properties = await config.getProperties();
470
+ if (!(await config.checkProperties(properties))) {
471
+ return null;
472
+ }
473
+ if (!Util._isValidType(selectedType)) {
748
474
  return;
749
475
  }
476
+ const [type, subType] = selectedType ? selectedType.split('-') : [];
750
477
 
751
478
  let retrieveTypesArr;
752
479
  if (
@@ -761,126 +488,78 @@ class Mcdev {
761
488
  }
762
489
  const buObject = await Cli.getCredentialObject(properties, businessUnit);
763
490
  if (buObject !== null) {
764
- let client;
765
- try {
766
- client = await Util.getETClient(buObject);
767
- } catch (ex) {
768
- Util.logger.error(ex.message);
769
- return;
770
- }
771
- const retriever = new Retriever(properties, buObject, client);
772
- if (this._checkMarket(market)) {
491
+ cache.initCache(buObject);
492
+ const retriever = new Retriever(properties, buObject);
493
+ if (Util.checkMarket(market, properties)) {
773
494
  return retriever.retrieve(retrieveTypesArr, name, properties.markets[market]);
774
495
  }
775
496
  }
776
497
  }
777
498
 
778
499
  /**
779
- * Build a specific metadata file based on a template.
780
- * @param {String} businessUnit references credentials from properties.json
781
- * @param {String} type supported metadata type
782
- * @param {String} name name of the metadata
783
- * @param {String} market market localizations
784
- * @returns {Promise<void>} -
500
+ * Build a template based on a list of metadata files in the retrieve folder.
501
+ *
502
+ * @param {string} businessUnit references credentials from properties.json
503
+ * @param {string} selectedType supported metadata type
504
+ * @param {string[]} keyArr customerkey of the metadata
505
+ * @param {string} market market localizations
506
+ * @returns {Promise.<TYPE.MultiMetadataTypeList>} -
785
507
  */
786
- static async buildDefinition(businessUnit, type, name, market) {
787
- Util.logger.info('mcdev:: Build Definition from Template');
788
- properties = properties || File.loadConfigFile();
789
- if (type.includes('-')) {
790
- Util.logger.error(
791
- `:: '${type}' is not a valid metadata type. Please don't include subtypes.`
792
- );
793
- return;
794
- }
795
- if (type && !MetadataTypeInfo[type]) {
796
- Util.logger.error(`:: '${type}' is not a valid metadata type`);
797
- return;
798
- }
799
- const buObject = await Cli.getCredentialObject(properties, businessUnit);
800
- if (buObject !== null) {
801
- const builder = new Builder(properties, buObject, null);
802
- if (market === '*') {
803
- for (const oneMarket in properties.markets) {
804
- builder.buildDefinition(type, name, properties.markets[oneMarket]);
805
- }
806
- } else {
807
- if (this._checkMarket(market)) {
808
- builder.buildDefinition(type, name, properties.markets[market]);
809
- }
810
- }
811
- }
508
+ static async buildTemplate(businessUnit, selectedType, keyArr, market) {
509
+ Util.logger.info('mcdev:: Build Template from retrieved files');
510
+ return Builder.buildTemplate(businessUnit, selectedType, keyArr, market);
812
511
  }
813
512
  /**
814
- * check if a market name exists in current mcdev config
815
- * @param {String} market market localizations
816
- * @returns {Boolean} found market or not
513
+ * Build a specific metadata file based on a template.
514
+ *
515
+ * @param {string} businessUnit references credentials from properties.json
516
+ * @param {string} selectedType supported metadata type
517
+ * @param {string} name name of the metadata
518
+ * @param {string} market market localizations
519
+ * @returns {Promise.<void>} -
817
520
  */
818
- static _checkMarket(market) {
819
- properties = properties || File.loadConfigFile();
820
- if (properties.markets[market]) {
821
- return true;
822
- } else {
823
- Util.logger.error(`Could not find the market '${market}' in your configuration file.`);
824
- const marketArr = [];
825
- for (const oneMarket in properties.markets) {
826
- marketArr.push(oneMarket);
827
- }
828
- if (marketArr.length) {
829
- Util.logger.info('Available markets are: ' + marketArr.join(', '));
830
- }
831
- return false;
832
- }
521
+ static async buildDefinition(businessUnit, selectedType, name, market) {
522
+ Util.logger.info('mcdev:: Build Definition from Template');
523
+ return Builder.buildDefinition(businessUnit, selectedType, name, market);
833
524
  }
834
525
 
835
526
  /**
836
527
  * Build a specific metadata file based on a template using a list of bu-market combos
837
- * @param {String} listName name of list of BU-market combos
838
- * @param {String} type supported metadata type
839
- * @param {String} name name of the metadata
840
- * @returns {Promise<void>} -
528
+ *
529
+ * @param {string} listName name of list of BU-market combos
530
+ * @param {string} type supported metadata type
531
+ * @param {string} name name of the metadata
532
+ * @returns {Promise.<void>} -
841
533
  */
842
534
  static async buildDefinitionBulk(listName, type, name) {
843
535
  Util.logger.info('mcdev:: Build Definition from Template Bulk');
844
- properties = properties || File.loadConfigFile();
845
- if (!properties.marketList) {
846
- Util.logger.error('Please define properties.marketList object in your config');
847
- return;
536
+ return Builder.buildDefinitionBulk(listName, type, name);
537
+ }
538
+ /**
539
+ *
540
+ * @param {string} businessUnit references credentials from properties.json
541
+ * @param {string} selectedType supported metadata type
542
+ * @param {string[]} keyArr customerkey of the metadata
543
+ * @returns {Promise.<string[]>} list of all files that need to be committed in a flat array ['path/file1.ext', 'path/file2.ext']
544
+ */
545
+ static async getFilesToCommit(businessUnit, selectedType, keyArr) {
546
+ Util.logger.info('mcdev:: getFilesToCommit');
547
+ const properties = await config.getProperties();
548
+ if (!(await config.checkProperties(properties))) {
549
+ return null;
848
550
  }
849
- if (!properties.marketList[listName]) {
850
- Util.logger.error(`Please define properties.marketList.${listName} in your config`);
551
+ if (!Util._isValidType(selectedType)) {
851
552
  return;
852
553
  }
853
- if (type && !MetadataTypeInfo[type]) {
854
- Util.logger.error(`:: '${type}' is not a valid metadata type`);
554
+ if (selectedType.includes('-')) {
555
+ Util.logger.error(
556
+ `:: '${selectedType}' is not a valid metadata type. Please don't include subtypes.`
557
+ );
855
558
  return;
856
559
  }
857
- let i = 0;
858
- for (const businessUnit in properties.marketList[listName]) {
859
- if (businessUnit === 'description') {
860
- // skip, it's just a metadata on this list and not a BU
861
- continue;
862
- }
863
- i++;
864
- const market = properties.marketList[listName][businessUnit];
865
- let marketList = [];
866
- if ('string' === typeof market) {
867
- marketList.push(market);
868
- } else {
869
- marketList = market;
870
- }
871
- marketList.forEach((market) => {
872
- if (market && properties.markets[market]) {
873
- Util.logger.info(`Executing for '${businessUnit}': '${market}'`);
874
- this.buildDefinition(businessUnit, type, name, market);
875
- } else {
876
- Util.logger.error(
877
- `Could not find '${market}' in properties.markets. Please check your properties.marketList.${listName} confguration.`
878
- );
879
- }
880
- });
881
- }
882
- if (!i) {
883
- Util.logger.error('Please define properties.marketList in your config');
560
+ const buObject = await Cli.getCredentialObject(properties, businessUnit);
561
+ if (buObject !== null) {
562
+ return DevOps.getFilesToCommit(properties, buObject, selectedType, keyArr);
884
563
  }
885
564
  }
886
565
  }