From 6522f654527d81dc69a38ebc4cdf96e171084614 Mon Sep 17 00:00:00 2001 From: "douglas.midgley" Date: Sun, 22 May 2022 22:24:16 +0200 Subject: [PATCH 01/14] #294: Initial Carve out --- docs/dist/documentation.md | 107 ++++++-------- lib/Builder.js | 7 +- lib/index.js | 38 ++--- lib/util/cli.js | 5 +- lib/util/config.js | 289 +++++++++++++++++++++++++++++++++++++ lib/util/file.js | 51 ------- lib/util/init.config.js | 5 +- lib/util/init.js | 5 +- lib/util/util.js | 223 +--------------------------- 9 files changed, 365 insertions(+), 365 deletions(-) create mode 100644 lib/util/config.js diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index b81271308..1bb725b7c 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -130,6 +130,9 @@ Provides default functionality that can be overwritten by child metadata type cl
Cli

CLI helper class

+
Config
+

Central class for loading and validating properties from config and auth

+
DevOps

DevOps helper class

@@ -4139,9 +4142,7 @@ CLI entry for SFMC DevTools * [.isTrue(attrValue)](#Util.isTrue) ⇒ boolean * [.isFalse(attrValue)](#Util.isFalse) ⇒ boolean * [._isValidType(selectedType)](#Util._isValidType) ⇒ boolean - * [.getDefaultProperties()](#Util.getDefaultProperties) ⇒ object * [.getRetrieveTypeChoices()](#Util.getRetrieveTypeChoices) ⇒ Array.<string> - * [.checkProperties(properties, [silent])](#Util.checkProperties) ⇒ Promise.<(boolean\|Array.<string>)> * [.metadataLogger(level, type, method, payload, [source])](#Util.metadataLogger) ⇒ void * [.replaceByObject(str, obj)](#Util.replaceByObject) ⇒ string \| object * [.inverseGet(objs, val)](#Util.inverseGet) ⇒ string @@ -4228,14 +4229,6 @@ helper for retrieve, retrieveAsTemplate and deploy | --- | --- | --- | | selectedType | string | type or type-subtype | - - -### Util.getDefaultProperties() ⇒ object -defines how the properties.json should look like -used for creating a template and for checking if variables are set - -**Kind**: static method of [Util](#Util) -**Returns**: object - default properties ### Util.getRetrieveTypeChoices() ⇒ Array.<string> @@ -4243,19 +4236,6 @@ helper for getDefaultProperties() **Kind**: static method of [Util](#Util) **Returns**: Array.<string> - type choices - - -### Util.checkProperties(properties, [silent]) ⇒ Promise.<(boolean\|Array.<string>)> -check if the config file is correctly formatted and has values - -**Kind**: static method of [Util](#Util) -**Returns**: Promise.<(boolean\|Array.<string>)> - file structure ok OR list of fields to be fixed - -| Param | Type | Description | -| --- | --- | --- | -| properties | object | javascript object in .mcdevrc.json | -| [silent] | boolean | set to true for internal use w/o cli output | - ### Util.metadataLogger(level, type, method, payload, [source]) ⇒ void @@ -4566,6 +4546,51 @@ shows metadata type descriptions **Kind**: static method of [Cli](#Cli) **Returns**: void - - + + +## Config +Central class for loading and validating properties from config and auth + +**Kind**: global constant + +* [Config](#Config) + * [.getProperties([silent])](#Config.getProperties) ⇒ object + * [.checkProperties(properties, [silent])](#Config.checkProperties) ⇒ Promise.<(boolean\|Array.<string>)> + * [.getDefaultProperties()](#Config.getDefaultProperties) ⇒ object + + + +### Config.getProperties([silent]) ⇒ object +loads central properties from config file + +**Kind**: static method of [Config](#Config) +**Returns**: object - central properties object + +| Param | Type | Description | +| --- | --- | --- | +| [silent] | boolean | omit throwing errors and print messages; assuming not silent if not set | + + + +### Config.checkProperties(properties, [silent]) ⇒ Promise.<(boolean\|Array.<string>)> +check if the config file is correctly formatted and has values + +**Kind**: static method of [Config](#Config) +**Returns**: Promise.<(boolean\|Array.<string>)> - file structure ok OR list of fields to be fixed + +| Param | Type | Description | +| --- | --- | --- | +| properties | object | javascript object in .mcdevrc.json | +| [silent] | boolean | set to true for internal use w/o cli output | + + + +### Config.getDefaultProperties() ⇒ object +defines how the properties.json should look like +used for creating a template and for checking if variables are set + +**Kind**: static method of [Config](#Config) +**Returns**: object - default properties ## DevOps @@ -4655,7 +4680,6 @@ File extends fs-extra. It adds logger and util methods for file handling * [.readFile(directory, filename, filetype, [encoding])](#File.readFile) ⇒ Promise.<string> * [.readDirectories(directory, depth, [includeStem], [_stemLength])](#File.readDirectories) ⇒ Promise.<Array.<string>> * [.readDirectoriesSync(directory, [depth], [includeStem], [_stemLength])](#File.readDirectoriesSync) ⇒ Array.<string> - * [.loadConfigFile([silent])](#File.loadConfigFile) ⇒ object * [.saveConfigFile(properties)](#File.saveConfigFile) ⇒ Promise.<void> * [.initPrettier([filetype])](#File.initPrettier) ⇒ Promise.<boolean> @@ -4853,18 +4877,6 @@ of file paths to be iterated over using sync api (required in constructors) ```js ['deploy/mcdev/bu1'] ``` - - -### File.loadConfigFile([silent]) ⇒ object -loads central properties from config file - -**Kind**: static method of [File](#File) -**Returns**: object - central properties object - -| Param | Type | Description | -| --- | --- | --- | -| [silent] | boolean | omit throwing errors and print messages; assuming not silent if not set | - ### File.saveConfigFile(properties) ⇒ Promise.<void> @@ -5820,9 +5832,7 @@ Util that contains logger and simple util methods * [.isTrue(attrValue)](#Util.isTrue) ⇒ boolean * [.isFalse(attrValue)](#Util.isFalse) ⇒ boolean * [._isValidType(selectedType)](#Util._isValidType) ⇒ boolean - * [.getDefaultProperties()](#Util.getDefaultProperties) ⇒ object * [.getRetrieveTypeChoices()](#Util.getRetrieveTypeChoices) ⇒ Array.<string> - * [.checkProperties(properties, [silent])](#Util.checkProperties) ⇒ Promise.<(boolean\|Array.<string>)> * [.metadataLogger(level, type, method, payload, [source])](#Util.metadataLogger) ⇒ void * [.replaceByObject(str, obj)](#Util.replaceByObject) ⇒ string \| object * [.inverseGet(objs, val)](#Util.inverseGet) ⇒ string @@ -5909,14 +5919,6 @@ helper for retrieve, retrieveAsTemplate and deploy | --- | --- | --- | | selectedType | string | type or type-subtype | - - -### Util.getDefaultProperties() ⇒ object -defines how the properties.json should look like -used for creating a template and for checking if variables are set - -**Kind**: static method of [Util](#Util) -**Returns**: object - default properties ### Util.getRetrieveTypeChoices() ⇒ Array.<string> @@ -5924,19 +5926,6 @@ helper for getDefaultProperties() **Kind**: static method of [Util](#Util) **Returns**: Array.<string> - type choices - - -### Util.checkProperties(properties, [silent]) ⇒ Promise.<(boolean\|Array.<string>)> -check if the config file is correctly formatted and has values - -**Kind**: static method of [Util](#Util) -**Returns**: Promise.<(boolean\|Array.<string>)> - file structure ok OR list of fields to be fixed - -| Param | Type | Description | -| --- | --- | --- | -| properties | object | javascript object in .mcdevrc.json | -| [silent] | boolean | set to true for internal use w/o cli output | - ### Util.metadataLogger(level, type, method, payload, [source]) ⇒ void diff --git a/lib/Builder.js b/lib/Builder.js index c3e499302..2b4b4540a 100644 --- a/lib/Builder.js +++ b/lib/Builder.js @@ -4,6 +4,7 @@ const TYPE = require('../types/mcdev.d'); const Util = require('./util/util'); const File = require('./util/file'); const Cli = require('./util/cli'); +const Config = require('./util/config'); const auth = require('./util/auth'); const MetadataTypeInfo = require('./MetadataTypeInfo'); // @ts-ignore @@ -106,7 +107,7 @@ class Builder { */ static async buildTemplate(businessUnit, selectedType, keyArr, market) { Util.logger.info('mcdev:: Build Definition from Template'); - const properties = File.loadConfigFile(); + const properties = await Config.getProperties(); if (!Util._isValidType(selectedType)) { return; } @@ -173,7 +174,7 @@ class Builder { * @returns {Promise.} - */ static async buildDefinition(businessUnit, selectedType, name, market) { - const properties = File.loadConfigFile(); + const properties = await Config.getProperties(); if (!Util._isValidType(selectedType)) { return; } @@ -200,7 +201,7 @@ class Builder { * @returns {Promise.} - */ static async buildDefinitionBulk(listName, type, name) { - const properties = File.loadConfigFile(); + const properties = await Config.getProperties(); Util.verifyMarketList(listName, properties); if (type && !MetadataTypeInfo[type]) { Util.logger.error(`:: '${type}' is not a valid metadata type`); diff --git a/lib/index.js b/lib/index.js index 8d1e52c74..0de4241ee 100644 --- a/lib/index.js +++ b/lib/index.js @@ -17,7 +17,7 @@ const MetadataTypeInfo = require('./MetadataTypeInfo'); const MetadataTypeDefinitions = require('./MetadataTypeDefinitions'); const Retriever = require('./Retriever'); const cache = require('./util/cache'); -let properties; +const Config = require('./util/config'); /** * main class @@ -43,10 +43,7 @@ class Mcdev { * @returns {void} */ static async createDeltaPkg(argv) { - properties = properties || File.loadConfigFile(); - if (!(await Util.checkProperties(properties))) { - return null; - } + const properties = await Config.getProperties(); // get source market and source BU from config if (argv.filter) { return DevOps.getDeltaList(properties, argv.range, true, argv.filter); @@ -60,10 +57,7 @@ class Mcdev { * @returns {Promise} . */ static async selectTypes() { - properties = properties || File.loadConfigFile(); - if (!(await Util.checkProperties(properties))) { - return null; - } + const properties = await Config.getProperties(); await Cli.selectTypes(properties); } /** @@ -77,7 +71,7 @@ class Mcdev { * @returns {Promise} . */ static async upgrade(skipInteraction) { - properties = properties || File.loadConfigFile(); + const properties = await Config.getProperties(); if (!properties) { Util.logger.error('No config found. Please run mcdev init'); return; @@ -99,11 +93,7 @@ class Mcdev { */ static async retrieve(businessUnit, selectedTypesArr, changelogOnly) { Util.logger.info('mcdev:: Retrieve'); - properties = properties || File.loadConfigFile(); - if (!(await Util.checkProperties(properties))) { - // return null here to avoid seeing 2 error messages for the same issue - return null; - } + const properties = await Config.getProperties(); // assume a list was passed in and check each entry's validity if (selectedTypesArr) { @@ -186,7 +176,7 @@ class Mcdev { * @returns {Promise.} ensure that BUs are worked on sequentially */ static async _retrieveBU(cred, bu, selectedTypesArr, changelogOnly) { - properties = properties || File.loadConfigFile(); + const properties = await Config.getProperties(); const buObject = await Cli.getCredentialObject( properties, cred !== null ? cred + '/' + bu : null, @@ -273,7 +263,7 @@ class Mcdev { static async _deployBU(cred, bu, typeArr) { const buPath = `${cred}/${bu}`; Util.logger.info(`::Deploying ${buPath}`); - properties = properties || File.loadConfigFile(); + const properties = await Config.getProperties(); const buObject = await Cli.getCredentialObject(properties, buPath, null, true); if (buObject !== null) { cache.initCache(buObject); @@ -297,7 +287,7 @@ class Mcdev { */ static async deploy(businessUnit, selectedTypesArr) { Util.logger.info('mcdev:: Deploy'); - properties = properties || File.loadConfigFile(); + const properties = await Config.getProperties(); if (selectedTypesArr) { for (const selectedType of selectedTypesArr) { @@ -382,7 +372,7 @@ class Mcdev { */ static async initProject(credentialsName, skipInteraction) { Util.logger.info('mcdev:: Setting up project'); - properties = properties || File.loadConfigFile(!!credentialsName); + const properties = await Config.getProperties(!!credentialsName); await Init.initProject(properties, credentialsName, skipInteraction); } @@ -394,7 +384,7 @@ class Mcdev { */ static async findBUs(credentialsName) { Util.logger.info('mcdev:: Load BUs'); - properties = properties || File.loadConfigFile(); + const properties = await Config.getProperties(); const buObject = await Cli.getCredentialObject(properties, credentialsName, true); if (buObject !== null) { BuHelper.refreshBUProperties(properties, buObject.credential); @@ -410,7 +400,7 @@ class Mcdev { */ static async document(businessUnit, type) { Util.logger.info('mcdev:: Document'); - properties = properties || File.loadConfigFile(); + const properties = await Config.getProperties(); if (type && !MetadataTypeInfo[type]) { Util.logger.error(`:: '${type}' is not a valid metadata type`); return; @@ -445,7 +435,7 @@ class Mcdev { */ static async deleteByKey(businessUnit, type, customerKey) { Util.logger.info('mcdev:: delete'); - properties = properties || File.loadConfigFile(); + const properties = await Config.getProperties(); const buObject = await Cli.getCredentialObject(properties, businessUnit); if (buObject !== null) { if ('string' !== typeof type) { @@ -474,7 +464,7 @@ class Mcdev { * @returns {Promise.} - */ static async badKeys(businessUnit) { - properties = properties || File.loadConfigFile(); + const properties = await Config.getProperties(); const buObject = await Cli.getCredentialObject(properties, businessUnit); if (buObject !== null) { Util.logger.info('Gathering list of Name<>External Key mismatches (bad keys)'); @@ -553,7 +543,7 @@ class Mcdev { */ static async retrieveAsTemplate(businessUnit, selectedType, name, market) { Util.logger.info('mcdev:: Retrieve as Template'); - properties = properties || File.loadConfigFile(); + const properties = await Config.getProperties(); if (!Util._isValidType(selectedType)) { return; } diff --git a/lib/util/cli.js b/lib/util/cli.js index d60b52520..bf9fe7461 100644 --- a/lib/util/cli.js +++ b/lib/util/cli.js @@ -7,6 +7,7 @@ const inquirer = require('inquirer'); const MetadataDefinitions = require('./../MetadataTypeDefinitions'); const Util = require('./util'); const auth = require('./auth'); +const Config = require('./config'); /** * CLI helper class @@ -38,7 +39,7 @@ const Cli = { * @returns {Promise.} - */ async addExtraCredential(properties, skipInteraction) { - if (!(await Util.checkProperties(properties))) { + if (!(await Config.checkProperties(properties))) { // return null here to avoid seeing 2 error messages for the same issue return null; } else { @@ -85,7 +86,7 @@ const Cli = { */ async getCredentialObject(properties, target, isCredentialOnly, allowAll) { try { - if (!(await Util.checkProperties(properties))) { + if (!(await Config.checkProperties(properties))) { // return null here to avoid seeing 2 error messages for the same issue return null; } diff --git a/lib/util/config.js b/lib/util/config.js new file mode 100644 index 000000000..8dc01c9b9 --- /dev/null +++ b/lib/util/config.js @@ -0,0 +1,289 @@ +const Util = require('./util'); +const File = require('./file'); +const inquirer = require('inquirer'); +const semver = require('semver'); +const path = require('path'); +const packageJsonMcdev = require('../../package.json'); +/** + * Central class for loading and validating properties from config and auth + */ +const Config = { + properties: null, + /** + * loads central properties from config file + * + * @param {boolean} [silent] omit throwing errors and print messages; assuming not silent if not set + * @returns {object} central properties object + */ + async getProperties(silent) { + // already loaded, return existing + if (Config.properties) { + return Config.properties; + } + if (await File.pathExists(Util.configFileName)) { + try { + Config.properties = await File.readJson(Util.configFileName); + } catch (ex) { + Util.logger.error(`${ex.code}: ${ex.message}`); + return; + } + if (await File.pathExists(Util.authFileName)) { + let auth; + try { + auth = await File.readJson(Util.authFileName); + } catch (ex) { + Util.logger.error(`${ex.code}: ${ex.message}`); + return; + } + + if (!auth) { + const err = `${Util.authFileName} is not set up correctly.`; + Util.logger.error(err); + throw new Error(err); + } + for (const cred in Config.properties.credentials) { + if (auth[cred]) { + if ( + Config.properties.credentials[cred].eid !== auth[cred].account_id && + !silent + ) { + const err = `'${cred}' found in ${Util.configFileName} and ${Util.authFileName} have a Enterprise ID mismatch. Please check.`; + Util.logger.error(err); + throw new Error(err); + } + // TODO add auth checks #294 + } else if (!silent) { + Util.logger.warn( + `'${cred}' found in ${Util.configFileName} but not in ${Util.authFileName}. Please run 'mcdev init' to provide the missing credential details.` + ); + } + } + } else if (!silent) { + Util.logger.warn( + `${Util.authFileName} not found. Please run 'mcdev init' to provide the missing credential details.` + ); + } + } + Config.checkProperties(Config.properties); + return Config.properties; + }, + /** + * check if the config file is correctly formatted and has values + * + * @param {object} properties javascript object in .mcdevrc.json + * @param {boolean} [silent] set to true for internal use w/o cli output + * @returns {Promise.} file structure ok OR list of fields to be fixed + */ + checkProperties: async function (properties, silent) { + if (!(await File.pathExists(Util.configFileName)) || !properties) { + Util.logger.error(`\nCould not find ${Util.configFileName} in ${process.cwd()}.`); + Util.logger.error(`Run 'mcdev init' to initialize your project.\n`); + return false; + } + if (!(await File.pathExists(Util.authFileName)) || !properties) { + Util.logger.error(`\nCould not find ${Util.authFileName} in ${process.cwd()}.`); + Util.logger.error(`Run 'mcdev init' to initialize your project.\n`); + return false; + } + + // check if user is running older (ignores patches) mcdev version than whats saved to the config + if (properties.version && semver.gt(properties.version, packageJsonMcdev.version)) { + Util.logger.error( + `Your Accenture SFMC DevTools version ${packageJsonMcdev.version} is lower than your project's config version ${properties.version}` + ); + if (Util.skipInteraction) { + return false; + } + + const responses = await inquirer.prompt([ + { + type: 'confirm', + name: 'runUpgradeNow', + message: `Do you want to run 'npm update -g mcdev@${properties.version}' now? This may take a few minutes.`, + default: true, + }, + ]); + if (responses.runUpgradeNow) { + // use _execSync here to avoid a circular dependency + Util.execSync('npm', ['update', '-g', `mcdev@${properties.version}`]); + } + return false; + } + + // check config properties + const defaultProps = Config.getDefaultProperties(); + const errorMsgs = []; + const solutionSet = new Set(); + const missingFields = []; + for (const key in defaultProps) { + if (Object.prototype.hasOwnProperty.call(defaultProps, key)) { + if (!Object.prototype.hasOwnProperty.call(properties, key)) { + errorMsgs.push(`${key}{} missing`); + solutionSet.add( + `Run 'mcdev upgrade' to fix missing or changed configuration options` + ); + missingFields.push(key); + } else { + if (!silent && key === 'credentials') { + if (!Object.keys(properties.credentials)) { + errorMsgs.push(`no Credential defined`); + } else { + for (const cred in properties.credentials) { + if (cred.includes('/') || cred.includes('\\')) { + errorMsgs.push( + `Credential names may not includes slashes: ${cred}` + ); + solutionSet.add('Apply manual fix in your config.'); + } + if ( + !properties.credentials[cred].eid || + properties.credentials[cred].eid === 0 + ) { + errorMsgs.push(`invalid account_id (EID) on ${cred}`); + solutionSet.add(`Run 'mcdev init ${cred}'`); + } + let i = 0; + for (const buName in properties.credentials[cred].businessUnits) { + if (buName.includes('/') || buName.includes('\\')) { + errorMsgs.push( + `Business Unit names may not includes slashes: ${cred}: ${buName}` + ); + solutionSet.add(`Run 'mcdev reloadBUs ${cred}'`); + } + if ( + Object.prototype.hasOwnProperty.call( + properties.credentials[cred].businessUnits, + buName + ) && + properties.credentials[cred].businessUnits[buName] !== 0 + ) { + i++; + } + } + if (!i) { + errorMsgs.push(`no Business Units defined`); + solutionSet.add(`Run 'mcdev reloadBUs ${cred}'`); + } + } + } + } else if (['directories', 'metaDataTypes', 'options'].includes(key)) { + for (const subkey in defaultProps[key]) { + if ( + Object.prototype.hasOwnProperty.call(defaultProps[key], subkey) && + !Object.prototype.hasOwnProperty.call(properties[key], subkey) + ) { + errorMsgs.push( + `${key}.${subkey} missing. Default value (${ + Array.isArray(defaultProps[key][subkey]) + ? 'Array' + : typeof defaultProps[key][subkey] + }): ${defaultProps[key][subkey]}` + ); + solutionSet.add( + `Run 'mcdev upgrade' to fix missing or changed configuration options` + ); + missingFields.push(`${key}.${subkey}`); + } else if (subkey === 'deployment') { + for (const dkey in defaultProps[key][subkey]) { + if ( + Object.prototype.hasOwnProperty.call( + defaultProps[key][subkey], + dkey + ) && + !Object.prototype.hasOwnProperty.call( + properties[key][subkey], + dkey + ) + ) { + errorMsgs.push( + `${key}.${subkey} missing. Default value (${ + Array.isArray(defaultProps[key][subkey][dkey]) + ? 'Array' + : typeof defaultProps[key][subkey][dkey] + }): ${defaultProps[key][subkey][dkey]}` + ); + solutionSet.add( + `Run 'mcdev upgrade' to fix missing or changed configuration options` + ); + missingFields.push(`${key}.${subkey}.${dkey}`); + } + } + } + } + } + } + } + } + // check if project config version is outdated compared to user's mcdev version + if ( + !properties.version || + (![null, 'patch'].includes(semver.diff(packageJsonMcdev.version, properties.version)) && + semver.gt(packageJsonMcdev.version, properties.version)) + ) { + errorMsgs.push( + `Your project's config version ${properties.version} is lower than your Accenture SFMC DevTools version ${packageJsonMcdev.version}` + ); + solutionSet.add(`Run 'mcdev upgrade' to ensure optimal performance`); + missingFields.push('version'); + } + if (silent) { + return missingFields; + } else { + if (errorMsgs.length) { + const errorMsgOutput = [ + `Found problems in your ./${Util.configFileName} that you need to fix first:`, + ]; + for (const msg of errorMsgs) { + errorMsgOutput.push(' - ' + msg); + } + Util.logger.error(errorMsgOutput.join('\n')); + if (Util.skipInteraction) { + return false; + } + Util.logger.info( + [ + 'Here is what you can do to fix these issues:', + ...Array.from(solutionSet), + ].join('\n- ') + ); + const responses = await inquirer.prompt([ + { + type: 'confirm', + name: 'runUpgradeNow', + message: `Do you want to run 'mcdev upgrade' now?`, + default: true, + }, + ]); + if (responses.runUpgradeNow) { + // use _execSync here to avoid a circular dependency + this.execSync('mcdev', ['upgrade']); + } + return false; + } else { + return true; + } + } + }, + /** + * defines how the properties.json should look like + * used for creating a template and for checking if variables are set + * + * @returns {object} default properties + */ + getDefaultProperties: async function () { + const configFileName = path.resolve(__dirname, Util.boilerplateDirectory, 'config.json'); + if (!(await File.pathExists(configFileName))) { + this.logger.debug(`Default config file not found in ${configFileName}`); + return false; + } + const defaultProperties = await File.readJson(configFileName); + // set default name for parent BU + defaultProperties.credentials.default.businessUnits[Util.parentBuName] = 0; + // set default retrieve values + defaultProperties.metaDataTypes.retrieve = Util.getRetrieveTypeChoices(); + + return defaultProperties; + }, +}; + +module.exports = Config; diff --git a/lib/util/file.js b/lib/util/file.js index 8cfc8d722..b79fecb75 100644 --- a/lib/util/file.js +++ b/lib/util/file.js @@ -442,57 +442,6 @@ const File = { Util.logger.debug(ex.stack); } }, - /** - * loads central properties from config file - * - * @param {boolean} [silent] omit throwing errors and print messages; assuming not silent if not set - * @returns {object} central properties object - */ - loadConfigFile(silent) { - let properties; - if (fs.existsSync(Util.configFileName)) { - try { - properties = fs.readJsonSync(Util.configFileName); - } catch (ex) { - Util.logger.error(`${ex.code}: ${ex.message}`); - return; - } - if (fs.existsSync(Util.authFileName)) { - let auth; - try { - auth = fs.readJsonSync(Util.authFileName); - } catch (ex) { - Util.logger.error(`${ex.code}: ${ex.message}`); - return; - } - - if (!auth) { - const err = `${Util.authFileName} is not set up correctly.`; - Util.logger.error(err); - throw new Error(err); - } - for (const cred in properties.credentials) { - if (auth[cred]) { - if (properties.credentials[cred].eid !== auth[cred].account_id && !silent) { - const err = `'${cred}' found in ${Util.configFileName} and ${Util.authFileName} have a Enterprise ID mismatch. Please check.`; - Util.logger.error(err); - throw new Error(err); - } - // TODO add auth checks #294 - } else if (!silent) { - Util.logger.warn( - `'${cred}' found in ${Util.configFileName} but not in ${Util.authFileName}. Please run 'mcdev init' to provide the missing credential details.` - ); - } - } - } else if (!silent) { - Util.logger.warn( - `${Util.authFileName} not found. Please run 'mcdev init' to provide the missing credential details.` - ); - } - } - return properties; - }, /** * helper that splits the config back into auth & config parts to save them separately * diff --git a/lib/util/init.config.js b/lib/util/init.config.js index 031426e4a..09d232c53 100644 --- a/lib/util/init.config.js +++ b/lib/util/init.config.js @@ -6,6 +6,7 @@ const Util = require('./util'); const inquirer = require('inquirer'); const path = require('path'); const semver = require('semver'); +const Config = require('./config'); /** * CLI helper class @@ -28,8 +29,8 @@ const Init = { const upgradeMsgs = [`Upgrading existing ${Util.configFileName}:`]; - const missingFields = await Util.checkProperties(properties, true); - const defaultProps = Util.getDefaultProperties(); + const missingFields = await Config.checkProperties(properties, true); + const defaultProps = Config.getDefaultProperties(); if (missingFields.length) { missingFields.forEach((fieldName) => { switch (fieldName) { diff --git a/lib/util/init.js b/lib/util/init.js index 444d80f0f..b10f4ffa9 100644 --- a/lib/util/init.js +++ b/lib/util/init.js @@ -7,6 +7,7 @@ const InitNpm = require('./init.npm'); const InitConfig = require('./init.config'); const inquirer = require('inquirer'); const Util = require('./util'); +const Config = require('./config'); /** * CLI helper class @@ -52,7 +53,7 @@ const Init = { } } while (error && !skipInteraction); Util.logger.debug('reloading config'); - properties = File.loadConfigFile(true); + properties = await Config.getProperties(true); } else if (missingCredentials.length) { // forced update-credential mode - user likely cloned repo and is missing mcdev-auth.json Util.logger.warn( @@ -82,7 +83,7 @@ const Init = { } } while (error); Util.logger.debug('reloading config'); - properties = File.loadConfigFile(true); + properties = await Config.getProperties(true); } Util.logger.info('✔️ All credentials updated.'); // assume node dependencies are not installed diff --git a/lib/util/util.js b/lib/util/util.js index e457b879e..71d69d6b1 100644 --- a/lib/util/util.js +++ b/lib/util/util.js @@ -1,16 +1,12 @@ 'use strict'; const TYPE = require('../../types/mcdev.d'); -const fs = require('fs-extra'); // ! do not switch to util/file.js to avoid circular dependency const MetadataDefinitions = require('./../MetadataTypeDefinitions'); -const packageJsonMcdev = require('../../package.json'); -const path = require('path'); const process = require('process'); const toposort = require('toposort'); const winston = require('winston'); -const inquirer = require('inquirer'); const child_process = require('child_process'); -const semver = require('semver'); +const packageJsonMcdev = require('../../package.json'); /** * Util that contains logger and simple util methods @@ -139,26 +135,6 @@ const Util = { return true; }, - /** - * defines how the properties.json should look like - * used for creating a template and for checking if variables are set - * - * @returns {object} default properties - */ - getDefaultProperties() { - const configFileName = path.resolve(__dirname, this.boilerplateDirectory, 'config.json'); - if (!fs.existsSync(configFileName)) { - this.logger.debug(`Default config file not found in ${configFileName}`); - return false; - } - const defaultProperties = fs.readJsonSync(configFileName); - // set default name for parent BU - defaultProperties.credentials.default.businessUnits[this.parentBuName] = 0; - // set default retrieve values - defaultProperties.metaDataTypes.retrieve = this.getRetrieveTypeChoices(); - - return defaultProperties; - }, /** * helper for getDefaultProperties() * @@ -190,203 +166,6 @@ const Util = { return typeChoices; }, - /** - * check if the config file is correctly formatted and has values - * - * @param {object} properties javascript object in .mcdevrc.json - * @param {boolean} [silent] set to true for internal use w/o cli output - * @returns {Promise.} file structure ok OR list of fields to be fixed - */ - checkProperties: async function (properties, silent) { - if (!(await fs.pathExists(Util.configFileName)) || !properties) { - Util.logger.error(`\nCould not find ${Util.configFileName} in ${process.cwd()}.`); - Util.logger.error(`Run 'mcdev init' to initialize your project.\n`); - return false; - } - if (!(await fs.pathExists(Util.authFileName)) || !properties) { - Util.logger.error(`\nCould not find ${Util.authFileName} in ${process.cwd()}.`); - Util.logger.error(`Run 'mcdev init' to initialize your project.\n`); - return false; - } - - // check if user is running older (ignores patches) mcdev version than whats saved to the config - if (properties.version && semver.gt(properties.version, packageJsonMcdev.version)) { - Util.logger.error( - `Your Accenture SFMC DevTools version ${packageJsonMcdev.version} is lower than your project's config version ${properties.version}` - ); - if (Util.skipInteraction) { - return false; - } - - const responses = await inquirer.prompt([ - { - type: 'confirm', - name: 'runUpgradeNow', - message: `Do you want to run 'npm update -g mcdev@${properties.version}' now? This may take a few minutes.`, - default: true, - }, - ]); - if (responses.runUpgradeNow) { - // use _execSync here to avoid a circular dependency - this.execSync('npm', ['update', '-g', `mcdev@${properties.version}`]); - } - return false; - } - - // check config properties - const defaultProps = this.getDefaultProperties(); - const errorMsgs = []; - const solutionSet = new Set(); - const missingFields = []; - for (const key in defaultProps) { - if (Object.prototype.hasOwnProperty.call(defaultProps, key)) { - if (!Object.prototype.hasOwnProperty.call(properties, key)) { - errorMsgs.push(`${key}{} missing`); - solutionSet.add( - `Run 'mcdev upgrade' to fix missing or changed configuration options` - ); - missingFields.push(key); - } else { - if (!silent && key === 'credentials') { - if (!Object.keys(properties.credentials)) { - errorMsgs.push(`no Credential defined`); - } else { - for (const cred in properties.credentials) { - if (cred.includes('/') || cred.includes('\\')) { - errorMsgs.push( - `Credential names may not includes slashes: ${cred}` - ); - solutionSet.add('Apply manual fix in your config.'); - } - if ( - !properties.credentials[cred].eid || - properties.credentials[cred].eid === 0 - ) { - errorMsgs.push(`invalid account_id (EID) on ${cred}`); - solutionSet.add(`Run 'mcdev init ${cred}'`); - } - let i = 0; - for (const buName in properties.credentials[cred].businessUnits) { - if (buName.includes('/') || buName.includes('\\')) { - errorMsgs.push( - `Business Unit names may not includes slashes: ${cred}: ${buName}` - ); - solutionSet.add(`Run 'mcdev reloadBUs ${cred}'`); - } - if ( - Object.prototype.hasOwnProperty.call( - properties.credentials[cred].businessUnits, - buName - ) && - properties.credentials[cred].businessUnits[buName] !== 0 - ) { - i++; - } - } - if (!i) { - errorMsgs.push(`no Business Units defined`); - solutionSet.add(`Run 'mcdev reloadBUs ${cred}'`); - } - } - } - } else if (['directories', 'metaDataTypes', 'options'].includes(key)) { - for (const subkey in defaultProps[key]) { - if ( - Object.prototype.hasOwnProperty.call(defaultProps[key], subkey) && - !Object.prototype.hasOwnProperty.call(properties[key], subkey) - ) { - errorMsgs.push( - `${key}.${subkey} missing. Default value (${ - Array.isArray(defaultProps[key][subkey]) - ? 'Array' - : typeof defaultProps[key][subkey] - }): ${defaultProps[key][subkey]}` - ); - solutionSet.add( - `Run 'mcdev upgrade' to fix missing or changed configuration options` - ); - missingFields.push(`${key}.${subkey}`); - } else if (subkey === 'deployment') { - for (const dkey in defaultProps[key][subkey]) { - if ( - Object.prototype.hasOwnProperty.call( - defaultProps[key][subkey], - dkey - ) && - !Object.prototype.hasOwnProperty.call( - properties[key][subkey], - dkey - ) - ) { - errorMsgs.push( - `${key}.${subkey} missing. Default value (${ - Array.isArray(defaultProps[key][subkey][dkey]) - ? 'Array' - : typeof defaultProps[key][subkey][dkey] - }): ${defaultProps[key][subkey][dkey]}` - ); - solutionSet.add( - `Run 'mcdev upgrade' to fix missing or changed configuration options` - ); - missingFields.push(`${key}.${subkey}.${dkey}`); - } - } - } - } - } - } - } - } - // check if project config version is outdated compared to user's mcdev version - if ( - !properties.version || - (![null, 'patch'].includes(semver.diff(packageJsonMcdev.version, properties.version)) && - semver.gt(packageJsonMcdev.version, properties.version)) - ) { - errorMsgs.push( - `Your project's config version ${properties.version} is lower than your Accenture SFMC DevTools version ${packageJsonMcdev.version}` - ); - solutionSet.add(`Run 'mcdev upgrade' to ensure optimal performance`); - missingFields.push('version'); - } - if (silent) { - return missingFields; - } else { - if (errorMsgs.length) { - const errorMsgOutput = [ - `Found problems in your ./${Util.configFileName} that you need to fix first:`, - ]; - for (const msg of errorMsgs) { - errorMsgOutput.push(' - ' + msg); - } - Util.logger.error(errorMsgOutput.join('\n')); - if (Util.skipInteraction) { - return false; - } - Util.logger.info( - [ - 'Here is what you can do to fix these issues:', - ...Array.from(solutionSet), - ].join('\n- ') - ); - const responses = await inquirer.prompt([ - { - type: 'confirm', - name: 'runUpgradeNow', - message: `Do you want to run 'mcdev upgrade' now?`, - default: true, - }, - ]); - if (responses.runUpgradeNow) { - // use _execSync here to avoid a circular dependency - this.execSync('mcdev', ['upgrade']); - } - return false; - } else { - return true; - } - } - }, loggerTransports: null, /** * Logger that creates timestamped log file in 'logs/' directory From 3e555d7886c1503b3ee5392eaaa51af346e2f723 Mon Sep 17 00:00:00 2001 From: "douglas.midgley" Date: Sun, 19 Jun 2022 11:52:56 +0200 Subject: [PATCH 02/14] #294: capitalization change --- docs/dist/documentation.md | 32 ++++++++++++++++---------------- lib/Builder.js | 8 ++++---- lib/index.js | 28 ++++++++++++++-------------- lib/util/cli.js | 6 +++--- lib/util/config.js | 20 ++++++++++---------- lib/util/init.config.js | 6 +++--- lib/util/init.js | 6 +++--- 7 files changed, 53 insertions(+), 53 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index e15bc6e07..74523a320 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -130,7 +130,7 @@ Provides default functionality that can be overwritten by child metadata type cl
Cli

CLI helper class

-
Config
+
config

Central class for loading and validating properties from config and auth

DevOps
@@ -4649,36 +4649,36 @@ shows metadata type descriptions **Kind**: static method of [Cli](#Cli) **Returns**: void - - - + -## Config +## config Central class for loading and validating properties from config and auth **Kind**: global constant -* [Config](#Config) - * [.getProperties([silent])](#Config.getProperties) ⇒ object - * [.checkProperties(properties, [silent])](#Config.checkProperties) ⇒ Promise.<(boolean\|Array.<string>)> - * [.getDefaultProperties()](#Config.getDefaultProperties) ⇒ object +* [config](#config) + * [.getProperties([silent])](#config.getProperties) ⇒ object + * [.checkProperties(properties, [silent])](#config.checkProperties) ⇒ Promise.<(boolean\|Array.<string>)> + * [.getDefaultProperties()](#config.getDefaultProperties) ⇒ object - + -### Config.getProperties([silent]) ⇒ object +### config.getProperties([silent]) ⇒ object loads central properties from config file -**Kind**: static method of [Config](#Config) +**Kind**: static method of [config](#config) **Returns**: object - central properties object | Param | Type | Description | | --- | --- | --- | | [silent] | boolean | omit throwing errors and print messages; assuming not silent if not set | - + -### Config.checkProperties(properties, [silent]) ⇒ Promise.<(boolean\|Array.<string>)> +### config.checkProperties(properties, [silent]) ⇒ Promise.<(boolean\|Array.<string>)> check if the config file is correctly formatted and has values -**Kind**: static method of [Config](#Config) +**Kind**: static method of [config](#config) **Returns**: Promise.<(boolean\|Array.<string>)> - file structure ok OR list of fields to be fixed | Param | Type | Description | @@ -4686,13 +4686,13 @@ check if the config file is correctly formatted and has values | properties | object | javascript object in .mcdevrc.json | | [silent] | boolean | set to true for internal use w/o cli output | - + -### Config.getDefaultProperties() ⇒ object +### config.getDefaultProperties() ⇒ object defines how the properties.json should look like used for creating a template and for checking if variables are set -**Kind**: static method of [Config](#Config) +**Kind**: static method of [config](#config) **Returns**: object - default properties diff --git a/lib/Builder.js b/lib/Builder.js index 285eb9daa..125d905ac 100644 --- a/lib/Builder.js +++ b/lib/Builder.js @@ -4,7 +4,7 @@ const TYPE = require('../types/mcdev.d'); const Util = require('./util/util'); const File = require('./util/file'); const Cli = require('./util/cli'); -const Config = require('./util/config'); +const config = require('./util/config'); const auth = require('./util/auth'); const MetadataTypeInfo = require('./MetadataTypeInfo'); // @ts-ignore @@ -107,7 +107,7 @@ class Builder { */ static async buildTemplate(businessUnit, selectedType, keyArr, market) { Util.logger.info('mcdev:: Build Definition from Template'); - const properties = await Config.getProperties(); + const properties = await config.getProperties(); if (!Util._isValidType(selectedType)) { return; } @@ -174,7 +174,7 @@ class Builder { * @returns {Promise.} - */ static async buildDefinition(businessUnit, selectedType, name, market) { - const properties = await Config.getProperties(); + const properties = await config.getProperties(); if (!Util._isValidType(selectedType)) { return; } @@ -201,7 +201,7 @@ class Builder { * @returns {Promise.} - */ static async buildDefinitionBulk(listName, type, name) { - const properties = await Config.getProperties(); + const properties = await config.getProperties(); Util.verifyMarketList(listName, properties); if (type && !MetadataTypeInfo[type]) { Util.logger.error(`:: '${type}' is not a valid metadata type`); diff --git a/lib/index.js b/lib/index.js index 7f85ab3d3..d9242d0a0 100644 --- a/lib/index.js +++ b/lib/index.js @@ -17,7 +17,7 @@ const MetadataTypeInfo = require('./MetadataTypeInfo'); const MetadataTypeDefinitions = require('./MetadataTypeDefinitions'); const Retriever = require('./Retriever'); const cache = require('./util/cache'); -const Config = require('./util/config'); +const config = require('./util/config'); /** * main class @@ -43,7 +43,7 @@ class Mcdev { * @returns {Promise.} list of changed items */ static async createDeltaPkg(argv) { - const properties = await Config.getProperties(); + const properties = await config.getProperties(); // get source market and source BU from config if (argv.filter) { return DevOps.getDeltaList(properties, argv.range, true, argv.filter); @@ -57,7 +57,7 @@ class Mcdev { * @returns {Promise} . */ static async selectTypes() { - const properties = await Config.getProperties(); + const properties = await config.getProperties(); await Cli.selectTypes(properties); } /** @@ -71,7 +71,7 @@ class Mcdev { * @returns {Promise} . */ static async upgrade(skipInteraction) { - const properties = await Config.getProperties(); + const properties = await config.getProperties(); if (!properties) { Util.logger.error('No config found. Please run mcdev init'); return; @@ -93,7 +93,7 @@ class Mcdev { */ static async retrieve(businessUnit, selectedTypesArr, changelogOnly) { Util.logger.info('mcdev:: Retrieve'); - const properties = await Config.getProperties(); + const properties = await config.getProperties(); // assume a list was passed in and check each entry's validity if (selectedTypesArr) { @@ -176,7 +176,7 @@ class Mcdev { * @returns {Promise.} ensure that BUs are worked on sequentially */ static async _retrieveBU(cred, bu, selectedTypesArr, changelogOnly) { - const properties = await Config.getProperties(); + const properties = await config.getProperties(); const buObject = await Cli.getCredentialObject( properties, cred !== null ? cred + '/' + bu : null, @@ -263,7 +263,7 @@ class Mcdev { static async _deployBU(cred, bu, typeArr) { const buPath = `${cred}/${bu}`; Util.logger.info(`::Deploying ${buPath}`); - const properties = await Config.getProperties(); + const properties = await config.getProperties(); const buObject = await Cli.getCredentialObject(properties, buPath, null, true); if (buObject !== null) { cache.initCache(buObject); @@ -287,7 +287,7 @@ class Mcdev { */ static async deploy(businessUnit, selectedTypesArr) { Util.logger.info('mcdev:: Deploy'); - const properties = await Config.getProperties(); + const properties = await config.getProperties(); if (selectedTypesArr) { for (const selectedType of selectedTypesArr) { @@ -372,7 +372,7 @@ class Mcdev { */ static async initProject(credentialsName, skipInteraction) { Util.logger.info('mcdev:: Setting up project'); - const properties = await Config.getProperties(!!credentialsName); + const properties = await config.getProperties(!!credentialsName); await Init.initProject(properties, credentialsName, skipInteraction); } @@ -384,7 +384,7 @@ class Mcdev { */ static async findBUs(credentialsName) { Util.logger.info('mcdev:: Load BUs'); - const properties = await Config.getProperties(); + const properties = await config.getProperties(); const buObject = await Cli.getCredentialObject(properties, credentialsName, true); if (buObject !== null) { BuHelper.refreshBUProperties(properties, buObject.credential); @@ -400,7 +400,7 @@ class Mcdev { */ static async document(businessUnit, type) { Util.logger.info('mcdev:: Document'); - const properties = await Config.getProperties(); + const properties = await config.getProperties(); if (type && !MetadataTypeInfo[type]) { Util.logger.error(`:: '${type}' is not a valid metadata type`); return; @@ -435,7 +435,7 @@ class Mcdev { */ static async deleteByKey(businessUnit, type, customerKey) { Util.logger.info('mcdev:: delete'); - const properties = await Config.getProperties(); + const properties = await config.getProperties(); const buObject = await Cli.getCredentialObject(properties, businessUnit); if (buObject !== null) { if ('string' !== typeof type) { @@ -464,7 +464,7 @@ class Mcdev { * @returns {Promise.} - */ static async badKeys(businessUnit) { - const properties = await Config.getProperties(); + const properties = await config.getProperties(); const buObject = await Cli.getCredentialObject(properties, businessUnit); if (buObject !== null) { Util.logger.info('Gathering list of Name<>External Key mismatches (bad keys)'); @@ -535,7 +535,7 @@ class Mcdev { */ static async retrieveAsTemplate(businessUnit, selectedType, name, market) { Util.logger.info('mcdev:: Retrieve as Template'); - const properties = await Config.getProperties(); + const properties = await config.getProperties(); if (!Util._isValidType(selectedType)) { return; } diff --git a/lib/util/cli.js b/lib/util/cli.js index bf9fe7461..cae35f155 100644 --- a/lib/util/cli.js +++ b/lib/util/cli.js @@ -7,7 +7,7 @@ const inquirer = require('inquirer'); const MetadataDefinitions = require('./../MetadataTypeDefinitions'); const Util = require('./util'); const auth = require('./auth'); -const Config = require('./config'); +const config = require('./config'); /** * CLI helper class @@ -39,7 +39,7 @@ const Cli = { * @returns {Promise.} - */ async addExtraCredential(properties, skipInteraction) { - if (!(await Config.checkProperties(properties))) { + if (!(await config.checkProperties(properties))) { // return null here to avoid seeing 2 error messages for the same issue return null; } else { @@ -86,7 +86,7 @@ const Cli = { */ async getCredentialObject(properties, target, isCredentialOnly, allowAll) { try { - if (!(await Config.checkProperties(properties))) { + if (!(await config.checkProperties(properties))) { // return null here to avoid seeing 2 error messages for the same issue return null; } diff --git a/lib/util/config.js b/lib/util/config.js index 8dc01c9b9..3ebc1e286 100644 --- a/lib/util/config.js +++ b/lib/util/config.js @@ -7,7 +7,7 @@ const packageJsonMcdev = require('../../package.json'); /** * Central class for loading and validating properties from config and auth */ -const Config = { +const config = { properties: null, /** * loads central properties from config file @@ -17,12 +17,12 @@ const Config = { */ async getProperties(silent) { // already loaded, return existing - if (Config.properties) { - return Config.properties; + if (config.properties) { + return config.properties; } if (await File.pathExists(Util.configFileName)) { try { - Config.properties = await File.readJson(Util.configFileName); + config.properties = await File.readJson(Util.configFileName); } catch (ex) { Util.logger.error(`${ex.code}: ${ex.message}`); return; @@ -41,10 +41,10 @@ const Config = { Util.logger.error(err); throw new Error(err); } - for (const cred in Config.properties.credentials) { + for (const cred in config.properties.credentials) { if (auth[cred]) { if ( - Config.properties.credentials[cred].eid !== auth[cred].account_id && + config.properties.credentials[cred].eid !== auth[cred].account_id && !silent ) { const err = `'${cred}' found in ${Util.configFileName} and ${Util.authFileName} have a Enterprise ID mismatch. Please check.`; @@ -64,8 +64,8 @@ const Config = { ); } } - Config.checkProperties(Config.properties); - return Config.properties; + await config.checkProperties(config.properties); + return config.properties; }, /** * check if the config file is correctly formatted and has values @@ -111,7 +111,7 @@ const Config = { } // check config properties - const defaultProps = Config.getDefaultProperties(); + const defaultProps = config.getDefaultProperties(); const errorMsgs = []; const solutionSet = new Set(); const missingFields = []; @@ -286,4 +286,4 @@ const Config = { }, }; -module.exports = Config; +module.exports = config; diff --git a/lib/util/init.config.js b/lib/util/init.config.js index f09d49243..fb38ce34d 100644 --- a/lib/util/init.config.js +++ b/lib/util/init.config.js @@ -6,7 +6,7 @@ const Util = require('./util'); const inquirer = require('inquirer'); const path = require('path'); const semver = require('semver'); -const Config = require('./config'); +const config = require('./config'); /** * CLI helper class @@ -29,8 +29,8 @@ const Init = { const upgradeMsgs = [`Upgrading existing ${Util.configFileName}:`]; - const missingFields = await Config.checkProperties(properties, true); - const defaultProps = Config.getDefaultProperties(); + const missingFields = await config.checkProperties(properties, true); + const defaultProps = config.getDefaultProperties(); if (missingFields.length) { missingFields.forEach((fieldName) => { switch (fieldName) { diff --git a/lib/util/init.js b/lib/util/init.js index a05038ca8..f65567e76 100644 --- a/lib/util/init.js +++ b/lib/util/init.js @@ -7,7 +7,7 @@ const InitNpm = require('./init.npm'); const InitConfig = require('./init.config'); const inquirer = require('inquirer'); const Util = require('./util'); -const Config = require('./config'); +const config = require('./config'); /** * CLI helper class @@ -53,7 +53,7 @@ const Init = { } } while (error && !skipInteraction); Util.logger.debug('reloading config'); - properties = await Config.getProperties(true); + properties = await config.getProperties(true); } else if (missingCredentials.length) { // forced update-credential mode - user likely cloned repo and is missing mcdev-auth.json Util.logger.warn( @@ -83,7 +83,7 @@ const Init = { } } while (error); Util.logger.debug('reloading config'); - properties = await Config.getProperties(true); + properties = await config.getProperties(true); } Util.logger.info('✔️ All credentials updated.'); // assume node dependencies are not installed From e92652b4dad1b66c0595899015bedd0910ec430a Mon Sep 17 00:00:00 2001 From: "douglas.midgley" Date: Sat, 25 Jun 2022 21:08:17 +0200 Subject: [PATCH 03/14] #294: moved to error-based handling --- docs/dist/documentation.md | 42 +++-- lib/cli.js | 12 +- lib/index.js | 364 +++++++++++++++++++------------------ lib/util/businessUnit.js | 11 +- lib/util/cli.js | 36 ++-- lib/util/config.js | 69 +++---- lib/util/file.js | 6 +- lib/util/init.config.js | 174 +++++++++--------- lib/util/init.js | 19 +- 9 files changed, 376 insertions(+), 357 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 74523a320..7202d2ef7 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -421,8 +421,8 @@ main class * [.retrieve(businessUnit, [selectedTypesArr], [changelogOnly])](#Mcdev.retrieve) ⇒ Promise.<object> * [._deployBU(cred, bu, [typeArr])](#Mcdev._deployBU) ⇒ Promise * [.deploy(businessUnit, [selectedTypesArr])](#Mcdev.deploy) ⇒ Promise.<void> - * [.initProject([credentialsName], [skipInteraction])](#Mcdev.initProject) ⇒ Promise.<void> - * [.findBUs(credentialsName)](#Mcdev.findBUs) ⇒ Promise.<void> + * [.initProject([credentialName], credentialName, [skipInteraction])](#Mcdev.initProject) ⇒ Promise.<void> + * [.findBUs(credentialName)](#Mcdev.findBUs) ⇒ Promise.<void> * [.document(businessUnit, type)](#Mcdev.document) ⇒ Promise.<void> * [.deleteByKey(businessUnit, type, customerKey)](#Mcdev.deleteByKey) ⇒ Promise.<void> * [.badKeys(businessUnit)](#Mcdev.badKeys) ⇒ Promise.<void> @@ -522,7 +522,7 @@ Deploys all metadata located in the 'deploy' directory to the specified business -### Mcdev.initProject([credentialsName], [skipInteraction]) ⇒ Promise.<void> +### Mcdev.initProject([credentialName], credentialName, [skipInteraction]) ⇒ Promise.<void> Creates template file for properties.json **Kind**: static method of [Mcdev](#Mcdev) @@ -530,12 +530,13 @@ Creates template file for properties.json | Param | Type | Description | | --- | --- | --- | -| [credentialsName] | string | identifying name of the installed package / project | +| [credentialName] | string | identifying name of the installed package / project | +| credentialName | | | | [skipInteraction] | boolean \| object | signals what to insert automatically for things usually asked via wizard | -### Mcdev.findBUs(credentialsName) ⇒ Promise.<void> +### Mcdev.findBUs(credentialName) ⇒ Promise.<void> Refreshes BU names and ID's from MC instance **Kind**: static method of [Mcdev](#Mcdev) @@ -543,7 +544,7 @@ Refreshes BU names and ID's from MC instance | Param | Type | Description | | --- | --- | --- | -| credentialsName | string | identifying name of the installed package / project | +| credentialName | string | identifying name of the installed package / project | @@ -4484,7 +4485,7 @@ Helper that handles retrieval of BU info **Kind**: global constant -### BusinessUnit.refreshBUProperties(properties, credentialsName) ⇒ Promise.<boolean> +### BusinessUnit.refreshBUProperties(properties, credentialName, credentialName) ⇒ Promise.<boolean> Refreshes BU names and ID's from MC instance **Kind**: static method of [BusinessUnit](#BusinessUnit) @@ -4493,7 +4494,8 @@ Refreshes BU names and ID's from MC instance | Param | Type | Description | | --- | --- | --- | | properties | object | current properties that have to be refreshed | -| credentialsName | string | identifying name of the installed package / project | +| credentialName | string | identifying name of the installed package / project | +| credentialName | | | @@ -5043,7 +5045,7 @@ CLI helper class * [.initProject(properties, credentialName, [skipInteraction])](#Init.initProject) ⇒ Promise.<void> * [._downloadAllBUs(bu, gitStatus, [skipInteraction])](#Init._downloadAllBUs) ⇒ Promise.<void> * [.upgradeProject(properties, [initial], [repoName])](#Init.upgradeProject) ⇒ Promise.<boolean> - * [._getMissingCredentials(properties)](#Init._getMissingCredentials) ⇒ Array.<string> + * [._getMissingCredentials(properties)](#Init._getMissingCredentials) ⇒ Promise.<Array.<string>> * [.installDependencies([repoName])](#Init.installDependencies) ⇒ Promise.<boolean> * [._getDefaultPackageJson([currentContent])](#Init._getDefaultPackageJson) ⇒ Promise.<{script: object, author: string, license: string}> @@ -5214,11 +5216,11 @@ wrapper around npm dependency & configuration file setup -### Init.\_getMissingCredentials(properties) ⇒ Array.<string> +### Init.\_getMissingCredentials(properties) ⇒ Promise.<Array.<string>> finds credentials that are set up in config but not in auth file **Kind**: static method of [Init](#Init) -**Returns**: Array.<string> - list of credential names +**Returns**: Promise.<Array.<string>> - list of credential names | Param | Type | Description | | --- | --- | --- | @@ -5272,7 +5274,7 @@ CLI helper class * [.initProject(properties, credentialName, [skipInteraction])](#Init.initProject) ⇒ Promise.<void> * [._downloadAllBUs(bu, gitStatus, [skipInteraction])](#Init._downloadAllBUs) ⇒ Promise.<void> * [.upgradeProject(properties, [initial], [repoName])](#Init.upgradeProject) ⇒ Promise.<boolean> - * [._getMissingCredentials(properties)](#Init._getMissingCredentials) ⇒ Array.<string> + * [._getMissingCredentials(properties)](#Init._getMissingCredentials) ⇒ Promise.<Array.<string>> * [.installDependencies([repoName])](#Init.installDependencies) ⇒ Promise.<boolean> * [._getDefaultPackageJson([currentContent])](#Init._getDefaultPackageJson) ⇒ Promise.<{script: object, author: string, license: string}> @@ -5443,11 +5445,11 @@ wrapper around npm dependency & configuration file setup -### Init.\_getMissingCredentials(properties) ⇒ Array.<string> +### Init.\_getMissingCredentials(properties) ⇒ Promise.<Array.<string>> finds credentials that are set up in config but not in auth file **Kind**: static method of [Init](#Init) -**Returns**: Array.<string> - list of credential names +**Returns**: Promise.<Array.<string>> - list of credential names | Param | Type | Description | | --- | --- | --- | @@ -5501,7 +5503,7 @@ CLI helper class * [.initProject(properties, credentialName, [skipInteraction])](#Init.initProject) ⇒ Promise.<void> * [._downloadAllBUs(bu, gitStatus, [skipInteraction])](#Init._downloadAllBUs) ⇒ Promise.<void> * [.upgradeProject(properties, [initial], [repoName])](#Init.upgradeProject) ⇒ Promise.<boolean> - * [._getMissingCredentials(properties)](#Init._getMissingCredentials) ⇒ Array.<string> + * [._getMissingCredentials(properties)](#Init._getMissingCredentials) ⇒ Promise.<Array.<string>> * [.installDependencies([repoName])](#Init.installDependencies) ⇒ Promise.<boolean> * [._getDefaultPackageJson([currentContent])](#Init._getDefaultPackageJson) ⇒ Promise.<{script: object, author: string, license: string}> @@ -5672,11 +5674,11 @@ wrapper around npm dependency & configuration file setup -### Init.\_getMissingCredentials(properties) ⇒ Array.<string> +### Init.\_getMissingCredentials(properties) ⇒ Promise.<Array.<string>> finds credentials that are set up in config but not in auth file **Kind**: static method of [Init](#Init) -**Returns**: Array.<string> - list of credential names +**Returns**: Promise.<Array.<string>> - list of credential names | Param | Type | Description | | --- | --- | --- | @@ -5730,7 +5732,7 @@ CLI helper class * [.initProject(properties, credentialName, [skipInteraction])](#Init.initProject) ⇒ Promise.<void> * [._downloadAllBUs(bu, gitStatus, [skipInteraction])](#Init._downloadAllBUs) ⇒ Promise.<void> * [.upgradeProject(properties, [initial], [repoName])](#Init.upgradeProject) ⇒ Promise.<boolean> - * [._getMissingCredentials(properties)](#Init._getMissingCredentials) ⇒ Array.<string> + * [._getMissingCredentials(properties)](#Init._getMissingCredentials) ⇒ Promise.<Array.<string>> * [.installDependencies([repoName])](#Init.installDependencies) ⇒ Promise.<boolean> * [._getDefaultPackageJson([currentContent])](#Init._getDefaultPackageJson) ⇒ Promise.<{script: object, author: string, license: string}> @@ -5901,11 +5903,11 @@ wrapper around npm dependency & configuration file setup -### Init.\_getMissingCredentials(properties) ⇒ Array.<string> +### Init.\_getMissingCredentials(properties) ⇒ Promise.<Array.<string>> finds credentials that are set up in config but not in auth file **Kind**: static method of [Init](#Init) -**Returns**: Array.<string> - list of credential names +**Returns**: Promise.<Array.<string>> - list of credential names | Param | Type | Description | | --- | --- | --- | diff --git a/lib/cli.js b/lib/cli.js index e2a87ac36..3ff41f63e 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -61,10 +61,10 @@ yargs }, }) .command({ - command: 'init [credentialsName]', + command: 'init [credentialName]', desc: `creates '${Util.configFileName}' in your root or adds additional credentials to the existing one`, builder: (yargs) => { - yargs.positional('credentialsName', { + yargs.positional('credentialName', { type: 'string', describe: 'name of your installed package', }); @@ -72,15 +72,15 @@ yargs handler: (argv) => { Mcdev.setSkipInteraction(!!argv.skipInteraction); Util.setLoggingLevel(argv); - Mcdev.initProject(argv.credentialsName, argv.skipInteraction); + Mcdev.initProject(argv.credentialName, argv.skipInteraction); }, }) .command({ - command: 'reloadBUs [credentialsName]', + command: 'reloadBUs [credentialName]', aliases: ['rb'], desc: 'loads the list of available BUs from the server and saves it to your config', builder: (yargs) => { - yargs.positional('credentialsName', { + yargs.positional('credentialName', { type: 'string', describe: 'name of your installed package', }); @@ -88,7 +88,7 @@ yargs handler: (argv) => { Mcdev.setSkipInteraction(!!argv.skipInteraction); Util.setLoggingLevel(argv); - Mcdev.findBUs(argv.credentialsName); + Mcdev.findBUs(argv.credentialName); }, }) .command({ diff --git a/lib/index.js b/lib/index.js index d9242d0a0..c245e72c0 100644 --- a/lib/index.js +++ b/lib/index.js @@ -79,8 +79,11 @@ class Mcdev { if ((await InitGit.initGitRepo(skipInteraction)).status === 'error') { return; } - - Init.upgradeProject(properties, false); + try { + Init.upgradeProject(properties, false); + } catch (ex) { + Util.logger.error(`Upgrade project failed: ${ex.message}`); + } } /** @@ -93,76 +96,80 @@ class Mcdev { */ static async retrieve(businessUnit, selectedTypesArr, changelogOnly) { Util.logger.info('mcdev:: Retrieve'); - const properties = await config.getProperties(); + try { + const properties = await config.getProperties(); - // assume a list was passed in and check each entry's validity - if (selectedTypesArr) { - for (const selectedType of selectedTypesArr) { - if (!Util._isValidType(selectedType)) { - return; + // assume a list was passed in and check each entry's validity + if (selectedTypesArr) { + for (const selectedType of selectedTypesArr) { + if (!Util._isValidType(selectedType)) { + return; + } } } - } - if (businessUnit === '*') { - Util.logger.info('\n :: Retrieving all BUs for all credentials'); - let counter_credTotal = 0; - for (const cred in properties.credentials) { - Util.logger.info(`\n :: Retrieving all BUs for ${cred}`); - let counter_credBu = 0; - for (const bu in properties.credentials[cred].businessUnits) { - await this._retrieveBU(cred, bu, selectedTypesArr); - counter_credBu++; - Util.restartLogger(); + if (businessUnit === '*') { + Util.logger.info('\n :: Retrieving all BUs for all credentials'); + let counter_credTotal = 0; + for (const cred in properties.credentials) { + Util.logger.info(`\n :: Retrieving all BUs for ${cred}`); + let counter_credBu = 0; + for (const bu in properties.credentials[cred].businessUnits) { + await this._retrieveBU(cred, bu, selectedTypesArr); + counter_credBu++; + Util.restartLogger(); + } + counter_credTotal += counter_credBu; + Util.logger.info(`\n :: ${counter_credBu} BUs for ${cred}\n`); } - counter_credTotal += counter_credBu; - Util.logger.info(`\n :: ${counter_credBu} BUs for ${cred}\n`); - } - Util.logger.info(`\n :: ${counter_credTotal} BUs in total\n`); - } else { - let [cred, bu] = businessUnit ? businessUnit.split('/') : [null, null]; - // to allow all-BU via user selection we need to run this here already - if ( - properties.credentials && - (!properties.credentials[cred] || - (bu !== '*' && properties.credentials[cred].businessUnits[bu])) - ) { - const buObject = await Cli.getCredentialObject( - properties, - cred !== null ? cred + '/' + bu : null, - null, - true - ); - if (buObject !== null) { - cred = buObject.credential; - bu = buObject.businessUnit; - } else { - return; + Util.logger.info(`\n :: ${counter_credTotal} BUs in total\n`); + } else { + let [cred, bu] = businessUnit ? businessUnit.split('/') : [null, null]; + // to allow all-BU via user selection we need to run this here already + if ( + properties.credentials && + (!properties.credentials[cred] || + (bu !== '*' && properties.credentials[cred].businessUnits[bu])) + ) { + const buObject = await Cli.getCredentialObject( + properties, + cred !== null ? cred + '/' + bu : null, + null, + true + ); + if (buObject !== null) { + cred = buObject.credential; + bu = buObject.businessUnit; + } else { + return; + } } - } - if (bu === '*' && properties.credentials && properties.credentials[cred]) { - Util.logger.info(`\n :: Retrieving all BUs for ${cred}`); - let counter_credBu = 0; - for (const bu in properties.credentials[cred].businessUnits) { - await this._retrieveBU(cred, bu, selectedTypesArr); - counter_credBu++; - Util.restartLogger(); - } - Util.logger.info(`\n :: ${counter_credBu} BUs for ${cred}\n`); - } else { - // retrieve a single BU; return - const retrieveChangelog = await this._retrieveBU( - cred, - bu, - selectedTypesArr, - changelogOnly - ); - if (changelogOnly) { - return retrieveChangelog; + if (bu === '*' && properties.credentials && properties.credentials[cred]) { + Util.logger.info(`\n :: Retrieving all BUs for ${cred}`); + let counter_credBu = 0; + for (const bu in properties.credentials[cred].businessUnits) { + await this._retrieveBU(cred, bu, selectedTypesArr); + counter_credBu++; + Util.restartLogger(); + } + Util.logger.info(`\n :: ${counter_credBu} BUs for ${cred}\n`); + } else { + // retrieve a single BU; return + const retrieveChangelog = await this._retrieveBU( + cred, + bu, + selectedTypesArr, + changelogOnly + ); + if (changelogOnly) { + return retrieveChangelog; + } + Util.logger.info(`:: Done\n`); } - Util.logger.info(`:: Done\n`); } + } catch (ex) { + Util.logger.error(`Retrieve failed: ${ex.message}`); } } /** @@ -287,105 +294,110 @@ class Mcdev { */ static async deploy(businessUnit, selectedTypesArr) { Util.logger.info('mcdev:: Deploy'); - const properties = await config.getProperties(); - - if (selectedTypesArr) { - for (const selectedType of selectedTypesArr) { - if (!Util._isValidType(selectedType)) { - return; - } - } - } - let counter_credBu = 0; - if (businessUnit === '*') { - // all credentials and all BUs shall be deployed to - const deployFolders = await File.readDirectories( - properties.directories.deploy, - 2, - false - ); - for (const buPath of deployFolders.filter((r) => r.includes(path.sep))) { - const [cred, bu] = buPath.split(path.sep); - await this._deployBU(cred, bu, selectedTypesArr); - counter_credBu++; - Util.logger.info(''); - Util.restartLogger(); - } - } else { - // anything but "*" passed in - let [cred, bu] = businessUnit ? businessUnit.split('/') : [null, null]; + try { + const properties = await config.getProperties(); - // to allow all-BU via user selection we need to run this here already - if ( - properties.credentials && - (!properties.credentials[cred] || - (bu !== '*' && properties.credentials[cred].businessUnits[bu])) - ) { - const buObject = await Cli.getCredentialObject( - properties, - cred !== null ? cred + '/' + bu : null, - null, - true - ); - if (buObject !== null) { - cred = buObject.credential; - bu = buObject.businessUnit; - } else { - return; + if (selectedTypesArr) { + for (const selectedType of selectedTypesArr) { + if (!Util._isValidType(selectedType)) { + return; + } } } - - if (bu === '*' && properties.credentials && properties.credentials[cred]) { - // valid credential given and -all- BUs targeted - Util.logger.info(`\n :: Deploying all BUs for ${cred}`); - let counter_credBu = 0; - // for (const bu in properties.credentials[cred].businessUnits) { + let counter_credBu = 0; + if (businessUnit === '*') { + // all credentials and all BUs shall be deployed to const deployFolders = await File.readDirectories( - File.normalizePath([properties.directories.deploy, cred]), - 1, + properties.directories.deploy, + 2, false ); - for (const buPath of deployFolders) { - await this._deployBU(cred, buPath, selectedTypesArr); + for (const buPath of deployFolders.filter((r) => r.includes(path.sep))) { + const [cred, bu] = buPath.split(path.sep); + await this._deployBU(cred, bu, selectedTypesArr); counter_credBu++; Util.logger.info(''); Util.restartLogger(); } - Util.logger.info(`\n :: ${counter_credBu} BUs for ${cred}\n`); } else { - // either bad credential or specific BU or no BU given - await this._deployBU(cred, bu, selectedTypesArr); - counter_credBu++; + // anything but "*" passed in + let [cred, bu] = businessUnit ? businessUnit.split('/') : [null, null]; + + // to allow all-BU via user selection we need to run this here already + if ( + properties.credentials && + (!properties.credentials[cred] || + (bu !== '*' && properties.credentials[cred].businessUnits[bu])) + ) { + const buObject = await Cli.getCredentialObject( + properties, + cred !== null ? cred + '/' + bu : null, + null, + true + ); + if (buObject !== null) { + cred = buObject.credential; + bu = buObject.businessUnit; + } else { + return; + } + } + + if (bu === '*' && properties.credentials && properties.credentials[cred]) { + // valid credential given and -all- BUs targeted + Util.logger.info(`\n :: Deploying all BUs for ${cred}`); + let counter_credBu = 0; + // for (const bu in properties.credentials[cred].businessUnits) { + const deployFolders = await File.readDirectories( + File.normalizePath([properties.directories.deploy, cred]), + 1, + false + ); + for (const buPath of deployFolders) { + await this._deployBU(cred, buPath, selectedTypesArr); + counter_credBu++; + Util.logger.info(''); + Util.restartLogger(); + } + Util.logger.info(`\n :: ${counter_credBu} BUs for ${cred}\n`); + } else { + // either bad credential or specific BU or no BU given + await this._deployBU(cred, bu, selectedTypesArr); + counter_credBu++; + } } - } - if (counter_credBu !== 0) { - Util.logger.info(`\n :: Deployed ${counter_credBu} BUs\n`); + if (counter_credBu !== 0) { + Util.logger.info(`\n :: Deployed ${counter_credBu} BUs\n`); + } + } catch (ex) { + Util.logger.error(`Deploy failed: ${ex.message}`); } } /** * Creates template file for properties.json * - * @param {string} [credentialsName] identifying name of the installed package / project + * @param {string} [credentialName] identifying name of the installed package / project + * @param credentialName * @param {boolean | object} [skipInteraction] signals what to insert automatically for things usually asked via wizard * @returns {Promise.} - */ - static async initProject(credentialsName, skipInteraction) { + static async initProject(credentialName, skipInteraction) { Util.logger.info('mcdev:: Setting up project'); - const properties = await config.getProperties(!!credentialsName); - await Init.initProject(properties, credentialsName, skipInteraction); + const properties = await config.getProperties(!!credentialName); + await Init.initProject(properties, credentialName, skipInteraction); } /** * Refreshes BU names and ID's from MC instance * - * @param {string} credentialsName identifying name of the installed package / project + * @param {string} credentialName identifying name of the installed package / project * @returns {Promise.} - */ - static async findBUs(credentialsName) { + static async findBUs(credentialName) { Util.logger.info('mcdev:: Load BUs'); const properties = await config.getProperties(); - const buObject = await Cli.getCredentialObject(properties, credentialsName, true); + const buObject = await Cli.getCredentialObject(properties, credentialName, true); if (buObject !== null) { BuHelper.refreshBUProperties(properties, buObject.credential); } @@ -400,28 +412,32 @@ class Mcdev { */ static async document(businessUnit, type) { Util.logger.info('mcdev:: Document'); - const properties = await config.getProperties(); - if (type && !MetadataTypeInfo[type]) { - Util.logger.error(`:: '${type}' is not a valid metadata type`); - return; - } try { - const parentBUOnlyTypes = ['accountUser', 'role']; - const buObject = await Cli.getCredentialObject( - properties, - parentBUOnlyTypes.includes(type) ? businessUnit.split('/')[0] : businessUnit, - parentBUOnlyTypes.includes(type) ? Util.parentBuName : null - ); - if (buObject !== null) { - MetadataTypeInfo[type].properties = properties; - MetadataTypeInfo[type].document(buObject); + const properties = await config.getProperties(); + if (type && !MetadataTypeInfo[type]) { + Util.logger.error(`:: '${type}' is not a valid metadata type`); + return; + } + try { + const parentBUOnlyTypes = ['accountUser', 'role']; + const buObject = await Cli.getCredentialObject( + properties, + parentBUOnlyTypes.includes(type) ? businessUnit.split('/')[0] : businessUnit, + parentBUOnlyTypes.includes(type) ? Util.parentBuName : null + ); + if (buObject !== null) { + MetadataTypeInfo[type].properties = properties; + MetadataTypeInfo[type].document(buObject); + } + } catch (ex) { + Util.logger.error('mcdev.document ' + ex.message); + Util.logger.debug(ex.stack); + Util.logger.info( + 'If the directoy does not exist, you may need to retrieve this BU first.' + ); } } catch (ex) { - Util.logger.error('mcdev.document ' + ex.message); - Util.logger.debug(ex.stack); - Util.logger.info( - 'If the directoy does not exist, you may need to retrieve this BU first.' - ); + Util.logger.error(`Document failed: ${ex.message}`); } } @@ -535,30 +551,34 @@ class Mcdev { */ static async retrieveAsTemplate(businessUnit, selectedType, name, market) { Util.logger.info('mcdev:: Retrieve as Template'); - const properties = await config.getProperties(); - if (!Util._isValidType(selectedType)) { - return; - } - const [type, subType] = selectedType ? selectedType.split('-') : []; + try { + const properties = await config.getProperties(); + if (!Util._isValidType(selectedType)) { + return; + } + const [type, subType] = selectedType ? selectedType.split('-') : []; - let retrieveTypesArr; - if ( - type && - subType && - MetadataTypeInfo[type] && - MetadataTypeDefinitions[type].subTypes.includes(subType) - ) { - retrieveTypesArr = [selectedType]; - } else if (type && MetadataTypeInfo[type]) { - retrieveTypesArr = [type]; - } - const buObject = await Cli.getCredentialObject(properties, businessUnit); - if (buObject !== null) { - cache.initCache(buObject); - const retriever = new Retriever(properties, buObject); - if (Util.checkMarket(market, properties)) { - return retriever.retrieve(retrieveTypesArr, name, properties.markets[market]); + let retrieveTypesArr; + if ( + type && + subType && + MetadataTypeInfo[type] && + MetadataTypeDefinitions[type].subTypes.includes(subType) + ) { + retrieveTypesArr = [selectedType]; + } else if (type && MetadataTypeInfo[type]) { + retrieveTypesArr = [type]; + } + const buObject = await Cli.getCredentialObject(properties, businessUnit); + if (buObject !== null) { + cache.initCache(buObject); + const retriever = new Retriever(properties, buObject); + if (Util.checkMarket(market, properties)) { + return retriever.retrieve(retrieveTypesArr, name, properties.markets[market]); + } } + } catch (ex) { + Util.logger.error(`Retrieve as Template failed: ${ex.message}`); } } diff --git a/lib/util/businessUnit.js b/lib/util/businessUnit.js index eb46586ee..697c7f78f 100644 --- a/lib/util/businessUnit.js +++ b/lib/util/businessUnit.js @@ -12,17 +12,18 @@ const BusinessUnit = { * Refreshes BU names and ID's from MC instance * * @param {object} properties current properties that have to be refreshed - * @param {string} credentialsName identifying name of the installed package / project + * @param {string} credentialName identifying name of the installed package / project + * @param credentialName * @returns {Promise.} success of refresh */ - refreshBUProperties: async function (properties, credentialsName) { - const currentCredentials = properties.credentials[credentialsName]; + refreshBUProperties: async function (properties, credentialName) { + const currentCredentials = properties.credentials[credentialName]; Util.logger.info(`Loading BUs`); try { const client = auth.getSDK({ mid: currentCredentials.eid, eid: currentCredentials.eid, - credential: credentialsName, + credential: credentialName, businessUnit: '_ParentBU_', }); const buResult = await client.soap.retrieve( @@ -92,7 +93,7 @@ const BusinessUnit = { // store BU list for repo await File.writeJSONToFile( properties.directories.businessUnits, - File.filterIllegalFilenames(credentialsName + '.businessUnits'), + File.filterIllegalFilenames(credentialName + '.businessUnits'), buResult.Results ); // update config diff --git a/lib/util/cli.js b/lib/util/cli.js index cae35f155..bc53477fc 100644 --- a/lib/util/cli.js +++ b/lib/util/cli.js @@ -39,24 +39,22 @@ const Cli = { * @returns {Promise.} - */ async addExtraCredential(properties, skipInteraction) { - if (!(await config.checkProperties(properties))) { - // return null here to avoid seeing 2 error messages for the same issue - return null; - } else { - Util.logger.info('Found the following credentials in your config file:'); - for (const cred in properties.credentials) { - if (Object.prototype.hasOwnProperty.call(properties.credentials, cred)) { - Util.logger.info(` - ${cred}`); - } - } - Util.logger.info('\nPlease enter your new credentials'); - if (skipInteraction && properties.credentials[skipInteraction.credentialName]) { - Util.logger.error( - `Credential '${skipInteraction.credentialName}' already existing. If you tried updating please provide run 'mcdev init ${skipInteraction.credentialName}'` - ); + // check existing properties before adding additional // todo CONFIRM if needed + await config.checkProperties(properties); + + Util.logger.info('Found the following credentials in your config file:'); + for (const cred in properties.credentials) { + if (Object.prototype.hasOwnProperty.call(properties.credentials, cred)) { + Util.logger.info(` - ${cred}`); } - return this._setCredential(properties, null, skipInteraction); } + Util.logger.info('\nPlease enter your new credentials'); + if (skipInteraction && properties.credentials[skipInteraction.credentialName]) { + Util.logger.error( + `Credential '${skipInteraction.credentialName}' already existing. If you tried updating please provide run 'mcdev init ${skipInteraction.credentialName}'` + ); + } + return this._setCredential(properties, null, skipInteraction); }, /** * Extends template file for properties.json @@ -86,10 +84,8 @@ const Cli = { */ async getCredentialObject(properties, target, isCredentialOnly, allowAll) { try { - if (!(await config.checkProperties(properties))) { - // return null here to avoid seeing 2 error messages for the same issue - return null; - } + // check existing object before continuing // todo confirm if required stil + await config.checkProperties(properties); let [credential, businessUnit] = target ? target.split('/') : [null, null]; if ( credential && diff --git a/lib/util/config.js b/lib/util/config.js index 3ebc1e286..ebeff6da3 100644 --- a/lib/util/config.js +++ b/lib/util/config.js @@ -21,45 +21,29 @@ const config = { return config.properties; } if (await File.pathExists(Util.configFileName)) { - try { - config.properties = await File.readJson(Util.configFileName); - } catch (ex) { - Util.logger.error(`${ex.code}: ${ex.message}`); - return; - } - if (await File.pathExists(Util.authFileName)) { - let auth; - try { - auth = await File.readJson(Util.authFileName); - } catch (ex) { - Util.logger.error(`${ex.code}: ${ex.message}`); - return; - } + config.properties = await File.readJson(Util.configFileName); - if (!auth) { - const err = `${Util.authFileName} is not set up correctly.`; - Util.logger.error(err); - throw new Error(err); - } + if (await File.pathExists(Util.authFileName)) { + const auth = await File.readJson(Util.authFileName); for (const cred in config.properties.credentials) { if (auth[cred]) { if ( config.properties.credentials[cred].eid !== auth[cred].account_id && !silent ) { - const err = `'${cred}' found in ${Util.configFileName} and ${Util.authFileName} have a Enterprise ID mismatch. Please check.`; - Util.logger.error(err); - throw new Error(err); + throw new Error( + `'${cred}' found in ${Util.configFileName} and ${Util.authFileName} have a Enterprise ID mismatch. Please check.` + ); } // TODO add auth checks #294 } else if (!silent) { - Util.logger.warn( + throw new Error( `'${cred}' found in ${Util.configFileName} but not in ${Util.authFileName}. Please run 'mcdev init' to provide the missing credential details.` ); } } } else if (!silent) { - Util.logger.warn( + throw new Error( `${Util.authFileName} not found. Please run 'mcdev init' to provide the missing credential details.` ); } @@ -76,25 +60,26 @@ const config = { */ checkProperties: async function (properties, silent) { if (!(await File.pathExists(Util.configFileName)) || !properties) { - Util.logger.error(`\nCould not find ${Util.configFileName} in ${process.cwd()}.`); - Util.logger.error(`Run 'mcdev init' to initialize your project.\n`); - return false; + throw new Error( + `Could not find ${ + Util.configFileName + } in ${process.cwd()}.Run 'mcdev init' to initialize your project.\n` + ); } if (!(await File.pathExists(Util.authFileName)) || !properties) { - Util.logger.error(`\nCould not find ${Util.authFileName} in ${process.cwd()}.`); - Util.logger.error(`Run 'mcdev init' to initialize your project.\n`); - return false; + throw new Error( + `Could not find ${ + Util.authFileName + } in ${process.cwd()}.Run 'mcdev init' to initialize your project.\n` + ); } // check if user is running older (ignores patches) mcdev version than whats saved to the config if (properties.version && semver.gt(properties.version, packageJsonMcdev.version)) { - Util.logger.error( - `Your Accenture SFMC DevTools version ${packageJsonMcdev.version} is lower than your project's config version ${properties.version}` - ); + const errorMsg = `Your Accenture SFMC DevTools version ${packageJsonMcdev.version} is lower than your project's config version ${properties.version}`; if (Util.skipInteraction) { - return false; + throw new Error(errorMsg); } - const responses = await inquirer.prompt([ { type: 'confirm', @@ -107,11 +92,11 @@ const config = { // use _execSync here to avoid a circular dependency Util.execSync('npm', ['update', '-g', `mcdev@${properties.version}`]); } - return false; + throw new Error(errorMsg); } // check config properties - const defaultProps = config.getDefaultProperties(); + const defaultProps = await config.getDefaultProperties(); const errorMsgs = []; const solutionSet = new Set(); const missingFields = []; @@ -227,7 +212,9 @@ const config = { missingFields.push('version'); } if (silent) { - return missingFields; + const err = new Error('Missing Fields'); + err.missingFields = missingFields; + throw err; } else { if (errorMsgs.length) { const errorMsgOutput = [ @@ -238,7 +225,7 @@ const config = { } Util.logger.error(errorMsgOutput.join('\n')); if (Util.skipInteraction) { - return false; + throw new Error(errorMsgOutput); } Util.logger.info( [ @@ -258,9 +245,7 @@ const config = { // use _execSync here to avoid a circular dependency this.execSync('mcdev', ['upgrade']); } - return false; - } else { - return true; + throw new Error(errorMsgOutput); } } }, diff --git a/lib/util/file.js b/lib/util/file.js index 9cf425c2a..0dc7a279d 100644 --- a/lib/util/file.js +++ b/lib/util/file.js @@ -144,7 +144,11 @@ const File = { filename = this.filterIllegalFilenames(filename); await fs.ensureDir(directory); try { - return fs.writeJSON(path.join(directory, filename + '.json'), content, { spaces: 4 }); + return fs.writeJSON( + path.join(directory, filename.endsWith('.json') ? filename : filename + '.json'), + content, + { spaces: 4 } + ); } catch (ex) { Util.logger.error('File.writeJSONToFile:: error | ' + ex.message); } diff --git a/lib/util/init.config.js b/lib/util/init.config.js index fb38ce34d..998acd23f 100644 --- a/lib/util/init.config.js +++ b/lib/util/init.config.js @@ -28,96 +28,102 @@ const Init = { let updateConfigNeeded = false; const upgradeMsgs = [`Upgrading existing ${Util.configFileName}:`]; + const defaultProps = await config.getDefaultProperties(); + try { + await config.checkProperties(properties, true); + } catch (ex) { + if (ex.missingFields?.length) { + ex.missingFields.forEach((fieldName) => { + switch (fieldName) { + case 'marketList': + if (properties.marketBulk) { + upgradeMsgs.push(`- ✔️ converted 'marketBulk' to '${fieldName}'`); + properties[fieldName] = properties.marketBulk; + delete properties.marketBulk; + } else { + upgradeMsgs.push(`- ✔️ added '${fieldName}'`); + this._updateLeaf(properties, defaultProps, fieldName); + } + break; + case 'directories.docs': + if (properties.directories.badKeys) { + delete properties.directories.badKeys; + upgradeMsgs.push(`- ✋ removed 'directories.badKeys'`); + } + if (properties.directories.dataExtension) { + File.removeSync(properties.directories.dataExtension); + delete properties.directories.dataExtension; + upgradeMsgs.push(`- ✋ removed 'directories.dataExtension'`); + } + if (properties.directories.deltaPackage) { + delete properties.directories.deltaPackage; + upgradeMsgs.push(`- ✋ removed 'directories.deltaPackage'`); + } + if (properties.directories.dataextension) { + delete properties.directories.dataextension; + upgradeMsgs.push(`- ✋ removed 'directories.dataextension'`); + } + if (properties.directories.roles) { + delete properties.directories.roles; + upgradeMsgs.push(`- ✋ removed 'directories.roles'`); + } + if (properties.directories.users) { + delete properties.directories.users; + upgradeMsgs.push(`- ✋ removed 'directories.users'`); + } - const missingFields = await config.checkProperties(properties, true); - const defaultProps = config.getDefaultProperties(); - if (missingFields.length) { - missingFields.forEach((fieldName) => { - switch (fieldName) { - case 'marketList': - if (properties.marketBulk) { - upgradeMsgs.push(`- ✔️ converted 'marketBulk' to '${fieldName}'`); - properties[fieldName] = properties.marketBulk; - delete properties.marketBulk; - } else { - upgradeMsgs.push(`- ✔️ added '${fieldName}'`); - this._updateLeaf(properties, defaultProps, fieldName); - } - break; - case 'directories.docs': - if (properties.directories.badKeys) { - delete properties.directories.badKeys; - upgradeMsgs.push(`- ✋ removed 'directories.badKeys'`); - } - if (properties.directories.dataExtension) { - File.removeSync(properties.directories.dataExtension); - delete properties.directories.dataExtension; - upgradeMsgs.push(`- ✋ removed 'directories.dataExtension'`); - } - if (properties.directories.deltaPackage) { - delete properties.directories.deltaPackage; - upgradeMsgs.push(`- ✋ removed 'directories.deltaPackage'`); - } - if (properties.directories.dataextension) { - delete properties.directories.dataextension; - upgradeMsgs.push(`- ✋ removed 'directories.dataextension'`); - } - if (properties.directories.roles) { - delete properties.directories.roles; - upgradeMsgs.push(`- ✋ removed 'directories.roles'`); - } - if (properties.directories.users) { - delete properties.directories.users; - upgradeMsgs.push(`- ✋ removed 'directories.users'`); - } - - this._updateLeaf(properties, defaultProps, fieldName); - upgradeMsgs.push(`- ✔️ added '${fieldName}'`); - break; - case 'metaDataTypes.documentOnRetrieve': - if (!properties.options.documentOnRetrieve) { - properties.metaDataTypes.documentOnRetrieve = []; - } else { this._updateLeaf(properties, defaultProps, fieldName); - } - delete properties.options.documentOnRetrieve; - upgradeMsgs.push( - `- ✔️ converted 'options.documentOnRetrieve' to '${fieldName}'` - ); - break; - case 'options.deployment.commitHistory': - if (properties.options.commitHistory) { + upgradeMsgs.push(`- ✔️ added '${fieldName}'`); + break; + case 'metaDataTypes.documentOnRetrieve': + if (!properties.options.documentOnRetrieve) { + properties.metaDataTypes.documentOnRetrieve = []; + } else { + this._updateLeaf(properties, defaultProps, fieldName); + } + delete properties.options.documentOnRetrieve; upgradeMsgs.push( - `- ✔️ converted 'options.commitHistory' to '${fieldName}'` + `- ✔️ converted 'options.documentOnRetrieve' to '${fieldName}'` ); - properties.options.deployment.commitHistory = - properties.options.commitHistory; - delete properties.options.commitHistory; - } else { - upgradeMsgs.push(`- ✔️ added '${fieldName}'`); + break; + case 'options.deployment.commitHistory': + if (properties.options.commitHistory) { + upgradeMsgs.push( + `- ✔️ converted 'options.commitHistory' to '${fieldName}'` + ); + properties.options.deployment.commitHistory = + properties.options.commitHistory; + delete properties.options.commitHistory; + } else { + upgradeMsgs.push(`- ✔️ added '${fieldName}'`); + this._updateLeaf(properties, defaultProps, fieldName); + } + break; + case 'options.exclude': + if (properties.options.filter) { + upgradeMsgs.push( + `- ✔️ converted 'options.filter' to '${fieldName}'` + ); + properties.options.exclude = properties.options.filter; + delete properties.options.filter; + } else { + upgradeMsgs.push(`- ✔️ added '${fieldName}'`); + this._updateLeaf(properties, defaultProps, fieldName); + } + break; + case 'version': + // do nothing other than ensure we re-save the config (with the new version) + upgradeMsgs.push(`- ✔️ version updated`); + break; + default: this._updateLeaf(properties, defaultProps, fieldName); - } - break; - case 'options.exclude': - if (properties.options.filter) { - upgradeMsgs.push(`- ✔️ converted 'options.filter' to '${fieldName}'`); - properties.options.exclude = properties.options.filter; - delete properties.options.filter; - } else { upgradeMsgs.push(`- ✔️ added '${fieldName}'`); - this._updateLeaf(properties, defaultProps, fieldName); - } - break; - case 'version': - // do nothing other than ensure we re-save the config (with the new version) - upgradeMsgs.push(`- ✔️ version updated`); - break; - default: - this._updateLeaf(properties, defaultProps, fieldName); - upgradeMsgs.push(`- ✔️ added '${fieldName}'`); - } - }); - updateConfigNeeded = true; + } + }); + updateConfigNeeded = true; + } else { + throw ex; + } } // ensure we document dataExtensions and automations on retrieve as they should now be in the retrieve folder diff --git a/lib/util/init.js b/lib/util/init.js index f65567e76..abf8f3717 100644 --- a/lib/util/init.js +++ b/lib/util/init.js @@ -23,12 +23,13 @@ const Init = { * @returns {Promise.} - */ async initProject(properties, credentialName, skipInteraction) { - const missingCredentials = this._getMissingCredentials(properties); + const missingCredentials = await this._getMissingCredentials(properties); if ((await File.pathExists(Util.configFileName)) && properties) { // config exists if (credentialName) { Util.logger.info(`Updating credential '${credentialName}'`); // update-credential mode + console.log('CHECK', properties); if (!properties.credentials[credentialName]) { Util.logger.error(`Could not find credential '${credentialName}'`); const response = await Cli._selectBU(properties, null, true); @@ -260,17 +261,21 @@ const Init = { * finds credentials that are set up in config but not in auth file * * @param {object} properties javascript object in .mcdevrc.json - * @returns {string[]} list of credential names + * @returns {Promise.} list of credential names */ - _getMissingCredentials(properties) { + async _getMissingCredentials(properties) { let missingCredentials; + const authCredentials = await File.readJson(Util.authFileName); + console.log('GET', authCredentials); if (properties && properties.credentials) { missingCredentials = Object.keys(properties.credentials).filter( (cred) => - !properties.credentials[cred].client_id || - !properties.credentials[cred].client_secret || - !properties.credentials[cred].auth_url || - !properties.credentials[cred].account_id + !authCredentials || + !authCredentials[cred] || + !authCredentials[cred].client_id || + !authCredentials[cred].client_secret || + !authCredentials[cred].auth_url || + !authCredentials[cred].account_id ); } return missingCredentials || []; From e6696315a2b05adb0d953d9b8d3c66d5211690ca Mon Sep 17 00:00:00 2001 From: "douglas.midgley" Date: Tue, 28 Jun 2022 22:15:18 +0200 Subject: [PATCH 04/14] #294: top level error handling on CLI --- lib/cli.js | 68 ++++++++-------- lib/index.js | 225 ++++++++++++++++++++++++--------------------------- 2 files changed, 142 insertions(+), 151 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index ae2698671..cd58287dd 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -33,10 +33,10 @@ yargs describe: 'metadata keys that shall be exclusively downloaded', }); }, - handler: (argv) => { + handler: async (argv) => { Mcdev.setSkipInteraction(argv.skipInteraction); Mcdev.setLoggingLevel(argv); - Mcdev.retrieve(argv.BU, csvToArray(argv.TYPE), csvToArray(argv.KEY)); + await Mcdev.retrieve(argv.BU, csvToArray(argv.TYPE), csvToArray(argv.KEY)); }, }) .command({ @@ -59,10 +59,10 @@ yargs describe: 'metadata key that shall be exclusively uploaded', }); }, - handler: (argv) => { + handler: async (argv) => { Mcdev.setSkipInteraction(argv.skipInteraction); Mcdev.setLoggingLevel(argv); - Mcdev.deploy(argv.BU, csvToArray(argv.TYPE), csvToArray(argv.KEY)); + await Mcdev.deploy(argv.BU, csvToArray(argv.TYPE), csvToArray(argv.KEY)); }, }) .command({ @@ -74,10 +74,10 @@ yargs describe: 'name of your installed package', }); }, - handler: (argv) => { + handler: async (argv) => { Mcdev.setSkipInteraction(argv.skipInteraction); Mcdev.setLoggingLevel(argv); - Mcdev.initProject(argv.credentialsName, argv.skipInteraction); + await Mcdev.initProject(argv.credentialsName, argv.skipInteraction); }, }) .command({ @@ -90,10 +90,10 @@ yargs describe: 'name of your installed package', }); }, - handler: (argv) => { + handler: async (argv) => { Mcdev.setSkipInteraction(argv.skipInteraction); Mcdev.setLoggingLevel(argv); - Mcdev.findBUs(argv.credentialsName); + await Mcdev.findBUs(argv.credentialsName); }, }) .command({ @@ -105,10 +105,10 @@ yargs describe: 'the business unit to deploy to', }); }, - handler: (argv) => { + handler: async (argv) => { Mcdev.setSkipInteraction(argv.skipInteraction); Mcdev.setLoggingLevel(argv); - Mcdev.badKeys(argv.BU); + await Mcdev.badKeys(argv.BU); }, }) .command({ @@ -128,10 +128,10 @@ yargs 'metadata type to generate docs for; currently supported: dataExtension, role', }); }, - handler: (argv) => { + handler: async (argv) => { Mcdev.setSkipInteraction(argv.skipInteraction); Mcdev.setLoggingLevel(argv); - Mcdev.document(argv.BU, argv.TYPE); + await Mcdev.document(argv.BU, argv.TYPE); }, }) .command({ @@ -154,10 +154,10 @@ yargs describe: 'the key to delete', }); }, - handler: (argv) => { + handler: async (argv) => { Mcdev.setSkipInteraction(argv.skipInteraction); Mcdev.setLoggingLevel(argv); - Mcdev.deleteByKey(argv.BU, argv.TYPE, argv.EXTERNALKEY); + await Mcdev.deleteByKey(argv.BU, argv.TYPE, argv.EXTERNALKEY); }, }) .command({ @@ -184,10 +184,10 @@ yargs describe: 'market used for reverse building template', }); }, - handler: (argv) => { + handler: async (argv) => { Mcdev.setSkipInteraction(argv.skipInteraction); Mcdev.setLoggingLevel(argv); - Mcdev.retrieveAsTemplate(argv.BU, argv.TYPE, csvToArray(argv.NAME), argv.MARKET); + await Mcdev.retrieveAsTemplate(argv.BU, argv.TYPE, csvToArray(argv.NAME), argv.MARKET); }, }) .command({ @@ -214,12 +214,12 @@ yargs describe: 'market used for reverse building template', }); }, - handler: (argv) => { + handler: async (argv) => { Mcdev.setSkipInteraction(argv.skipInteraction); Mcdev.setLoggingLevel(argv); const keyArr = csvToArray(argv.KEY); - Mcdev.buildTemplate(argv.BU, argv.TYPE, keyArr, argv.MARKET); + await Mcdev.buildTemplate(argv.BU, argv.TYPE, keyArr, argv.MARKET); }, }) .command({ @@ -245,10 +245,10 @@ yargs describe: 'the business unit to deploy to', }); }, - handler: (argv) => { + handler: async (argv) => { Mcdev.setSkipInteraction(argv.skipInteraction); Mcdev.setLoggingLevel(argv); - Mcdev.buildDefinition(argv.BU, argv.TYPE, argv.NAME, argv.MARKET); + await Mcdev.buildDefinition(argv.BU, argv.TYPE, argv.NAME, argv.MARKET); }, }) .command({ @@ -270,30 +270,30 @@ yargs describe: 'name of the metadata component', }); }, - handler: (argv) => { + handler: async (argv) => { Mcdev.setSkipInteraction(argv.skipInteraction); Mcdev.setLoggingLevel(argv); - Mcdev.buildDefinitionBulk(argv.LISTNAME, argv.TYPE, argv.NAME); + await Mcdev.buildDefinitionBulk(argv.LISTNAME, argv.TYPE, argv.NAME); }, }) .command({ command: 'selectTypes', aliases: ['st'], desc: 'lets you choose what metadata types to retrieve', - handler: (argv) => { + handler: async (argv) => { Mcdev.setSkipInteraction(argv.skipInteraction); Mcdev.setLoggingLevel(argv); - Mcdev.selectTypes(); + await Mcdev.selectTypes(); }, }) .command({ command: 'explainTypes', aliases: ['et'], desc: 'explains metadata types that can be retrieved', - handler: (argv) => { + handler: async (argv) => { Mcdev.setSkipInteraction(argv.skipInteraction); Mcdev.setLoggingLevel(argv); - Mcdev.explainTypes(); + await Mcdev.explainTypes(); }, }) .command({ @@ -312,10 +312,10 @@ yargs 'Disable templating & instead filter by the specified file path (comma separated)', }); }, - handler: (argv) => { + handler: async (argv) => { Mcdev.setSkipInteraction(argv.skipInteraction); Mcdev.setLoggingLevel(argv); - Mcdev.createDeltaPkg(argv); + await Mcdev.createDeltaPkg(argv); }, }) .command({ @@ -338,22 +338,22 @@ yargs describe: 'key(s) of the metadata component(s)', }); }, - handler: (argv) => { + handler: async (argv) => { Mcdev.setSkipInteraction(argv.skipInteraction); Mcdev.setLoggingLevel(argv); const keyArr = csvToArray(argv.KEY); - Mcdev.getFilesToCommit(argv.BU, argv.TYPE, keyArr); + await Mcdev.getFilesToCommit(argv.BU, argv.TYPE, keyArr); }, }) .command({ command: 'upgrade', aliases: ['up'], desc: 'Add NPM dependencies and IDE configuration files to your project', - handler: (argv) => { + handler: async (argv) => { Mcdev.setSkipInteraction(argv.skipInteraction); Mcdev.setLoggingLevel(argv); - Mcdev.upgrade(argv.skipInteraction); + await Mcdev.upgrade(argv.skipInteraction); }, }) .option('verbose', { @@ -374,6 +374,10 @@ yargs }) .demandCommand(1, 'Please enter a valid command') .strict() + .fail((_, err) => { + Util.logger.error(err.message); + process.exit(1); + }) .recommendCommands() .wrap(yargs.terminalWidth()) .epilog( diff --git a/lib/index.js b/lib/index.js index 4caec2ca3..e91a2f01e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -85,10 +85,6 @@ class Mcdev { static async upgrade(skipInteraction) { Mcdev.setSkipInteraction(skipInteraction); const properties = await config.getProperties(); - if (!properties) { - Util.logger.error('No config found. Please run mcdev init'); - return false; - } if ((await InitGit.initGitRepo(skipInteraction)).status === 'error') { return false; } @@ -110,81 +106,78 @@ class Mcdev { */ static async retrieve(businessUnit, selectedTypesArr, keys, changelogOnly) { Util.logger.info('mcdev:: Retrieve'); - try { - const properties = await config.getProperties(); - // assume a list was passed in and check each entry's validity - if (selectedTypesArr) { - for (const selectedType of selectedTypesArr) { - if (!Util._isValidType(selectedType)) { - return; - } + const properties = await config.getProperties(); + + // assume a list was passed in and check each entry's validity + if (selectedTypesArr) { + for (const selectedType of selectedTypesArr) { + if (!Util._isValidType(selectedType)) { + return; } } + } - if (businessUnit === '*') { - Util.logger.info('\n :: Retrieving all BUs for all credentials'); - let counter_credTotal = 0; - for (const cred in properties.credentials) { - Util.logger.info(`\n :: Retrieving all BUs for ${cred}`); - let counter_credBu = 0; - for (const bu in properties.credentials[cred].businessUnits) { - await this._retrieveBU(cred, bu, selectedTypesArr, keys); - counter_credBu++; - Util.restartLogger(); - } - counter_credTotal += counter_credBu; - Util.logger.info(`\n :: ${counter_credBu} BUs for ${cred}\n`); + if (businessUnit === '*') { + Util.logger.info('\n :: Retrieving all BUs for all credentials'); + let counter_credTotal = 0; + for (const cred in properties.credentials) { + Util.logger.info(`\n :: Retrieving all BUs for ${cred}`); + let counter_credBu = 0; + for (const bu in properties.credentials[cred].businessUnits) { + await this._retrieveBU(cred, bu, selectedTypesArr, keys); + counter_credBu++; + Util.restartLogger(); } - Util.logger.info(`\n :: ${counter_credTotal} BUs in total\n`); - } else { - let [cred, bu] = businessUnit ? businessUnit.split('/') : [null, null]; - // to allow all-BU via user selection we need to run this here already - if ( - properties.credentials && - (!properties.credentials[cred] || - (bu !== '*' && properties.credentials[cred].businessUnits[bu])) - ) { - const buObject = await Cli.getCredentialObject( - properties, - cred !== null ? cred + '/' + bu : null, - null, - true - ); - if (buObject !== null) { - cred = buObject.credential; - bu = buObject.businessUnit; - } else { - return; - } + counter_credTotal += counter_credBu; + Util.logger.info(`\n :: ${counter_credBu} BUs for ${cred}\n`); + } + Util.logger.info(`\n :: ${counter_credTotal} BUs in total\n`); + } else { + let [cred, bu] = businessUnit ? businessUnit.split('/') : [null, null]; + // to allow all-BU via user selection we need to run this here already + if ( + properties.credentials && + (!properties.credentials[cred] || + (bu !== '*' && properties.credentials[cred].businessUnits[bu])) + ) { + const buObject = await Cli.getCredentialObject( + properties, + cred !== null ? cred + '/' + bu : null, + null, + true + ); + if (buObject !== null) { + cred = buObject.credential; + bu = buObject.businessUnit; + } else { + return; } + } - if (bu === '*' && properties.credentials && properties.credentials[cred]) { - Util.logger.info(`\n :: Retrieving all BUs for ${cred}`); - let counter_credBu = 0; - for (const bu in properties.credentials[cred].businessUnits) { - await this._retrieveBU(cred, bu, selectedTypesArr, keys); - counter_credBu++; - Util.restartLogger(); - } - Util.logger.info(`\n :: ${counter_credBu} BUs for ${cred}\n`); - } else { - // retrieve a single BU; return - const retrieveChangelog = await this._retrieveBU( - cred, - bu, - selectedTypesArr, - keys, - changelogOnly - ); - if (changelogOnly) { - return retrieveChangelog; - } - Util.logger.info(`:: Done\n`); + if (bu === '*' && properties.credentials && properties.credentials[cred]) { + Util.logger.info(`\n :: Retrieving all BUs for ${cred}`); + let counter_credBu = 0; + for (const bu in properties.credentials[cred].businessUnits) { + await this._retrieveBU(cred, bu, selectedTypesArr, keys); + counter_credBu++; + Util.restartLogger(); + } + Util.logger.info(`\n :: ${counter_credBu} BUs for ${cred}\n`); + } else { + // retrieve a single BU; return + const retrieveChangelog = await this._retrieveBU( + cred, + bu, + selectedTypesArr, + keys, + changelogOnly + ); + if (changelogOnly) { + return retrieveChangelog; } + Util.logger.info(`:: Done\n`); } - } catch (ex) { - Util.logger.error(`Retrieve failed: ${ex.message}`); } } /** @@ -313,32 +306,29 @@ class Mcdev { */ static async document(businessUnit, type) { Util.logger.info('mcdev:: Document'); + + const properties = await config.getProperties(); + if (type && !MetadataTypeInfo[type]) { + Util.logger.error(`:: '${type}' is not a valid metadata type`); + return; + } try { - const properties = await config.getProperties(); - if (type && !MetadataTypeInfo[type]) { - Util.logger.error(`:: '${type}' is not a valid metadata type`); - return; - } - try { - const parentBUOnlyTypes = ['accountUser', 'role']; - const buObject = await Cli.getCredentialObject( - properties, - parentBUOnlyTypes.includes(type) ? businessUnit.split('/')[0] : businessUnit, - parentBUOnlyTypes.includes(type) ? Util.parentBuName : null - ); - if (buObject !== null) { - MetadataTypeInfo[type].properties = properties; - MetadataTypeInfo[type].document(buObject); - } - } catch (ex) { - Util.logger.error('mcdev.document ' + ex.message); - Util.logger.debug(ex.stack); - Util.logger.info( - 'If the directoy does not exist, you may need to retrieve this BU first.' - ); + const parentBUOnlyTypes = ['accountUser', 'role']; + const buObject = await Cli.getCredentialObject( + properties, + parentBUOnlyTypes.includes(type) ? businessUnit.split('/')[0] : businessUnit, + parentBUOnlyTypes.includes(type) ? Util.parentBuName : null + ); + if (buObject !== null) { + MetadataTypeInfo[type].properties = properties; + MetadataTypeInfo[type].document(buObject); } } catch (ex) { - Util.logger.error(`Document failed: ${ex.message}`); + Util.logger.error('mcdev.document ' + ex.message); + Util.logger.debug(ex.stack); + Util.logger.info( + 'If the directoy does not exist, you may need to retrieve this BU first.' + ); } } @@ -452,34 +442,31 @@ class Mcdev { */ static async retrieveAsTemplate(businessUnit, selectedType, name, market) { Util.logger.info('mcdev:: Retrieve as Template'); - try { - const properties = await config.getProperties(); - if (!Util._isValidType(selectedType)) { - return; - } - const [type, subType] = selectedType ? selectedType.split('-') : []; - let retrieveTypesArr; - if ( - type && - subType && - MetadataTypeInfo[type] && - MetadataTypeDefinitions[type].subTypes.includes(subType) - ) { - retrieveTypesArr = [selectedType]; - } else if (type && MetadataTypeInfo[type]) { - retrieveTypesArr = [type]; - } - const buObject = await Cli.getCredentialObject(properties, businessUnit); - if (buObject !== null) { - cache.initCache(buObject); - const retriever = new Retriever(properties, buObject); - if (Util.checkMarket(market, properties)) { - return retriever.retrieve(retrieveTypesArr, name, properties.markets[market]); - } + const properties = await config.getProperties(); + if (!Util._isValidType(selectedType)) { + return; + } + const [type, subType] = selectedType ? selectedType.split('-') : []; + + let retrieveTypesArr; + if ( + type && + subType && + MetadataTypeInfo[type] && + MetadataTypeDefinitions[type].subTypes.includes(subType) + ) { + retrieveTypesArr = [selectedType]; + } else if (type && MetadataTypeInfo[type]) { + retrieveTypesArr = [type]; + } + const buObject = await Cli.getCredentialObject(properties, businessUnit); + if (buObject !== null) { + cache.initCache(buObject); + const retriever = new Retriever(properties, buObject); + if (Util.checkMarket(market, properties)) { + return retriever.retrieve(retrieveTypesArr, name, properties.markets[market]); } - } catch (ex) { - Util.logger.error(`Retrieve as Template failed: ${ex.message}`); } } From c71eb3fdb279459b285573033408433ff07d9faa Mon Sep 17 00:00:00 2001 From: "douglas.midgley" Date: Tue, 28 Jun 2022 22:51:23 +0200 Subject: [PATCH 05/14] #294: review feedback --- docs/dist/documentation.md | 4 ++-- lib/index.js | 2 +- lib/util/businessUnit.js | 4 ++-- lib/util/cli.js | 2 -- lib/util/config.js | 7 ++++--- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 2550c3eba..a623b7481 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -574,7 +574,7 @@ Creates template file for properties.json | Param | Type | Description | | --- | --- | --- | | [credentialName] | string | identifying name of the installed package / project | -| [skipInteraction] | boolean \| object | signals what to insert automatically for things usually asked via wizard | +| [skipInteraction] | boolean \| TYPE.skipInteraction | signals what to insert automatically for things usually asked via wizard | @@ -4683,7 +4683,7 @@ Refreshes BU names and ID's from MC instance | Param | Type | Description | | --- | --- | --- | -| properties | object | current properties that have to be refreshed | +| properties | TYPE.Mcdevrc | current properties that have to be refreshed | | credentialName | string | identifying name of the installed package / project | diff --git a/lib/index.js b/lib/index.js index e91a2f01e..a6a413776 100644 --- a/lib/index.js +++ b/lib/index.js @@ -272,7 +272,7 @@ class Mcdev { * Creates template file for properties.json * * @param {string} [credentialName] identifying name of the installed package / project - * @param {boolean | object} [skipInteraction] signals what to insert automatically for things usually asked via wizard + * @param {boolean | TYPE.skipInteraction} [skipInteraction] signals what to insert automatically for things usually asked via wizard * @returns {Promise.} - */ static async initProject(credentialName, skipInteraction) { diff --git a/lib/util/businessUnit.js b/lib/util/businessUnit.js index 0276e0ec1..690d51044 100644 --- a/lib/util/businessUnit.js +++ b/lib/util/businessUnit.js @@ -1,5 +1,5 @@ 'use strict'; - +const TYPE = require('../../types/mcdev.d'); const Util = require('./util'); const File = require('./file'); const auth = require('./auth'); @@ -11,7 +11,7 @@ const BusinessUnit = { /** * Refreshes BU names and ID's from MC instance * - * @param {object} properties current properties that have to be refreshed + * @param {TYPE.Mcdevrc} properties current properties that have to be refreshed * @param {string} credentialName identifying name of the installed package / project * @returns {Promise.} success of refresh */ diff --git a/lib/util/cli.js b/lib/util/cli.js index a5586be7c..ce3fd89e5 100644 --- a/lib/util/cli.js +++ b/lib/util/cli.js @@ -83,8 +83,6 @@ const Cli = { */ async getCredentialObject(properties, target, isCredentialOnly, allowAll) { try { - // check existing object before continuing // todo confirm if required stil - await config.checkProperties(properties); let [credential, businessUnit] = target ? target.split('/') : [null, null]; if ( credential && diff --git a/lib/util/config.js b/lib/util/config.js index ebeff6da3..0de3f7a27 100644 --- a/lib/util/config.js +++ b/lib/util/config.js @@ -48,7 +48,7 @@ const config = { ); } } - await config.checkProperties(config.properties); + await config.checkProperties(config.properties, silent); return config.properties; }, /** @@ -223,10 +223,11 @@ const config = { for (const msg of errorMsgs) { errorMsgOutput.push(' - ' + msg); } - Util.logger.error(errorMsgOutput.join('\n')); + const errorMsgText = errorMsgOutput.join('\n'); if (Util.skipInteraction) { - throw new Error(errorMsgOutput); + throw new Error(errorMsgText); } + Util.logger.error(errorMsgText); Util.logger.info( [ 'Here is what you can do to fix these issues:', From bb47453b4ed3ff34d92f03411f64ee5c7be9b607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 1 Jul 2022 16:45:59 +0200 Subject: [PATCH 06/14] #346: turn auth class into properly named (and resolved) object --- docs/dist/documentation.md | 24 +++++++------- lib/util/auth.js | 68 ++++++++++++++++++++------------------ 2 files changed, 47 insertions(+), 45 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index dc94b1869..3707931c8 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -4398,7 +4398,7 @@ CLI entry for SFMC DevTools * [.isTrue(attrValue)](#Util.isTrue) ⇒ boolean * [.isFalse(attrValue)](#Util.isFalse) ⇒ boolean * [._isValidType(selectedType)](#Util._isValidType) ⇒ boolean - * [.getRetrieveTypeChoices()](#Util.getRetrieveTypeChoices) ⇒ Array.<string> + * [.getRetrieveTypeChoices()](#Util.getRetrieveTypeChoices) ⇒ Array.<TYPE.SupportedMetadataTypes> * [.metadataLogger(level, type, method, payload, [source])](#Util.metadataLogger) ⇒ void * [.replaceByObject(str, obj)](#Util.replaceByObject) ⇒ string \| object * [.inverseGet(objs, val)](#Util.inverseGet) ⇒ string @@ -4523,15 +4523,15 @@ helper for retrieve, retrieveAsTemplate and deploy | Param | Type | Description | | --- | --- | --- | -| selectedType | string | type or type-subtype | +| selectedType | TYPE.SupportedMetadataTypes | type or type-subtype | -### Util.getRetrieveTypeChoices() ⇒ Array.<string> +### Util.getRetrieveTypeChoices() ⇒ Array.<TYPE.SupportedMetadataTypes> helper for getDefaultProperties() **Kind**: static method of [Util](#Util) -**Returns**: Array.<string> - type choices +**Returns**: Array.<TYPE.SupportedMetadataTypes> - type choices ### Util.metadataLogger(level, type, method, payload, [source]) ⇒ void @@ -4852,7 +4852,7 @@ Central class for loading and validating properties from config and auth * [config](#config) * [.getProperties([silent])](#config.getProperties) ⇒ object * [.checkProperties(properties, [silent])](#config.checkProperties) ⇒ Promise.<(boolean\|Array.<string>)> - * [.getDefaultProperties()](#config.getDefaultProperties) ⇒ object + * [.getDefaultProperties()](#config.getDefaultProperties) ⇒ TYPE.Mcdevrc @@ -4876,17 +4876,17 @@ check if the config file is correctly formatted and has values | Param | Type | Description | | --- | --- | --- | -| properties | object | javascript object in .mcdevrc.json | +| properties | TYPE.Mcdevrc | javascript object in .mcdevrc.json | | [silent] | boolean | set to true for internal use w/o cli output | -### config.getDefaultProperties() ⇒ object +### config.getDefaultProperties() ⇒ TYPE.Mcdevrc defines how the properties.json should look like used for creating a template and for checking if variables are set **Kind**: static method of [config](#config) -**Returns**: object - default properties +**Returns**: TYPE.Mcdevrc - default properties ## DevOps @@ -6150,7 +6150,7 @@ Util that contains logger and simple util methods * [.isTrue(attrValue)](#Util.isTrue) ⇒ boolean * [.isFalse(attrValue)](#Util.isFalse) ⇒ boolean * [._isValidType(selectedType)](#Util._isValidType) ⇒ boolean - * [.getRetrieveTypeChoices()](#Util.getRetrieveTypeChoices) ⇒ Array.<string> + * [.getRetrieveTypeChoices()](#Util.getRetrieveTypeChoices) ⇒ Array.<TYPE.SupportedMetadataTypes> * [.metadataLogger(level, type, method, payload, [source])](#Util.metadataLogger) ⇒ void * [.replaceByObject(str, obj)](#Util.replaceByObject) ⇒ string \| object * [.inverseGet(objs, val)](#Util.inverseGet) ⇒ string @@ -6275,15 +6275,15 @@ helper for retrieve, retrieveAsTemplate and deploy | Param | Type | Description | | --- | --- | --- | -| selectedType | string | type or type-subtype | +| selectedType | TYPE.SupportedMetadataTypes | type or type-subtype | -### Util.getRetrieveTypeChoices() ⇒ Array.<string> +### Util.getRetrieveTypeChoices() ⇒ Array.<TYPE.SupportedMetadataTypes> helper for getDefaultProperties() **Kind**: static method of [Util](#Util) -**Returns**: Array.<string> - type choices +**Returns**: Array.<TYPE.SupportedMetadataTypes> - type choices ### Util.metadataLogger(level, type, method, payload, [source]) ⇒ void diff --git a/lib/util/auth.js b/lib/util/auth.js index 46e61dd8d..7185fd5b5 100644 --- a/lib/util/auth.js +++ b/lib/util/auth.js @@ -8,7 +8,40 @@ const credentialStore = new Conf({ configName: 'sessions', clearInvalidConfig: t const initializedSDKs = {}; let authfile; -module.exports = { +/** + * Returns an SDK instance to be used for API calls + * + * @param {string} sessionKey key for specific BU + * @param {TYPE.AuthObject} authObject credentials for specific BU + * @returns {SDK} auth object + */ +function setupSDK(sessionKey, authObject) { + return new SDK(authObject, { + eventHandlers: { + onLoop: (type, req) => { + Util.logger.info( + `- Requesting next batch (currently ${req.Results.length} records)` + ); + }, + onRefresh: (authObject) => { + authObject.scope = authObject.scope.split(' '); // Scope is usually not an array, but we enforce conversion here for simplicity + credentialStore.set(sessionKey, authObject); + }, + onConnectionError: (ex, remainingAttempts) => { + Util.logger.warn( + `- Connection problem (Code: ${ex.code}). Retrying ${remainingAttempts} time${ + remainingAttempts > 1 ? 's' : '' + }` + ); + Util.logger.errorStack(ex); + }, + }, + requestAttempts: 4, + retryOnConnectionError: true, + }); +} + +const Auth = { /** * For each business unit, set up base credentials to be used. * @@ -81,35 +114,4 @@ module.exports = { Util.logger.info(`Auth sessions cleared`); }, }; -/** - * Returns an SDK instance to be used for API calls - * - * @param {string} sessionKey key for specific BU - * @param {TYPE.AuthObject} authObject credentials for specific BU - * @returns {SDK} auth object - */ - -const setupSDK = (sessionKey, authObject) => - new SDK(authObject, { - eventHandlers: { - onLoop: (type, req) => { - Util.logger.info( - `- Requesting next batch (currently ${req.Results.length} records)` - ); - }, - onRefresh: (authObject) => { - authObject.scope = authObject.scope.split(' '); // Scope is usually not an array, but we enforce conversion here for simplicity - credentialStore.set(sessionKey, authObject); - }, - onConnectionError: (ex, remainingAttempts) => { - Util.logger.warn( - `- Connection problem (Code: ${ex.code}). Retrying ${remainingAttempts} time${ - remainingAttempts > 1 ? 's' : '' - }` - ); - Util.logger.errorStack(ex); - }, - }, - requestAttempts: 4, - retryOnConnectionError: true, - }); +module.exports = Auth; From 45e2cf117145428252f60721ef0117b37df25163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 1 Jul 2022 16:48:45 +0200 Subject: [PATCH 07/14] #294: jsdoc and function expression changed to function declaration https://stackoverflow.com/a/336868/818732 --- lib/util/config.js | 13 +++++++------ lib/util/util.js | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/util/config.js b/lib/util/config.js index 0de3f7a27..6b8ba41be 100644 --- a/lib/util/config.js +++ b/lib/util/config.js @@ -1,3 +1,4 @@ +const TYPE = require('../../types/mcdev.d'); const Util = require('./util'); const File = require('./file'); const inquirer = require('inquirer'); @@ -48,17 +49,17 @@ const config = { ); } } - await config.checkProperties(config.properties, silent); + await this.checkProperties(config.properties, silent); return config.properties; }, /** * check if the config file is correctly formatted and has values * - * @param {object} properties javascript object in .mcdevrc.json + * @param {TYPE.Mcdevrc} properties javascript object in .mcdevrc.json * @param {boolean} [silent] set to true for internal use w/o cli output * @returns {Promise.} file structure ok OR list of fields to be fixed */ - checkProperties: async function (properties, silent) { + async checkProperties(properties, silent) { if (!(await File.pathExists(Util.configFileName)) || !properties) { throw new Error( `Could not find ${ @@ -96,7 +97,7 @@ const config = { } // check config properties - const defaultProps = await config.getDefaultProperties(); + const defaultProps = await this.getDefaultProperties(); const errorMsgs = []; const solutionSet = new Set(); const missingFields = []; @@ -254,9 +255,9 @@ const config = { * defines how the properties.json should look like * used for creating a template and for checking if variables are set * - * @returns {object} default properties + * @returns {TYPE.Mcdevrc} default properties */ - getDefaultProperties: async function () { + async getDefaultProperties() { const configFileName = path.resolve(__dirname, Util.boilerplateDirectory, 'config.json'); if (!(await File.pathExists(configFileName))) { this.logger.debug(`Default config file not found in ${configFileName}`); diff --git a/lib/util/util.js b/lib/util/util.js index ec9b50ba2..ec7621ad4 100644 --- a/lib/util/util.js +++ b/lib/util/util.js @@ -152,7 +152,7 @@ const Util = { /** * helper for retrieve, retrieveAsTemplate and deploy * - * @param {string} selectedType type or type-subtype + * @param {TYPE.SupportedMetadataTypes} selectedType type or type-subtype * @returns {boolean} type ok or not */ _isValidType(selectedType) { @@ -174,7 +174,7 @@ const Util = { /** * helper for getDefaultProperties() * - * @returns {string[]} type choices + * @returns {TYPE.SupportedMetadataTypes[]} type choices */ getRetrieveTypeChoices() { const typeChoices = []; From 7ffd72e54de4ce8bf6caf2f2656a427cb8494344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 1 Jul 2022 16:49:30 +0200 Subject: [PATCH 08/14] #294: fix bad reference --- lib/util/cli.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util/cli.js b/lib/util/cli.js index 40ae71731..e63a16fa9 100644 --- a/lib/util/cli.js +++ b/lib/util/cli.js @@ -24,7 +24,7 @@ const Cli = { async initMcdevConfig(skipInteraction) { Util.logger.info('-- Initialising server connection --'); Util.logger.info('Please enter a name for your "Installed Package" credentials:'); - const propertiesTemplate = Util.getDefaultProperties(); + const propertiesTemplate = config.getDefaultProperties(); delete propertiesTemplate.credentials.default; // wait for the interaction to finish or else an outer await will run before this is done From 4157213d3a957549d83667e18e2b1f3f24afcc83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Sun, 3 Jul 2022 10:03:05 +0200 Subject: [PATCH 09/14] #294: extracted getProblems() --- docs/dist/documentation.md | 19 +++- lib/util/config.js | 132 ++++++++++++++------------- lib/util/init.config.js | 179 ++++++++++++++++++------------------- 3 files changed, 172 insertions(+), 158 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 3707931c8..16df173b8 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -4851,8 +4851,9 @@ Central class for loading and validating properties from config and auth * [config](#config) * [.getProperties([silent])](#config.getProperties) ⇒ object - * [.checkProperties(properties, [silent])](#config.checkProperties) ⇒ Promise.<(boolean\|Array.<string>)> + * [.checkProperties(properties, [skipCredentialValidation])](#config.checkProperties) ⇒ Promise.<(boolean\|Array.<string>)> * [.getDefaultProperties()](#config.getDefaultProperties) ⇒ TYPE.Mcdevrc + * [.getProblems(properties, [defaultProps], [skipCredentialValidation])](#config.getProblems) ⇒ Promise.<{missingFields: Array.<string>, errorMsgs: Array.<string>, solutionSet: Set.<string>}> @@ -4868,7 +4869,7 @@ loads central properties from config file -### config.checkProperties(properties, [silent]) ⇒ Promise.<(boolean\|Array.<string>)> +### config.checkProperties(properties, [skipCredentialValidation]) ⇒ Promise.<(boolean\|Array.<string>)> check if the config file is correctly formatted and has values **Kind**: static method of [config](#config) @@ -4877,7 +4878,7 @@ check if the config file is correctly formatted and has values | Param | Type | Description | | --- | --- | --- | | properties | TYPE.Mcdevrc | javascript object in .mcdevrc.json | -| [silent] | boolean | set to true for internal use w/o cli output | +| [skipCredentialValidation] | boolean | set to true for internal use w/o cli output | @@ -4887,6 +4888,18 @@ used for creating a template and for checking if variables are set **Kind**: static method of [config](#config) **Returns**: TYPE.Mcdevrc - default properties + + +### config.getProblems(properties, [defaultProps], [skipCredentialValidation]) ⇒ Promise.<{missingFields: Array.<string>, errorMsgs: Array.<string>, solutionSet: Set.<string>}> +**Kind**: static method of [config](#config) +**Returns**: Promise.<{missingFields: Array.<string>, errorMsgs: Array.<string>, solutionSet: Set.<string>}> - - + +| Param | Type | Description | +| --- | --- | --- | +| properties | TYPE.Mcdevrc | javascript object in .mcdevrc.json | +| [defaultProps] | TYPE.Mcdevrc | default properties | +| [skipCredentialValidation] | boolean | used by init.config.js>fixMcdevConfig() to auto-fix the config file | + ## DevOps diff --git a/lib/util/config.js b/lib/util/config.js index 6b8ba41be..fa1779275 100644 --- a/lib/util/config.js +++ b/lib/util/config.js @@ -56,10 +56,10 @@ const config = { * check if the config file is correctly formatted and has values * * @param {TYPE.Mcdevrc} properties javascript object in .mcdevrc.json - * @param {boolean} [silent] set to true for internal use w/o cli output + * @param {boolean} [skipCredentialValidation] set to true for internal use w/o cli output * @returns {Promise.} file structure ok OR list of fields to be fixed */ - async checkProperties(properties, silent) { + async checkProperties(properties, skipCredentialValidation) { if (!(await File.pathExists(Util.configFileName)) || !properties) { throw new Error( `Could not find ${ @@ -97,7 +97,72 @@ const config = { } // check config properties - const defaultProps = await this.getDefaultProperties(); + const { errorMsgs, solutionSet } = await this.getProblems( + properties, + null, + skipCredentialValidation + ); + if (errorMsgs.length) { + const errorMsgOutput = [ + `Found problems in your ./${Util.configFileName} that you need to fix first:`, + ]; + for (const msg of errorMsgs) { + errorMsgOutput.push(' - ' + msg); + } + const errorMsgText = errorMsgOutput.join('\n'); + if (Util.skipInteraction) { + throw new Error(errorMsgText); + } + Util.logger.error(errorMsgText); + Util.logger.info( + ['Here is what you can do to fix these issues:', ...Array.from(solutionSet)].join( + '\n- ' + ) + ); + const responses = await inquirer.prompt([ + { + type: 'confirm', + name: 'runUpgradeNow', + message: `Do you want to run 'mcdev upgrade' now?`, + default: true, + }, + ]); + if (responses.runUpgradeNow) { + // use _execSync here to avoid a circular dependency + this.execSync('mcdev', ['upgrade']); + } + throw new Error(errorMsgOutput); + } + }, + /** + * defines how the properties.json should look like + * used for creating a template and for checking if variables are set + * + * @returns {TYPE.Mcdevrc} default properties + */ + async getDefaultProperties() { + const configFileName = path.resolve(__dirname, Util.boilerplateDirectory, 'config.json'); + if (!(await File.pathExists(configFileName))) { + this.logger.debug(`Default config file not found in ${configFileName}`); + return false; + } + const defaultProperties = await File.readJson(configFileName); + // set default name for parent BU + defaultProperties.credentials.default.businessUnits[Util.parentBuName] = 0; + // set default retrieve values + defaultProperties.metaDataTypes.retrieve = Util.getRetrieveTypeChoices(); + + return defaultProperties; + }, + /** + * + * @param {TYPE.Mcdevrc} properties javascript object in .mcdevrc.json + * @param {TYPE.Mcdevrc} [defaultProps] default properties + * @param {boolean} [skipCredentialValidation] used by init.config.js>fixMcdevConfig() to auto-fix the config file + * @returns {Promise.<{missingFields: string[], errorMsgs: string[], solutionSet: Set}>} - + */ + async getProblems(properties, defaultProps, skipCredentialValidation) { + defaultProps = defaultProps || (await this.getDefaultProperties()); const errorMsgs = []; const solutionSet = new Set(); const missingFields = []; @@ -110,7 +175,7 @@ const config = { ); missingFields.push(key); } else { - if (!silent && key === 'credentials') { + if (!skipCredentialValidation && key === 'credentials') { if (!Object.keys(properties.credentials)) { errorMsgs.push(`no Credential defined`); } else { @@ -212,64 +277,7 @@ const config = { solutionSet.add(`Run 'mcdev upgrade' to ensure optimal performance`); missingFields.push('version'); } - if (silent) { - const err = new Error('Missing Fields'); - err.missingFields = missingFields; - throw err; - } else { - if (errorMsgs.length) { - const errorMsgOutput = [ - `Found problems in your ./${Util.configFileName} that you need to fix first:`, - ]; - for (const msg of errorMsgs) { - errorMsgOutput.push(' - ' + msg); - } - const errorMsgText = errorMsgOutput.join('\n'); - if (Util.skipInteraction) { - throw new Error(errorMsgText); - } - Util.logger.error(errorMsgText); - Util.logger.info( - [ - 'Here is what you can do to fix these issues:', - ...Array.from(solutionSet), - ].join('\n- ') - ); - const responses = await inquirer.prompt([ - { - type: 'confirm', - name: 'runUpgradeNow', - message: `Do you want to run 'mcdev upgrade' now?`, - default: true, - }, - ]); - if (responses.runUpgradeNow) { - // use _execSync here to avoid a circular dependency - this.execSync('mcdev', ['upgrade']); - } - throw new Error(errorMsgOutput); - } - } - }, - /** - * defines how the properties.json should look like - * used for creating a template and for checking if variables are set - * - * @returns {TYPE.Mcdevrc} default properties - */ - async getDefaultProperties() { - const configFileName = path.resolve(__dirname, Util.boilerplateDirectory, 'config.json'); - if (!(await File.pathExists(configFileName))) { - this.logger.debug(`Default config file not found in ${configFileName}`); - return false; - } - const defaultProperties = await File.readJson(configFileName); - // set default name for parent BU - defaultProperties.credentials.default.businessUnits[Util.parentBuName] = 0; - // set default retrieve values - defaultProperties.metaDataTypes.retrieve = Util.getRetrieveTypeChoices(); - - return defaultProperties; + return { missingFields, errorMsgs, solutionSet }; }, }; diff --git a/lib/util/init.config.js b/lib/util/init.config.js index e21d66ebe..b24b90665 100644 --- a/lib/util/init.config.js +++ b/lib/util/init.config.js @@ -30,102 +30,95 @@ const Init = { const upgradeMsgs = [`Upgrading existing ${Util.configFileName}:`]; const defaultProps = await config.getDefaultProperties(); - try { - await config.checkProperties(properties, true); - } catch (ex) { - if (ex.missingFields?.length) { - ex.missingFields.forEach((fieldName) => { - switch (fieldName) { - case 'marketList': - if (properties.marketBulk) { - upgradeMsgs.push(`- ✔️ converted 'marketBulk' to '${fieldName}'`); - properties[fieldName] = properties.marketBulk; - delete properties.marketBulk; - } else { - upgradeMsgs.push(`- ✔️ added '${fieldName}'`); - this._updateLeaf(properties, defaultProps, fieldName); - } - break; - case 'directories.docs': - if (properties.directories.badKeys) { - delete properties.directories.badKeys; - upgradeMsgs.push(`- ✋ removed 'directories.badKeys'`); - } - if (properties.directories.dataExtension) { - File.removeSync(properties.directories.dataExtension); - delete properties.directories.dataExtension; - upgradeMsgs.push(`- ✋ removed 'directories.dataExtension'`); - } - if (properties.directories.deltaPackage) { - delete properties.directories.deltaPackage; - upgradeMsgs.push(`- ✋ removed 'directories.deltaPackage'`); - } - if (properties.directories.dataextension) { - delete properties.directories.dataextension; - upgradeMsgs.push(`- ✋ removed 'directories.dataextension'`); - } - if (properties.directories.roles) { - delete properties.directories.roles; - upgradeMsgs.push(`- ✋ removed 'directories.roles'`); - } - if (properties.directories.users) { - delete properties.directories.users; - upgradeMsgs.push(`- ✋ removed 'directories.users'`); - } + const { missingFields } = await config.getProblems(properties, defaultProps, true); + if (missingFields?.length) { + updateConfigNeeded = true; + } + missingFields.forEach((fieldName) => { + switch (fieldName) { + case 'marketList': + if (properties.marketBulk) { + upgradeMsgs.push(`- ✔️ converted 'marketBulk' to '${fieldName}'`); + properties[fieldName] = properties.marketBulk; + delete properties.marketBulk; + } else { + upgradeMsgs.push(`- ✔️ added '${fieldName}'`); + this._updateLeaf(properties, defaultProps, fieldName); + } + break; + case 'directories.docs': + if (properties.directories.badKeys) { + delete properties.directories.badKeys; + upgradeMsgs.push(`- ✋ removed 'directories.badKeys'`); + } + if (properties.directories.dataExtension) { + File.removeSync(properties.directories.dataExtension); + delete properties.directories.dataExtension; + upgradeMsgs.push(`- ✋ removed 'directories.dataExtension'`); + } + if (properties.directories.deltaPackage) { + delete properties.directories.deltaPackage; + upgradeMsgs.push(`- ✋ removed 'directories.deltaPackage'`); + } + if (properties.directories.dataextension) { + delete properties.directories.dataextension; + upgradeMsgs.push(`- ✋ removed 'directories.dataextension'`); + } + if (properties.directories.roles) { + delete properties.directories.roles; + upgradeMsgs.push(`- ✋ removed 'directories.roles'`); + } + if (properties.directories.users) { + delete properties.directories.users; + upgradeMsgs.push(`- ✋ removed 'directories.users'`); + } - this._updateLeaf(properties, defaultProps, fieldName); - upgradeMsgs.push(`- ✔️ added '${fieldName}'`); - break; - case 'metaDataTypes.documentOnRetrieve': - if (!properties.options.documentOnRetrieve) { - properties.metaDataTypes.documentOnRetrieve = []; - } else { - this._updateLeaf(properties, defaultProps, fieldName); - } - delete properties.options.documentOnRetrieve; - upgradeMsgs.push( - `- ✔️ converted 'options.documentOnRetrieve' to '${fieldName}'` - ); - break; - case 'options.deployment.commitHistory': - if (properties.options.commitHistory) { - upgradeMsgs.push( - `- ✔️ converted 'options.commitHistory' to '${fieldName}'` - ); - properties.options.deployment.commitHistory = - properties.options.commitHistory; - delete properties.options.commitHistory; - } else { - upgradeMsgs.push(`- ✔️ added '${fieldName}'`); - this._updateLeaf(properties, defaultProps, fieldName); - } - break; - case 'options.exclude': - if (properties.options.filter) { - upgradeMsgs.push( - `- ✔️ converted 'options.filter' to '${fieldName}'` - ); - properties.options.exclude = properties.options.filter; - delete properties.options.filter; - } else { - upgradeMsgs.push(`- ✔️ added '${fieldName}'`); - this._updateLeaf(properties, defaultProps, fieldName); - } - break; - case 'version': - // do nothing other than ensure we re-save the config (with the new version) - upgradeMsgs.push(`- ✔️ version updated`); - break; - default: - this._updateLeaf(properties, defaultProps, fieldName); - upgradeMsgs.push(`- ✔️ added '${fieldName}'`); + this._updateLeaf(properties, defaultProps, fieldName); + upgradeMsgs.push(`- ✔️ added '${fieldName}'`); + break; + case 'metaDataTypes.documentOnRetrieve': + if (!properties.options.documentOnRetrieve) { + properties.metaDataTypes.documentOnRetrieve = []; + } else { + this._updateLeaf(properties, defaultProps, fieldName); } - }); - updateConfigNeeded = true; - } else { - throw ex; + delete properties.options.documentOnRetrieve; + upgradeMsgs.push( + `- ✔️ converted 'options.documentOnRetrieve' to '${fieldName}'` + ); + break; + case 'options.deployment.commitHistory': + if (properties.options.commitHistory) { + upgradeMsgs.push( + `- ✔️ converted 'options.commitHistory' to '${fieldName}'` + ); + properties.options.deployment.commitHistory = + properties.options.commitHistory; + delete properties.options.commitHistory; + } else { + upgradeMsgs.push(`- ✔️ added '${fieldName}'`); + this._updateLeaf(properties, defaultProps, fieldName); + } + break; + case 'options.exclude': + if (properties.options.filter) { + upgradeMsgs.push(`- ✔️ converted 'options.filter' to '${fieldName}'`); + properties.options.exclude = properties.options.filter; + delete properties.options.filter; + } else { + upgradeMsgs.push(`- ✔️ added '${fieldName}'`); + this._updateLeaf(properties, defaultProps, fieldName); + } + break; + case 'version': + // do nothing other than ensure we re-save the config (with the new version) + upgradeMsgs.push(`- ✔️ version updated`); + break; + default: + this._updateLeaf(properties, defaultProps, fieldName); + upgradeMsgs.push(`- ✔️ added '${fieldName}'`); } - } + }); // ensure we document dataExtensions and automations on retrieve as they should now be in the retrieve folder this._updateLeaf(properties, defaultProps, 'metaDataTypes.documentOnRetrieve'); From 0ba3e76398ac0f99549b184fe11f066286f6448c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Sun, 3 Jul 2022 10:41:37 +0200 Subject: [PATCH 10/14] #294: skipChecks param in getProperties() --- docs/dist/documentation.md | 6 +++--- lib/Builder.js | 1 - lib/index.js | 2 ++ lib/util/config.js | 15 +++++++++------ lib/util/devops.js | 1 - 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 16df173b8..941cc81cb 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -4850,14 +4850,14 @@ Central class for loading and validating properties from config and auth **Kind**: global constant * [config](#config) - * [.getProperties([silent])](#config.getProperties) ⇒ object + * [.getProperties([skipChecks])](#config.getProperties) ⇒ object * [.checkProperties(properties, [skipCredentialValidation])](#config.checkProperties) ⇒ Promise.<(boolean\|Array.<string>)> * [.getDefaultProperties()](#config.getDefaultProperties) ⇒ TYPE.Mcdevrc * [.getProblems(properties, [defaultProps], [skipCredentialValidation])](#config.getProblems) ⇒ Promise.<{missingFields: Array.<string>, errorMsgs: Array.<string>, solutionSet: Set.<string>}> -### config.getProperties([silent]) ⇒ object +### config.getProperties([skipChecks]) ⇒ object loads central properties from config file **Kind**: static method of [config](#config) @@ -4865,7 +4865,7 @@ loads central properties from config file | Param | Type | Description | | --- | --- | --- | -| [silent] | boolean | omit throwing errors and print messages; assuming not silent if not set | +| [skipChecks] | boolean | omit throwing errors and print messages | diff --git a/lib/Builder.js b/lib/Builder.js index 3b266576f..1e636ae44 100644 --- a/lib/Builder.js +++ b/lib/Builder.js @@ -103,7 +103,6 @@ saved * @returns {Promise.} - */ static async buildTemplate(businessUnit, selectedType, keyArr, market) { - Util.logger.info('mcdev:: Build Definition from Template'); const properties = await config.getProperties(); if (!Util._isValidType(selectedType)) { return; diff --git a/lib/index.js b/lib/index.js index a11546c19..ac0667e5b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -54,6 +54,7 @@ class Mcdev { * @returns {Promise.} list of changed items */ static async createDeltaPkg(argv) { + Util.logger.info('Create Delta Package ::'); Mcdev.setSkipInteraction(argv.skipInteraction); const properties = await config.getProperties(); // get source market and source BU from config @@ -481,6 +482,7 @@ class Mcdev { * @returns {Promise.} - */ static async buildTemplate(businessUnit, selectedType, keyArr, market) { + Util.logger.info('mcdev:: Build Definition from Template'); return Builder.buildTemplate(businessUnit, selectedType, keyArr, market); } /** diff --git a/lib/util/config.js b/lib/util/config.js index fa1779275..47a25ad33 100644 --- a/lib/util/config.js +++ b/lib/util/config.js @@ -13,10 +13,10 @@ const config = { /** * loads central properties from config file * - * @param {boolean} [silent] omit throwing errors and print messages; assuming not silent if not set + * @param {boolean} [skipChecks] omit throwing errors and print messages * @returns {object} central properties object */ - async getProperties(silent) { + async getProperties(skipChecks = false) { // already loaded, return existing if (config.properties) { return config.properties; @@ -30,26 +30,29 @@ const config = { if (auth[cred]) { if ( config.properties.credentials[cred].eid !== auth[cred].account_id && - !silent + !skipChecks ) { + // ! this is the only we want to run even if skipChecks is true throw new Error( `'${cred}' found in ${Util.configFileName} and ${Util.authFileName} have a Enterprise ID mismatch. Please check.` ); } // TODO add auth checks #294 - } else if (!silent) { + } else if (!skipChecks) { throw new Error( `'${cred}' found in ${Util.configFileName} but not in ${Util.authFileName}. Please run 'mcdev init' to provide the missing credential details.` ); } } - } else if (!silent) { + } else if (!skipChecks) { throw new Error( `${Util.authFileName} not found. Please run 'mcdev init' to provide the missing credential details.` ); } } - await this.checkProperties(config.properties, silent); + if (!skipChecks) { + await this.checkProperties(config.properties); + } return config.properties; }, /** diff --git a/lib/util/devops.js b/lib/util/devops.js index 8a8a9ab3b..80da447f6 100644 --- a/lib/util/devops.js +++ b/lib/util/devops.js @@ -24,7 +24,6 @@ const DevOps = { */ async getDeltaList(properties, range, saveToDeployDir, filterPaths) { const rangeUserInput = range; - Util.logger.info('Create Delta Package ::'); if (filterPaths) { filterPaths = filterPaths.split(',').map((filePath) => path From 169650e56b44e68727b3977b1d2954d7009d4383 Mon Sep 17 00:00:00 2001 From: "douglas.midgley" Date: Sun, 3 Jul 2022 23:28:49 +0200 Subject: [PATCH 11/14] #294: missing Util ref --- lib/util/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util/config.js b/lib/util/config.js index 47a25ad33..74ad68630 100644 --- a/lib/util/config.js +++ b/lib/util/config.js @@ -132,7 +132,7 @@ const config = { ]); if (responses.runUpgradeNow) { // use _execSync here to avoid a circular dependency - this.execSync('mcdev', ['upgrade']); + Util.execSync('mcdev', ['upgrade']); } throw new Error(errorMsgOutput); } From cf78dcca056a7846e050a45de4ba3768e670caf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Mon, 4 Jul 2022 15:26:48 +0200 Subject: [PATCH 12/14] #294: bad class reference fixed --- lib/util/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util/config.js b/lib/util/config.js index 74ad68630..2a70fd079 100644 --- a/lib/util/config.js +++ b/lib/util/config.js @@ -146,7 +146,7 @@ const config = { async getDefaultProperties() { const configFileName = path.resolve(__dirname, Util.boilerplateDirectory, 'config.json'); if (!(await File.pathExists(configFileName))) { - this.logger.debug(`Default config file not found in ${configFileName}`); + Util.logger.debug(`Default config file not found in ${configFileName}`); return false; } const defaultProperties = await File.readJson(configFileName); From 2c0f27f9473afb7fada3ce8ea0fc34d8a6eb3a42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Mon, 4 Jul 2022 15:39:36 +0200 Subject: [PATCH 13/14] #294: fix error handler to no longer fail on "mcdev" without params --- lib/cli.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index 42966d963..a5695ce9c 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -378,9 +378,14 @@ yargs }) .demandCommand(1, 'Please enter a valid command') .strict() - .fail((_, err) => { - Util.logger.error(err.message); - process.exit(1); + .fail((msg, ex, yargs) => { + if (ex) { + Util.logger.error(ex.message); + } else if (msg) { + Util.logger.info(msg); + console.error(yargs.help()); + process.exit(1); + } }) .recommendCommands() .wrap(yargs.terminalWidth()) From 2015df406b2871e975901bae5659d109f78c5a0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Mon, 4 Jul 2022 15:47:56 +0200 Subject: [PATCH 14/14] #294: otherwise causing loop --- lib/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/index.js b/lib/index.js index ac0667e5b..612194eff 100644 --- a/lib/index.js +++ b/lib/index.js @@ -85,7 +85,7 @@ class Mcdev { */ static async upgrade(skipInteraction) { Mcdev.setSkipInteraction(skipInteraction); - const properties = await config.getProperties(); + const properties = await config.getProperties(true); if ((await InitGit.initGitRepo(skipInteraction)).status === 'error') { return false; }