mcdev 3.1.1 → 4.0.0
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 +5 -1
- package/.github/ISSUE_TEMPLATE/task.md +1 -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 +28 -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 +2795 -1724
- package/jsconfig.json +1 -1
- package/lib/Builder.js +166 -75
- 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 +133 -25
- package/lib/index.js +242 -563
- package/lib/metadataTypes/AccountUser.js +101 -95
- package/lib/metadataTypes/Asset.js +677 -248
- package/lib/metadataTypes/AttributeGroup.js +23 -12
- package/lib/metadataTypes/Automation.js +456 -357
- package/lib/metadataTypes/Campaign.js +33 -93
- package/lib/metadataTypes/ContentArea.js +31 -11
- package/lib/metadataTypes/DataExtension.js +391 -376
- package/lib/metadataTypes/DataExtensionField.js +131 -54
- package/lib/metadataTypes/DataExtensionTemplate.js +22 -4
- package/lib/metadataTypes/DataExtract.js +67 -50
- 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 +69 -47
- package/lib/metadataTypes/FileTransfer.js +78 -54
- 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 +69 -69
- package/lib/metadataTypes/Interaction.js +19 -4
- package/lib/metadataTypes/List.js +54 -13
- package/lib/metadataTypes/MetadataType.js +687 -479
- package/lib/metadataTypes/MobileCode.js +46 -0
- package/lib/metadataTypes/MobileKeyword.js +114 -0
- package/lib/metadataTypes/Query.js +204 -103
- package/lib/metadataTypes/Role.js +76 -61
- package/lib/metadataTypes/Script.js +146 -82
- 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 +240 -50
- package/lib/util/file.js +120 -191
- package/lib/util/init.config.js +195 -69
- 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 +44 -33
- 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
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const TYPE = require('../../types/mcdev.d');
|
|
4
|
+
const MetadataType = require('./MetadataType');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* MobileCode MetadataType
|
|
8
|
+
*
|
|
9
|
+
* @augments MetadataType
|
|
10
|
+
*/
|
|
11
|
+
class MobileCode extends MetadataType {
|
|
12
|
+
/**
|
|
13
|
+
* Retrieves Metadata of Mobile Keywords
|
|
14
|
+
* Endpoint /legacy/v1/beta/mobile/code/ return all Mobile Codes with all details.
|
|
15
|
+
*
|
|
16
|
+
* @param {string} retrieveDir Directory where retrieved metadata directory will be saved
|
|
17
|
+
* @param {void} [_] unused parameter
|
|
18
|
+
* @param {void} [__] unused parameter
|
|
19
|
+
* @param {void} [___] unused parameter
|
|
20
|
+
* @param {string} [key] customer key of single item to retrieve
|
|
21
|
+
* @returns {Promise.<TYPE.MetadataTypeMapObj>} Promise of metadata
|
|
22
|
+
*/
|
|
23
|
+
static retrieve(retrieveDir, _, __, ___, key) {
|
|
24
|
+
return super.retrieveREST(
|
|
25
|
+
retrieveDir,
|
|
26
|
+
'/legacy/v1/beta/mobile/code/' + (key ? `?$where=keyword%20eq%20%27${key}%27%20` : ''),
|
|
27
|
+
null,
|
|
28
|
+
null,
|
|
29
|
+
key
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Retrieves event definition metadata for caching
|
|
35
|
+
*
|
|
36
|
+
* @returns {Promise.<TYPE.MetadataTypeMapObj>} Promise of metadata
|
|
37
|
+
*/
|
|
38
|
+
static retrieveForCache() {
|
|
39
|
+
return super.retrieveREST(null, '/legacy/v1/beta/mobile/code/');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Assign definition to static attributes
|
|
44
|
+
MobileCode.definition = require('../MetadataTypeDefinitions').mobileCode;
|
|
45
|
+
|
|
46
|
+
module.exports = MobileCode;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const TYPE = require('../../types/mcdev.d');
|
|
4
|
+
const MetadataType = require('./MetadataType');
|
|
5
|
+
const Util = require('../util/util');
|
|
6
|
+
const File = require('../util/file');
|
|
7
|
+
const cache = require('../util/cache');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* MobileKeyword MetadataType
|
|
11
|
+
*
|
|
12
|
+
* @augments MetadataType
|
|
13
|
+
*/
|
|
14
|
+
class MobileKeyword extends MetadataType {
|
|
15
|
+
/**
|
|
16
|
+
* Retrieves Metadata of Mobile Keywords
|
|
17
|
+
* Endpoint /legacy/v1/beta/mobile/keyword/ return all Mobile Keywords with all details.
|
|
18
|
+
*
|
|
19
|
+
* @param {string} retrieveDir Directory where retrieved metadata directory will be saved
|
|
20
|
+
* @param {void} [_] unused parameter
|
|
21
|
+
* @param {void} [__] unused parameter
|
|
22
|
+
* @param {void} [___] unused parameter
|
|
23
|
+
* @param {string} [key] customer key of single item to retrieve
|
|
24
|
+
* @returns {Promise.<TYPE.MetadataTypeMapObj>} Promise of metadata
|
|
25
|
+
*/
|
|
26
|
+
static retrieve(retrieveDir, _, __, ___, key) {
|
|
27
|
+
return super.retrieveREST(
|
|
28
|
+
retrieveDir,
|
|
29
|
+
'/legacy/v1/beta/mobile/keyword/?view=simple' +
|
|
30
|
+
(key ? `&$where=keyword%20eq%20%27${key}%27%20` : ''),
|
|
31
|
+
null,
|
|
32
|
+
null,
|
|
33
|
+
key
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Retrieves event definition metadata for caching
|
|
39
|
+
*
|
|
40
|
+
* @returns {Promise.<TYPE.MetadataTypeMapObj>} Promise of metadata
|
|
41
|
+
*/
|
|
42
|
+
static retrieveForCache() {
|
|
43
|
+
return super.retrieveREST(null, '/legacy/v1/beta/mobile/keyword/?view=simple');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Retrieve a specific keyword
|
|
48
|
+
*
|
|
49
|
+
* @param {string} templateDir Directory where retrieved metadata directory will be saved
|
|
50
|
+
* @param {string} name name of the metadata file
|
|
51
|
+
* @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
|
|
52
|
+
* @returns {Promise.<TYPE.MetadataTypeItemObj>} Promise of metadata
|
|
53
|
+
*/
|
|
54
|
+
static async retrieveAsTemplate(templateDir, name, templateVariables) {
|
|
55
|
+
// TODO: Decide if we want to keep default handling (and move other types) or keep unique parsing?
|
|
56
|
+
try {
|
|
57
|
+
const res = await this.client.rest.get(
|
|
58
|
+
`/legacy/v1/beta/mobile/keyword/?view=simple&$where=keyword%20eq%20%27${name}%27%20`
|
|
59
|
+
);
|
|
60
|
+
const metadata = JSON.parse(
|
|
61
|
+
Util.replaceByObject(
|
|
62
|
+
JSON.stringify(Util.templateSearchResult(res.entry, 'keyword', name)),
|
|
63
|
+
templateVariables
|
|
64
|
+
)
|
|
65
|
+
);
|
|
66
|
+
if (!metadata.code.id) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
`MobileKeyword.parseMetadata:: ` +
|
|
69
|
+
`No Mobile Code was found for ` +
|
|
70
|
+
`event: ${metadata.name}. ` +
|
|
71
|
+
`This cannot be templated`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
// remove all fields listed in Definition for templating
|
|
75
|
+
this.keepTemplateFields(metadata);
|
|
76
|
+
await File.writeJSONToFile(
|
|
77
|
+
[templateDir, this.definition.type].join('/'),
|
|
78
|
+
metadata.keyword + '.' + this.definition.type + '-meta',
|
|
79
|
+
metadata
|
|
80
|
+
);
|
|
81
|
+
Util.logger.info(`- templated ${this.definition.type}: ${name}`);
|
|
82
|
+
return { metadata: metadata, type: this.definition.type };
|
|
83
|
+
} catch (ex) {
|
|
84
|
+
Util.logger.error('MobileKeyword.retrieveAsTemplate:: ' + ex);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Creates a single Event Definition
|
|
91
|
+
*
|
|
92
|
+
* @param {TYPE.MetadataTypeItem} MobileKeyword a single Event Definition
|
|
93
|
+
* @returns {Promise} Promise
|
|
94
|
+
*/
|
|
95
|
+
static create(MobileKeyword) {
|
|
96
|
+
return super.createREST(MobileKeyword, '/legacy/v1/beta/mobile/keyword/');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* prepares an event definition for deployment
|
|
101
|
+
*
|
|
102
|
+
* @param {TYPE.MetadataTypeItem} metadata a single MobileKeyword
|
|
103
|
+
* @returns {TYPE.MetadataTypeItem} Promise
|
|
104
|
+
*/
|
|
105
|
+
static preDeployTasks(metadata) {
|
|
106
|
+
metadata.code.id = cache.searchForField('mobileCode', metadata.code.code, 'code', 'id');
|
|
107
|
+
return metadata;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Assign definition to static attributes
|
|
112
|
+
MobileKeyword.definition = require('../MetadataTypeDefinitions').mobileKeyword;
|
|
113
|
+
|
|
114
|
+
module.exports = MobileKeyword;
|
|
@@ -1,97 +1,98 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const TYPE = require('../../types/mcdev.d');
|
|
3
4
|
const MetadataType = require('./MetadataType');
|
|
4
5
|
const Util = require('../util/util');
|
|
5
6
|
const File = require('../util/file');
|
|
7
|
+
const cache = require('../util/cache');
|
|
6
8
|
const Mustache = require('mustache');
|
|
7
9
|
|
|
8
|
-
/**
|
|
9
|
-
* @typedef {Object} QueryItem
|
|
10
|
-
* @property {string} name name
|
|
11
|
-
* @property {string} key key
|
|
12
|
-
* @property {string} description -
|
|
13
|
-
* @property {string} targetKey key of target data extension
|
|
14
|
-
* @property {string} createdDate e.g. "2020-09-14T01:42:03.017"
|
|
15
|
-
* @property {string} modifiedDate e.g. "2020-09-14T01:42:03.017"
|
|
16
|
-
* @property {'Overwrite'|'Update'|'Append'} targetUpdateTypeName defines how the query writes into the target data extension
|
|
17
|
-
* @property {0|1|2} [targetUpdateTypeId] mapped to targetUpdateTypeName via this.definition.targetUpdateTypeMapping
|
|
18
|
-
* @property {string} [targetId] Object ID of DE (removed before save)
|
|
19
|
-
* @property {string} [targetDescription] Description DE (removed before save)
|
|
20
|
-
* @property {boolean} isFrozen looks like this is always set to false
|
|
21
|
-
* @property {string} [queryText] contains SQL query with line breaks converted to '\n'. The content is extracted during retrieval and written into a separate *.sql file
|
|
22
|
-
* @property {string} [categoryId] holds folder ID, replaced with r__folder_Path during retrieve
|
|
23
|
-
* @property {string} r__folder_Path folder path in which this DE is saved
|
|
24
|
-
*
|
|
25
|
-
* @typedef {Object.<string, QueryItem>} QueryMap
|
|
26
|
-
*
|
|
27
|
-
* @typedef {Object} CodeExtractItem
|
|
28
|
-
* @property {QueryItem} json metadata of one item w/o code
|
|
29
|
-
* @property {MetadataType.CodeExtract[]} codeArr list of code snippets in this item
|
|
30
|
-
* @property {string[]} subFolder mostly set to null, otherwise list of subfolders
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
10
|
/**
|
|
34
11
|
* Query MetadataType
|
|
12
|
+
*
|
|
35
13
|
* @augments MetadataType
|
|
36
14
|
*/
|
|
37
15
|
class Query extends MetadataType {
|
|
38
16
|
/**
|
|
39
17
|
* Retrieves Metadata of queries
|
|
18
|
+
*
|
|
40
19
|
* @param {string} retrieveDir Directory where retrieved metadata directory will be saved
|
|
41
|
-
* @
|
|
20
|
+
* @param {void} [_] unused parameter
|
|
21
|
+
* @param {void} [__] unused parameter
|
|
22
|
+
* @param {void} [___] unused parameter
|
|
23
|
+
* @param {string} [key] customer key of single item to retrieve
|
|
24
|
+
* @returns {Promise.<{metadata: TYPE.QueryMap, type: string}>} Promise of metadata
|
|
25
|
+
*/
|
|
26
|
+
static async retrieve(retrieveDir, _, __, ___, key) {
|
|
27
|
+
await File.initPrettier('sql');
|
|
28
|
+
const objectId = key ? await this._getObjectIdForSingleRetrieve(key) : null;
|
|
29
|
+
return super.retrieveREST(
|
|
30
|
+
retrieveDir,
|
|
31
|
+
'/automation/v1/queries/' + (objectId || ''),
|
|
32
|
+
null,
|
|
33
|
+
null,
|
|
34
|
+
key
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* helper to allow us to select single metadata entries via REST
|
|
39
|
+
*
|
|
40
|
+
* @private
|
|
41
|
+
* @param {string} key customer key
|
|
42
|
+
* @returns {Promise.<string>} objectId or enpty string
|
|
42
43
|
*/
|
|
43
|
-
static async
|
|
44
|
-
|
|
44
|
+
static async _getObjectIdForSingleRetrieve(key) {
|
|
45
|
+
const response = await this.client.soap.retrieve('QueryDefinition', ['ObjectID'], {
|
|
46
|
+
filter: {
|
|
47
|
+
leftOperand: 'CustomerKey',
|
|
48
|
+
operator: 'equals',
|
|
49
|
+
rightOperand: key,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
return response?.Results?.length ? response.Results[0].ObjectID : null;
|
|
45
53
|
}
|
|
46
54
|
|
|
47
55
|
/**
|
|
48
56
|
* Retrieves query metadata for caching
|
|
49
|
-
*
|
|
57
|
+
*
|
|
58
|
+
* @returns {Promise.<{metadata: TYPE.QueryMap, type: string}>} Promise of metadata
|
|
50
59
|
*/
|
|
51
60
|
static async retrieveForCache() {
|
|
52
|
-
return super.retrieveREST(null, '/automation/v1/queries/'
|
|
61
|
+
return super.retrieveREST(null, '/automation/v1/queries/');
|
|
53
62
|
}
|
|
54
63
|
|
|
55
64
|
/**
|
|
56
65
|
* Retrieve a specific Query by Name
|
|
66
|
+
*
|
|
57
67
|
* @param {string} templateDir Directory where retrieved metadata directory will be saved
|
|
58
68
|
* @param {string} name name of the metadata file
|
|
59
|
-
* @param {
|
|
60
|
-
* @returns {Promise
|
|
69
|
+
* @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
|
|
70
|
+
* @returns {Promise.<{metadata: Query, type: string}>} Promise of metadata
|
|
61
71
|
*/
|
|
62
72
|
static async retrieveAsTemplate(templateDir, name, templateVariables) {
|
|
63
|
-
|
|
73
|
+
await File.initPrettier('sql');
|
|
74
|
+
return super.retrieveREST(
|
|
64
75
|
templateDir,
|
|
65
|
-
'/automation/v1/queries/?$filter=Name%20eq%20' + name
|
|
76
|
+
'/automation/v1/queries/?$filter=Name%20eq%20' + encodeURIComponent(name),
|
|
66
77
|
null,
|
|
67
78
|
templateVariables
|
|
68
79
|
);
|
|
69
|
-
if (response && response.metadata && !Object.keys(response.metadata).length) {
|
|
70
|
-
Util.logger.error(`${this.definition.type} '${name}' not found on server.`);
|
|
71
|
-
}
|
|
72
|
-
return response;
|
|
73
80
|
}
|
|
74
81
|
|
|
75
82
|
/**
|
|
76
83
|
* manages post retrieve steps
|
|
77
|
-
*
|
|
78
|
-
* @param {
|
|
79
|
-
* @
|
|
80
|
-
* @returns {CodeExtractItem} Array with one metadata object and one query string
|
|
84
|
+
*
|
|
85
|
+
* @param {TYPE.QueryItem} metadata a single query
|
|
86
|
+
* @returns {TYPE.CodeExtractItem} Array with one metadata object and one query string
|
|
81
87
|
*/
|
|
82
|
-
static postRetrieveTasks(metadata
|
|
83
|
-
// if retrieving template, replace the name with customer key if that wasn't already the case
|
|
84
|
-
if (isTemplating) {
|
|
85
|
-
const warningMsg =
|
|
86
|
-
'Ensure that Automations using this query are updated with the new query-key before deployment.';
|
|
87
|
-
this.overrideKeyWithName(metadata, warningMsg);
|
|
88
|
-
}
|
|
88
|
+
static postRetrieveTasks(metadata) {
|
|
89
89
|
return this.parseMetadata(metadata);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
93
|
* Creates a single query
|
|
94
|
-
*
|
|
94
|
+
*
|
|
95
|
+
* @param {TYPE.QueryItem} query a single query
|
|
95
96
|
* @returns {Promise} Promise
|
|
96
97
|
*/
|
|
97
98
|
static create(query) {
|
|
@@ -101,7 +102,8 @@ class Query extends MetadataType {
|
|
|
101
102
|
|
|
102
103
|
/**
|
|
103
104
|
* Updates a single query
|
|
104
|
-
*
|
|
105
|
+
*
|
|
106
|
+
* @param {TYPE.QueryItem} query a single query
|
|
105
107
|
* @returns {Promise} Promise
|
|
106
108
|
*/
|
|
107
109
|
static update(query) {
|
|
@@ -111,82 +113,142 @@ class Query extends MetadataType {
|
|
|
111
113
|
|
|
112
114
|
/**
|
|
113
115
|
* prepares a Query for deployment
|
|
114
|
-
*
|
|
116
|
+
*
|
|
117
|
+
* @param {TYPE.QueryItem} metadata a single query activity
|
|
115
118
|
* @param {string} deployDir directory of deploy files
|
|
116
|
-
* @returns {Promise
|
|
119
|
+
* @returns {Promise.<TYPE.QueryItem>} Promise
|
|
117
120
|
*/
|
|
118
121
|
static async preDeployTasks(metadata, deployDir) {
|
|
119
|
-
metadata.queryText = await File.
|
|
122
|
+
metadata.queryText = await File.readFilteredFilename(
|
|
120
123
|
deployDir + '/' + this.definition.type,
|
|
121
124
|
metadata.key + '.' + this.definition.type + '-meta',
|
|
122
125
|
'sql'
|
|
123
126
|
);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
} catch (ex) {
|
|
133
|
-
throw new Error(`Query '${metadata.key}': ${ex.message}`);
|
|
134
|
-
}
|
|
135
|
-
try {
|
|
136
|
-
metadata.categoryId = Util.getFromCache(
|
|
137
|
-
this.cache,
|
|
138
|
-
'folder',
|
|
139
|
-
metadata.r__folder_Path,
|
|
140
|
-
'Path',
|
|
141
|
-
'ID'
|
|
142
|
-
);
|
|
143
|
-
delete metadata.r__folder_Path;
|
|
144
|
-
} catch (ex) {
|
|
145
|
-
throw new Error(`Query '${metadata.key}': ${ex.message}`);
|
|
146
|
-
}
|
|
127
|
+
metadata.targetKey = cache.searchForField(
|
|
128
|
+
'dataExtension',
|
|
129
|
+
metadata.targetKey,
|
|
130
|
+
'CustomerKey',
|
|
131
|
+
'CustomerKey'
|
|
132
|
+
);
|
|
133
|
+
metadata.categoryId = cache.searchForField('folder', metadata.r__folder_Path, 'Path', 'ID');
|
|
134
|
+
delete metadata.r__folder_Path;
|
|
147
135
|
metadata.targetUpdateTypeId =
|
|
148
136
|
this.definition.targetUpdateTypeMapping[metadata.targetUpdateTypeName];
|
|
149
137
|
return metadata;
|
|
150
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* helper for buildDefinitionForNested
|
|
141
|
+
* searches extracted SQL file for template variables and applies the market values
|
|
142
|
+
*
|
|
143
|
+
* @param {string} code code from extracted code
|
|
144
|
+
* @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
|
|
145
|
+
* @returns {string} code with markets applied
|
|
146
|
+
*/
|
|
147
|
+
static applyTemplateValues(code, templateVariables) {
|
|
148
|
+
// fix bad formatting applied by SQL Formatter Plus
|
|
149
|
+
code = code
|
|
150
|
+
.split(' { { { ')
|
|
151
|
+
.join('{{{')
|
|
152
|
+
.split('{ { { ')
|
|
153
|
+
.join('{{{')
|
|
154
|
+
.split(' } } } ')
|
|
155
|
+
.join('}}}')
|
|
156
|
+
.split(' } } }')
|
|
157
|
+
.join('}}}');
|
|
151
158
|
|
|
159
|
+
// replace template variables with their values
|
|
160
|
+
return Mustache.render(code, templateVariables);
|
|
161
|
+
}
|
|
152
162
|
/**
|
|
153
163
|
* helper for buildDefinition
|
|
154
164
|
* handles extracted code if any are found for complex types
|
|
165
|
+
*
|
|
155
166
|
* @param {string} templateDir Directory where metadata templates are stored
|
|
156
167
|
* @param {string|string[]} targetDir (List of) Directory where built definitions will be saved
|
|
157
|
-
* @param {
|
|
158
|
-
* @param {
|
|
168
|
+
* @param {TYPE.QueryItem} metadata main JSON file that was read from file system
|
|
169
|
+
* @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
|
|
159
170
|
* @param {string} templateName name of the template to be built
|
|
160
|
-
* @returns {Promise}
|
|
171
|
+
* @returns {Promise.<string[][]>} list of extracted files with path-parts provided as an array
|
|
172
|
+
*/
|
|
173
|
+
static buildDefinitionForNested(
|
|
174
|
+
templateDir,
|
|
175
|
+
targetDir,
|
|
176
|
+
metadata,
|
|
177
|
+
templateVariables,
|
|
178
|
+
templateName
|
|
179
|
+
) {
|
|
180
|
+
return this._buildForNested(
|
|
181
|
+
templateDir,
|
|
182
|
+
targetDir,
|
|
183
|
+
metadata,
|
|
184
|
+
templateVariables,
|
|
185
|
+
templateName,
|
|
186
|
+
'definition'
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* helper for buildTemplate
|
|
191
|
+
* handles extracted code if any are found for complex types
|
|
192
|
+
*
|
|
193
|
+
* @example queries are saved as 1 json and 1 sql file. both files need to be run through templating
|
|
194
|
+
* @param {string} templateDir Directory where metadata templates are stored
|
|
195
|
+
* @param {string|string[]} targetDir (List of) Directory where built definitions will be saved
|
|
196
|
+
* @param {TYPE.QueryItem} metadata main JSON file that was read from file system
|
|
197
|
+
* @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
|
|
198
|
+
* @param {string} templateName name of the template to be built
|
|
199
|
+
* @returns {Promise.<string[][]>} list of extracted files with path-parts provided as an array
|
|
161
200
|
*/
|
|
162
|
-
static
|
|
201
|
+
static buildTemplateForNested(
|
|
163
202
|
templateDir,
|
|
164
203
|
targetDir,
|
|
165
204
|
metadata,
|
|
166
|
-
|
|
205
|
+
templateVariables,
|
|
167
206
|
templateName
|
|
207
|
+
) {
|
|
208
|
+
return this._buildForNested(
|
|
209
|
+
templateDir,
|
|
210
|
+
targetDir,
|
|
211
|
+
metadata,
|
|
212
|
+
templateVariables,
|
|
213
|
+
templateName,
|
|
214
|
+
'template'
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* helper for buildTemplateForNested / buildDefinitionForNested
|
|
219
|
+
* handles extracted code if any are found for complex types
|
|
220
|
+
*
|
|
221
|
+
* @private
|
|
222
|
+
* @param {string} templateDir Directory where metadata templates are stored
|
|
223
|
+
* @param {string|string[]} targetDir (List of) Directory where built definitions will be saved
|
|
224
|
+
* @param {TYPE.QueryItem} metadata main JSON file that was read from file system
|
|
225
|
+
* @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
|
|
226
|
+
* @param {string} templateName name of the template to be built
|
|
227
|
+
* @param {'definition'|'template'} mode defines what we use this helper for
|
|
228
|
+
* @returns {Promise.<string[][]>} list of extracted files with path-parts provided as an array
|
|
229
|
+
*/
|
|
230
|
+
static async _buildForNested(
|
|
231
|
+
templateDir,
|
|
232
|
+
targetDir,
|
|
233
|
+
metadata,
|
|
234
|
+
templateVariables,
|
|
235
|
+
templateName,
|
|
236
|
+
mode
|
|
168
237
|
) {
|
|
169
238
|
// get SQL from filesystem
|
|
170
|
-
let code = await File.
|
|
239
|
+
let code = await File.readFilteredFilename(
|
|
171
240
|
[templateDir, this.definition.type],
|
|
172
241
|
templateName + '.' + this.definition.type + '-meta',
|
|
173
242
|
'sql'
|
|
174
243
|
);
|
|
175
|
-
// fix bad formatting applied by SQL Formatter Plus
|
|
176
|
-
code = code
|
|
177
|
-
.split(' { { { ')
|
|
178
|
-
.join('{{{')
|
|
179
|
-
.split('{ { { ')
|
|
180
|
-
.join('{{{')
|
|
181
|
-
.split(' } } } ')
|
|
182
|
-
.join('}}}')
|
|
183
|
-
.split(' } } }')
|
|
184
|
-
.join('}}}');
|
|
185
244
|
|
|
186
|
-
// replace template variables with their values
|
|
187
245
|
try {
|
|
188
|
-
|
|
189
|
-
|
|
246
|
+
if (mode === 'definition') {
|
|
247
|
+
code = this.applyTemplateValues(code, templateVariables);
|
|
248
|
+
} else if (mode === 'template') {
|
|
249
|
+
code = this.applyTemplateNames(code, templateVariables);
|
|
250
|
+
}
|
|
251
|
+
} catch {
|
|
190
252
|
throw new Error(
|
|
191
253
|
`${this.definition.type}:: Error applying template variables on ${
|
|
192
254
|
metadata[this.definition.keyField] + '.' + this.definition.type
|
|
@@ -196,6 +258,7 @@ class Query extends MetadataType {
|
|
|
196
258
|
|
|
197
259
|
// write to file
|
|
198
260
|
const targetDirArr = Array.isArray(targetDir) ? targetDir : [targetDir];
|
|
261
|
+
const nestedFilePaths = [];
|
|
199
262
|
|
|
200
263
|
for (const targetDir of targetDirArr) {
|
|
201
264
|
File.writeToFile(
|
|
@@ -204,19 +267,25 @@ class Query extends MetadataType {
|
|
|
204
267
|
'sql',
|
|
205
268
|
code
|
|
206
269
|
);
|
|
270
|
+
nestedFilePaths.push([
|
|
271
|
+
targetDir,
|
|
272
|
+
this.definition.type,
|
|
273
|
+
metadata[this.definition.keyField] + '.' + this.definition.type + '-meta.sql',
|
|
274
|
+
]);
|
|
207
275
|
}
|
|
276
|
+
return nestedFilePaths;
|
|
208
277
|
}
|
|
209
278
|
|
|
210
279
|
/**
|
|
211
280
|
* parses retrieved Metadata before saving
|
|
212
|
-
*
|
|
213
|
-
* @
|
|
281
|
+
*
|
|
282
|
+
* @param {TYPE.QueryItem} metadata a single query activity definition
|
|
283
|
+
* @returns {TYPE.CodeExtractItem} a single item with code parts extracted
|
|
214
284
|
*/
|
|
215
285
|
static parseMetadata(metadata) {
|
|
216
286
|
// folder
|
|
217
287
|
try {
|
|
218
|
-
metadata.r__folder_Path =
|
|
219
|
-
this.cache,
|
|
288
|
+
metadata.r__folder_Path = cache.searchForField(
|
|
220
289
|
'folder',
|
|
221
290
|
metadata.categoryId,
|
|
222
291
|
'ID',
|
|
@@ -224,7 +293,7 @@ class Query extends MetadataType {
|
|
|
224
293
|
);
|
|
225
294
|
delete metadata.categoryId;
|
|
226
295
|
} catch (ex) {
|
|
227
|
-
Util.logger.warn(`Query '${metadata.key}': ${ex.message}`);
|
|
296
|
+
Util.logger.warn(` - Query '${metadata.key}': ${ex.message}`);
|
|
228
297
|
}
|
|
229
298
|
|
|
230
299
|
// extract SQL
|
|
@@ -240,11 +309,43 @@ class Query extends MetadataType {
|
|
|
240
309
|
|
|
241
310
|
return { json: metadata, codeArr: codeArr, subFolder: null };
|
|
242
311
|
}
|
|
312
|
+
/**
|
|
313
|
+
* should return only the json for all but asset, query and script that are saved as multiple files
|
|
314
|
+
* additionally, the documentation for dataExtension and automation should be returned
|
|
315
|
+
*
|
|
316
|
+
* @param {string[]} keyArr customerkey of the metadata
|
|
317
|
+
* @returns {string[]} list of all files that need to be committed in a flat array ['path/file1.ext', 'path/file2.ext']
|
|
318
|
+
*/
|
|
319
|
+
static getFilesToCommit(keyArr) {
|
|
320
|
+
const path = File.normalizePath([
|
|
321
|
+
this.properties.directories.retrieve,
|
|
322
|
+
this.buObject.credential,
|
|
323
|
+
this.buObject.businessUnit,
|
|
324
|
+
this.definition.type,
|
|
325
|
+
]);
|
|
326
|
+
|
|
327
|
+
const fileList = keyArr.flatMap((key) => [
|
|
328
|
+
File.normalizePath([path, `${key}.${this.definition.type}-meta.json`]),
|
|
329
|
+
File.normalizePath([path, `${key}.${this.definition.type}-meta.sql`]),
|
|
330
|
+
]);
|
|
331
|
+
return fileList;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Standardizes a check for multiple messages but adds query specific filters to error texts
|
|
336
|
+
*
|
|
337
|
+
* @param {object} ex response payload from REST API
|
|
338
|
+
* @returns {string[]} formatted Error Message
|
|
339
|
+
*/
|
|
340
|
+
static checkForErrors(ex) {
|
|
341
|
+
const errors = super.checkForErrors(ex);
|
|
342
|
+
if (errors?.length > 0) {
|
|
343
|
+
return errors.map((msg) => msg.split('Error saving the Query field.').join(''));
|
|
344
|
+
}
|
|
345
|
+
}
|
|
243
346
|
}
|
|
244
347
|
|
|
245
348
|
// Assign definition & cache to static attributes
|
|
246
349
|
Query.definition = require('../MetadataTypeDefinitions').query;
|
|
247
|
-
Query.cache = {};
|
|
248
|
-
Query.client = undefined;
|
|
249
350
|
|
|
250
351
|
module.exports = Query;
|