mcdev 7.0.2 → 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 +1 -0
- 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/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/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/Journey.js +2 -5
- package/lib/metadataTypes/MetadataType.js +6 -1
- package/lib/metadataTypes/definitions/Automation.definition.js +1 -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 +5 -3
- 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
|
@@ -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) {
|
|
@@ -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,
|
package/lib/util/init.config.js
CHANGED
|
@@ -240,7 +240,9 @@ const Init = {
|
|
|
240
240
|
const creationLog = [];
|
|
241
241
|
await File.ensureDir('deploy/');
|
|
242
242
|
await File.ensureDir('src/cloudPages');
|
|
243
|
-
const
|
|
243
|
+
const relevantForced = await this._getForcedUpdateList(versionBeforeUpgrade);
|
|
244
|
+
|
|
245
|
+
await this._removeIdeConfigFiles(relevantForced);
|
|
244
246
|
|
|
245
247
|
// copy in .gitignore (cant be retrieved via npm install directly)
|
|
246
248
|
const gitignoreFileName = path.resolve(
|
|
@@ -253,7 +255,7 @@ const Init = {
|
|
|
253
255
|
creationLog.push(
|
|
254
256
|
await this._createIdeConfigFile(
|
|
255
257
|
['.' + path.sep, '', '.gitignore'],
|
|
256
|
-
|
|
258
|
+
relevantForced,
|
|
257
259
|
fileContent
|
|
258
260
|
)
|
|
259
261
|
);
|
|
@@ -280,7 +282,7 @@ const Init = {
|
|
|
280
282
|
creationLog.push(
|
|
281
283
|
await this._createIdeConfigFile(
|
|
282
284
|
[subdir + path.sep, fileArr.join('.'), ext],
|
|
283
|
-
|
|
285
|
+
relevantForced
|
|
284
286
|
)
|
|
285
287
|
);
|
|
286
288
|
}
|
|
@@ -328,12 +330,13 @@ const Init = {
|
|
|
328
330
|
* returns list of files that need to be updated
|
|
329
331
|
*
|
|
330
332
|
* @param {string} projectVersion version found in config file of the current project
|
|
331
|
-
* @returns {Promise.<string[]>} relevant files with path that need to be updated
|
|
333
|
+
* @returns {Promise.<{updates:string[],deletes:string[]}>} relevant files with path that need to be updated
|
|
332
334
|
*/
|
|
333
335
|
async _getForcedUpdateList(projectVersion) {
|
|
334
336
|
// list of files that absolutely need to get overwritten, no questions asked, when upgrading from a version lower than the given.
|
|
335
337
|
let forceIdeConfigUpdate;
|
|
336
|
-
const
|
|
338
|
+
const updates = [];
|
|
339
|
+
const deletes = [];
|
|
337
340
|
if (await File.pathExists(Util.configFileName)) {
|
|
338
341
|
forceIdeConfigUpdate = await File.readJSON(
|
|
339
342
|
path.resolve(__dirname, Util.boilerplateDirectory, 'forcedUpdates.json')
|
|
@@ -341,27 +344,33 @@ const Init = {
|
|
|
341
344
|
// return all if no project version was found or only changes from "newer" versions otherwise
|
|
342
345
|
for (const element of forceIdeConfigUpdate) {
|
|
343
346
|
if (!projectVersion || semver.gt(element.version, projectVersion)) {
|
|
344
|
-
|
|
347
|
+
updates.push(
|
|
345
348
|
// adapt it for local file systems
|
|
346
349
|
...element.files.map((item) => path.normalize(item))
|
|
347
350
|
);
|
|
351
|
+
if (element.filesRemove) {
|
|
352
|
+
deletes.push(
|
|
353
|
+
// adapt it for local file systems
|
|
354
|
+
...element.filesRemove.map((item) => path.normalize(item))
|
|
355
|
+
);
|
|
356
|
+
}
|
|
348
357
|
} else {
|
|
349
358
|
continue;
|
|
350
359
|
}
|
|
351
360
|
}
|
|
352
361
|
}
|
|
353
362
|
|
|
354
|
-
return
|
|
363
|
+
return { updates, deletes };
|
|
355
364
|
},
|
|
356
365
|
/**
|
|
357
366
|
* handles creation/update of one config file from the boilerplate at a time
|
|
358
367
|
*
|
|
359
368
|
* @param {string[]} fileNameArr 0: path, 1: filename, 2: extension with dot
|
|
360
|
-
* @param {string[]}
|
|
369
|
+
* @param {{updates:string[],deletes:string[]}} relevantForced if fileNameArr is in this list we require an override
|
|
361
370
|
* @param {string} [boilerplateFileContent] in case we cannot copy files 1:1 this can be used to pass in content
|
|
362
371
|
* @returns {Promise.<boolean>} install successful or error occured
|
|
363
372
|
*/
|
|
364
|
-
async _createIdeConfigFile(fileNameArr,
|
|
373
|
+
async _createIdeConfigFile(fileNameArr, relevantForced, boilerplateFileContent) {
|
|
365
374
|
let update = false;
|
|
366
375
|
const fileName = fileNameArr.join('');
|
|
367
376
|
const boilerplateFileName = path.resolve(
|
|
@@ -372,16 +381,27 @@ const Init = {
|
|
|
372
381
|
);
|
|
373
382
|
boilerplateFileContent ||= await File.readFile(boilerplateFileName, 'utf8');
|
|
374
383
|
|
|
384
|
+
let todo = null;
|
|
385
|
+
|
|
375
386
|
if (await File.pathExists(fileName)) {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
387
|
+
if (relevantForced.deletes.includes(path.normalize(fileName))) {
|
|
388
|
+
Util.logger.info(
|
|
389
|
+
`- ✋ ${fileName} found but it is required to delete it. Commencing rename instead for your convenience:`
|
|
390
|
+
);
|
|
391
|
+
todo = 'delete';
|
|
392
|
+
} else {
|
|
393
|
+
const existingFileContent = await File.readFile(fileName, 'utf8');
|
|
394
|
+
if (existingFileContent === boilerplateFileContent) {
|
|
395
|
+
Util.logger.info(`- ✔️ ${fileName} found. No update needed`);
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
380
398
|
}
|
|
381
|
-
|
|
399
|
+
|
|
400
|
+
if (relevantForced.updates.includes(path.normalize(fileName))) {
|
|
382
401
|
Util.logger.info(
|
|
383
402
|
`- ✋ ${fileName} found but an update is required. Commencing with override:`
|
|
384
403
|
);
|
|
404
|
+
todo = 'update';
|
|
385
405
|
} else {
|
|
386
406
|
Util.logger.info(
|
|
387
407
|
`- ✋ ${fileName} found with differences to the new standard version. We recommend updating it.`
|
|
@@ -400,33 +420,60 @@ const Init = {
|
|
|
400
420
|
return true;
|
|
401
421
|
}
|
|
402
422
|
}
|
|
423
|
+
update = true;
|
|
403
424
|
}
|
|
404
|
-
update = true;
|
|
405
425
|
|
|
406
426
|
// ensure our update is not leading to data loss in case config files were not versioned correctly by the user
|
|
407
427
|
await File.rename(fileName, fileName + '.BAK');
|
|
428
|
+
} else if (!relevantForced.deletes.includes(path.normalize(fileName))) {
|
|
429
|
+
todo = 'create';
|
|
408
430
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
if (saveStatus) {
|
|
417
|
-
Util.logger.info(
|
|
418
|
-
`- ✔️ ${fileName} ${
|
|
419
|
-
update
|
|
420
|
-
? `updated (we created a backup of the old file under ${fileName + '.BAK'})`
|
|
421
|
-
: 'created'
|
|
422
|
-
}`
|
|
431
|
+
if (todo === 'create' || todo === 'update') {
|
|
432
|
+
const saveStatus = await File.writeToFile(
|
|
433
|
+
fileNameArr[0],
|
|
434
|
+
fileNameArr[1],
|
|
435
|
+
fileNameArr[2].slice(1),
|
|
436
|
+
boilerplateFileContent
|
|
423
437
|
);
|
|
438
|
+
|
|
439
|
+
if (saveStatus) {
|
|
440
|
+
Util.logger.info(
|
|
441
|
+
`- ✔️ ${fileName} ${
|
|
442
|
+
update
|
|
443
|
+
? `updated (we created a backup of the old file under ${fileName + '.BAK'})`
|
|
444
|
+
: 'created'
|
|
445
|
+
}`
|
|
446
|
+
);
|
|
447
|
+
return true;
|
|
448
|
+
} else {
|
|
449
|
+
Util.logger.warn(`- ❌ ${fileName} ${update ? 'update' : 'creation'} failed`);
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
} else if (todo === 'delete') {
|
|
453
|
+
await File.rename(fileName, fileName + '.BAK');
|
|
454
|
+
Util.logger.info(`- ✔️ ${fileName} removed (renamed to ${fileName + '.BAK'})`);
|
|
424
455
|
return true;
|
|
425
|
-
} else {
|
|
426
|
-
Util.logger.warn(`- ❌ ${fileName} ${update ? 'update' : 'creation'} failed`);
|
|
427
|
-
return false;
|
|
428
456
|
}
|
|
429
457
|
},
|
|
458
|
+
/**
|
|
459
|
+
* handles deletion of no longer needed config files
|
|
460
|
+
*
|
|
461
|
+
* @param {{updates:string[],deletes:string[]}} relevantForced if file is in .deletes, we require deleting/renaming it
|
|
462
|
+
* @returns {Promise.<boolean>} deletion successful or error occured
|
|
463
|
+
*/
|
|
464
|
+
async _removeIdeConfigFiles(relevantForced) {
|
|
465
|
+
for (const fileName of relevantForced.deletes) {
|
|
466
|
+
if (await File.pathExists(fileName)) {
|
|
467
|
+
Util.logger.info(
|
|
468
|
+
`- ✋ ${fileName} found but it is required to delete it. Commencing rename instead for your convenience:`
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
await File.rename(fileName, fileName + '.BAK');
|
|
472
|
+
Util.logger.info(`- ✔️ ${fileName} removed (renamed to ${fileName + '.BAK'})`);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return true;
|
|
476
|
+
},
|
|
430
477
|
/**
|
|
431
478
|
* helper method for this.upgradeProject that upgrades project config if needed
|
|
432
479
|
*
|
package/lib/util/init.npm.js
CHANGED
|
@@ -57,6 +57,8 @@ const Init = {
|
|
|
57
57
|
if (fileContent) {
|
|
58
58
|
projectPackageJson = JSON.parse(fileContent);
|
|
59
59
|
}
|
|
60
|
+
// type="module" is solely required for the new flat configs of ESLint >=9
|
|
61
|
+
Util.execSync('npm', ['pkg', 'set', 'type="module"'], true);
|
|
60
62
|
Util.logger.info('✔️ package.json created');
|
|
61
63
|
} catch {
|
|
62
64
|
Util.logger.error('No package.json found. Please run "npm init" manually');
|
|
@@ -105,6 +107,8 @@ const Init = {
|
|
|
105
107
|
!projectPackageJson.devDependencies ||
|
|
106
108
|
!projectPackageJson.devDependencies[name] ||
|
|
107
109
|
versionsDefault[name] == 'latest' ||
|
|
110
|
+
// filters out file:.. references instead of versions that would otherwise lead to an error in semver.gt()
|
|
111
|
+
!semver.valid(versionsProject[name]) ||
|
|
108
112
|
semver.gt(versionsDefault[name], versionsProject[name])
|
|
109
113
|
);
|
|
110
114
|
if (loadDependencies.length) {
|
|
@@ -152,6 +156,10 @@ const Init = {
|
|
|
152
156
|
if (!currentContent.license || currentContent.license === 'ISC') {
|
|
153
157
|
currentContent.license = 'UNLICENSED';
|
|
154
158
|
}
|
|
159
|
+
if (!currentContent.type || currentContent.type !== 'module') {
|
|
160
|
+
// type="module" is solely required for the new flat configs of ESLint >=9
|
|
161
|
+
currentContent.type = 'module';
|
|
162
|
+
}
|
|
155
163
|
return currentContent;
|
|
156
164
|
},
|
|
157
165
|
};
|
|
@@ -45,7 +45,7 @@ export default class ReplaceContentBlockReference {
|
|
|
45
45
|
key: {},
|
|
46
46
|
name: {},
|
|
47
47
|
};
|
|
48
|
-
/** @type {{id: RegExp[], key: RegExp[], name: RegExp[]}} */
|
|
48
|
+
/** @type {Object.<string, {id: RegExp[], key: RegExp[], name: RegExp[]}>} */
|
|
49
49
|
static #regexBy = {
|
|
50
50
|
// TODO: handle cases in which variables or functions are passed into ContentBlockByX
|
|
51
51
|
|
|
@@ -140,7 +140,6 @@ export default class ReplaceContentBlockReference {
|
|
|
140
140
|
}
|
|
141
141
|
/**
|
|
142
142
|
*
|
|
143
|
-
* @private
|
|
144
143
|
* @param {ContentBlockConversionTypes} from replace with
|
|
145
144
|
* @param {string|number} identifier id, key or name of asset
|
|
146
145
|
* @param {string} parentName name of the object that was passed in; used in error message only
|
|
@@ -250,6 +249,7 @@ saved
|
|
|
250
249
|
const resultAssetShared = await Asset.retrieveForCache(
|
|
251
250
|
undefined,
|
|
252
251
|
['asset', 'code', 'textfile', 'block', 'other'],
|
|
252
|
+
undefined,
|
|
253
253
|
true
|
|
254
254
|
);
|
|
255
255
|
for (const element of Object.values(resultAssetShared.metadata)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcdev",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.3",
|
|
4
4
|
"description": "Accenture Salesforce Marketing Cloud DevTools",
|
|
5
5
|
"author": "Accenture: joern.berkefeld, douglas.midgley, robert.zimmermann, maciej.barnas",
|
|
6
6
|
"license": "MIT",
|
|
@@ -86,6 +86,7 @@
|
|
|
86
86
|
"yargs": "17.7.2"
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
|
89
|
+
"@eslint/js": "9.6.0",
|
|
89
90
|
"@types/mocha": "10.0.6",
|
|
90
91
|
"@types/node": "20.14.9",
|
|
91
92
|
"assert": "2.1.0",
|
|
@@ -93,14 +94,15 @@
|
|
|
93
94
|
"c8": "10.0.0",
|
|
94
95
|
"chai": "5.1.1",
|
|
95
96
|
"chai-files": "1.4.0",
|
|
96
|
-
"eslint": "
|
|
97
|
+
"eslint": "9.6.0",
|
|
97
98
|
"eslint-config-prettier": "9.1.0",
|
|
98
|
-
"eslint-config-ssjs": "
|
|
99
|
+
"eslint-config-ssjs": "2.0.0",
|
|
99
100
|
"eslint-plugin-jsdoc": "48.2.7",
|
|
100
101
|
"eslint-plugin-mocha": "10.4.3",
|
|
101
102
|
"eslint-plugin-prettier": "5.1.2",
|
|
102
103
|
"eslint-plugin-unicorn": "53.0.0",
|
|
103
104
|
"fast-xml-parser": "4.4.0",
|
|
105
|
+
"globals": "15.6.0",
|
|
104
106
|
"husky": "9.0.11",
|
|
105
107
|
"lint-staged": "15.2.7",
|
|
106
108
|
"mocha": "10.4.0",
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "created on deploy",
|
|
3
|
+
"key": "testNew_automation",
|
|
4
|
+
"name": "testNew_automation",
|
|
5
|
+
"status": "Scheduled",
|
|
6
|
+
"steps": [
|
|
7
|
+
{
|
|
8
|
+
"activities": [
|
|
9
|
+
{
|
|
10
|
+
"displayOrder": 1,
|
|
11
|
+
"activityObjectId": "56c5370a-f988-4f36-b0ee-0f876573f6d7",
|
|
12
|
+
"name": "testExisting_dataExtract",
|
|
13
|
+
"objectTypeId": 73
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"displayOrder": 2,
|
|
17
|
+
"activityObjectId": "9b1c7bf9-4964-ed11-b849-48df37d1de8b",
|
|
18
|
+
"name": "testExisting_emailSend",
|
|
19
|
+
"objectTypeId": 42
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"displayOrder": 3,
|
|
23
|
+
"activityObjectId": "72c328ac-f5b0-4e37-91d3-a775666f15a6",
|
|
24
|
+
"name": "testExisting_fileTransfer",
|
|
25
|
+
"objectTypeId": 53
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"displayOrder": 4,
|
|
29
|
+
"activityObjectId": "9d16f42c-2260-ed11-b849-48df37d1de8b",
|
|
30
|
+
"name": "testExisting_importFile",
|
|
31
|
+
"objectTypeId": 43
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"displayOrder": 5,
|
|
35
|
+
"activityObjectId": "549f0568-607c-4940-afef-437965094dat",
|
|
36
|
+
"name": "testExisting_query",
|
|
37
|
+
"objectTypeId": 300
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"displayOrder": 6,
|
|
41
|
+
"activityObjectId": "39f6a488-20eb-4ba0-b0b9-023725b574e4",
|
|
42
|
+
"name": "testExisting_script",
|
|
43
|
+
"objectTypeId": 423
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"displayOrder": 7,
|
|
47
|
+
"activityObjectId": "testNew_RANDOM_NEW_GUID",
|
|
48
|
+
"name": "testNew_RANDOM_NEW_GUID",
|
|
49
|
+
"objectTypeId": 1000
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
"annotation": "",
|
|
53
|
+
"stepNumber": 0
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"categoryId": 290937,
|
|
57
|
+
"startSource": {
|
|
58
|
+
"schedule": {
|
|
59
|
+
"startDate": "2020-05-14T02:30:32.11",
|
|
60
|
+
"endDate": "2079-06-06T21:00:00",
|
|
61
|
+
"icalRecur": "FREQ=MINUTELY;UNTIL=20790607T050000;INTERVAL=5",
|
|
62
|
+
"timezoneId": 5
|
|
63
|
+
},
|
|
64
|
+
"typeId": 1
|
|
65
|
+
}
|
|
66
|
+
}
|