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/jsconfig.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "include": ["lib/**/*.js"]
2
+ "include": ["lib/**/*.js", "types/**/*.js"]
3
3
  }
package/lib/Builder.js CHANGED
@@ -1,7 +1,11 @@
1
1
  'use strict';
2
2
 
3
+ const TYPE = require('../types/mcdev.d');
3
4
  const Util = require('./util/util');
4
-
5
+ const File = require('./util/file');
6
+ const config = require('./util/config');
7
+ const Cli = require('./util/cli');
8
+ const auth = require('./util/auth');
5
9
  const MetadataTypeInfo = require('./MetadataTypeInfo');
6
10
  // @ts-ignore
7
11
 
@@ -12,50 +16,46 @@ class Builder {
12
16
  /**
13
17
  * Creates a Builder, uses v2 auth if v2AuthOptions are passed.
14
18
  *
15
- * @param {Object} properties properties for auth
16
- * @param {String} properties.clientId clientId for FuelSDK auth
17
- * @param {String} properties.clientSecret clientSecret for FuelSDK auth
18
- * @param {Object} properties.directories list of default directories
19
- * @param {String} properties.directories.template where templates are saved
20
- * @param {String} properties.directories.templateBuilds where template-based deployment definitions are saved
21
- * @param {String} properties.tenant v2 Auth Tenant Information
22
- * @param {String} properties.businessUnits ID of Business Unit to authenticate with
23
- * @param {Object} buObject properties for auth
24
- * @param {String} buObject.clientId clientId for FuelSDK auth
25
- * @param {String} buObject.clientSecret clientSecret for FuelSDK auth
26
- * @param {Object} buObject.credential clientId for FuelSDK auth
27
- * @param {String} buObject.tenant v2 Auth Tenant Information
28
- * @param {String} buObject.mid ID of Business Unit to authenticate with
29
- * @param {String} buObject.businessUnit name of Business Unit to authenticate with
30
- * @param {Util.ET_Client} client fuel client
19
+ * @param {TYPE.Mcdevrc} properties properties for auth
20
+ saved
21
+ * @param {TYPE.BuObject} buObject properties for auth
31
22
  */
32
- constructor(properties, buObject, client) {
33
- this.client = client;
23
+ constructor(properties, buObject) {
34
24
  this.properties = properties;
35
25
  this.templateDir = properties.directories.template;
26
+ this.retrieveDir = File.normalizePath([
27
+ properties.directories.retrieve,
28
+ buObject.credential,
29
+ buObject.businessUnit,
30
+ ]);
31
+ this.buObject = buObject;
36
32
 
37
33
  // allow multiple target directories
38
34
  const templateBuildsArr = Array.isArray(properties.directories.templateBuilds)
39
35
  ? properties.directories.templateBuilds
40
36
  : [properties.directories.templateBuilds];
41
-
42
37
  this.targetDir = templateBuildsArr.map(
43
38
  (directoriesTemplateBuilds) =>
44
39
  directoriesTemplateBuilds + buObject.credential + '/' + buObject.businessUnit
45
40
  );
46
41
 
42
+ /**
43
+ * @type {TYPE.MultiMetadataTypeList}
44
+ */
47
45
  this.metadata = {};
48
46
  }
49
47
 
50
48
  /**
51
49
  * Builds a specific metadata file by name
52
- * @param {String} metadataType metadata type to build
53
- * @param {String} name name of metadata to build
54
- * @param {Object} variables variables to be replaced in the metadata
55
- * @returns {Promise} Promise
50
+ *
51
+ * @param {string} metadataType metadata type to build
52
+ * @param {string} name name of metadata to build
53
+ * @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
54
+ * @returns {Promise.<TYPE.MultiMetadataTypeList>} Promise
56
55
  */
57
- async buildDefinition(metadataType, name, variables) {
56
+ async _buildDefinition(metadataType, name, templateVariables) {
58
57
  let nameArr;
58
+ /* eslint-disable unicorn/prefer-ternary */
59
59
  if (name.includes(',')) {
60
60
  nameArr = name.split(',').map((item) =>
61
61
  // allow whitespace in comma-separated lists
@@ -64,6 +64,8 @@ class Builder {
64
64
  } else {
65
65
  nameArr = [name.trim()];
66
66
  }
67
+ /* eslint-enable unicorn/prefer-ternary */
68
+
67
69
  const type = metadataType;
68
70
  try {
69
71
  const result = await Promise.all(
@@ -72,77 +74,166 @@ class Builder {
72
74
  // we hence require users to put %20 in their stead and have to convert that back
73
75
  name = name.split('%20').join(' ');
74
76
 
75
- MetadataTypeInfo[type].cache = null;
76
- MetadataTypeInfo[type].client = this.client;
77
+ MetadataTypeInfo[type].client = auth.getSDK(this.buObject);
77
78
  MetadataTypeInfo[type].properties = this.properties;
79
+ MetadataTypeInfo[type].buObject = this.buObject;
78
80
  return MetadataTypeInfo[type].buildDefinition(
79
81
  this.templateDir,
80
82
  this.targetDir,
81
83
  name,
82
- variables
84
+ templateVariables
83
85
  );
84
86
  })
85
87
  );
86
88
  if (result) {
87
89
  this.metadata[result[0].type] = [];
88
- result.forEach((element) => {
90
+ for (const element of result) {
89
91
  this.metadata[result[0].type].push(element.metadata);
90
- });
92
+ }
91
93
  }
92
94
  } catch (ex) {
93
- Util.logger.error('mcdev.buildDefinition:' + ex.message);
94
- Util.logger.debug(ex.stack);
95
- if (Util.logger.level === 'debug') {
96
- console.log(ex.stack);
97
- }
95
+ Util.logger.errorStack(ex, 'mcdev.buildDefinition');
98
96
  }
99
97
  return this.metadata;
100
98
  }
101
99
  /**
102
- * ensure provided MarketList exists and it's content including markets and BUs checks out
103
- * @param {String} mlName name of marketList
104
- * @param {Object} properties General configuration to be used in retrieve
105
- * @param {Object} properties.markets list of template variable combos
106
- * @param {Object} properties.marketList list of bu-market combos
107
- * @param {Object} properties.credentials list of credentials and their BUs
108
- * @returns {void} throws errors if problems were found
100
+ * Build a template based on a list of metadata files in the retrieve folder.
101
+ *
102
+ * @param {string} businessUnit references credentials from properties.json
103
+ * @param {string} selectedType supported metadata type
104
+ * @param {string[]} keyArr customerkey of the metadata
105
+ * @param {string} market market localizations
106
+ * @returns {Promise.<TYPE.MultiMetadataTypeList>} -
109
107
  */
110
- static verifyMarketList(mlName, properties) {
111
- if (!properties.marketList[mlName]) {
112
- // ML does not exist
113
- throw new Error(`Market List ${mlName} is not defined`);
114
- } else {
115
- // ML exists, check if it is properly set up
108
+ static async buildTemplate(businessUnit, selectedType, keyArr, market) {
109
+ const properties = await config.getProperties();
110
+ if (!(await config.checkProperties(properties))) {
111
+ return null;
112
+ }
113
+ if (!Util._isValidType(selectedType)) {
114
+ return;
115
+ }
116
+ if (selectedType.includes('-')) {
117
+ Util.logger.error(
118
+ `:: '${selectedType}' is not a valid metadata type. Please don't include subtypes.`
119
+ );
120
+ return;
121
+ }
122
+ const buObject = await Cli.getCredentialObject(properties, businessUnit);
123
+ if (buObject !== null) {
124
+ const builder = new Builder(properties, buObject);
125
+ if (Util.checkMarket(market, properties)) {
126
+ return builder._buildTemplate(selectedType, keyArr, properties.markets[market]);
127
+ }
128
+ }
129
+ }
130
+ /**
131
+ * Build a template based on a list of metadata files in the retrieve folder.
132
+ *
133
+ * @param {string} metadataType metadata type to create a template of
134
+ * @param {string[]} keyArr customerkey of metadata to create a template of
135
+ * @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
136
+ * @returns {Promise.<TYPE.MultiMetadataTypeList>} Promise
137
+ */
138
+ async _buildTemplate(metadataType, keyArr, templateVariables) {
139
+ const type = metadataType;
140
+ try {
141
+ /** @type {TYPE.MetadataTypeItemObj[]} */
142
+ const result = await Promise.all(
143
+ keyArr.map((key) => {
144
+ MetadataTypeInfo[type].client = this.client;
145
+ MetadataTypeInfo[type].properties = this.properties;
146
+ MetadataTypeInfo[type].buObject = this.buObject;
116
147
 
117
- // check if BUs in marketList are valid
118
- let buCounter = 0;
119
- for (const businessUnit in properties.marketList[mlName]) {
120
- if (businessUnit !== 'description') {
121
- buCounter++;
122
- const [cred, bu] = businessUnit ? businessUnit.split('/') : [null, null];
123
- if (
124
- !properties.credentials[cred] ||
125
- !properties.credentials[cred].businessUnits[bu]
126
- ) {
127
- throw new Error(`'${businessUnit}' in Market ${mlName} is not defined.`);
128
- }
129
- // check if markets are valid
130
- let marketArr = properties.marketList[mlName][businessUnit];
131
- if ('string' === typeof marketArr) {
132
- marketArr = [marketArr];
133
- }
134
- for (const market of marketArr) {
135
- if (!properties.markets[market]) {
136
- throw new Error(`Market '${market}' is not defined.`);
137
- } else {
138
- // * markets can be empty or include variables. Nothing we can test here
139
- }
140
- }
141
- }
148
+ /** @type {TYPE.MetadataTypeItemObj} */
149
+ return MetadataTypeInfo[type].buildTemplate(
150
+ this.retrieveDir,
151
+ this.templateDir,
152
+ key,
153
+ templateVariables
154
+ );
155
+ })
156
+ );
157
+ if (result) {
158
+ this.metadata[result[0].type] = result.map((element) => element.metadata);
159
+ }
160
+ } catch (ex) {
161
+ Util.logger.errorStack(ex, 'mcdev.buildTemplate');
162
+ }
163
+ return this.metadata;
164
+ }
165
+ /**
166
+ * Build a specific metadata file based on a template.
167
+ *
168
+ * @param {string} businessUnit references credentials from properties.json
169
+ * @param {string} selectedType supported metadata type
170
+ * @param {string} name name of the metadata
171
+ * @param {string} market market localizations
172
+ * @returns {Promise.<TYPE.MultiMetadataTypeList>} -
173
+ */
174
+ static async buildDefinition(businessUnit, selectedType, name, market) {
175
+ const properties = await config.getProperties();
176
+ if (!(await config.checkProperties(properties))) {
177
+ return null;
178
+ }
179
+ if (!Util._isValidType(selectedType)) {
180
+ return;
181
+ }
182
+ if (selectedType.includes('-')) {
183
+ Util.logger.error(
184
+ `:: '${selectedType}' is not a valid metadata type. Please don't include subtypes.`
185
+ );
186
+ return;
187
+ }
188
+ const buObject = await Cli.getCredentialObject(properties, businessUnit);
189
+ if (buObject !== null) {
190
+ const builder = new Builder(properties, buObject);
191
+ if (Util.checkMarket(market, properties)) {
192
+ return builder._buildDefinition(selectedType, name, properties.markets[market]);
142
193
  }
143
- if (!buCounter) {
144
- throw new Error(`No BUs defined in marketList ${mlName}`);
194
+ }
195
+ }
196
+ /**
197
+ * Build a specific metadata file based on a template using a list of bu-market combos
198
+ *
199
+ * @param {string} listName name of list of BU-market combos
200
+ * @param {string} type supported metadata type
201
+ * @param {string} name name of the metadata
202
+ * @returns {Promise.<void>} -
203
+ */
204
+ static async buildDefinitionBulk(listName, type, name) {
205
+ const properties = await config.getProperties();
206
+ if (!(await config.checkProperties(properties))) {
207
+ return null;
208
+ }
209
+ Util.verifyMarketList(listName, properties);
210
+ if (type && !MetadataTypeInfo[type]) {
211
+ Util.logger.error(`:: '${type}' is not a valid metadata type`);
212
+ return;
213
+ }
214
+ let i = 0;
215
+ for (const businessUnit in properties.marketList[listName]) {
216
+ if (businessUnit === 'description') {
217
+ // skip, it's just a metadata on this list and not a BU
218
+ continue;
219
+ }
220
+ i++;
221
+ const market = properties.marketList[listName][businessUnit];
222
+ let marketList = [];
223
+ if ('string' === typeof market) {
224
+ marketList.push(market);
225
+ } else {
226
+ marketList = market;
145
227
  }
228
+ for (const market of marketList) {
229
+ if (Util.checkMarket(market, properties)) {
230
+ Util.logger.info(`Executing for '${businessUnit}': '${market}'`);
231
+ this.buildDefinition(businessUnit, type, name, market);
232
+ }
233
+ }
234
+ }
235
+ if (!i) {
236
+ Util.logger.error('Please define properties.marketList in your config');
146
237
  }
147
238
  }
148
239
  }