diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md
index d0f6ea335..941cc81cb 100644
--- a/docs/dist/documentation.md
+++ b/docs/dist/documentation.md
@@ -132,6 +132,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
@@ -466,8 +469,8 @@ main class
* [.upgrade([skipInteraction])](#Mcdev.upgrade) ⇒ Promise.<boolean>
* [.retrieve(businessUnit, [selectedTypesArr], [keys], [changelogOnly])](#Mcdev.retrieve) ⇒ Promise.<object>
* [.deploy(businessUnit, [selectedTypesArr], [keyArr], [fromRetrieve])](#Mcdev.deploy) ⇒ Promise.<void>
- * [.initProject([credentialsName], [skipInteraction])](#Mcdev.initProject) ⇒ Promise.<void>
- * [.findBUs(credentialsName)](#Mcdev.findBUs) ⇒ Promise.<void>
+ * [.initProject([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>
@@ -568,7 +571,7 @@ Deploys all metadata located in the 'deploy' directory to the specified business
-### Mcdev.initProject([credentialsName], [skipInteraction]) ⇒ Promise.<void>
+### Mcdev.initProject([credentialName], [skipInteraction]) ⇒ Promise.<void>
Creates template file for properties.json
**Kind**: static method of [Mcdev
](#Mcdev)
@@ -576,12 +579,12 @@ 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 |
| [skipInteraction] | boolean
\| TYPE.skipInteraction
| 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)
@@ -589,7 +592,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 |
@@ -4395,9 +4398,7 @@ CLI entry for SFMC DevTools
* [.isTrue(attrValue)](#Util.isTrue) ⇒ boolean
* [.isFalse(attrValue)](#Util.isFalse) ⇒ boolean
* [._isValidType(selectedType)](#Util._isValidType) ⇒ boolean
- * [.getDefaultProperties()](#Util.getDefaultProperties) ⇒ TYPE.Mcdevrc
- * [.getRetrieveTypeChoices()](#Util.getRetrieveTypeChoices) ⇒ Array.<string>
- * [.checkProperties(properties, [silent])](#Util.checkProperties) ⇒ Promise.<(boolean\|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
@@ -4522,36 +4523,15 @@ helper for retrieve, retrieveAsTemplate and deploy
| Param | Type | Description |
| --- | --- | --- |
-| selectedType | string
| type or type-subtype |
-
-
-
-### Util.getDefaultProperties() ⇒ TYPE.Mcdevrc
-defines how the properties.json should look like
-used for creating a template and for checking if variables are set
+| selectedType | TYPE.SupportedMetadataTypes
| type or type-subtype |
-**Kind**: static method of [Util
](#Util)
-**Returns**: TYPE.Mcdevrc
- default properties
-### Util.getRetrieveTypeChoices() ⇒ Array.<string>
+### Util.getRetrieveTypeChoices() ⇒ Array.<TYPE.SupportedMetadataTypes>
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 | TYPE.Mcdevrc
| javascript object in .mcdevrc.json |
-| [silent] | boolean
| set to true for internal use w/o cli output |
-
+**Returns**: Array.<TYPE.SupportedMetadataTypes>
- type choices
### Util.metadataLogger(level, type, method, payload, [source]) ⇒ void
@@ -4697,7 +4677,7 @@ Helper that handles retrieval of BU info
**Kind**: global constant
-### BusinessUnit.refreshBUProperties(properties, credentialsName) ⇒ Promise.<boolean>
+### BusinessUnit.refreshBUProperties(properties, credentialName) ⇒ Promise.<boolean>
Refreshes BU names and ID's from MC instance
**Kind**: static method of [BusinessUnit
](#BusinessUnit)
@@ -4706,7 +4686,7 @@ Refreshes BU names and ID's from MC instance
| Param | Type | Description |
| --- | --- | --- |
| properties | TYPE.Mcdevrc
| 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 |
@@ -4862,6 +4842,64 @@ this keeps the config automatically upgradable when we add new subtypes or chang
shows metadata type descriptions
**Kind**: static method of [Cli
](#Cli)
+
+
+## config
+Central class for loading and validating properties from config and auth
+
+**Kind**: global constant
+
+* [config](#config)
+ * [.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([skipChecks]) ⇒ object
+loads central properties from config file
+
+**Kind**: static method of [config
](#config)
+**Returns**: object
- central properties object
+
+| Param | Type | Description |
+| --- | --- | --- |
+| [skipChecks] | boolean
| omit throwing errors and print messages |
+
+
+
+### 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)
+**Returns**: Promise.<(boolean\|Array.<string>)>
- file structure ok OR list of fields to be fixed
+
+| Param | Type | Description |
+| --- | --- | --- |
+| properties | TYPE.Mcdevrc
| javascript object in .mcdevrc.json |
+| [skipCredentialValidation] | boolean
| set to true for internal use w/o cli output |
+
+
+
+### 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**: 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
@@ -4968,7 +5006,6 @@ File extends fs-extra. It adds logger and util methods for file handling
* [.readFilteredFilename(directory, filename, filetype, [encoding])](#File.readFilteredFilename) ⇒ 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) ⇒ TYPE.Mcdevrc
* [.saveConfigFile(properties)](#File.saveConfigFile) ⇒ Promise.<void>
* [.initPrettier([filetype])](#File.initPrettier) ⇒ Promise.<boolean>
@@ -5167,18 +5204,6 @@ TODO - merge with readDirectories. so far the logic is really different
```js
['deploy/mcdev/bu1']
```
-
-
-### File.loadConfigFile([silent]) ⇒ TYPE.Mcdevrc
-loads central properties from config file
-
-**Kind**: static method of [File
](#File)
-**Returns**: TYPE.Mcdevrc
- 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>
@@ -5225,7 +5250,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}>
@@ -5396,15 +5421,15 @@ 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 |
| --- | --- | --- |
-| properties | TYPE.Mcdevrc
| javascript object in .mcdevrc.json |
+| properties | object
| javascript object in .mcdevrc.json |
@@ -5454,7 +5479,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}>
@@ -5625,15 +5650,15 @@ 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 |
| --- | --- | --- |
-| properties | TYPE.Mcdevrc
| javascript object in .mcdevrc.json |
+| properties | object
| javascript object in .mcdevrc.json |
@@ -5683,7 +5708,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}>
@@ -5854,15 +5879,15 @@ 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 |
| --- | --- | --- |
-| properties | TYPE.Mcdevrc
| javascript object in .mcdevrc.json |
+| properties | object
| javascript object in .mcdevrc.json |
@@ -5912,7 +5937,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}>
@@ -6083,15 +6108,15 @@ 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 |
| --- | --- | --- |
-| properties | TYPE.Mcdevrc
| javascript object in .mcdevrc.json |
+| properties | object
| javascript object in .mcdevrc.json |
@@ -6138,9 +6163,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) ⇒ TYPE.Mcdevrc
- * [.getRetrieveTypeChoices()](#Util.getRetrieveTypeChoices) ⇒ Array.<string>
- * [.checkProperties(properties, [silent])](#Util.checkProperties) ⇒ Promise.<(boolean\|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
@@ -6265,36 +6288,15 @@ helper for retrieve, retrieveAsTemplate and deploy
| Param | Type | Description |
| --- | --- | --- |
-| selectedType | string
| type or type-subtype |
-
-
-
-### Util.getDefaultProperties() ⇒ TYPE.Mcdevrc
-defines how the properties.json should look like
-used for creating a template and for checking if variables are set
+| selectedType | TYPE.SupportedMetadataTypes
| type or type-subtype |
-**Kind**: static method of [Util
](#Util)
-**Returns**: TYPE.Mcdevrc
- default properties
-### Util.getRetrieveTypeChoices() ⇒ Array.<string>
+### Util.getRetrieveTypeChoices() ⇒ Array.<TYPE.SupportedMetadataTypes>
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 | TYPE.Mcdevrc
| javascript object in .mcdevrc.json |
-| [silent] | boolean
| set to true for internal use w/o cli output |
-
+**Returns**: Array.<TYPE.SupportedMetadataTypes>
- type choices
### Util.metadataLogger(level, type, method, payload, [source]) ⇒ void
diff --git a/lib/Builder.js b/lib/Builder.js
index 4a418604c..1e636ae44 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
@@ -102,8 +103,7 @@ saved
* @returns {Promise.} -
*/
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;
}
@@ -170,7 +170,7 @@ saved
* @returns {Promise.} -
*/
static async buildDefinition(businessUnit, selectedType, name, market) {
- const properties = File.loadConfigFile();
+ const properties = await config.getProperties();
if (!Util._isValidType(selectedType)) {
return;
}
@@ -197,7 +197,7 @@ saved
* @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/Deployer.js b/lib/Deployer.js
index 2bb289b9a..8d8d66d85 100644
--- a/lib/Deployer.js
+++ b/lib/Deployer.js
@@ -8,6 +8,7 @@ const Util = require('./util/util');
const File = require('./util/file');
const cache = require('./util/cache');
const auth = require('./util/auth');
+const config = require('./util/config');
/**
* Reads metadata from local directory and deploys it to specified target business unit.
@@ -51,7 +52,8 @@ class Deployer {
*/
static async deploy(businessUnit, selectedTypesArr, keyArr, fromRetrieve) {
Util.logger.info('mcdev:: Deploy');
- const properties = File.loadConfigFile();
+
+ const properties = await config.getProperties();
if (fromRetrieve) {
properties.directories.deploy = properties.directories.retrieve;
}
diff --git a/lib/cli.js b/lib/cli.js
index aefc90336..a5695ce9c 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({
@@ -63,41 +63,41 @@ yargs
describe: 'optionally deploy from retrieve folder',
});
},
- handler: (argv) => {
+ handler: async (argv) => {
Mcdev.setSkipInteraction(argv.skipInteraction);
Mcdev.setLoggingLevel(argv);
Mcdev.deploy(argv.BU, csvToArray(argv.TYPE), csvToArray(argv.KEY), argv.fromRetrieve);
},
})
.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',
});
},
- 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({
- 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',
});
},
- handler: (argv) => {
+ handler: async (argv) => {
Mcdev.setSkipInteraction(argv.skipInteraction);
Mcdev.setLoggingLevel(argv);
- Mcdev.findBUs(argv.credentialsName);
+ await Mcdev.findBUs(argv.credentialsName);
},
})
.command({
@@ -109,10 +109,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({
@@ -132,10 +132,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({
@@ -158,10 +158,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({
@@ -188,10 +188,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({
@@ -218,12 +218,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({
@@ -249,10 +249,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({
@@ -274,30 +274,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({
@@ -316,10 +316,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({
@@ -342,22 +342,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', {
@@ -378,6 +378,15 @@ yargs
})
.demandCommand(1, 'Please enter a valid command')
.strict()
+ .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())
.epilog(
diff --git a/lib/index.js b/lib/index.js
index 98b63bcc9..612194eff 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -16,7 +16,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
@@ -54,11 +54,9 @@ class Mcdev {
* @returns {Promise.} list of changed items
*/
static async createDeltaPkg(argv) {
+ Util.logger.info('Create Delta Package ::');
Mcdev.setSkipInteraction(argv.skipInteraction);
- 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);
@@ -72,10 +70,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);
}
/**
@@ -90,16 +85,15 @@ class Mcdev {
*/
static async upgrade(skipInteraction) {
Mcdev.setSkipInteraction(skipInteraction);
- properties = properties || File.loadConfigFile();
- if (!properties) {
- Util.logger.error('No config found. Please run mcdev init');
- return false;
- }
+ const properties = await config.getProperties(true);
if ((await InitGit.initGitRepo(skipInteraction)).status === 'error') {
return false;
}
-
- return Init.upgradeProject(properties, false);
+ try {
+ return Init.upgradeProject(properties, false);
+ } catch (ex) {
+ Util.logger.error(`Upgrade project failed: ${ex.message}`);
+ }
}
/**
@@ -113,11 +107,8 @@ class Mcdev {
*/
static async retrieve(businessUnit, selectedTypesArr, keys, 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) {
@@ -202,7 +193,7 @@ class Mcdev {
* @returns {Promise.} ensure that BUs are worked on sequentially
*/
static async _retrieveBU(cred, bu, selectedTypesArr, keys, changelogOnly) {
- properties = properties || File.loadConfigFile();
+ const properties = await config.getProperties();
const buObject = await Cli.getCredentialObject(
properties,
cred !== null ? cred + '/' + bu : null,
@@ -282,27 +273,27 @@ class Mcdev {
/**
* 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 {boolean | TYPE.skipInteraction} [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');
Mcdev.setSkipInteraction(skipInteraction);
- properties = properties || File.loadConfigFile(!!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');
- properties = properties || File.loadConfigFile();
- const buObject = await Cli.getCredentialObject(properties, credentialsName, true);
+ const properties = await config.getProperties();
+ const buObject = await Cli.getCredentialObject(properties, credentialName, true);
if (buObject !== null) {
BuHelper.refreshBUProperties(properties, buObject.credential);
}
@@ -317,7 +308,8 @@ 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;
@@ -352,7 +344,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) {
@@ -381,7 +373,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)');
@@ -452,7 +444,8 @@ 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;
}
@@ -489,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);
}
/**
@@ -526,7 +520,7 @@ class Mcdev {
*/
static async getFilesToCommit(businessUnit, selectedType, keyArr) {
Util.logger.info('mcdev:: getFilesToCommit');
- const properties = File.loadConfigFile();
+ const properties = await config.getProperties();
if (!Util._isValidType(selectedType)) {
return;
}
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;
diff --git a/lib/util/businessUnit.js b/lib/util/businessUnit.js
index b6e1300a9..690d51044 100644
--- a/lib/util/businessUnit.js
+++ b/lib/util/businessUnit.js
@@ -1,5 +1,4 @@
'use strict';
-
const TYPE = require('../../types/mcdev.d');
const Util = require('./util');
const File = require('./file');
@@ -13,17 +12,17 @@ const BusinessUnit = {
* Refreshes BU names and ID's from MC instance
*
* @param {TYPE.Mcdevrc} 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
* @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(
@@ -93,7 +92,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 466b75da4..e63a16fa9 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
@@ -23,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
@@ -37,24 +38,22 @@ const Cli = {
* @returns {Promise.} -
*/
async addExtraCredential(properties, skipInteraction) {
- if (!(await Util.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
@@ -84,10 +83,6 @@ const Cli = {
*/
async getCredentialObject(properties, target, isCredentialOnly, allowAll) {
try {
- if (!(await Util.checkProperties(properties))) {
- // return null here to avoid seeing 2 error messages for the same issue
- return null;
- }
let [credential, businessUnit] = target ? target.split('/') : [null, null];
if (
credential &&
diff --git a/lib/util/config.js b/lib/util/config.js
new file mode 100644
index 000000000..2a70fd079
--- /dev/null
+++ b/lib/util/config.js
@@ -0,0 +1,287 @@
+const TYPE = require('../../types/mcdev.d');
+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} [skipChecks] omit throwing errors and print messages
+ * @returns {object} central properties object
+ */
+ async getProperties(skipChecks = false) {
+ // already loaded, return existing
+ if (config.properties) {
+ return config.properties;
+ }
+ if (await File.pathExists(Util.configFileName)) {
+ config.properties = await File.readJson(Util.configFileName);
+
+ 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 &&
+ !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 (!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 (!skipChecks) {
+ throw new Error(
+ `${Util.authFileName} not found. Please run 'mcdev init' to provide the missing credential details.`
+ );
+ }
+ }
+ if (!skipChecks) {
+ await this.checkProperties(config.properties);
+ }
+ return config.properties;
+ },
+ /**
+ * check if the config file is correctly formatted and has values
+ *
+ * @param {TYPE.Mcdevrc} properties javascript object in .mcdevrc.json
+ * @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, skipCredentialValidation) {
+ if (!(await File.pathExists(Util.configFileName)) || !properties) {
+ 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) {
+ 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)) {
+ const errorMsg = `Your Accenture SFMC DevTools version ${packageJsonMcdev.version} is lower than your project's config version ${properties.version}`;
+ if (Util.skipInteraction) {
+ throw new Error(errorMsg);
+ }
+ 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}`]);
+ }
+ throw new Error(errorMsg);
+ }
+
+ // check config properties
+ 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
+ Util.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))) {
+ Util.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 = [];
+ 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 (!skipCredentialValidation && 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');
+ }
+ return { missingFields, errorMsgs, solutionSet };
+ },
+};
+
+module.exports = config;
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
diff --git a/lib/util/file.js b/lib/util/file.js
index b6b361e75..52f45d972 100644
--- a/lib/util/file.js
+++ b/lib/util/file.js
@@ -145,7 +145,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);
}
@@ -440,63 +444,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 {TYPE.Mcdevrc} central properties object
- */
- loadConfigFile(silent) {
- let properties;
- if (fs.pathExistsSync(Util.configFileName)) {
- try {
- properties = fs.readJsonSync(Util.configFileName);
- } catch (ex) {
- Util.logger.error(`${ex.code}: ${ex.message}`);
- return;
- }
- if (fs.pathExistsSync(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
- } (${typeof properties.credentials[cred].eid} ${
- properties.credentials[cred].eid
- }) and ${Util.authFileName} (${typeof auth[cred].account_id} ${
- auth[cred].account_id
- }) 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 7981e9294..b24b90665 100644
--- a/lib/util/init.config.js
+++ b/lib/util/init.config.js
@@ -7,6 +7,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,97 +29,96 @@ const Init = {
let updateConfigNeeded = false;
const upgradeMsgs = [`Upgrading existing ${Util.configFileName}:`];
+ const defaultProps = await config.getDefaultProperties();
+ 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'`);
+ }
- const missingFields = await Util.checkProperties(properties, true);
- const defaultProps = Util.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);
- 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;
+ }
+ delete properties.options.documentOnRetrieve;
+ upgradeMsgs.push(
+ `- ✔️ converted 'options.documentOnRetrieve' to '${fieldName}'`
+ );
+ break;
+ case 'options.deployment.commitHistory':
+ if (properties.options.commitHistory) {
upgradeMsgs.push(
- `- ✔️ converted 'options.documentOnRetrieve' to '${fieldName}'`
+ `- ✔️ converted 'options.commitHistory' 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:
+ 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}'`);
- }
- });
- updateConfigNeeded = true;
- }
+ 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');
diff --git a/lib/util/init.js b/lib/util/init.js
index d6e90461e..679f10fd2 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
@@ -22,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);
@@ -52,7 +54,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 +84,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
@@ -258,18 +260,22 @@ const Init = {
/**
* finds credentials that are set up in config but not in auth file
*
- * @param {TYPE.Mcdevrc} properties javascript object in .mcdevrc.json
- * @returns {string[]} list of credential names
+ * @param {object} properties javascript object in .mcdevrc.json
+ * @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 || [];
diff --git a/lib/util/util.js b/lib/util/util.js
index 490bf345a..ec7621ad4 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
@@ -156,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) {
@@ -175,30 +171,10 @@ 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 {TYPE.Mcdevrc} default properties
- */
- getDefaultProperties() {
- const configFileName = path.resolve(__dirname, this.boilerplateDirectory, 'config.json');
- if (!fs.pathExistsSync(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()
*
- * @returns {string[]} type choices
+ * @returns {TYPE.SupportedMetadataTypes[]} type choices
*/
getRetrieveTypeChoices() {
const typeChoices = [];
@@ -226,203 +202,6 @@ const Util = {
return typeChoices;
},
- /**
- * 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
- * @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