mcdev 7.0.1 → 7.0.3

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 (48) hide show
  1. package/.github/ISSUE_TEMPLATE/bug.yml +2 -0
  2. package/.github/workflows/coverage-base-update.yml +2 -2
  3. package/.github/workflows/coverage.yml +1 -1
  4. package/@types/lib/index.d.ts.map +1 -1
  5. package/@types/lib/metadataTypes/Asset.d.ts +11 -10
  6. package/@types/lib/metadataTypes/Asset.d.ts.map +1 -1
  7. package/@types/lib/metadataTypes/Automation.d.ts +0 -6
  8. package/@types/lib/metadataTypes/Automation.d.ts.map +1 -1
  9. package/@types/lib/metadataTypes/DataExtension.d.ts.map +1 -1
  10. package/@types/lib/metadataTypes/Folder.d.ts +1 -0
  11. package/@types/lib/metadataTypes/Folder.d.ts.map +1 -1
  12. package/@types/lib/metadataTypes/Journey.d.ts +14 -8
  13. package/@types/lib/metadataTypes/Journey.d.ts.map +1 -1
  14. package/@types/lib/metadataTypes/MetadataType.d.ts +2 -2
  15. package/@types/lib/metadataTypes/MetadataType.d.ts.map +1 -1
  16. package/@types/lib/metadataTypes/definitions/Folder.definition.d.ts +1 -0
  17. package/@types/lib/util/init.config.d.ts +20 -4
  18. package/@types/lib/util/init.config.d.ts.map +1 -1
  19. package/@types/lib/util/init.npm.d.ts.map +1 -1
  20. package/@types/lib/util/replaceContentBlockReference.d.ts +7 -6
  21. package/@types/lib/util/replaceContentBlockReference.d.ts.map +1 -1
  22. package/boilerplate/files/eslint.config.js +82 -0
  23. package/boilerplate/forcedUpdates.json +5 -0
  24. package/boilerplate/npm-dependencies.json +2 -0
  25. package/eslint.config.js +143 -0
  26. package/lib/index.js +5 -2
  27. package/lib/metadataTypes/Asset.js +11 -5
  28. package/lib/metadataTypes/Automation.js +27 -46
  29. package/lib/metadataTypes/DataExtension.js +9 -7
  30. package/lib/metadataTypes/Folder.js +33 -20
  31. package/lib/metadataTypes/Journey.js +2 -5
  32. package/lib/metadataTypes/MetadataType.js +6 -1
  33. package/lib/metadataTypes/definitions/Automation.definition.js +1 -0
  34. package/lib/metadataTypes/definitions/Folder.definition.js +2 -0
  35. package/lib/util/init.config.js +79 -32
  36. package/lib/util/init.npm.js +8 -0
  37. package/lib/util/replaceContentBlockReference.js +2 -2
  38. package/package.json +8 -6
  39. package/test/mockRoot/.mcdevrc.json +1 -1
  40. package/test/resources/9999999/automation/create-callout-expected.json +66 -0
  41. package/test/resources/9999999/automation/update-callout-expected.json +68 -0
  42. package/test/resources/9999999/automation/v1/automations/08afb0e2-b00a-4c88-ad2e-1f7f8788c560/get-response.json +7 -7
  43. package/test/type.automation.test.js +46 -0
  44. package/test/utils.js +28 -3
  45. package/.eslintignore +0 -3
  46. package/.eslintrc.json +0 -116
  47. package/boilerplate/files/.eslintignore +0 -5
  48. package/boilerplate/files/.eslintrc +0 -37
