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.
- package/.github/ISSUE_TEMPLATE/bug.yml +2 -0
- package/.github/workflows/coverage-base-update.yml +2 -2
- package/.github/workflows/coverage.yml +1 -1
- package/@types/lib/index.d.ts.map +1 -1
- package/@types/lib/metadataTypes/Asset.d.ts +11 -10
- package/@types/lib/metadataTypes/Asset.d.ts.map +1 -1
- package/@types/lib/metadataTypes/Automation.d.ts +0 -6
- package/@types/lib/metadataTypes/Automation.d.ts.map +1 -1
- package/@types/lib/metadataTypes/DataExtension.d.ts.map +1 -1
- package/@types/lib/metadataTypes/Folder.d.ts +1 -0
- package/@types/lib/metadataTypes/Folder.d.ts.map +1 -1
- package/@types/lib/metadataTypes/Journey.d.ts +14 -8
- package/@types/lib/metadataTypes/Journey.d.ts.map +1 -1
- package/@types/lib/metadataTypes/MetadataType.d.ts +2 -2
- package/@types/lib/metadataTypes/MetadataType.d.ts.map +1 -1
- package/@types/lib/metadataTypes/definitions/Folder.definition.d.ts +1 -0
- package/@types/lib/util/init.config.d.ts +20 -4
- package/@types/lib/util/init.config.d.ts.map +1 -1
- package/@types/lib/util/init.npm.d.ts.map +1 -1
- package/@types/lib/util/replaceContentBlockReference.d.ts +7 -6
- package/@types/lib/util/replaceContentBlockReference.d.ts.map +1 -1
- package/boilerplate/files/eslint.config.js +82 -0
- package/boilerplate/forcedUpdates.json +5 -0
- package/boilerplate/npm-dependencies.json +2 -0
- package/eslint.config.js +143 -0
- package/lib/index.js +5 -2
- package/lib/metadataTypes/Asset.js +11 -5
- package/lib/metadataTypes/Automation.js +27 -46
- package/lib/metadataTypes/DataExtension.js +9 -7
- package/lib/metadataTypes/Folder.js +33 -20
- package/lib/metadataTypes/Journey.js +2 -5
- package/lib/metadataTypes/MetadataType.js +6 -1
- package/lib/metadataTypes/definitions/Automation.definition.js +1 -0
- package/lib/metadataTypes/definitions/Folder.definition.js +2 -0
- package/lib/util/init.config.js +79 -32
- package/lib/util/init.npm.js +8 -0
- package/lib/util/replaceContentBlockReference.js +2 -2
- package/package.json +8 -6
- package/test/mockRoot/.mcdevrc.json +1 -1
- package/test/resources/9999999/automation/create-callout-expected.json +66 -0
- package/test/resources/9999999/automation/update-callout-expected.json +68 -0
- package/test/resources/9999999/automation/v1/automations/08afb0e2-b00a-4c88-ad2e-1f7f8788c560/get-response.json +7 -7
- package/test/type.automation.test.js +46 -0
- package/test/utils.js +28 -3
- package/.eslintignore +0 -3
- package/.eslintrc.json +0 -116
- package/boilerplate/files/.eslintignore +0 -5
- package/boilerplate/files/.eslintrc +0 -37
package/eslint.config.js
ADDED
|
@@ -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
|
-
|
|
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.
|
|
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'];
|
|
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'];
|
|
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';
|
|
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.
|
|
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.
|
|
856
|
+
this.createdKeyMap?.[buName]?.verification?.[activity.r__key]
|
|
855
857
|
) {
|
|
856
|
-
Util.logger.
|
|
858
|
+
Util.logger.debug(
|
|
857
859
|
Util.getGrayMsg(
|
|
858
860
|
` - updated verification activity name from ${
|
|
859
|
-
activity.
|
|
861
|
+
activity.r__key
|
|
860
862
|
} to ${
|
|
861
|
-
this.createdKeyMap[buName].verification[activity.
|
|
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.
|
|
867
|
-
this.createdKeyMap[buName].verification[activity.
|
|
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 =
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
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 =
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
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
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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
|
-
|
|
374
|
-
|
|
374
|
+
Util.logger.info(` - created folder: ${path}`);
|
|
375
|
+
return returnObject;
|
|
376
|
+
} else {
|
|
377
|
+
throw new Error(response);
|
|
378
|
+
}
|
|
375
379
|
} else {
|
|
376
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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',
|