From 92926ac7c57d0e82b7925144c287c54bab9605ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 15 May 2024 19:08:56 +0200 Subject: [PATCH 1/8] #1325: upgrade buildDefinition to accept --metadata --- lib/Builder.js | 28 ++++++++-------------------- lib/cli.js | 30 ++++++++++++++++++++++++++---- lib/index.js | 41 ++++++++++++++++++++++++++++++++++------- 3 files changed, 68 insertions(+), 31 deletions(-) diff --git a/lib/Builder.js b/lib/Builder.js index 46740e707..35faa6644 100644 --- a/lib/Builder.js +++ b/lib/Builder.js @@ -62,23 +62,11 @@ saved * Builds a specific metadata file by name * * @param {string} metadataType metadata type to build - * @param {string} name name of metadata to build + * @param {string[]} nameArr name of metadata to build * @param {TemplateMap} templateVariables variables to be replaced in the metadata * @returns {Promise.} Promise */ - async _buildDefinition(metadataType, name, templateVariables) { - let nameArr; - /* eslint-disable unicorn/prefer-ternary */ - if (name.includes(',')) { - nameArr = name.split(',').map((item) => - // allow whitespace in comma-separated lists - item.trim() - ); - } else { - nameArr = [name.trim()]; - } - /* eslint-enable unicorn/prefer-ternary */ - + async _buildDefinition(metadataType, nameArr, templateVariables) { const type = metadataType; try { const result = await Promise.all( @@ -179,11 +167,11 @@ saved * * @param {string} businessUnit references credentials from properties.json * @param {string} selectedType supported metadata type - * @param {string} name name of the metadata + * @param {string[]} nameArr name of the metadata * @param {string} market market localizations * @returns {Promise.} - */ - static async buildDefinition(businessUnit, selectedType, name, market) { + static async buildDefinition(businessUnit, selectedType, nameArr, market) { const properties = await config.getProperties(); if (!(await config.checkProperties(properties))) { return null; @@ -201,7 +189,7 @@ saved if (buObject !== null) { const builder = new Builder(properties, buObject); if (Util.checkMarket(market, properties)) { - return builder._buildDefinition(selectedType, name, properties.markets[market]); + return builder._buildDefinition(selectedType, nameArr, properties.markets[market]); } } } @@ -210,10 +198,10 @@ saved * * @param {string} listName name of list of BU-market combos * @param {string} type supported metadata type - * @param {string} name name of the metadata + * @param {string[]} nameArr name of the metadata * @returns {Promise.} - */ - static async buildDefinitionBulk(listName, type, name) { + static async buildDefinitionBulk(listName, type, nameArr) { const properties = await config.getProperties(); if (!(await config.checkProperties(properties))) { return null; @@ -248,7 +236,7 @@ saved if (Util.checkMarket(market, properties)) { Util.logger.info(`Executing for '${businessUnit}': '${market}'`); // omitting "await" to speed up creation - bdPromises.push(this.buildDefinition(businessUnit, type, name, market)); + bdPromises.push(this.buildDefinition(businessUnit, type, nameArr, market)); } } } diff --git a/lib/cli.js b/lib/cli.js index ece283ce5..831939f22 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -370,7 +370,7 @@ yargs(hideBin(process.argv)) }) // @ts-expect-error .command({ - command: 'buildDefinition ', + command: 'buildDefinition [TYPE] [FILENAME] [MARKET]', aliases: ['bd'], desc: 'builds metadata definition based on template', builder: (yargs) => { @@ -389,12 +389,34 @@ yargs(hideBin(process.argv)) }) .positional('MARKET', { type: 'string', - describe: 'the business unit to deploy to', + describe: 'market used for building deployable definition', + }) + .option('metadata', { + type: 'string', + alias: 'm', + group: 'Options for buildDefinition:', + describe: 'type:key combos to build template for', + }) + .option('market', { + type: 'string', + group: 'Options for buildDefinition:', + describe: 'market used for building deployable definition', }); }, handler: (argv) => { Mcdev.setOptions(argv); - Mcdev.buildDefinition(argv.BU, argv.TYPE, argv.FILENAME, argv.MARKET); + const typeKeyCombo = Mcdev.metadataToTypeKey(argv.metadata); + if ('undefined' === typeof typeKeyCombo) { + Mcdev.buildDefinition( + argv.BU, + argv.TYPE, + csvToArray(argv.FILENAME), + argv.MARKET || argv.market + ); + } else { + Mcdev.buildDefinition(argv.BU, typeKeyCombo, null, argv.MARKET || argv.market); + } + // Mcdev.buildDefinition(argv.BU, argv.TYPE, argv.FILENAME, argv.MARKET); }, }) // @ts-expect-error @@ -419,7 +441,7 @@ yargs(hideBin(process.argv)) }, handler: (argv) => { Mcdev.setOptions(argv); - Mcdev.buildDefinitionBulk(argv.LISTNAME, argv.TYPE, argv.FILENAME); + Mcdev.buildDefinitionBulk(argv.LISTNAME, argv.TYPE, csvToArray(argv.FILENAME)); }, }) // @ts-expect-error diff --git a/lib/index.js b/lib/index.js index 119328912..0b52df816 100644 --- a/lib/index.js +++ b/lib/index.js @@ -794,15 +794,42 @@ class Mcdev { * Build a specific metadata file based on a template. * * @param {string} businessUnit references credentials from properties.json - * @param {string} selectedType supported metadata type - * @param {string} name name of the metadata + * @param {string | TypeKeyCombo} selectedTypes limit retrieval to given metadata type + * @param {string[]} nameArr name of the metadata * @param {string} market market localizations * @returns {Promise.} - */ - static async buildDefinition(businessUnit, selectedType, name, market) { + static async buildDefinition(businessUnit, selectedTypes, nameArr, market) { Util.startLogger(); Util.logger.info('mcdev:: Build Definition from Template'); - return Builder.buildDefinition(businessUnit, selectedType, name, market); + let selectedTypesArr; + if ('string' === typeof selectedTypes) { + selectedTypesArr = [selectedTypes]; + } else { + selectedTypesArr = Object.keys(selectedTypes); + // check if types are valid + for (const selectedType of selectedTypesArr) { + if (!Util._isValidType(selectedType)) { + return; + } + if (!Array.isArray(selectedTypes[selectedType])) { + // we need an array of keys here + return; + } + } + } + /** @type {MultiMetadataTypeList} */ + const returnObj = {}; + for (const selectedType of selectedTypesArr) { + const result = await Builder.buildDefinition( + businessUnit, + selectedType, + 'string' === typeof selectedTypes ? nameArr : selectedTypes[selectedType], + market + ); + returnObj[selectedType] = result[selectedType]; + } + return returnObj; } /** @@ -810,13 +837,13 @@ class Mcdev { * * @param {string} listName name of list of BU-market combos * @param {string} type supported metadata type - * @param {string} name name of the metadata + * @param {string[]} nameArr name of the metadata * @returns {Promise.} - */ - static async buildDefinitionBulk(listName, type, name) { + static async buildDefinitionBulk(listName, type, nameArr) { Util.startLogger(); Util.logger.info('mcdev:: Build Definition from Template Bulk'); - return Builder.buildDefinitionBulk(listName, type, name); + return Builder.buildDefinitionBulk(listName, type, nameArr); } /** * From 16d14d6d35aa40622428eb80471a97360d113d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 15 May 2024 19:10:41 +0200 Subject: [PATCH 2/8] #1325: upgrade buildTemplate to accept --metadata --- lib/cli.js | 25 +++++++++++++++++++++++-- lib/index.js | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index 831939f22..e359eefa5 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -340,7 +340,7 @@ yargs(hideBin(process.argv)) }) // @ts-expect-error .command({ - command: 'buildTemplate ', + command: 'buildTemplate [TYPE] [KEY] [MARKET]', aliases: ['bt'], desc: 'builds a template out of a specific metadata file already in your retrieve folder', builder: (yargs) => { @@ -361,11 +361,32 @@ yargs(hideBin(process.argv)) .positional('MARKET', { type: 'string', describe: 'market used for reverse building template', + }) + .option('metadata', { + type: 'string', + alias: 'm', + group: 'Options for buildTemplate:', + describe: 'type:key combos to build template for', + }) + .option('market', { + type: 'string', + group: 'Options for buildTemplate:', + describe: 'market used for reverse building template', }); }, handler: (argv) => { Mcdev.setOptions(argv); - Mcdev.buildTemplate(argv.BU, argv.TYPE, csvToArray(argv.KEY), argv.MARKET); + const typeKeyCombo = Mcdev.metadataToTypeKey(argv.metadata); + if ('undefined' === typeof typeKeyCombo) { + Mcdev.buildTemplate( + argv.BU, + argv.TYPE, + csvToArray(argv.KEY), + argv.MARKET || argv.market + ); + } else { + Mcdev.buildTemplate(argv.BU, typeKeyCombo, null, argv.MARKET || argv.market); + } }, }) // @ts-expect-error diff --git a/lib/index.js b/lib/index.js index 0b52df816..0456c481c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -780,15 +780,42 @@ class Mcdev { * Build a template based on a list of metadata files in the retrieve folder. * * @param {string} businessUnit references credentials from properties.json - * @param {string} selectedType supported metadata type + * @param {string | TypeKeyCombo} selectedTypes limit retrieval to given metadata type * @param {string[]} keyArr customerkey of the metadata * @param {string} market market localizations * @returns {Promise.} - */ - static async buildTemplate(businessUnit, selectedType, keyArr, market) { + static async buildTemplate(businessUnit, selectedTypes, keyArr, market) { Util.startLogger(); Util.logger.info('mcdev:: Build Template from retrieved files'); - return Builder.buildTemplate(businessUnit, selectedType, keyArr, market); + let selectedTypesArr; + if ('string' === typeof selectedTypes) { + selectedTypesArr = [selectedTypes]; + } else { + selectedTypesArr = Object.keys(selectedTypes); + // check if types are valid + for (const selectedType of selectedTypesArr) { + if (!Util._isValidType(selectedType)) { + return; + } + if (!Array.isArray(selectedTypes[selectedType])) { + // we need an array of keys here + return; + } + } + } + /** @type {MultiMetadataTypeList} */ + const returnObj = {}; + for (const selectedType of selectedTypesArr) { + const result = await Builder.buildTemplate( + businessUnit, + selectedType, + 'string' === typeof selectedTypes ? keyArr : selectedTypes[selectedType], + market + ); + returnObj[selectedType] = result[selectedType]; + } + return returnObj; } /** * Build a specific metadata file based on a template. From e73f3f39fc9829cc7139a4a0997315406e64f084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 15 May 2024 19:18:06 +0200 Subject: [PATCH 3/8] #0: add deprecation notice to retrieveAsTemplate --- lib/cli.js | 3 ++- lib/index.js | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index e359eefa5..b59ef2495 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -312,7 +312,7 @@ yargs(hideBin(process.argv)) .command({ command: 'retrieveAsTemplate ', aliases: ['rt'], - desc: 'Retrieves a specific metadata file by name from the server for templating', + desc: '[DEPRECATED] Retrieves a specific metadata file by name from the server for templating', builder: (yargs) => { yargs .positional('BU', { @@ -337,6 +337,7 @@ yargs(hideBin(process.argv)) Mcdev.setOptions(argv); Mcdev.retrieveAsTemplate(argv.BU, argv.TYPE, csvToArray(argv.NAME), argv.MARKET); }, + deprecated: true, }) // @ts-expect-error .command({ diff --git a/lib/index.js b/lib/index.js index 0456c481c..38646935a 100644 --- a/lib/index.js +++ b/lib/index.js @@ -745,7 +745,9 @@ class Mcdev { */ static async retrieveAsTemplate(businessUnit, selectedType, name, market) { Util.startLogger(); - Util.logger.info('mcdev:: Retrieve as Template'); + Util.logger.warn( + 'mcdev:: [DEPRECATED] Retrieve as Template [DEPRECATED] - use "retrieve" + "buildTemplate" instead' + ); const properties = await config.getProperties(); if (!(await config.checkProperties(properties))) { return null; From af1b42a1c25ce00765923db73ed2aabee78303bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 15 May 2024 19:19:56 +0200 Subject: [PATCH 4/8] #0: update copyright notice in yargs help to 2024 --- lib/cli.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli.js b/lib/cli.js index b59ef2495..9286960b2 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -791,7 +791,7 @@ yargs(hideBin(process.argv)) .recommendCommands() .wrap(yargs(hideBin(process.argv)).terminalWidth()) .epilog( - 'Copyright 2023. Accenture. Get support at https://github.com/Accenture/sfmc-devtools/issues' + 'Copyright 2024. Accenture. Get support at https://github.com/Accenture/sfmc-devtools/issues' ) .help().argv; From 8391f4768b9c98728e6d49ced7198fc94865e407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 15 May 2024 19:33:53 +0200 Subject: [PATCH 5/8] #1325: adapt test cases to changed parameter type --- test/type.automation.test.js | 4 ++-- test/type.dataExtension.test.js | 4 ++-- test/type.dataExtract.test.js | 4 ++-- test/type.emailSend.test.js | 2 +- test/type.fileTransfer.test.js | 4 ++-- test/type.importFile.test.js | 4 ++-- test/type.journey.test.js | 2 +- test/type.mobileKeyword.test.js | 2 +- test/type.mobileMessage.test.js | 2 +- test/type.query.test.js | 4 ++-- test/type.script.test.js | 4 ++-- test/type.sendClassification.test.js | 2 +- test/type.senderProfile.test.js | 2 +- test/type.transactionalEmail.test.js | 2 +- test/type.transactionalPush.test.js | 2 +- test/type.transactionalSMS.test.js | 2 +- test/type.triggeredSend.test.js | 2 +- test/type.user.test.js | 2 +- test/type.verification.test.js | 2 +- 19 files changed, 26 insertions(+), 26 deletions(-) diff --git a/test/type.automation.test.js b/test/type.automation.test.js index 5e143625b..96cc86460 100644 --- a/test/type.automation.test.js +++ b/test/type.automation.test.js @@ -604,7 +604,7 @@ describe('type: automation', () => { await handler.buildDefinition( 'testInstance/testBU', 'automation', - 'testExisting_automation', + ['testExisting_automation'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); @@ -647,7 +647,7 @@ describe('type: automation', () => { await handler.buildDefinition( 'testInstance/testBU', 'automation', - 'testExisting_automation', + ['testExisting_automation'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); diff --git a/test/type.dataExtension.test.js b/test/type.dataExtension.test.js index d615f0d95..d53101ed8 100644 --- a/test/type.dataExtension.test.js +++ b/test/type.dataExtension.test.js @@ -272,7 +272,7 @@ describe('type: dataExtension', () => { await handler.buildDefinition( 'testInstance/testBU', 'dataExtension', - 'testExisting_dataExtension', + ['testExisting_dataExtension'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); @@ -318,7 +318,7 @@ describe('type: dataExtension', () => { await handler.buildDefinition( 'testInstance/testBU', 'dataExtension', - 'testExisting_dataExtension', + ['testExisting_dataExtension'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); diff --git a/test/type.dataExtract.test.js b/test/type.dataExtract.test.js index 1677e0079..d204fa0ac 100644 --- a/test/type.dataExtract.test.js +++ b/test/type.dataExtract.test.js @@ -109,7 +109,7 @@ describe('type: dataExtract', () => { await handler.buildDefinition( 'testInstance/testBU', 'dataExtract', - 'testExisting_dataExtract', + ['testExisting_dataExtract'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); @@ -151,7 +151,7 @@ describe('type: dataExtract', () => { await handler.buildDefinition( 'testInstance/testBU', 'dataExtract', - 'testExisting_dataExtract', + ['testExisting_dataExtract'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); diff --git a/test/type.emailSend.test.js b/test/type.emailSend.test.js index 54d12e0ad..ebd055a12 100644 --- a/test/type.emailSend.test.js +++ b/test/type.emailSend.test.js @@ -109,7 +109,7 @@ describe('type: emailSend', () => { await handler.buildDefinition( 'testInstance/testBU', 'emailSend', - 'testExisting_emailSend', + ['testExisting_emailSend'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); diff --git a/test/type.fileTransfer.test.js b/test/type.fileTransfer.test.js index 72eed54c1..c93dc40dd 100644 --- a/test/type.fileTransfer.test.js +++ b/test/type.fileTransfer.test.js @@ -106,7 +106,7 @@ describe('type: fileTransfer', () => { await handler.buildDefinition( 'testInstance/testBU', 'fileTransfer', - 'testExisting_fileTransfer', + ['testExisting_fileTransfer'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); @@ -148,7 +148,7 @@ describe('type: fileTransfer', () => { await handler.buildDefinition( 'testInstance/testBU', 'fileTransfer', - 'testExisting_fileTransfer', + ['testExisting_fileTransfer'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); diff --git a/test/type.importFile.test.js b/test/type.importFile.test.js index 1de52f156..81335cd85 100644 --- a/test/type.importFile.test.js +++ b/test/type.importFile.test.js @@ -141,7 +141,7 @@ describe('type: importFile', () => { await handler.buildDefinition( 'testInstance/testBU', 'importFile', - 'testExisting_importFile', + ['testExisting_importFile'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); @@ -183,7 +183,7 @@ describe('type: importFile', () => { await handler.buildDefinition( 'testInstance/testBU', 'importFile', - 'testExisting_importFile', + ['testExisting_importFile'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); diff --git a/test/type.journey.test.js b/test/type.journey.test.js index 421d06c19..7ffc6aa7e 100644 --- a/test/type.journey.test.js +++ b/test/type.journey.test.js @@ -217,7 +217,7 @@ describe('type: journey', () => { await handler.buildDefinition( 'testInstance/testBU', 'journey', - 'testExisting_journey_Quicksend', + ['testExisting_journey_Quicksend'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); diff --git a/test/type.mobileKeyword.test.js b/test/type.mobileKeyword.test.js index 613844ff3..0f10856df 100644 --- a/test/type.mobileKeyword.test.js +++ b/test/type.mobileKeyword.test.js @@ -211,7 +211,7 @@ describe('type: mobileKeyword', () => { await handler.buildDefinition( 'testInstance/testBU', 'mobileKeyword', - '4912312345678.TESTEXISTING_KEYWORD', + ['4912312345678.TESTEXISTING_KEYWORD'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); diff --git a/test/type.mobileMessage.test.js b/test/type.mobileMessage.test.js index 691acc40d..8823496d9 100644 --- a/test/type.mobileMessage.test.js +++ b/test/type.mobileMessage.test.js @@ -141,7 +141,7 @@ describe('type: mobileMessage', () => { await handler.buildDefinition( 'testInstance/testBU', 'mobileMessage', - 'NTIzOjc4OjA', + ['NTIzOjc4OjA'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); diff --git a/test/type.query.test.js b/test/type.query.test.js index 69949bae0..232d04cc2 100644 --- a/test/type.query.test.js +++ b/test/type.query.test.js @@ -755,7 +755,7 @@ describe('type: query', () => { await handler.buildDefinition( 'testInstance/testBU', 'query', - 'testExisting_query', + ['testExisting_query'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); @@ -807,7 +807,7 @@ describe('type: query', () => { await handler.buildDefinition( 'testInstance/testBU', 'query', - 'testExisting_query', + ['testExisting_query'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); diff --git a/test/type.script.test.js b/test/type.script.test.js index 328dacb00..c40cdcfb9 100644 --- a/test/type.script.test.js +++ b/test/type.script.test.js @@ -297,7 +297,7 @@ describe('type: script', () => { await handler.buildDefinition( 'testInstance/testBU', 'script', - 'testExisting_script', + ['testExisting_script'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); @@ -349,7 +349,7 @@ describe('type: script', () => { await handler.buildDefinition( 'testInstance/testBU', 'script', - 'testExisting_script', + ['testExisting_script'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); diff --git a/test/type.sendClassification.test.js b/test/type.sendClassification.test.js index 6e88f865a..d269cf4d0 100644 --- a/test/type.sendClassification.test.js +++ b/test/type.sendClassification.test.js @@ -118,7 +118,7 @@ describe('type: sendClassification', () => { await handler.buildDefinition( 'testInstance/testBU', 'sendClassification', - 'testExisting_sendClassification', + ['testExisting_sendClassification'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); diff --git a/test/type.senderProfile.test.js b/test/type.senderProfile.test.js index a148880aa..90356b34e 100644 --- a/test/type.senderProfile.test.js +++ b/test/type.senderProfile.test.js @@ -112,7 +112,7 @@ describe('type: senderProfile', () => { await handler.buildDefinition( 'testInstance/testBU', 'senderProfile', - 'testExisting_senderProfile', + ['testExisting_senderProfile'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); diff --git a/test/type.transactionalEmail.test.js b/test/type.transactionalEmail.test.js index 56d690c62..a3d0a8447 100644 --- a/test/type.transactionalEmail.test.js +++ b/test/type.transactionalEmail.test.js @@ -126,7 +126,7 @@ describe('type: transactionalEmail', () => { await handler.buildDefinition( 'testInstance/testBU', 'transactionalEmail', - 'testExisting_temail', + ['testExisting_temail'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); diff --git a/test/type.transactionalPush.test.js b/test/type.transactionalPush.test.js index d45bc0b50..5a0221b4b 100644 --- a/test/type.transactionalPush.test.js +++ b/test/type.transactionalPush.test.js @@ -126,7 +126,7 @@ describe('type: transactionalPush', () => { await handler.buildDefinition( 'testInstance/testBU', 'transactionalPush', - 'testExisting_tpush', + ['testExisting_tpush'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); diff --git a/test/type.transactionalSMS.test.js b/test/type.transactionalSMS.test.js index ee19fe601..8915390a5 100644 --- a/test/type.transactionalSMS.test.js +++ b/test/type.transactionalSMS.test.js @@ -151,7 +151,7 @@ describe('type: transactionalSMS', () => { await handler.buildDefinition( 'testInstance/testBU', 'transactionalSMS', - 'testExisting_tsms', + ['testExisting_tsms'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); diff --git a/test/type.triggeredSend.test.js b/test/type.triggeredSend.test.js index 5fd16de52..5bfcc87b2 100644 --- a/test/type.triggeredSend.test.js +++ b/test/type.triggeredSend.test.js @@ -111,7 +111,7 @@ describe('type: triggeredSend', () => { await handler.buildDefinition( 'testInstance/testBU', 'triggeredSend', - 'testExisting_triggeredSend', + ['testExisting_triggeredSend'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); diff --git a/test/type.user.test.js b/test/type.user.test.js index d512482c6..16100fe2f 100644 --- a/test/type.user.test.js +++ b/test/type.user.test.js @@ -198,7 +198,7 @@ describe('type: user', () => { await handler.buildDefinition( 'testInstance/_ParentBU_', 'user', - 'testExisting_user', + ['testExisting_user'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); diff --git a/test/type.verification.test.js b/test/type.verification.test.js index c2aa57f8c..34a17cc16 100644 --- a/test/type.verification.test.js +++ b/test/type.verification.test.js @@ -133,7 +133,7 @@ describe('type: verification', () => { await handler.buildDefinition( 'testInstance/testBU', 'verification', - 'testExisting_39f6a488-20eb-4ba0-b0b9', + ['testExisting_39f6a488-20eb-4ba0-b0b9'], 'testTargetMarket' ); assert.equal(process.exitCode, 0, 'buildDefinition should not have thrown an error'); From 9724aa8373e5fd085f18ba31dda551a0d362e03b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 16 May 2024 00:17:44 +0200 Subject: [PATCH 6/8] #1296: handle unknown keys gracefully in buildTemplate --- lib/Builder.js | 4 ++-- lib/metadataTypes/MetadataType.js | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/Builder.js b/lib/Builder.js index 35faa6644..e722dd6ba 100644 --- a/lib/Builder.js +++ b/lib/Builder.js @@ -154,8 +154,8 @@ saved ); }) ); - if (result) { - this.metadata[result[0].type] = result.map((element) => element.metadata); + if (result && type === result[0].type) { + this.metadata[type] = result.filter(Boolean).map((element) => element.metadata); } } catch (ex) { Util.logger.errorStack(ex, 'mcdev.buildTemplate'); diff --git a/lib/metadataTypes/MetadataType.js b/lib/metadataTypes/MetadataType.js index bc9d5caf3..397fb3994 100644 --- a/lib/metadataTypes/MetadataType.js +++ b/lib/metadataTypes/MetadataType.js @@ -369,6 +369,13 @@ class MetadataType { } // return; } + if (!metadataStr) { + Util.logger.info( + Util.getGrayMsg(`- skipped ${this.definition.type} ${key}: not found`) + ); + return; + } + if (this.definition.stringifyFieldsBeforeTemplate) { // numeric fields are returned as numbers by the SDK/API. If we try to replace them in buildTemplate it would break the JSON format - but not if we stringify them first because then the {{{var}}} is in quotes metadataStr = JSON.parse(metadataStr); From c482414338685884ec5c2abc0d91866ea60d0c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 16 May 2024 00:28:35 +0200 Subject: [PATCH 7/8] #1296: handle unknown keys gracefully in buildDefinition --- lib/Builder.js | 7 ++----- lib/metadataTypes/MetadataType.js | 8 ++++++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/Builder.js b/lib/Builder.js index e722dd6ba..738984d33 100644 --- a/lib/Builder.js +++ b/lib/Builder.js @@ -86,11 +86,8 @@ saved ); }) ); - if (result) { - this.metadata[result[0].type] = []; - for (const element of result) { - this.metadata[result[0].type].push(element.metadata); - } + if (result && type === result[0].type) { + this.metadata[type] = result.filter(Boolean).map((element) => element.metadata); } } catch (ex) { Util.logger.errorStack(ex, 'mcdev.buildDefinition'); diff --git a/lib/metadataTypes/MetadataType.js b/lib/metadataTypes/MetadataType.js index 397fb3994..153515858 100644 --- a/lib/metadataTypes/MetadataType.js +++ b/lib/metadataTypes/MetadataType.js @@ -1968,6 +1968,14 @@ class MetadataType { } // return; } + if (!metadataStr) { + Util.logger.info( + Util.getGrayMsg( + `- skipped ${this.definition.type} ${templateName}: template not found` + ) + ); + return; + } let metadata; try { From b0390323840aac8032d071304718efcbfed6e637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 16 May 2024 00:31:55 +0200 Subject: [PATCH 8/8] #1325: add test case for multi-type buildTemplate/buildDefinition --- test/general.test.js | 109 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/test/general.test.js b/test/general.test.js index 735fb9c4d..be9751cd9 100644 --- a/test/general.test.js +++ b/test/general.test.js @@ -6,6 +6,7 @@ import chaiFiles from 'chai-files'; import * as testUtils from './utils.js'; import handler from '../lib/index.js'; chai.use(chaiFiles); +const file = chaiFiles.file; describe('GENERAL', () => { beforeEach(() => { @@ -284,6 +285,114 @@ describe('GENERAL', () => { ); }); }); + + describe('template --metadata ~~~', () => { + it('buildDefinition and buildTemplate multiple type with keys', async () => { + // download first before we test buildTemplate + await handler.retrieve('testInstance/testBU', ['automation', 'query']); + + const expectedApiCallsRetrieve = 25; + assert.equal( + testUtils.getAPIHistoryLength(), + expectedApiCallsRetrieve, + 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' + ); + + // preparation + const argvMetadata = [ + 'automation:testExisting_automation', + 'query:testExisting_query', + 'query:bad', + ]; + const typeKeyCombo = handler.metadataToTypeKey(argvMetadata); + assert.notEqual( + typeof typeKeyCombo, + 'undefined', + 'typeKeyCombo should not be undefined' + ); + const buName = 'testInstance/testBU'; + + // *** buildTemplate *** + const templateResult = await handler.buildTemplate( + buName, + typeKeyCombo, + null, + 'testSourceMarket' + ); + assert.equal(process.exitCode, 0, 'buildTemplate should not have thrown an error'); + // check automation + assert.equal( + templateResult.automation ? Object.keys(templateResult.automation).length : 0, + 1, + 'only one automation expected' + ); + assert.deepEqual( + await testUtils.getActualTemplateJson('testExisting_automation', 'automation'), + await testUtils.getExpectedJson('9999999', 'automation', 'template'), + 'returned template was not equal expected' + ); + // check query + assert.equal( + templateResult.query ? Object.keys(templateResult.query).length : 0, + 1, + 'only one query expected' + ); + assert.deepEqual( + await testUtils.getActualTemplateJson('testExisting_query', 'query'), + await testUtils.getExpectedJson('9999999', 'query', 'template'), + 'returned template JSON of retrieveAsTemplate was not equal expected' + ); + expect( + file(testUtils.getActualTemplateFile('testExisting_query', 'query', 'sql')) + ).to.equal(file(testUtils.getExpectedFile('9999999', 'query', 'template', 'sql'))); + + // *** buildDefinition *** + const definitionResult = await handler.buildDefinition( + buName, + typeKeyCombo, + null, + 'testTargetMarket' + ); + assert.equal( + process.exitCode, + 0, + 'buildDefinition should not have thrown an error' + ); + + // check automation + assert.equal( + definitionResult.automation ? Object.keys(templateResult.automation).length : 0, + 1, + 'only one automation expected' + ); + assert.deepEqual( + await testUtils.getActualDeployJson('testTemplated_automation', 'automation'), + await testUtils.getExpectedJson('9999999', 'automation', 'build'), + 'returned deployment file was not equal expected' + ); + + // check query + assert.equal( + definitionResult.query ? Object.keys(templateResult.query).length : 0, + 1, + 'only one query expected' + ); + assert.deepEqual( + await testUtils.getActualDeployJson('testTemplated_query', 'query'), + await testUtils.getExpectedJson('9999999', 'query', 'build'), + 'returned deployment JSON was not equal expected' + ); + expect( + file(testUtils.getActualDeployFile('testTemplated_query', 'query', 'sql')) + ).to.equal(file(testUtils.getExpectedFile('9999999', 'query', 'build', 'sql'))); + + assert.equal( + testUtils.getAPIHistoryLength() - expectedApiCallsRetrieve, + 0, + 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' + ); + }); + }); }); describe('without --metadata ================', () => {