@@ -0,0 +1,143 @@
1
+ import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
2
+ import eslintPluginUnicorn from 'eslint-plugin-unicorn';
3
+ import globals from 'globals';
4
+ import mochaPlugin from 'eslint-plugin-mocha';
5
+ import jsdoc from 'eslint-plugin-jsdoc';
6
+ import js from '@eslint/js';
7
+
8
+ export default [
9
+ {
10
+ ignores: ['docs/**/*', 'node_modules/**/*', 'retrieve/**/*'],
11
+ },
12
+ js.configs.recommended,
13
+ eslintPluginPrettierRecommended,
14
+ mochaPlugin.configs.flat.recommended,
15
+ jsdoc.configs['flat/recommended'],
16
+ eslintPluginUnicorn.configs['flat/recommended'],
17
+ {
18
+ languageOptions: {
19
+ globals: {
20
+ ...globals.nodeBuiltin,
21
+ Atomics: 'readonly',
22
+ SharedArrayBuffer: 'readonly',
23
+ },
24
+
25
+ ecmaVersion: 2022,
26
+ sourceType: 'module',
27
+ },
28
+
29
+ settings: {
30
+ jsdoc: {
31
+ mode: 'typescript',
32
+
33
+ preferredTypes: {
34
+ array: 'Array',
35
+ 'array.<>': '[]',
36
+ 'Array.<>': '[]',
37
+ 'array<>': '[]',
38
+ 'Array<>': '[]',
39
+ Object: 'object',
40
+ 'object.<>': 'Object.<>',
41
+ 'object<>': 'Object.<>',
42
+ 'Object<>': 'Object.<>',
43
+ set: 'Set',
44
+ 'set.<>': 'Set.<>',
45
+ 'set<>': 'Set.<>',
46
+ 'Set<>': 'Set.<>',
47
+ promise: 'Promise',
48
+ 'promise.<>': 'Promise.<>',
49
+ 'promise<>': 'Promise.<>',
50
+ 'Promise<>': 'Promise.<>',
51
+ },
52
+ },
53
+ },
54
+
55
+ rules: {
56
+ 'logical-assignment-operators': ['error', 'always'],
57
+ 'unicorn/better-regex': 'off',
58
+
59
+ 'unicorn/catch-error-name': [
60
+ 'error',
61
+ {
62
+ name: 'ex',
63
+ },
64
+ ],
65
+
66
+ 'unicorn/explicit-length-check': 'off',
67
+ 'unicorn/no-null': 'off',
68
+ 'unicorn/prefer-module': 'off',
69
+ 'unicorn/prevent-abbreviations': 'off',
70
+ 'unicorn/filename-case': 'off',
71
+ 'unicorn/no-array-callback-reference': 'off',
72
+ 'unicorn/no-array-reduce': 'off',
73
+ 'unicorn/no-await-expression-member': 'off',
74
+ 'unicorn/no-hex-escape': 'off',
75
+ 'unicorn/no-nested-ternary': 'off',
76
+ 'unicorn/no-static-only-class': 'off',
77
+ 'unicorn/no-unused-properties': 'warn',
78
+ 'unicorn/numeric-separators-style': 'off',
79
+ 'unicorn/prefer-array-some': 'off',
80
+ 'unicorn/prefer-set-has': 'off',
81
+ 'unicorn/prefer-spread': 'off',
82
+ 'unicorn/prefer-string-replace-all': 'error',
83
+ 'arrow-body-style': ['error', 'as-needed'],
84
+ curly: 'error',
85
+ 'no-console': 'error',
86
+ 'jsdoc/check-line-alignment': 2,
87
+
88
+ 'jsdoc/require-jsdoc': [
89
+ 'warn',
90
+ {
91
+ require: {
92
+ FunctionDeclaration: true,
93
+ MethodDefinition: true,
94
+ ClassDeclaration: true,
95
+ ArrowFunctionExpression: false,
96
+ FunctionExpression: true,
97
+ },
98
+ },
99
+ ],
100
+
101
+ 'jsdoc/require-param-type': 'error',
102
+
103
+ 'jsdoc/tag-lines': [
104
+ 'warn',
105
+ 'any',
106
+ {
107
+ startLines: 1,
108
+ },
109
+ ],
110
+
111
+ 'jsdoc/no-undefined-types': 'error',
112
+ 'valid-jsdoc': 'off',
113
+
114
+ 'spaced-comment': [
115
+ 'warn',
116
+ 'always',
117
+ {
118
+ block: {
119
+ exceptions: ['*'],
120
+ balanced: true,
121
+ },
122
+ },
123
+ ],
124
+ },
125
+ },
126
+ {
127
+ files: ['**/*.js'],
128
+
129
+ rules: {
130
+ 'no-var': 'error',
131
+ 'prefer-const': 'error',
132
+ 'prettier/prettier': 'warn',
133
+ 'prefer-arrow-callback': 'warn',
134
+ },
135
+ },
136
+ {
137
+ files: ['test/*.js'],
138
+ rules: {
139
+ 'mocha/no-mocha-arrows': 'off',
140
+ 'mocha/no-pending-tests': 'off',
141
+ },
142
+ },
143
+ ];
package/lib/index.js CHANGED
@@ -399,7 +399,10 @@ class Mcdev {
399
399
  // Clear output folder structure for selected type
400
400
  retrieveTypesArr.push(type);
401
401
  }
