mcdev 3.1.3 → 4.0.1

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