Skip to content

Commit

Permalink
Merge pull request #1327 from Accenture/feature/1325-add-multi-type-s…
Browse files Browse the repository at this point in the history
…upport-and-metadata-option-to-buildtemplate-and-builddefinition

Feature/1325 add multi type support and metadata option to buildtemplate and builddefinition
  • Loading branch information
JoernBerkefeld authored May 15, 2024
2 parents 65257cb + b039032 commit 56e3141
Show file tree
Hide file tree
Showing 24 changed files with 281 additions and 72 deletions.
39 changes: 12 additions & 27 deletions lib/Builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.<MultiMetadataTypeList>} 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(
Expand All @@ -98,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');
Expand Down Expand Up @@ -166,8 +151,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');
Expand All @@ -179,11 +164,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.<MultiMetadataTypeList>} -
*/
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;
Expand All @@ -201,7 +186,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]);
}
}
}
Expand All @@ -210,10 +195,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.<MultiMetadataTypeList[]>} -
*/
static async buildDefinitionBulk(listName, type, name) {
static async buildDefinitionBulk(listName, type, nameArr) {
const properties = await config.getProperties();
if (!(await config.checkProperties(properties))) {
return null;
Expand Down Expand Up @@ -248,7 +233,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));
}
}
}
Expand Down
60 changes: 52 additions & 8 deletions lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ yargs(hideBin(process.argv))
.command({
command: 'retrieveAsTemplate <BU> <TYPE> <NAME> <MARKET>',
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', {
Expand All @@ -337,10 +337,11 @@ yargs(hideBin(process.argv))
Mcdev.setOptions(argv);
Mcdev.retrieveAsTemplate(argv.BU, argv.TYPE, csvToArray(argv.NAME), argv.MARKET);
},
deprecated: true,
})
// @ts-expect-error
.command({
command: 'buildTemplate <BU> <TYPE> <KEY> <MARKET>',
command: 'buildTemplate <BU> [TYPE] [KEY] [MARKET]',
aliases: ['bt'],
desc: 'builds a template out of a specific metadata file already in your retrieve folder',
builder: (yargs) => {
Expand All @@ -361,16 +362,37 @@ 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
.command({
command: 'buildDefinition <BU> <TYPE> <FILENAME> <MARKET>',
command: 'buildDefinition <BU> [TYPE] [FILENAME] [MARKET]',
aliases: ['bd'],
desc: 'builds metadata definition based on template',
builder: (yargs) => {
Expand All @@ -389,12 +411,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
Expand All @@ -419,7 +463,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
Expand Down Expand Up @@ -747,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;

Expand Down
78 changes: 67 additions & 11 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -780,43 +782,97 @@ 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.<MultiMetadataTypeList>} -
*/
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.
*
* @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.<MultiMetadataTypeList>} -
*/
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;
}

/**
* Build a specific metadata file based on a template using a list of bu-market combos
*
* @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.<MultiMetadataTypeList[]>} -
*/
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);
}
/**
*
Expand Down
15 changes: 15 additions & 0 deletions lib/metadataTypes/MetadataType.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -1961,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 {
Expand Down
Loading

0 comments on commit 56e3141

Please sign in to comment.