402
- if (!keys) {
402
+ const areKeySet = Array.isArray(selectedTypesArr)
403
+ ? keys
404
+ : Object.values(selectedTypesArr).filter(Boolean).length;
405
+ if (!areKeySet) {
403
406
  // dont delete directories if we are just re-retrieving a single file
404
407
  await File.remove(File.normalizePath(removePathArr));
405
408
  // clean up old folders after types were renamed
@@ -420,7 +423,7 @@ class Mcdev {
420
423
  if (!retrieveTypesArr.length) {
421
424
  // assume no type was given and config settings are used instead:
422
425
  // Clear output folder structure
423
- File.removeSync(File.normalizePath([properties.directories.retrieve, cred, bu]));
426
+ await File.remove(File.normalizePath([properties.directories.retrieve, cred, bu]));
424
427
  // removes subtypes and removes duplicates
425
428
  retrieveTypesArr.push(
426
429
  ...new Set(properties.metaDataTypes.retrieve.map((type) => type.split('-')[0]))
@@ -117,10 +117,11 @@ class Asset extends MetadataType {
117
117
  *
118
118
  * @param {void | string[]} [_] parameter not used
119
119
  * @param {string[]} [subTypeArr] optionally limit to a single subtype
120
+ * @param {void | string} [__] parameter not used
120
121
  * @param {boolean} [loadShared] optionally retrieve assets from other BUs that were shared with the current
121
122
  * @returns {Promise.<{metadata: AssetMap, type: string}>} Promise
122
123
  */
123
- static retrieveForCache(_, subTypeArr, loadShared = false) {
124
+ static retrieveForCache(_, subTypeArr, __, loadShared = false) {
124
125
  return this.retrieve(null, null, subTypeArr, undefined, loadShared);
125
126
  }
126
127
 
@@ -1010,6 +1011,11 @@ class Asset extends MetadataType {
1010
1011
  * @param {MetadataTypeItem} metadata a single item
1011
1012
  */
1012
1013
  static setFolderId(metadata) {
1014
+ if (!metadata.r__folder_Path) {
1015
+ throw new Error(
1016
+ `Dependent folder could not be found because r__folder_Path is not set`
1017
+ );
1018
+ }
1013
1019
  metadata.category = {
1014
1020
  id: cache.searchForField('folder', metadata.r__folder_Path, 'Path', 'ID'),
1015
1021
  };
@@ -1158,7 +1164,7 @@ class Asset extends MetadataType {
1158
1164
  readDirArr = [deployDir, ...subDirArr, templateFileName];
1159
1165
  const fileName = 'content' + subtypeExtension;
1160
1166
 
1161
- const fileExtArr = ['html']; // eslint-disable-line no-case-declarations
1167
+ const fileExtArr = ['html'];
1162
1168
  for (const ext of fileExtArr) {
1163
1169
  if (
1164
1170
  await File.pathExists(
@@ -1327,7 +1333,7 @@ class Asset extends MetadataType {
1327
1333
  // metadata.content
1328
1334
  subDirArr = [this.definition.type, subType];
1329
1335
  readDirArr = [deployDir, ...subDirArr];
1330
- const fileExtArr = ['html', 'ssjs', 'amp']; // eslint-disable-line no-case-declarations
1336
+ const fileExtArr = ['html', 'ssjs', 'amp'];
1331
1337
  for (const ext of fileExtArr) {
1332
1338
  if (
1333
1339
  await File.pathExists(
@@ -1510,7 +1516,7 @@ class Asset extends MetadataType {
1510
1516
  case 'template': {
1511
1517
  // template-template
1512
1518
  // metadata.content
1513
- const fileExt = 'html'; // eslint-disable-line no-case-declarations
1519
+ const fileExt = 'html';
1514
1520
  if (metadata?.content?.length) {
1515
1521
  codeArr.push({
1516
1522
  subFolder: null,
@@ -2116,7 +2122,7 @@ class Asset extends MetadataType {
2116
2122
  * @param {string} subType asset subtype
2117
2123
  * @param {object} item api response for metadata
2118
2124
  * @param {string} buName owner business unit name
2119
- * @returns {string} path to the asset's code
2125
+ * @returns {Promise.<string>} path to the asset's code
2120
2126
  */
2121
2127
  static async #getPath(subType, item, buName) {
2122
2128
  const pathBase1 = `./retrieve/${this.buObject.credential}/${buName}/${this.definition.type}/${subType}/${item[this.definition.keyField]}.${this.definition.type}-${subType}-meta.`;
@@ -506,6 +506,8 @@ class Automation extends MetadataType {
506
506
  );
507
507
  }
508
508
  }
509
+ // In some cases the displayOrder and array order are not equal which leads to a different order every time we retrieve & deployed the automation. To prevent that, we sort the activities by displayOrder on retrieve
510
+ step.activities.sort((a, b) => a.displayOrder - b.displayOrder);
509
511
  }
510
512
  }
511
513
  return structuredClone(metadata);
@@ -846,25 +848,25 @@ class Automation extends MetadataType {
846
848
  for (const activity of step.activities) {
847
849
  activity.displayOrder = ++displayOrder;
848
850
  if (
849
- activity.name &&
851
+ activity.r__key &&
850
852
  this.definition.dependencies.includes(activity.r__type)
851
853
  ) {
852
854
  if (
853
855
  activity.r__type === 'verification' &&
854
- this.createdKeyMap?.[buName]?.verification?.[activity.name]
856
+ this.createdKeyMap?.[buName]?.verification?.[activity.r__key]
855
857
  ) {
856
- Util.logger.info(
858
+ Util.logger.debug(
857
859
  Util.getGrayMsg(
858
860
  ` - updated verification activity name from ${
859
- activity.name
861
+ activity.r__key
860
862
  } to ${
861
- this.createdKeyMap[buName].verification[activity.name]
863
+ this.createdKeyMap[buName].verification[activity.r__key]
862
864
  }`
863
865
  )
864
866
  );
865
867
  // map structure: cred/bu --> type --> old key --> new key
866
- activity.name =
867
- this.createdKeyMap[buName].verification[activity.name];
868
+ activity.r__key =
869
+ this.createdKeyMap[buName].verification[activity.r__key];
868
870
  }
869
871
  // automations can have empty placeholder for activities with only their type defined
870
872
  activity.activityObjectId = cache.searchForField(
@@ -873,9 +875,16 @@ class Automation extends MetadataType {
873
875
  Definitions[activity.r__type].keyField,
874
876
  Definitions[activity.r__type].idField
875
877
  );
878
+ activity.name = cache.searchForField(
879
+ activity.r__type,
880
+ activity.r__key,
881
+ Definitions[activity.r__type].keyField,
882
+ Definitions[activity.r__type].nameField
883
+ );
876
884
  }
877
885
  activity.objectTypeId =
878
886
  this.definition.activityTypeMapping[activity.r__type];
887
+ delete activity.r__key;
879
888
  delete activity.r__type;
880
889
  }
881
890
  step.annotation = step.name;
@@ -911,13 +920,13 @@ class Automation extends MetadataType {
911
920
  // check if manual deploy required. if so then log warning
912
921
  if (this.definition.manualDeployTypes.includes(activity.r__type)) {
913
922
  Util.logger.warn(
914
- `- ${this.definition.type} '${metadata.name}' requires additional manual configuration: '${activity.name}' in step ${stepNumber}.${displayOrder}`
923
+ `- ${this.definition.type} '${metadata.name}' requires additional manual configuration: '${activity.r__key || activity.name}' in step ${stepNumber}.${displayOrder}`
915
924
  );
916
925
  }
917
926
  // cannot deploy because it is not supported
918
927
  else if (!this.definition.dependencies.includes(activity.r__type)) {
919
928
  errors.push(
920
- ` • not supported ${activity.r__type} activity '${activity.name}' in step ${stepNumber}.${displayOrder}`
929
+ ` • not supported ${activity.r__type} activity '${activity.r__key || activity.name}' in step ${stepNumber}.${displayOrder}`
921
930
  );
922
931
  deployable = false;
923
932
  }
@@ -1169,36 +1178,6 @@ class Automation extends MetadataType {
1169
1178
  }
1170
1179
  }
1171
1180
 
1172
- /**
1173
- * automation-specific script that retrieves the folder ID from cache and updates the given metadata with it before deploy
1174
- *
1175
- * @param {MetadataTypeItem} metadata a single item
1176
- */
1177
- static setFolderId(metadata) {
1178
- try {
1179
- metadata.categoryId = cache.searchForField(
1180
- 'folder',
1181
- metadata.r__folder_Path,
1182
- 'Path',
1183
- 'ID'
1184
- );
1185
- if (metadata.r__folder_Path !== 'my automations') {
1186
- Util.logger.warn(
1187
- ` - Automation '${
1188
- metadata[this.definition.nameField]
1189
- }' is located in subfolder ${
1190
- metadata.r__folder_Path
1191
- }. Please note that creating automation folders is not supported via API and hence you will have to create it manually in the GUI if you choose to deploy this automation.`
1192
- );
1193
- }
1194
- delete metadata.r__folder_Path;
1195
- } catch {
1196
- throw new Error(
1197
- `Folder '${metadata.r__folder_Path}' was not found on the server. Please create this manually in the GUI. Automation-folders cannot be deployed automatically.`
1198
- );
1199
- }
1200
- }
1201
-
1202
1181
  /**
1203
1182
  * Builds a schedule object to be used for scheduling an automation
1204
1183
  * based on combination of ical string and start/end dates.
@@ -1543,13 +1522,15 @@ class Automation extends MetadataType {
1543
1522
  static async document(metadata) {
1544
1523
  if (['md', 'both'].includes(this.properties.options.documentType)) {
1545
1524
  if (!metadata) {
1546
- metadata = await this.readBUMetadataForType(
1547
- File.normalizePath([
1548
- this.properties.directories.retrieve,
1549
- this.buObject.credential,
1550
- this.buObject.businessUnit,
1551
- ]),
1552
- true
1525
+ metadata = (
1526
+ await this.readBUMetadataForType(
1527
+ File.normalizePath([
1528
+ this.properties.directories.retrieve,
1529
+ this.buObject.credential,
1530
+ this.buObject.businessUnit,
1531
+ ]),
1532
+ true
1533
+ )
1553
1534
  ).automation;
1554
1535
  }
1555
1536
  const docPath = File.normalizePath([
@@ -1353,13 +1353,15 @@ class DataExtension extends MetadataType {
1353
1353
  static async document(metadataMap) {
1354
1354
  try {
1355
1355
  if (!metadataMap) {
1356
- metadataMap = await this.readBUMetadataForType(
1357
- File.normalizePath([
1358
- this.properties.directories.retrieve,
1359
- this.buObject.credential,
1360
- this.buObject.businessUnit,
1361
- ]),
1362
- true
1356
+ metadataMap = (
1357
+ await this.readBUMetadataForType(
1358
+ File.normalizePath([
1359
+ this.properties.directories.retrieve,
1360
+ this.buObject.credential,
1361
+ this.buObject.businessUnit,
1362
+ ]),
1363
+ true
1364
+ )
1363
1365
  ).dataExtension;
1364
1366
  }
1365
1367
  } catch (ex) {
@@ -351,29 +351,42 @@ class Folder extends MetadataType {
351
351
  }
352
352
  const path = metadataEntry.Path;
353
353
  try {
354
- // * We tried using the SOAP endpoint for creating folders but that did not support folders for automations nor journeys. This rest endpoint seems to cover everything though
355
- const restPayload = {
356
- parentCatId: metadataEntry.ParentFolder.ID,
357
- name: metadataEntry.Name,
358
- catType: metadataEntry.ContentType,
359
- };
360
- const response = await super.createREST(restPayload, '/email/v1/category', true);
361
- if (response?.objectId) {
362
- // convert the response to the same format as the SOAP response
363
- metadataEntry.ID = response.objectId;
364
- // the following is a bit of a hack to make the response look like the SOAP response; not sure if we actually need that anywhere like this --> future developers feel free to double check
365
- const returnObject = {
366
- Results: [
367
- {
368
- Object: metadataEntry,
369
- },
370
- ],
354
+ if (this.definition.deployFolderTypesRest.includes(metadataEntry.ContentType)) {
355
+ // * The SOAP endpoint for creating folders does not support folders for automations nor journeys. The Rest endpoint on the other hand errors out on certain characters in the folder names that are actually valid. We therefore only use Rest for the folder types that are not supported by SOAP.
356
+ const restPayload = {
357
+ parentCatId: metadataEntry.ParentFolder.ID,
358
+ name: metadataEntry.Name,
359
+ catType: metadataEntry.ContentType,
371
360
  };
361
+ const response = await super.createREST(restPayload, '/email/v1/category', true);
362
+ if (response?.objectId) {
363
+ // convert the response to the same format as the SOAP response
364
+ metadataEntry.ID = response.objectId;
365
+ // the following is a bit of a hack to make the response look like the SOAP response; not sure if we actually need that anywhere like this --> future developers feel free to double check
366
+ const returnObject = {
367
+ Results: [
368
+ {
369
+ Object: metadataEntry,
370
+ },
371
+ ],
372
+ };
372
373
 
373
- Util.logger.info(` - created folder: ${path}`);
374
- return returnObject;
374
+ Util.logger.info(` - created folder: ${path}`);
375
+ return returnObject;
376
+ } else {
377
+ throw new Error(response);
378
+ }
375
379
  } else {
376
- throw new Error(response);
380
+ const response = await super.createSOAP(metadataEntry, true);
381
+ if (response) {
382
+ response.Results[0].Object = metadataEntry;
383
+ response.Results[0].Object.ID = response.Results[0].NewID;
384
+ response.Results[0].Object.CustomerKey = metadataEntry.CustomerKey;
385
+ delete response.Results[0].Object.$;
386
+
387
+ Util.logger.info(` - created folder: ${path}`);
388
+ return response;
389
+ }
377
390
  }
378
391
  } catch (ex) {
379
392
  if (ex?.results) {
@@ -49,8 +49,6 @@ class Journey extends MetadataType {
49
49
  let singleKey = '';
50
50
  let mode = 'key';
51
51
  if (key) {
52
- /* eslint-disable unicorn/prefer-ternary */
53
-
54
52
  if (key.startsWith('id:') || key.startsWith('%23')) {
55
53
  // ! allow selecting journeys by ID because that's what users see in the URL
56
54
  // if the key started with %23 assume an ID was copied from the URL but the user forgot to prefix it with id:
@@ -71,7 +69,6 @@ class Journey extends MetadataType {
71
69
  // assume actual key was provided
72
70
  singleKey = 'key:' + encodeURIComponent(key);
73
71
  }
74
- /* eslint-enable unicorn/prefer-ternary */
75
72
  }
76
73
 
77
74
  try {
@@ -176,7 +173,7 @@ class Journey extends MetadataType {
176
173
  static async deleteByKey(key) {
177
174
  let version;
178
175
  let singleKey = '';
179
- /* eslint-disable unicorn/prefer-ternary */
176
+
180
177
  if (key.startsWith('id:') || key.startsWith('%23')) {
181
178
  // ! allow selecting journeys by ID because that's what users see in the URL
182
179
  // if the key started with %23 assume an ID was copied from the URL but the user forgot to prefix it with id:
@@ -223,7 +220,7 @@ class Journey extends MetadataType {
223
220
  Util.logger.warn(
224
221
  `Deleting Journeys via this command breaks following retrieve-by-key/id requests until you've deployed/created a new draft version! You can get still get the latest available version of your journey by retrieving all interactions on this BU.`
225
222
  );
226
- /* eslint-enable unicorn/prefer-ternary */
223
+
227
224
  return super.deleteByKeyREST(
228
225
  '/interaction/v1/interactions/' + singleKey + `?versionNumber=${version}`,
229
226
  key,
@@ -245,6 +245,11 @@ class MetadataType {
245
245
  if (!this.definition.folderIdField) {
246
246
  return;
247
247
  }
248
+ if (!metadata.r__folder_Path) {
249
+ throw new Error(
250
+ `Dependent folder could not be found because r__folder_Path is not set`
251
+ );
252
+ }
248
253
  metadata[this.definition.folderIdField] = cache.searchForField(
249
254
  'folder',
250
255
  metadata.r__folder_Path,
@@ -1831,7 +1836,7 @@ class MetadataType {
1831
1836
  * @param {string[]} baseDir [retrieveDir, ...overrideType.split('-')]
1832
1837
  * @param {string} [subtypeExtension] e.g. ".asset-meta" or ".query-meta"
1833
1838
  * @param {TemplateMap} [templateVariables] variables to be replaced in the metadata
1834
- * @returns {MetadataTypeItem} saved metadata
1839
+ * @returns {Promise.<MetadataTypeItem>} saved metadata
1835
1840
  */
1836
1841
  static async saveToDisk(results, originalKey, baseDir, subtypeExtension, templateVariables) {
1837
1842
  subtypeExtension ||= '.' + this.definition.type + '-meta';
@@ -528,6 +528,7 @@ export default {
528
528
  template: false,
529
529
  },
530
530
  'steps[].activities[].displayOrder': {
531
+ // we remove it during post-processing but need to ensure the array order equals the displayOrder
531
532
  isCreateable: true,
532
533
  isUpdateable: true,
533
534
  retrieving: false,
@@ -4,6 +4,7 @@ export default {
4
4
  subTypes: [
5
5
  'asset-shared',
6
6
  'asset',
7
+ 'automations',
7
8
  'contextual_suppression_list',
8
9
  'dataextension',
9
10
  'filteractivity',
@@ -52,6 +53,7 @@ export default {
52
53
  'triggered_send_journeybuilder',
53
54
  'triggered_send',
54
55
  ],
56
+ deployFolderTypesRest: ['automations', 'journey', 'triggered_send_journeybuilder'],
55
57
  deployFolderBlacklist: [
56
58
  // lower-case values!
57
59
  'shared data extensions',