mcdev 3.0.3 → 3.1.3
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 +1 -1
- package/.github/ISSUE_TEMPLATE/bug.yml +75 -0
- package/.github/ISSUE_TEMPLATE/task.md +1 -1
- package/.issuetracker +11 -3
- package/.vscode/settings.json +3 -3
- package/CHANGELOG.md +66 -0
- package/README.md +245 -141
- package/boilerplate/config.json +3 -2
- package/docs/dist/documentation.md +799 -338
- package/lib/Deployer.js +4 -1
- package/lib/MetadataTypeDefinitions.js +1 -0
- package/lib/MetadataTypeInfo.js +1 -0
- package/lib/Retriever.js +30 -14
- package/lib/cli.js +298 -0
- package/lib/index.js +773 -1019
- package/lib/metadataTypes/AccountUser.js +389 -0
- package/lib/metadataTypes/Asset.js +8 -7
- package/lib/metadataTypes/Automation.js +121 -56
- package/lib/metadataTypes/DataExtension.js +133 -97
- package/lib/metadataTypes/DataExtensionField.js +134 -4
- package/lib/metadataTypes/DataExtract.js +9 -5
- package/lib/metadataTypes/EventDefinition.js +9 -5
- package/lib/metadataTypes/FileTransfer.js +9 -5
- package/lib/metadataTypes/ImportFile.js +13 -12
- package/lib/metadataTypes/MetadataType.js +41 -33
- package/lib/metadataTypes/Query.js +2 -3
- package/lib/metadataTypes/Role.js +13 -8
- package/lib/metadataTypes/Script.js +2 -2
- package/lib/metadataTypes/definitions/AccountUser.definition.js +227 -0
- package/lib/metadataTypes/definitions/Asset.definition.js +1 -0
- package/lib/metadataTypes/definitions/DataExtension.definition.js +1 -1
- package/lib/metadataTypes/definitions/ImportFile.definition.js +2 -1
- package/lib/metadataTypes/definitions/Script.definition.js +5 -5
- package/lib/retrieveChangelog.js +96 -0
- package/lib/util/cli.js +4 -6
- package/lib/util/init.git.js +2 -1
- package/lib/util/util.js +17 -0
- package/package.json +18 -22
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -30
- package/img/README.md/troubleshoot-nodejs-postinstall.jpg +0 -0
- package/postinstall.js +0 -41
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const Util = require('../util/util');
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* @typedef {Object} DataExtensionFieldItem
|
|
5
7
|
* @property {string} [ObjectID] id
|
|
6
|
-
* @property {string} [CustomerKey] key
|
|
8
|
+
* @property {string} [CustomerKey] key in format [DEkey].[FieldName]
|
|
7
9
|
* @property {Object} [DataExtension] -
|
|
8
10
|
* @property {string} DataExtension.CustomerKey key of DE
|
|
9
|
-
* @property {string} Name name
|
|
10
|
-
* @property {string}
|
|
11
|
+
* @property {string} Name name of field
|
|
12
|
+
* @property {string} [Name_new] custom attribute that is only used when trying to rename a field from Name to Name_new
|
|
13
|
+
* @property {string} DefaultValue empty string for not set
|
|
11
14
|
* @property {'true'|'false'} IsRequired -
|
|
12
15
|
* @property {'true'|'false'} IsPrimaryKey -
|
|
13
16
|
* @property {string} Ordinal 1, 2, 3, ...
|
|
14
|
-
* @property {'Text'|'Date'|'
|
|
17
|
+
* @property {'Text'|'Number'|'Date'|'Boolean'|'Decimal'|'EmailAddress'|'Phone'|'Locale'} FieldType can only be set on create
|
|
18
|
+
* @property {string} Scale the number of places after the decimal that the field can hold; example: "0","1", ...
|
|
15
19
|
*
|
|
16
20
|
* @typedef {Object.<string, DataExtensionFieldItem>} DataExtensionFieldMap
|
|
17
21
|
*/
|
|
@@ -91,9 +95,135 @@ class DataExtensionField extends MetadataType {
|
|
|
91
95
|
delete metadata.CustomerKey;
|
|
92
96
|
delete metadata.DataExtension;
|
|
93
97
|
delete metadata.Ordinal;
|
|
98
|
+
if (metadata.FieldType !== 'Decimal') {
|
|
99
|
+
// remove scale - it's only used for "Decimal" to define the digits behind the decimal
|
|
100
|
+
delete metadata.Scale;
|
|
101
|
+
}
|
|
94
102
|
}
|
|
95
103
|
return metadata;
|
|
96
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Mofifies passed deployColumns for update by mapping ObjectID to their target column's values.
|
|
107
|
+
* Removes FieldType field if its the same in deploy and target column, because it results in an error even if its of the same type
|
|
108
|
+
*
|
|
109
|
+
* @param {DataExtensionFieldItem[]} deployColumns Columns of data extension that will be deployed
|
|
110
|
+
* @param {string} deKey external/customer key of Data Extension
|
|
111
|
+
* @returns {Object<string,DataExtensionFieldItem>} existing fields by their original name to allow re-adding FieldType after update
|
|
112
|
+
*/
|
|
113
|
+
static async prepareDeployColumnsOnUpdate(deployColumns, deKey) {
|
|
114
|
+
// retrieve existing fields to enable updating them
|
|
115
|
+
const response = await this.retrieveForCache(
|
|
116
|
+
{
|
|
117
|
+
filter: {
|
|
118
|
+
leftOperand: 'DataExtension.CustomerKey',
|
|
119
|
+
operator: 'equals',
|
|
120
|
+
rightOperand: deKey,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
['Name', 'ObjectID']
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const fieldsObj = response.metadata;
|
|
127
|
+
|
|
128
|
+
// ensure fields can be updated properly by their adding ObjectId based on Name-matching
|
|
129
|
+
/** @type {Object<string,DataExtensionFieldItem>} */
|
|
130
|
+
const existingFieldByName = {};
|
|
131
|
+
|
|
132
|
+
Object.keys(fieldsObj).forEach((key) => {
|
|
133
|
+
existingFieldByName[fieldsObj[key].Name] = fieldsObj[key];
|
|
134
|
+
});
|
|
135
|
+
for (let i = deployColumns.length - 1; i >= 0; i--) {
|
|
136
|
+
const item = deployColumns[i];
|
|
137
|
+
const itemOld = existingFieldByName[item.Name];
|
|
138
|
+
if (itemOld) {
|
|
139
|
+
// field is getting updated ---
|
|
140
|
+
|
|
141
|
+
// Updating to a new FieldType will result in an error; warn & afterwards remove it
|
|
142
|
+
if (itemOld.FieldType !== item.FieldType) {
|
|
143
|
+
Util.logger.warn(
|
|
144
|
+
`- The Field Type of an existing field cannot be changed. Keeping the original value for [${deKey}].[${item.Name}]: '${itemOld.FieldType}'`
|
|
145
|
+
);
|
|
146
|
+
item.FieldType = itemOld.FieldType;
|
|
147
|
+
}
|
|
148
|
+
if (item.FieldType !== 'Decimal') {
|
|
149
|
+
// remove scale - it's only used for "Decimal" to define the digits behind the decimal
|
|
150
|
+
delete item.Scale;
|
|
151
|
+
}
|
|
152
|
+
delete item.FieldType;
|
|
153
|
+
|
|
154
|
+
if (itemOld.MaxLength > item.MaxLength) {
|
|
155
|
+
Util.logger.warn(
|
|
156
|
+
`- The length of an existing field cannot be decreased. Keeping the original value for [${deKey}].[${item.Name}]: '${itemOld.MaxLength}'`
|
|
157
|
+
);
|
|
158
|
+
item.MaxLength = itemOld.MaxLength;
|
|
159
|
+
}
|
|
160
|
+
if (Util.isFalse(itemOld.IsRequired) && Util.isTrue(item.IsRequired)) {
|
|
161
|
+
Util.logger.warn(
|
|
162
|
+
`- A field cannot be changed to be required on update after it was created to allow nulls: [${deKey}].[${item.Name}]`
|
|
163
|
+
);
|
|
164
|
+
item.IsRequired = itemOld.IsRequired;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// enable renaming
|
|
168
|
+
if (item.Name_new) {
|
|
169
|
+
item.Name = item.Name_new;
|
|
170
|
+
delete item.Name_new;
|
|
171
|
+
Util.logger.warn(
|
|
172
|
+
`Found 'Name_new' value '${item.Name_new}' for ${deKey}.${item.Name} - trying to rename.`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// check if any changes were found
|
|
177
|
+
let changeFound = false;
|
|
178
|
+
Object.keys(item).forEach((key) => {
|
|
179
|
+
if (item[key] !== itemOld[key]) {
|
|
180
|
+
changeFound = true;
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
if (!changeFound) {
|
|
184
|
+
deployColumns.splice(i, 1);
|
|
185
|
+
Util.logger.verbose(`no change - removed field [${deKey}].[${item.Name}]`);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// set the ObjectId for clear identification during update
|
|
190
|
+
item.ObjectID = itemOld.ObjectID;
|
|
191
|
+
} else {
|
|
192
|
+
// field is getting added ---
|
|
193
|
+
if (Util.isTrue(item.IsRequired) && item.DefaultValue === '') {
|
|
194
|
+
Util.logger.warn(
|
|
195
|
+
`- Adding new fields to an existing table requires that these fields are either not-required (nullable) or have a default value set. Changing [${deKey}].[${item.Name}] to be not-required`
|
|
196
|
+
);
|
|
197
|
+
item.IsRequired = 'false';
|
|
198
|
+
}
|
|
199
|
+
if (item.Name_new) {
|
|
200
|
+
Util.logger.warn(
|
|
201
|
+
`Found 'Name_new' value '${item.Name_new}' for ${deKey}.${item.Name} but could not find a corresponding DE field on the server - adding new field instead of updating.`
|
|
202
|
+
);
|
|
203
|
+
delete item.Name_new;
|
|
204
|
+
}
|
|
205
|
+
// Field doesn't exist in target, therefore Remove ObjectID if present
|
|
206
|
+
delete item.ObjectID;
|
|
207
|
+
}
|
|
208
|
+
if (!Util.isTrue(item.IsRequired) && !Util.isFalse(item.IsRequired)) {
|
|
209
|
+
Util.logger.error(
|
|
210
|
+
`- Invalid value for 'IsRequired' of [${deKey}].[${item.Name}]. Found '${item.IsRequired}' instead of 'true'/'false'. Removing field from deploy!`
|
|
211
|
+
);
|
|
212
|
+
deployColumns.splice(i, 1);
|
|
213
|
+
}
|
|
214
|
+
if (!Util.isTrue(item.IsPrimaryKey) && !Util.isFalse(item.IsPrimaryKey)) {
|
|
215
|
+
Util.logger.error(
|
|
216
|
+
`- Invalid value for 'IsPrimaryKey' of [${deKey}].[${item.Name}]. Found '${item.IsPrimaryKey}' instead of 'true'/'false'. Removing field from deploy!`
|
|
217
|
+
);
|
|
218
|
+
deployColumns.splice(i, 1);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
Util.logger.debug(
|
|
222
|
+
`${deployColumns.length} Fields added/updated for [${deKey}]: ` +
|
|
223
|
+
deployColumns.map((item) => item.Name).join(', ')
|
|
224
|
+
);
|
|
225
|
+
return deployColumns.length ? existingFieldByName : null;
|
|
226
|
+
}
|
|
97
227
|
}
|
|
98
228
|
|
|
99
229
|
// Assign definition to static attributes
|
|
@@ -30,10 +30,10 @@ class DataExtract extends MetadataType {
|
|
|
30
30
|
* Retrieve a specific dataExtract Definition by Name
|
|
31
31
|
* @param {String} templateDir Directory where retrieved metadata directory will be saved
|
|
32
32
|
* @param {String} name name of the metadata file
|
|
33
|
-
* @param {Object}
|
|
33
|
+
* @param {Object} templateVariables variables to be replaced in the metadata
|
|
34
34
|
* @returns {Promise<Object>} Promise of metadata
|
|
35
35
|
*/
|
|
36
|
-
static async retrieveAsTemplate(templateDir, name,
|
|
36
|
+
static async retrieveAsTemplate(templateDir, name, templateVariables) {
|
|
37
37
|
const options = {
|
|
38
38
|
uri: '/automation/v1/dataextracts/?$filter=name%20eq%20' + name.split(' ').join('%20'),
|
|
39
39
|
};
|
|
@@ -51,16 +51,20 @@ class DataExtract extends MetadataType {
|
|
|
51
51
|
const extended = await this.client.RestClient.get({
|
|
52
52
|
uri: '/automation/v1/dataextracts/' + metadata.id,
|
|
53
53
|
});
|
|
54
|
+
const originalKey = extended.body[this.definition.keyField];
|
|
54
55
|
const val = JSON.parse(
|
|
55
|
-
Util.replaceByObject(
|
|
56
|
+
Util.replaceByObject(
|
|
57
|
+
JSON.stringify(this.parseMetadata(extended.body)),
|
|
58
|
+
templateVariables
|
|
59
|
+
)
|
|
56
60
|
);
|
|
57
61
|
|
|
58
62
|
// remove all fields listed in Definition for templating
|
|
59
63
|
this.keepTemplateFields(val);
|
|
60
64
|
File.writeJSONToFile(
|
|
61
65
|
[templateDir, this.definition.type].join('/'),
|
|
62
|
-
|
|
63
|
-
JSON.parse(Util.replaceByObject(JSON.stringify(val),
|
|
66
|
+
originalKey + '.' + this.definition.type + '-meta',
|
|
67
|
+
JSON.parse(Util.replaceByObject(JSON.stringify(val), templateVariables))
|
|
64
68
|
);
|
|
65
69
|
Util.logger.info(
|
|
66
70
|
`Dataextracts.retrieveAsTemplate:: Written Metadata to filesystem (${name})`
|
|
@@ -32,10 +32,10 @@ class EventDefinition extends MetadataType {
|
|
|
32
32
|
* Retrieve a specific Event Definition by Name
|
|
33
33
|
* @param {String} templateDir Directory where retrieved metadata directory will be saved
|
|
34
34
|
* @param {String} name name of the metadata file
|
|
35
|
-
* @param {Object}
|
|
35
|
+
* @param {Object} templateVariables variables to be replaced in the metadata
|
|
36
36
|
* @returns {Promise<Object>} Promise of metadata
|
|
37
37
|
*/
|
|
38
|
-
static async retrieveAsTemplate(templateDir, name,
|
|
38
|
+
static async retrieveAsTemplate(templateDir, name, templateVariables) {
|
|
39
39
|
// todo template based on name
|
|
40
40
|
const options = {
|
|
41
41
|
uri: '/interaction/v1/EventDefinitions?name=' + encodeURIComponent(name),
|
|
@@ -51,8 +51,12 @@ class EventDefinition extends MetadataType {
|
|
|
51
51
|
`please rename to be unique to avoid issues`
|
|
52
52
|
);
|
|
53
53
|
} else if (event && event.length === 1) {
|
|
54
|
+
const originalKey = event[0][this.definition.keyField];
|
|
54
55
|
const eventDef = JSON.parse(
|
|
55
|
-
Util.replaceByObject(
|
|
56
|
+
Util.replaceByObject(
|
|
57
|
+
JSON.stringify(this.parseMetadata(event[0])),
|
|
58
|
+
templateVariables
|
|
59
|
+
)
|
|
56
60
|
);
|
|
57
61
|
if (!eventDef.dataExtensionId) {
|
|
58
62
|
throw new Error(
|
|
@@ -67,8 +71,8 @@ class EventDefinition extends MetadataType {
|
|
|
67
71
|
this.keepTemplateFields(eventDef);
|
|
68
72
|
File.writeJSONToFile(
|
|
69
73
|
[templateDir, this.definition.type].join('/'),
|
|
70
|
-
|
|
71
|
-
JSON.parse(Util.replaceByObject(JSON.stringify(eventDef),
|
|
74
|
+
originalKey + '.' + this.definition.type + '-meta',
|
|
75
|
+
JSON.parse(Util.replaceByObject(JSON.stringify(eventDef), templateVariables))
|
|
72
76
|
);
|
|
73
77
|
Util.logger.info(
|
|
74
78
|
`EventDefinition.retrieveAsTemplate:: Written Metadata to filesystem (${name})`
|
|
@@ -30,10 +30,10 @@ class FileTransfer extends MetadataType {
|
|
|
30
30
|
* Retrieve a specific File Transfer Definition by Name
|
|
31
31
|
* @param {String} templateDir Directory where retrieved metadata directory will be saved
|
|
32
32
|
* @param {String} name name of the metadata file
|
|
33
|
-
* @param {Object}
|
|
33
|
+
* @param {Object} templateVariables variables to be replaced in the metadata
|
|
34
34
|
* @returns {Promise} Promise
|
|
35
35
|
*/
|
|
36
|
-
static async retrieveAsTemplate(templateDir, name,
|
|
36
|
+
static async retrieveAsTemplate(templateDir, name, templateVariables) {
|
|
37
37
|
const options = {
|
|
38
38
|
uri: '/automation/v1/filetransfers/?$filter=name%20eq%20' + name.split(' ').join('%20'),
|
|
39
39
|
};
|
|
@@ -51,16 +51,20 @@ class FileTransfer extends MetadataType {
|
|
|
51
51
|
const extended = await this.client.RestClient.get({
|
|
52
52
|
uri: '/automation/v1/filetransfers/' + metadata.id,
|
|
53
53
|
});
|
|
54
|
+
const originalKey = extended.body[this.definition.keyField];
|
|
54
55
|
const val = JSON.parse(
|
|
55
|
-
Util.replaceByObject(
|
|
56
|
+
Util.replaceByObject(
|
|
57
|
+
JSON.stringify(this.parseMetadata(extended.body)),
|
|
58
|
+
templateVariables
|
|
59
|
+
)
|
|
56
60
|
);
|
|
57
61
|
|
|
58
62
|
// remove all fields listed in Definition for templating
|
|
59
63
|
this.keepTemplateFields(val);
|
|
60
64
|
File.writeJSONToFile(
|
|
61
65
|
[templateDir, this.definition.type].join('/'),
|
|
62
|
-
|
|
63
|
-
JSON.parse(Util.replaceByObject(JSON.stringify(val),
|
|
66
|
+
originalKey + '.' + this.definition.type + '-meta',
|
|
67
|
+
JSON.parse(Util.replaceByObject(JSON.stringify(val), templateVariables))
|
|
64
68
|
);
|
|
65
69
|
Util.logger.info(
|
|
66
70
|
`FileTransfer.retrieveAsTemplate:: Written Metadata to filesystem (${name})`
|
|
@@ -32,10 +32,10 @@ class ImportFile extends MetadataType {
|
|
|
32
32
|
* Retrieve a specific Import Definition by Name
|
|
33
33
|
* @param {String} templateDir Directory where retrieved metadata directory will be saved
|
|
34
34
|
* @param {String} name name of the metadata file
|
|
35
|
-
* @param {Object}
|
|
35
|
+
* @param {Object} templateVariables variables to be replaced in the metadata
|
|
36
36
|
* @returns {Promise} Promise
|
|
37
37
|
*/
|
|
38
|
-
static async retrieveAsTemplate(templateDir, name,
|
|
38
|
+
static async retrieveAsTemplate(templateDir, name, templateVariables) {
|
|
39
39
|
const options = {
|
|
40
40
|
uri: '/automation/v1/imports/?$filter=name%20eq%20' + name.split(' ').join('%20'),
|
|
41
41
|
};
|
|
@@ -48,17 +48,20 @@ class ImportFile extends MetadataType {
|
|
|
48
48
|
Util.logger.error(`No ${this.definition.typeName} found with name "${name}"`);
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
|
-
|
|
51
|
+
const originalKey = metadata[this.definition.keyField];
|
|
52
52
|
const val = JSON.parse(
|
|
53
|
-
Util.replaceByObject(
|
|
53
|
+
Util.replaceByObject(
|
|
54
|
+
JSON.stringify(this.parseMetadata(metadata)),
|
|
55
|
+
templateVariables
|
|
56
|
+
)
|
|
54
57
|
);
|
|
55
58
|
|
|
56
59
|
// remove all fields listed in Definition for templating
|
|
57
60
|
this.keepTemplateFields(val);
|
|
58
61
|
File.writeJSONToFile(
|
|
59
62
|
[templateDir, this.definition.type].join('/'),
|
|
60
|
-
|
|
61
|
-
JSON.parse(Util.replaceByObject(JSON.stringify(val),
|
|
63
|
+
originalKey + '.' + this.definition.type + '-meta',
|
|
64
|
+
JSON.parse(Util.replaceByObject(JSON.stringify(val), templateVariables))
|
|
62
65
|
);
|
|
63
66
|
Util.logger.info(
|
|
64
67
|
`ImportFile.retrieveAsTemplate:: Written Metadata to filesystem (${name})`
|
|
@@ -147,12 +150,10 @@ class ImportFile extends MetadataType {
|
|
|
147
150
|
}
|
|
148
151
|
}
|
|
149
152
|
// When the destinationObjectTypeId is 584 is refers to Mobile Connect which is not supported as an Import Type
|
|
150
|
-
metadata.destinationObjectTypeId =
|
|
151
|
-
metadata.c__destinationType
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
metadata.c__subscriberImportType
|
|
155
|
-
];
|
|
153
|
+
metadata.destinationObjectTypeId =
|
|
154
|
+
this.definition.destinationObjectTypeMapping[metadata.c__destinationType];
|
|
155
|
+
metadata.subscriberImportTypeId =
|
|
156
|
+
this.definition.subscriberImportTypeMapping[metadata.c__subscriberImportType];
|
|
156
157
|
metadata.updateTypeId = this.definition.updateTypeMapping[metadata.c__dataAction];
|
|
157
158
|
return metadata;
|
|
158
159
|
}
|
|
@@ -204,6 +204,16 @@ class MetadataType {
|
|
|
204
204
|
const metadata = {};
|
|
205
205
|
return { metadata: null, type: this.definition.type };
|
|
206
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* Gets metadata from Marketing Cloud
|
|
209
|
+
* @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true
|
|
210
|
+
* @param {Util.BuObject} buObject properties for auth
|
|
211
|
+
* @param {string} [subType] optionally limit to a single subtype
|
|
212
|
+
* @returns {Promise<{metadata:MetadataTypeMap,type:string}>} metadata
|
|
213
|
+
*/
|
|
214
|
+
static retrieveChangelog(additionalFields, buObject, subType) {
|
|
215
|
+
return this.retrieve(null, additionalFields, buObject, subType);
|
|
216
|
+
}
|
|
207
217
|
|
|
208
218
|
/**
|
|
209
219
|
* Gets metadata cache with limited fields and does not store value to disk
|
|
@@ -304,9 +314,10 @@ class MetadataType {
|
|
|
304
314
|
});
|
|
305
315
|
} else if (this.cache[this.definition.type][normalizedKey]) {
|
|
306
316
|
// normal way of processing update files
|
|
307
|
-
metadata[metadataKey][this.definition.idField] =
|
|
308
|
-
this.definition.type
|
|
309
|
-
|
|
317
|
+
metadata[metadataKey][this.definition.idField] =
|
|
318
|
+
this.cache[this.definition.type][normalizedKey][
|
|
319
|
+
this.definition.idField
|
|
320
|
+
];
|
|
310
321
|
metadataToUpdate.push({
|
|
311
322
|
before: this.cache[this.definition.type][normalizedKey],
|
|
312
323
|
after: metadata[metadataKey],
|
|
@@ -474,8 +485,12 @@ class MetadataType {
|
|
|
474
485
|
async () => (response = await this.client.RestClient.patch(options))
|
|
475
486
|
);
|
|
476
487
|
this.checkForErrors(response);
|
|
488
|
+
// some times, e.g. automation dont return a key in their update response and hence we need to fall back to name
|
|
477
489
|
Util.logger.info(
|
|
478
|
-
`- updated ${this.definition.type}: ${
|
|
490
|
+
`- updated ${this.definition.type}: ${
|
|
491
|
+
metadataEntry[this.definition.keyField] ||
|
|
492
|
+
metadataEntry[this.definition.nameField]
|
|
493
|
+
}`
|
|
479
494
|
);
|
|
480
495
|
return response;
|
|
481
496
|
} catch (ex) {
|
|
@@ -565,7 +580,6 @@ class MetadataType {
|
|
|
565
580
|
Util.logger.info(
|
|
566
581
|
`Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})`
|
|
567
582
|
);
|
|
568
|
-
|
|
569
583
|
if (
|
|
570
584
|
buObject &&
|
|
571
585
|
this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)
|
|
@@ -1058,11 +1072,11 @@ class MetadataType {
|
|
|
1058
1072
|
const savedResults = {};
|
|
1059
1073
|
const subtypeExtension = '.' + (overrideType || this.definition.type) + '-meta';
|
|
1060
1074
|
let filterCounter = 0;
|
|
1061
|
-
for (const
|
|
1075
|
+
for (const originalKey in results) {
|
|
1062
1076
|
try {
|
|
1063
1077
|
if (
|
|
1064
|
-
this.isFiltered(results[
|
|
1065
|
-
this.isFiltered(results[
|
|
1078
|
+
this.isFiltered(results[originalKey], true) ||
|
|
1079
|
+
this.isFiltered(results[originalKey], false)
|
|
1066
1080
|
) {
|
|
1067
1081
|
// if current metadata entry is filtered don't save it
|
|
1068
1082
|
filterCounter++;
|
|
@@ -1071,21 +1085,21 @@ class MetadataType {
|
|
|
1071
1085
|
// define directory into which the current metdata shall be saved
|
|
1072
1086
|
const baseDir = [retrieveDir, ...(overrideType || this.definition.type).split('-')];
|
|
1073
1087
|
|
|
1074
|
-
results[
|
|
1075
|
-
results[
|
|
1088
|
+
results[originalKey] = await this.postRetrieveTasks(
|
|
1089
|
+
results[originalKey],
|
|
1076
1090
|
retrieveDir,
|
|
1077
1091
|
templateVariables ? true : false
|
|
1078
1092
|
);
|
|
1079
|
-
if (!results[
|
|
1093
|
+
if (!results[originalKey] || results[originalKey] === null) {
|
|
1080
1094
|
// we encountered a situation in our postRetrieveTasks that made us want to filter this record
|
|
1081
|
-
delete results[
|
|
1095
|
+
delete results[originalKey];
|
|
1082
1096
|
filterCounter++;
|
|
1083
1097
|
continue;
|
|
1084
1098
|
}
|
|
1085
1099
|
|
|
1086
1100
|
if (
|
|
1087
|
-
this.isFilteredFolder(results[
|
|
1088
|
-
this.isFilteredFolder(results[
|
|
1101
|
+
this.isFilteredFolder(results[originalKey], true) ||
|
|
1102
|
+
this.isFilteredFolder(results[originalKey], false)
|
|
1089
1103
|
) {
|
|
1090
1104
|
// if current metadata entry is filtered don't save it
|
|
1091
1105
|
filterCounter++;
|
|
@@ -1093,20 +1107,20 @@ class MetadataType {
|
|
|
1093
1107
|
}
|
|
1094
1108
|
|
|
1095
1109
|
// for complex types like asset, script, query we need to save the scripts that were extracted from the JSON
|
|
1096
|
-
if (results[
|
|
1110
|
+
if (results[originalKey].json && results[originalKey].codeArr) {
|
|
1097
1111
|
// replace market values with template variable placeholders (do not do it on .codeArr)
|
|
1098
1112
|
if (templateVariables) {
|
|
1099
|
-
results[
|
|
1100
|
-
results[
|
|
1113
|
+
results[originalKey].json = Util.replaceByObject(
|
|
1114
|
+
results[originalKey].json,
|
|
1101
1115
|
templateVariables
|
|
1102
1116
|
);
|
|
1103
|
-
results[
|
|
1104
|
-
results[
|
|
1117
|
+
results[originalKey].subFolder = Util.replaceByObject(
|
|
1118
|
+
results[originalKey].subFolder,
|
|
1105
1119
|
templateVariables
|
|
1106
1120
|
);
|
|
1107
1121
|
}
|
|
1108
1122
|
|
|
1109
|
-
const postRetrieveData = results[
|
|
1123
|
+
const postRetrieveData = results[originalKey];
|
|
1110
1124
|
if (postRetrieveData.subFolder) {
|
|
1111
1125
|
// very complex types have their own subfolder
|
|
1112
1126
|
baseDir.push(...postRetrieveData.subFolder);
|
|
@@ -1127,13 +1141,13 @@ class MetadataType {
|
|
|
1127
1141
|
);
|
|
1128
1142
|
}
|
|
1129
1143
|
// normalize results[metadataEntry]
|
|
1130
|
-
results[
|
|
1144
|
+
results[originalKey] = postRetrieveData.json;
|
|
1131
1145
|
} else {
|
|
1132
1146
|
// not a complex type, run the the entire JSON through templating
|
|
1133
1147
|
// replace market values with template variable placeholders
|
|
1134
1148
|
if (templateVariables) {
|
|
1135
|
-
results[
|
|
1136
|
-
results[
|
|
1149
|
+
results[originalKey] = Util.replaceByObject(
|
|
1150
|
+
results[originalKey],
|
|
1137
1151
|
templateVariables
|
|
1138
1152
|
);
|
|
1139
1153
|
}
|
|
@@ -1142,7 +1156,7 @@ class MetadataType {
|
|
|
1142
1156
|
// we dont store Id on local disk, but we need it for caching logic,
|
|
1143
1157
|
// so its in retrieve but not in save. Here we put into the clone so that the original
|
|
1144
1158
|
// object used for caching doesnt have the Id removed.
|
|
1145
|
-
const saveClone = JSON.parse(JSON.stringify(results[
|
|
1159
|
+
const saveClone = JSON.parse(JSON.stringify(results[originalKey]));
|
|
1146
1160
|
if (!this.definition.keepId) {
|
|
1147
1161
|
delete saveClone[this.definition.idField];
|
|
1148
1162
|
}
|
|
@@ -1152,22 +1166,16 @@ class MetadataType {
|
|
|
1152
1166
|
} else {
|
|
1153
1167
|
this.keepRetrieveFields(saveClone);
|
|
1154
1168
|
}
|
|
1155
|
-
savedResults[
|
|
1169
|
+
savedResults[originalKey] = saveClone;
|
|
1156
1170
|
File.writeJSONToFile(
|
|
1157
1171
|
// manage subtypes
|
|
1158
1172
|
baseDir,
|
|
1159
|
-
|
|
1173
|
+
originalKey + subtypeExtension,
|
|
1160
1174
|
saveClone
|
|
1161
1175
|
);
|
|
1162
1176
|
} catch (ex) {
|
|
1163
1177
|
console.log(ex.stack);
|
|
1164
|
-
Util.metadataLogger(
|
|
1165
|
-
'error',
|
|
1166
|
-
this.definition.type,
|
|
1167
|
-
'saveResults',
|
|
1168
|
-
ex,
|
|
1169
|
-
metadataEntry
|
|
1170
|
-
);
|
|
1178
|
+
Util.metadataLogger('error', this.definition.type, 'saveResults', ex, originalKey);
|
|
1171
1179
|
}
|
|
1172
1180
|
}
|
|
1173
1181
|
if (filterCounter) {
|
|
@@ -144,9 +144,8 @@ class Query extends MetadataType {
|
|
|
144
144
|
} catch (ex) {
|
|
145
145
|
throw new Error(`Query '${metadata.key}': ${ex.message}`);
|
|
146
146
|
}
|
|
147
|
-
metadata.targetUpdateTypeId =
|
|
148
|
-
metadata.targetUpdateTypeName
|
|
149
|
-
];
|
|
147
|
+
metadata.targetUpdateTypeId =
|
|
148
|
+
this.definition.targetUpdateTypeMapping[metadata.targetUpdateTypeName];
|
|
150
149
|
return metadata;
|
|
151
150
|
}
|
|
152
151
|
|
|
@@ -107,14 +107,19 @@ class Role extends MetadataType {
|
|
|
107
107
|
return;
|
|
108
108
|
}
|
|
109
109
|
if (!metadata) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
110
|
+
try {
|
|
111
|
+
metadata = this.readBUMetadataForType(
|
|
112
|
+
File.normalizePath([
|
|
113
|
+
this.properties.directories.retrieve,
|
|
114
|
+
buObject.credential,
|
|
115
|
+
Util.parentBuName,
|
|
116
|
+
]),
|
|
117
|
+
true
|
|
118
|
+
).role;
|
|
119
|
+
} catch (ex) {
|
|
120
|
+
Util.logger.error(ex.message);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
118
123
|
}
|
|
119
124
|
const directory = this.properties.directories.roles;
|
|
120
125
|
|
|
@@ -178,12 +178,12 @@ class Script extends MetadataType {
|
|
|
178
178
|
templateName
|
|
179
179
|
) {
|
|
180
180
|
// get SSJS from filesystem
|
|
181
|
-
let code = this._mergeCode(metadata, templateDir, templateName);
|
|
182
|
-
|
|
181
|
+
let code = await this._mergeCode(metadata, templateDir, templateName);
|
|
183
182
|
// replace template variables with their values
|
|
184
183
|
try {
|
|
185
184
|
code = Mustache.render(code, variables);
|
|
186
185
|
} catch (ex) {
|
|
186
|
+
Util.logger.debug('script.buildDefinitionForExtracts: ' + ex.message);
|
|
187
187
|
throw new Error(
|
|
188
188
|
`${this.definition.type}:: Error applying template variables on ${
|
|
189
189
|
metadata[this.definition.keyField] + '.' + this.definition.type
|