mcdev 3.1.3 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +67 -7
- package/.github/ISSUE_TEMPLATE/bug.yml +2 -1
- package/.github/PULL_REQUEST_TEMPLATE.md +5 -3
- package/.github/dependabot.yml +14 -0
- package/.github/workflows/code-analysis.yml +57 -0
- package/.husky/commit-msg +10 -0
- package/.husky/post-checkout +5 -0
- package/.husky/pre-commit +2 -1
- package/.prettierrc +8 -0
- package/.vscode/settings.json +1 -1
- package/LICENSE +2 -2
- package/README.md +134 -45
- package/boilerplate/config.json +5 -11
- package/boilerplate/files/.prettierrc +8 -0
- package/boilerplate/files/.vscode/extensions.json +0 -1
- package/boilerplate/files/.vscode/settings.json +30 -2
- package/boilerplate/files/README.md +2 -2
- package/boilerplate/forcedUpdates.json +10 -0
- package/boilerplate/npm-dependencies.json +5 -5
- package/docs/dist/documentation.md +2807 -1730
- package/jsconfig.json +1 -1
- package/lib/Builder.js +171 -74
- package/lib/Deployer.js +244 -96
- package/lib/MetadataTypeDefinitions.js +2 -0
- package/lib/MetadataTypeInfo.js +2 -0
- package/lib/Retriever.js +61 -84
- package/lib/cli.js +116 -11
- package/lib/index.js +241 -561
- package/lib/metadataTypes/AccountUser.js +117 -103
- package/lib/metadataTypes/Asset.js +705 -255
- package/lib/metadataTypes/AttributeGroup.js +23 -12
- package/lib/metadataTypes/Automation.js +489 -392
- package/lib/metadataTypes/Campaign.js +33 -93
- package/lib/metadataTypes/ContentArea.js +31 -11
- package/lib/metadataTypes/DataExtension.js +387 -372
- package/lib/metadataTypes/DataExtensionField.js +131 -54
- package/lib/metadataTypes/DataExtensionTemplate.js +22 -4
- package/lib/metadataTypes/DataExtract.js +61 -48
- package/lib/metadataTypes/DataExtractType.js +14 -8
- package/lib/metadataTypes/Discovery.js +21 -16
- package/lib/metadataTypes/Email.js +32 -12
- package/lib/metadataTypes/EmailSendDefinition.js +85 -80
- package/lib/metadataTypes/EventDefinition.js +61 -43
- package/lib/metadataTypes/FileTransfer.js +72 -52
- package/lib/metadataTypes/Filter.js +11 -4
- package/lib/metadataTypes/Folder.js +149 -117
- package/lib/metadataTypes/FtpLocation.js +14 -8
- package/lib/metadataTypes/ImportFile.js +61 -64
- package/lib/metadataTypes/Interaction.js +19 -4
- package/lib/metadataTypes/List.js +54 -13
- package/lib/metadataTypes/MetadataType.js +664 -454
- package/lib/metadataTypes/MobileCode.js +46 -0
- package/lib/metadataTypes/MobileKeyword.js +114 -0
- package/lib/metadataTypes/Query.js +206 -105
- package/lib/metadataTypes/Role.js +76 -61
- package/lib/metadataTypes/Script.js +147 -83
- package/lib/metadataTypes/SetDefinition.js +20 -8
- package/lib/metadataTypes/TriggeredSendDefinition.js +78 -58
- package/lib/metadataTypes/definitions/Asset.definition.js +21 -10
- package/lib/metadataTypes/definitions/AttributeGroup.definition.js +12 -0
- package/lib/metadataTypes/definitions/Automation.definition.js +10 -5
- package/lib/metadataTypes/definitions/Campaign.definition.js +44 -1
- package/lib/metadataTypes/definitions/DataExtension.definition.js +4 -0
- package/lib/metadataTypes/definitions/DataExtensionTemplate.definition.js +6 -0
- package/lib/metadataTypes/definitions/DataExtract.definition.js +18 -14
- package/lib/metadataTypes/definitions/Discovery.definition.js +12 -0
- package/lib/metadataTypes/definitions/EmailSendDefinition.definition.js +4 -0
- package/lib/metadataTypes/definitions/EventDefinition.definition.js +22 -0
- package/lib/metadataTypes/definitions/FileTransfer.definition.js +4 -0
- package/lib/metadataTypes/definitions/Filter.definition.js +4 -0
- package/lib/metadataTypes/definitions/Folder.definition.js +6 -0
- package/lib/metadataTypes/definitions/FtpLocation.definition.js +4 -0
- package/lib/metadataTypes/definitions/ImportFile.definition.js +10 -5
- package/lib/metadataTypes/definitions/Interaction.definition.js +4 -0
- package/lib/metadataTypes/definitions/MobileCode.definition.js +163 -0
- package/lib/metadataTypes/definitions/MobileKeyword.definition.js +253 -0
- package/lib/metadataTypes/definitions/Query.definition.js +4 -0
- package/lib/metadataTypes/definitions/Role.definition.js +5 -0
- package/lib/metadataTypes/definitions/Script.definition.js +4 -0
- package/lib/metadataTypes/definitions/SetDefinition.definition.js +28 -0
- package/lib/metadataTypes/definitions/TriggeredSendDefinition.definition.js +4 -0
- package/lib/retrieveChangelog.js +7 -6
- package/lib/util/auth.js +117 -0
- package/lib/util/businessUnit.js +55 -66
- package/lib/util/cache.js +194 -0
- package/lib/util/cli.js +90 -116
- package/lib/util/config.js +302 -0
- package/lib/util/devops.js +250 -50
- package/lib/util/file.js +141 -201
- package/lib/util/init.config.js +208 -75
- package/lib/util/init.git.js +45 -50
- package/lib/util/init.js +72 -59
- package/lib/util/init.npm.js +48 -39
- package/lib/util/util.js +280 -564
- package/package.json +45 -34
- package/test/dataExtension.test.js +152 -0
- package/test/mockRoot/.mcdev-auth.json +8 -0
- package/test/mockRoot/.mcdevrc.json +67 -0
- package/test/mockRoot/deploy/testInstance/testBU/dataExtension/childBU_dataextension_test.dataExtension-meta.json +39 -0
- package/test/mockRoot/deploy/testInstance/testBU/dataExtension/testDataExtension.dataExtension-meta.json +23 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.json +11 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.sql +4 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.json +11 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.sql +4 -0
- package/test/query.test.js +149 -0
- package/test/resourceFactory.js +142 -0
- package/test/resources/1111111/dataFolder/retrieve-response.xml +43 -0
- package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json +18 -0
- package/test/resources/9999999/automation/v1/queries/get-response.json +24 -0
- package/test/resources/9999999/automation/v1/queries/post-response.json +18 -0
- package/test/resources/9999999/dataExtension/build-expected.json +51 -0
- package/test/resources/9999999/dataExtension/create-expected.json +23 -0
- package/test/resources/9999999/dataExtension/create-response.xml +54 -0
- package/test/resources/9999999/dataExtension/retrieve-expected.json +51 -0
- package/test/resources/9999999/dataExtension/retrieve-response.xml +47 -0
- package/test/resources/9999999/dataExtension/template-expected.json +51 -0
- package/test/resources/9999999/dataExtension/update-expected.json +55 -0
- package/test/resources/9999999/dataExtension/update-response.xml +52 -0
- package/test/resources/9999999/dataExtensionField/retrieve-response.xml +93 -0
- package/test/resources/9999999/dataExtensionTemplate/retrieve-response.xml +303 -0
- package/test/resources/9999999/dataFolder/retrieve-response.xml +65 -0
- package/test/resources/9999999/query/build-expected.json +8 -0
- package/test/resources/9999999/query/get-expected.json +11 -0
- package/test/resources/9999999/query/patch-expected.json +11 -0
- package/test/resources/9999999/query/post-expected.json +11 -0
- package/test/resources/9999999/query/template-expected.json +8 -0
- package/test/resources/auth.json +32 -0
- package/test/resources/rest404-response.json +5 -0
- package/test/resources/retrieve-response.xml +21 -0
- package/test/utils.js +107 -0
- package/types/mcdev.d.js +301 -0
- package/CHANGELOG.md +0 -126
- package/PULL_REQUEST_TEMPLATE.md +0 -19
- package/test/util/file.js +0 -51
package/lib/retrieveChangelog.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
|
-
|
|
3
|
+
/* eslint-disable unicorn/prefer-top-level-await */
|
|
4
4
|
/**
|
|
5
5
|
* sample file on how to retrieve a simple changelog to use in GUIs or automated processing of any kind
|
|
6
|
+
*
|
|
6
7
|
* @example
|
|
7
8
|
[{
|
|
8
9
|
name: 'deName',
|
|
@@ -45,10 +46,10 @@ const customDefinition = {
|
|
|
45
46
|
const userList = (await mcdev.retrieve('ACN-Learning/_ParentBU_', 'accountUser', true))
|
|
46
47
|
.accountUser;
|
|
47
48
|
// reduce userList to simple id-name map
|
|
48
|
-
Object.keys(userList)
|
|
49
|
+
for (const key of Object.keys(userList)) {
|
|
49
50
|
userList[userList[key].ID] = userList[key].Name;
|
|
50
51
|
delete userList[key];
|
|
51
|
-
}
|
|
52
|
+
}
|
|
52
53
|
|
|
53
54
|
// get changed metadata
|
|
54
55
|
const changelogList = await mcdev.retrieve('ACN-Learning/MCDEV_Training_Source', null, true);
|
|
@@ -81,13 +82,13 @@ const customDefinition = {
|
|
|
81
82
|
}
|
|
82
83
|
});
|
|
83
84
|
const finalResult = allMetadata.filter((item) => undefined !== item);
|
|
84
|
-
console.log('finalResult', finalResult);
|
|
85
|
+
console.log('finalResult', finalResult); // eslint-disable-line no-console
|
|
85
86
|
})();
|
|
86
87
|
|
|
87
88
|
/**
|
|
88
89
|
*
|
|
89
|
-
* @param {
|
|
90
|
-
* @param {
|
|
90
|
+
* @param {Object.<string, string>} userList user-id > user-name map
|
|
91
|
+
* @param {Object.<string, string>} item single metadata item
|
|
91
92
|
* @param {string} fieldname name of field containing the info
|
|
92
93
|
* @returns {string} username or user id or 'n/a'
|
|
93
94
|
*/
|
package/lib/util/auth.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
const TYPE = require('../../types/mcdev.d');
|
|
2
|
+
const Util = require('./util');
|
|
3
|
+
const File = require('./file');
|
|
4
|
+
const SDK = require('sfmc-sdk');
|
|
5
|
+
const Conf = require('conf');
|
|
6
|
+
const credentialStore = new Conf({ configName: 'sessions', clearInvalidConfig: true });
|
|
7
|
+
// const currentMID = null;
|
|
8
|
+
const initializedSDKs = {};
|
|
9
|
+
let authfile;
|
|
10
|
+
|
|
11
|
+
const Auth = {
|
|
12
|
+
/**
|
|
13
|
+
* For each business unit, set up base credentials to be used.
|
|
14
|
+
*
|
|
15
|
+
* @param {TYPE.AuthObject} authObject details for
|
|
16
|
+
* @param {string} credential of the instance
|
|
17
|
+
* @returns {void}
|
|
18
|
+
*/
|
|
19
|
+
async saveCredential(authObject, credential) {
|
|
20
|
+
const sdk = setupSDK(credential, authObject);
|
|
21
|
+
try {
|
|
22
|
+
// check credentials to allow clear log output and stop execution
|
|
23
|
+
const test = await sdk.auth.getAccessToken();
|
|
24
|
+
if (test.error) {
|
|
25
|
+
throw new Error(test.error_description);
|
|
26
|
+
} else if (test.scope) {
|
|
27
|
+
// find missing rights
|
|
28
|
+
const missingAccess = sdk.auth
|
|
29
|
+
.getSupportedScopes()
|
|
30
|
+
.filter((element) => !test.scope.includes(element));
|
|
31
|
+
|
|
32
|
+
if (missingAccess.length) {
|
|
33
|
+
Util.logger.warn(
|
|
34
|
+
'Installed package has insufficient access. You might encounter malfunctions!'
|
|
35
|
+
);
|
|
36
|
+
Util.logger.warn('Missing scope: ' + missingAccess.join(', '));
|
|
37
|
+
}
|
|
38
|
+
const existingAuth = (await File.pathExists(Util.authFileName))
|
|
39
|
+
? await File.readJson(Util.authFileName)
|
|
40
|
+
: {};
|
|
41
|
+
existingAuth[credential] = authObject;
|
|
42
|
+
await File.writeJSONToFile('./', Util.authFileName.split('.json')[0], existingAuth);
|
|
43
|
+
authfile = existingAuth;
|
|
44
|
+
}
|
|
45
|
+
} catch (ex) {
|
|
46
|
+
throw new Error(ex.message);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
/**
|
|
50
|
+
* Returns an SDK instance to be used for API calls
|
|
51
|
+
*
|
|
52
|
+
* @param {TYPE.BuObject} buObject information about current context
|
|
53
|
+
* @returns {SDK} auth object
|
|
54
|
+
*/
|
|
55
|
+
getSDK(buObject) {
|
|
56
|
+
const credentialKey = `${buObject.credential}/${buObject.businessUnit}`;
|
|
57
|
+
if (initializedSDKs[credentialKey]) {
|
|
58
|
+
// return initialied SDK if available
|
|
59
|
+
return initializedSDKs[credentialKey];
|
|
60
|
+
} else {
|
|
61
|
+
// check existing credentials cached
|
|
62
|
+
authfile = authfile || File.readJsonSync(Util.authFileName);
|
|
63
|
+
const newAuthObj = authfile[buObject.credential];
|
|
64
|
+
// use client_id + MID to ensure a unique combination across instances
|
|
65
|
+
const sessionKey = newAuthObj.client_id + '|' + buObject.mid;
|
|
66
|
+
const existingAuthObj = credentialStore.get(sessionKey);
|
|
67
|
+
if (!existingAuthObj) {
|
|
68
|
+
newAuthObj.account_id = buObject.mid;
|
|
69
|
+
}
|
|
70
|
+
initializedSDKs[credentialKey] = setupSDK(credentialKey, existingAuthObj || newAuthObj);
|
|
71
|
+
return initializedSDKs[credentialKey];
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
/**
|
|
75
|
+
* helper to clear all auth sessions
|
|
76
|
+
*
|
|
77
|
+
* @returns {void}
|
|
78
|
+
*/
|
|
79
|
+
clearSessions() {
|
|
80
|
+
credentialStore.clear();
|
|
81
|
+
Util.logger.info(`Auth sessions cleared`);
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Returns an SDK instance to be used for API calls
|
|
86
|
+
*
|
|
87
|
+
* @param {string} sessionKey key for specific BU
|
|
88
|
+
* @param {TYPE.AuthObject} authObject credentials for specific BU
|
|
89
|
+
* @returns {SDK} auth object
|
|
90
|
+
*/
|
|
91
|
+
function setupSDK(sessionKey, authObject) {
|
|
92
|
+
return new SDK(authObject, {
|
|
93
|
+
eventHandlers: {
|
|
94
|
+
onLoop: (type, req) => {
|
|
95
|
+
Util.logger.info(
|
|
96
|
+
`- Requesting next batch (currently ${req.Results.length} records)`
|
|
97
|
+
);
|
|
98
|
+
},
|
|
99
|
+
onRefresh: (authObject) => {
|
|
100
|
+
authObject.scope = authObject.scope.split(' '); // Scope is usually not an array, but we enforce conversion here for simplicity
|
|
101
|
+
credentialStore.set(sessionKey, authObject);
|
|
102
|
+
},
|
|
103
|
+
onConnectionError: (ex, remainingAttempts) => {
|
|
104
|
+
Util.logger.warn(
|
|
105
|
+
`- Connection problem (Code: ${ex.code}). Retrying ${remainingAttempts} time${
|
|
106
|
+
remainingAttempts > 1 ? 's' : ''
|
|
107
|
+
}`
|
|
108
|
+
);
|
|
109
|
+
Util.logger.errorStack(ex);
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
requestAttempts: 4,
|
|
113
|
+
retryOnConnectionError: true,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = Auth;
|
package/lib/util/businessUnit.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const TYPE = require('../../types/mcdev.d');
|
|
3
4
|
const Util = require('./util');
|
|
4
5
|
const File = require('./file');
|
|
5
|
-
const
|
|
6
|
+
const auth = require('./auth');
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Helper that handles retrieval of BU info
|
|
@@ -10,57 +11,42 @@ const Retriever = require('../Retriever');
|
|
|
10
11
|
const BusinessUnit = {
|
|
11
12
|
/**
|
|
12
13
|
* Refreshes BU names and ID's from MC instance
|
|
13
|
-
*
|
|
14
|
+
*
|
|
15
|
+
* @param {TYPE.Mcdevrc} properties current properties that have to be refreshed
|
|
14
16
|
* @param {string} credentialsName identifying name of the installed package / project
|
|
15
|
-
* @returns {Promise
|
|
17
|
+
* @returns {Promise.<boolean>} success of refresh
|
|
16
18
|
*/
|
|
17
19
|
refreshBUProperties: async function (properties, credentialsName) {
|
|
18
20
|
const currentCredentials = properties.credentials[credentialsName];
|
|
19
|
-
const buObject = {
|
|
20
|
-
clientId: currentCredentials.clientId,
|
|
21
|
-
clientSecret: currentCredentials.clientSecret,
|
|
22
|
-
tenant: currentCredentials.tenant,
|
|
23
|
-
};
|
|
24
|
-
let client;
|
|
25
|
-
Util.logger.info(`Testing credentials`);
|
|
26
|
-
try {
|
|
27
|
-
client = await Util.getETClient(buObject);
|
|
28
|
-
} catch (ex) {
|
|
29
|
-
Util.logger.error(ex.message);
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
const retriever = new Retriever(properties, buObject, client);
|
|
33
21
|
Util.logger.info(`Loading BUs`);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
22
|
+
try {
|
|
23
|
+
const client = auth.getSDK({
|
|
24
|
+
mid: currentCredentials.eid,
|
|
25
|
+
eid: currentCredentials.eid,
|
|
26
|
+
credential: credentialsName,
|
|
27
|
+
businessUnit: '_ParentBU_',
|
|
28
|
+
});
|
|
29
|
+
const buResult = await client.soap.retrieve(
|
|
37
30
|
'BusinessUnit',
|
|
38
31
|
['Name', 'ID', 'ParentName', 'ParentID', 'IsActive'],
|
|
39
|
-
{
|
|
40
|
-
(error, response) => {
|
|
41
|
-
if (error) {
|
|
42
|
-
Util.logger.error(`Credentials failure. ${error}`);
|
|
43
|
-
resolve(null);
|
|
44
|
-
// throw new Error(error);
|
|
45
|
-
} else {
|
|
46
|
-
resolve(response.body.Results);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
32
|
+
{ QueryAllAccounts: true }
|
|
49
33
|
);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
34
|
+
if (buResult !== null && !buResult.Results) {
|
|
35
|
+
Util.logger.error(`Credentials worked but no BUs found. Check access rights!`);
|
|
36
|
+
} else if (buResult !== null) {
|
|
37
|
+
Util.logger.info(`Found ${buResult.Results.length} BUs:`);
|
|
38
|
+
// create shortcut and reset BU list at the same time. we don't want old entries clutter the new list
|
|
39
|
+
const myBuList = (currentCredentials.businessUnits = {});
|
|
40
|
+
// sort array by name for better display (wont affect object in settings)
|
|
41
|
+
for (const element of buResult.Results.map((element) => {
|
|
42
|
+
element.ID = Number.parseInt(element.ID);
|
|
43
|
+
element.ParentID = Number.parseInt(element.ParentID);
|
|
44
|
+
return element;
|
|
45
|
+
}).sort((a, b) => {
|
|
46
|
+
if (a.ParentID === 0) {
|
|
61
47
|
return -1;
|
|
62
48
|
}
|
|
63
|
-
if (b.ParentID ===
|
|
49
|
+
if (b.ParentID === 0) {
|
|
64
50
|
return 1;
|
|
65
51
|
}
|
|
66
52
|
if (a.Name.toLowerCase() < b.Name.toLowerCase()) {
|
|
@@ -70,9 +56,8 @@ const BusinessUnit = {
|
|
|
70
56
|
return 1;
|
|
71
57
|
}
|
|
72
58
|
return 0;
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
if (element.ParentID === '0') {
|
|
59
|
+
})) {
|
|
60
|
+
if (element.ParentID === 0) {
|
|
76
61
|
myBuList[Util.parentBuName] = element.ID;
|
|
77
62
|
currentCredentials.eid = element.ID;
|
|
78
63
|
Util.logger.info(` - ${Util.parentBuName} (${element.Name})`);
|
|
@@ -87,29 +72,33 @@ const BusinessUnit = {
|
|
|
87
72
|
}`
|
|
88
73
|
);
|
|
89
74
|
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
75
|
+
}
|
|
76
|
+
Util.logger.debug(`EID: ${currentCredentials.eid}`);
|
|
77
|
+
if (currentCredentials.eid === null) {
|
|
78
|
+
Util.logger.warn(
|
|
79
|
+
`It seems your 'installed package' was not created on the Parent BU of your instance.`
|
|
80
|
+
);
|
|
81
|
+
Util.logger.warn(
|
|
82
|
+
`While basic functionality will work, it is strongly recommended that you create a new 'installed package' there to enable support for shared Data Extensions and automatic retrieval of the BU list.`
|
|
83
|
+
);
|
|
84
|
+
Util.logger.warn(
|
|
85
|
+
`If you cannot create a package on the Parent BU, please open your ./.mcdevrc.json and update the list of BUs and their MIDs manually.`
|
|
86
|
+
);
|
|
87
|
+
// allow user to work by setting this to an obviously false value which nonetheless doesn't block execution
|
|
88
|
+
currentCredentials.eid = -1;
|
|
89
|
+
}
|
|
90
|
+
// store BU list for repo
|
|
91
|
+
await File.writeJSONToFile(
|
|
92
|
+
properties.directories.businessUnits,
|
|
93
|
+
File.filterIllegalFilenames(credentialsName + '.businessUnits'),
|
|
94
|
+
buResult.Results
|
|
101
95
|
);
|
|
102
|
-
//
|
|
103
|
-
|
|
96
|
+
// update config
|
|
97
|
+
await File.saveConfigFile(properties);
|
|
104
98
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
File.filterIllegalFilenames(credentialsName + '.businessUnits'),
|
|
109
|
-
buResult
|
|
110
|
-
);
|
|
111
|
-
// update config
|
|
112
|
-
await File.saveConfigFile(properties);
|
|
99
|
+
} catch (ex) {
|
|
100
|
+
Util.logger.error(`Credentials failure. ${ex}`);
|
|
101
|
+
return null;
|
|
113
102
|
}
|
|
114
103
|
return true;
|
|
115
104
|
},
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
const TYPE = require('../../types/mcdev.d');
|
|
2
|
+
const Util = require('./util');
|
|
3
|
+
|
|
4
|
+
/** @type {TYPE.Cache} */
|
|
5
|
+
const dataStore = {};
|
|
6
|
+
let currentMID = null;
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
/**
|
|
10
|
+
* Method to setup buObject
|
|
11
|
+
* NOTE: in future this may need to restore, rather than wipe the cache
|
|
12
|
+
*
|
|
13
|
+
* @param {TYPE.BuObject} buObject for current Business unit
|
|
14
|
+
* @returns {void}
|
|
15
|
+
*/
|
|
16
|
+
initCache: (buObject) => {
|
|
17
|
+
if (buObject?.mid) {
|
|
18
|
+
currentMID = buObject.mid;
|
|
19
|
+
dataStore[currentMID] = {};
|
|
20
|
+
// If the EID is not setup, also do this for required types (ie. Folders)
|
|
21
|
+
if (!dataStore[buObject.eid]) {
|
|
22
|
+
dataStore[buObject.eid] = {};
|
|
23
|
+
}
|
|
24
|
+
} else {
|
|
25
|
+
throw new Error('Business Unit (buObject) used when initialzing cache was missing MID');
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
/**
|
|
29
|
+
* return entire cache for current MID
|
|
30
|
+
*
|
|
31
|
+
* @returns {TYPE.MultiMetadataTypeMap} cache for one Business Unit
|
|
32
|
+
*/
|
|
33
|
+
getCache: () => dataStore[currentMID],
|
|
34
|
+
|
|
35
|
+
/* eslint-disable unicorn/no-array-for-each */
|
|
36
|
+
/**
|
|
37
|
+
* clean cache for one BU if mid provided, otherwise whole cache
|
|
38
|
+
*
|
|
39
|
+
* @param {number} [mid] of business unit
|
|
40
|
+
* @returns {void}
|
|
41
|
+
*/
|
|
42
|
+
clearCache: (mid) =>
|
|
43
|
+
mid
|
|
44
|
+
? Object.keys(dataStore[mid]).forEach((key) => delete dataStore[mid][key])
|
|
45
|
+
: Object.keys(dataStore).forEach((key) => delete dataStore[key]),
|
|
46
|
+
/* eslint-enable unicorn/no-array-for-each */
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* return a specific item from cache
|
|
50
|
+
*
|
|
51
|
+
* @param {TYPE.SupportedMetadataTypes} type of Metadata to retrieve from cache
|
|
52
|
+
* @param {string} key of the specific metadata
|
|
53
|
+
* @returns {TYPE.MetadataTypeItem} cached metadata item
|
|
54
|
+
*/
|
|
55
|
+
getByKey: (type, key) => dataStore[currentMID]?.[type]?.[key],
|
|
56
|
+
/**
|
|
57
|
+
* override cache for given metadata type with new data
|
|
58
|
+
*
|
|
59
|
+
* @param {TYPE.SupportedMetadataTypes} type of Metadata to retrieve from cache
|
|
60
|
+
* @param {TYPE.MetadataTypeMap} metadataMap map to be set
|
|
61
|
+
* @returns {void}
|
|
62
|
+
*/
|
|
63
|
+
setMetadata: (type, metadataMap) => {
|
|
64
|
+
dataStore[currentMID][type] = metadataMap;
|
|
65
|
+
},
|
|
66
|
+
/**
|
|
67
|
+
* merges entire metadata type with existing cache
|
|
68
|
+
*
|
|
69
|
+
* @param {TYPE.SupportedMetadataTypes} type of Metadata to retrieve from cache
|
|
70
|
+
* @param {TYPE.MetadataTypeMap} metadataMap map to be merged
|
|
71
|
+
* @param {number} overrideMID which should be used for merging
|
|
72
|
+
* @returns {void}
|
|
73
|
+
*/
|
|
74
|
+
mergeMetadata: (type, metadataMap, overrideMID) => {
|
|
75
|
+
dataStore[overrideMID || currentMID][type] = Object.assign(
|
|
76
|
+
metadataMap,
|
|
77
|
+
dataStore[currentMID][type]
|
|
78
|
+
);
|
|
79
|
+
},
|
|
80
|
+
/**
|
|
81
|
+
* standardized method for getting data from cache.
|
|
82
|
+
*
|
|
83
|
+
* @param {TYPE.SupportedMetadataTypes} metadataType metadata type ie. query
|
|
84
|
+
* @param {string|number|boolean} searchValue unique identifier of metadata being looked for
|
|
85
|
+
* @param {string} searchField field name (key in object) which contains the unique identifer
|
|
86
|
+
* @param {string} returnField field which should be returned
|
|
87
|
+
* @param {number} [overrideMID] ignore currentMID and use alternative (for example parent MID)
|
|
88
|
+
* @returns {string|number|boolean} value of specified field. Error is thrown if not found
|
|
89
|
+
*/
|
|
90
|
+
searchForField(metadataType, searchValue, searchField, returnField, overrideMID) {
|
|
91
|
+
for (const key in dataStore[overrideMID || currentMID]?.[metadataType]) {
|
|
92
|
+
if (
|
|
93
|
+
Util.resolveObjPath(
|
|
94
|
+
searchField,
|
|
95
|
+
dataStore[overrideMID || currentMID][metadataType][key]
|
|
96
|
+
) == searchValue
|
|
97
|
+
) {
|
|
98
|
+
try {
|
|
99
|
+
if (
|
|
100
|
+
Util.resolveObjPath(
|
|
101
|
+
returnField,
|
|
102
|
+
dataStore[overrideMID || currentMID][metadataType][key]
|
|
103
|
+
)
|
|
104
|
+
) {
|
|
105
|
+
return Util.resolveObjPath(
|
|
106
|
+
returnField,
|
|
107
|
+
dataStore[overrideMID || currentMID][metadataType][key]
|
|
108
|
+
);
|
|
109
|
+
} else {
|
|
110
|
+
throw new Error(); // eslint-disable-line unicorn/error-message
|
|
111
|
+
}
|
|
112
|
+
} catch {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`${metadataType} with ${searchField} '${searchValue}' does not have field '${returnField}'`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
throw new Error(
|
|
120
|
+
`Dependent ${metadataType} with ${searchField}='${searchValue}' was not found on your BU`
|
|
121
|
+
);
|
|
122
|
+
},
|
|
123
|
+
/**
|
|
124
|
+
* standardized method for getting data from cache - adapted for special case of lists
|
|
125
|
+
* ! keeping this in util/cache.js rather than in metadataTypes/List.js to avoid potential circular dependencies
|
|
126
|
+
*
|
|
127
|
+
* @param {string} searchValue unique identifier of metadata being looked for
|
|
128
|
+
* @param {string} searchField ObjectID or ID
|
|
129
|
+
* @returns {string} unique folderPath/ListName combo of list
|
|
130
|
+
*/
|
|
131
|
+
getListPathName(searchValue, searchField) {
|
|
132
|
+
const returnField1 = 'r__folder_Path';
|
|
133
|
+
const returnField2 = 'ListName';
|
|
134
|
+
for (const key in dataStore[currentMID]['list']) {
|
|
135
|
+
if (dataStore[currentMID]['list'][key][searchField] === searchValue) {
|
|
136
|
+
try {
|
|
137
|
+
if (
|
|
138
|
+
dataStore[currentMID]['list'][key][returnField1] &&
|
|
139
|
+
dataStore[currentMID]['list'][key][returnField2]
|
|
140
|
+
) {
|
|
141
|
+
return (
|
|
142
|
+
dataStore[currentMID]['list'][key][returnField1] +
|
|
143
|
+
'/' +
|
|
144
|
+
dataStore[currentMID]['list'][key][returnField2]
|
|
145
|
+
);
|
|
146
|
+
} else {
|
|
147
|
+
throw new Error(); // eslint-disable-line unicorn/error-message
|
|
148
|
+
}
|
|
149
|
+
} catch {
|
|
150
|
+
throw new Error(
|
|
151
|
+
`${'list'} with ${searchField}='${searchValue}' does not have the fields ${returnField1} and ${returnField2}`
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
throw new Error(
|
|
157
|
+
`Dependent list with ${searchField}='${searchValue}' was not found on your BU`
|
|
158
|
+
);
|
|
159
|
+
},
|
|
160
|
+
/**
|
|
161
|
+
* standardized method for getting data from cache - adapted for special case of lists
|
|
162
|
+
* ! keeping this in util/cache.js rather than in metadataTypes/List.js to avoid potential circular dependencies
|
|
163
|
+
*
|
|
164
|
+
* @param {string} listPathName folderPath/ListName combo of list
|
|
165
|
+
* @param {string} returnField ObjectID or ID
|
|
166
|
+
* @returns {string} unique ObjectId of list
|
|
167
|
+
*/
|
|
168
|
+
getListObjectId(listPathName, returnField) {
|
|
169
|
+
let folderPath = listPathName.split('/');
|
|
170
|
+
const listName = folderPath.pop();
|
|
171
|
+
folderPath = folderPath.join('/');
|
|
172
|
+
for (const key in dataStore[currentMID]['list']) {
|
|
173
|
+
if (
|
|
174
|
+
dataStore[currentMID]['list'][key].ListName === listName &&
|
|
175
|
+
dataStore[currentMID]['list'][key].r__folder_Path === folderPath
|
|
176
|
+
) {
|
|
177
|
+
try {
|
|
178
|
+
if (dataStore[currentMID]['list'][key][returnField]) {
|
|
179
|
+
return dataStore[currentMID]['list'][key][returnField];
|
|
180
|
+
} else {
|
|
181
|
+
throw new Error(); // eslint-disable-line unicorn/error-message
|
|
182
|
+
}
|
|
183
|
+
} catch {
|
|
184
|
+
throw new Error(
|
|
185
|
+
`${'list'} with ListName='${listName}' and r__folder_Path='${folderPath}' does not have field '${returnField}'`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
throw new Error(
|
|
191
|
+
`Dependent list with ListName='${listName}' and r__folder_Path='${folderPath}' was not found on your BU`
|
|
192
|
+
);
|
|
193
|
+
},
|
|
194
|
+
};
|