mcdev 8.3.1 → 9.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/.github/ISSUE_TEMPLATE/bug.yml +2 -0
- package/.github/workflows/close_issues_on_merge.yml +4 -0
- package/.github/workflows/code-test.yml +2 -2
- package/.github/workflows/coverage-base-update.yml +2 -2
- package/.github/workflows/coverage-develop-branch.yml +1 -1
- package/.github/workflows/coverage-main-branch.yml +1 -1
- package/.github/workflows/coverage.yml +2 -2
- package/.prettierrc +1 -1
- package/@types/lib/index.d.ts.map +1 -1
- package/@types/lib/metadataTypes/Asset.d.ts.map +1 -1
- package/@types/lib/metadataTypes/Automation.d.ts.map +1 -1
- package/@types/lib/metadataTypes/DataExtension.d.ts.map +1 -1
- package/@types/lib/metadataTypes/DataFilter.d.ts +1 -0
- package/@types/lib/metadataTypes/DataFilter.d.ts.map +1 -1
- package/@types/lib/metadataTypes/DataFilterHidden.d.ts +1 -0
- package/@types/lib/metadataTypes/DataFilterHidden.d.ts.map +1 -1
- package/@types/lib/metadataTypes/Event.d.ts.map +1 -1
- package/@types/lib/metadataTypes/FileLocation.d.ts +74 -0
- package/@types/lib/metadataTypes/FileLocation.d.ts.map +1 -1
- package/@types/lib/metadataTypes/Folder.d.ts.map +1 -1
- package/@types/lib/metadataTypes/Journey.d.ts +6 -0
- package/@types/lib/metadataTypes/Journey.d.ts.map +1 -1
- package/@types/lib/metadataTypes/List.d.ts +1 -1
- package/@types/lib/metadataTypes/MetadataType.d.ts.map +1 -1
- package/@types/lib/metadataTypes/Script.d.ts +14 -0
- package/@types/lib/metadataTypes/Script.d.ts.map +1 -1
- package/@types/lib/metadataTypes/definitions/DataFilter.definition.d.ts +1 -0
- package/@types/lib/metadataTypes/definitions/DataFilterHidden.definition.d.ts +1 -0
- package/@types/lib/metadataTypes/definitions/FileLocation.definition.d.ts +62 -4
- package/@types/lib/metadataTypes/definitions/Journey.definition.d.ts +6 -0
- package/@types/lib/metadataTypes/definitions/Script.definition.d.ts +10 -0
- package/@types/lib/util/auth.d.ts.map +1 -1
- package/@types/lib/util/cli.d.ts.map +1 -1
- package/@types/lib/util/file.d.ts.map +1 -1
- package/@types/lib/util/init.config.d.ts.map +1 -1
- package/@types/lib/util/init.d.ts +1 -1
- package/@types/lib/util/init.d.ts.map +1 -1
- package/@types/lib/util/init.git.d.ts.map +1 -1
- package/lib/Deployer.js +3 -3
- package/lib/cli.js +6 -0
- package/lib/index.js +15 -14
- package/lib/metadataTypes/Asset.js +7 -8
- package/lib/metadataTypes/Automation.js +8 -10
- package/lib/metadataTypes/DataExtension.js +4 -3
- package/lib/metadataTypes/DataExtensionField.js +3 -3
- package/lib/metadataTypes/DataFilter.js +36 -22
- package/lib/metadataTypes/Event.js +8 -7
- package/lib/metadataTypes/FileLocation.js +204 -4
- package/lib/metadataTypes/Folder.js +8 -7
- package/lib/metadataTypes/Journey.js +74 -47
- package/lib/metadataTypes/List.js +1 -1
- package/lib/metadataTypes/MetadataType.js +17 -21
- package/lib/metadataTypes/Script.js +16 -0
- package/lib/metadataTypes/User.js +2 -2
- package/lib/metadataTypes/definitions/DataFilter.definition.js +1 -0
- package/lib/metadataTypes/definitions/DataFilterHidden.definition.js +1 -0
- package/lib/metadataTypes/definitions/FileLocation.definition.js +52 -7
- package/lib/metadataTypes/definitions/Journey.definition.js +6 -0
- package/lib/metadataTypes/definitions/Script.definition.js +6 -0
- package/lib/util/auth.js +20 -24
- package/lib/util/businessUnit.js +1 -1
- package/lib/util/cli.js +2 -1
- package/lib/util/file.js +2 -1
- package/lib/util/init.config.js +3 -1
- package/lib/util/init.git.js +2 -1
- package/lib/util/init.js +2 -1
- package/lib/util/util.js +3 -3
- package/package.json +21 -21
- package/test/general.test.js +11 -11
- package/test/mockRoot/.mcdevrc.json +1 -1
- package/test/mockRoot/deploy/testInstance/testBU/fileLocation/ExactTarget Enhanced FTP.fileLocation-meta.json +5 -0
- package/test/mockRoot/deploy/testInstance/testBU/fileLocation/testExisting_fileLocation_aws.fileLocation-meta.json +14 -0
- package/test/mockRoot/deploy/testInstance/testBU/fileLocation/testExisting_fileLocation_exsftp.fileLocation-meta.json +12 -0
- package/test/resourceFactory.js +10 -3
- package/test/resources/9999999/automation/v1/ftplocations/get-response.json +26 -1
- package/test/resources/9999999/automation/v1/scripts/39f6a488-20eb-4ba0-b0b9-023725b574e4/get-response.json +10 -0
- package/test/resources/9999999/automation/v1/scripts/39f6a488-20eb-4ba0-b0b9-023725b574e4/patch-response.json +2 -2
- package/test/resources/9999999/data/v1/filetransferlocation/Salesforce%20Objects%20%26%20Reports/get-response.json +4 -0
- package/test/resources/9999999/data/v1/filetransferlocation/testExisting_fileLocation_aws/patch-response.json +18 -0
- package/test/resources/9999999/data/v1/filetransferlocation/testExisting_fileLocation_azure/delete-response.json +4 -0
- package/test/resources/9999999/data/v1/filetransferlocation/testExisting_fileLocation_azure/get-response.json +18 -0
- package/test/resources/9999999/data/v1/filetransferlocation/testExisting_fileLocation_exsftp/patch-response.json +16 -0
- package/test/resources/9999999/data/v1/filetransferlocations/get-response.json +59 -0
- package/test/resources/9999999/fileLocation/build-expected.json +14 -0
- package/test/resources/9999999/fileLocation/get-aws-expected.json +14 -0
- package/test/resources/9999999/fileLocation/get-azure-expected.json +14 -0
- package/test/resources/9999999/fileLocation/get-eftp-expected.json +5 -0
- package/test/resources/9999999/fileLocation/get-exsftp-expected.json +12 -0
- package/test/resources/9999999/fileLocation/get-gcp-expected.json +10 -0
- package/test/resources/9999999/fileLocation/get-sor-expected.json +5 -0
- package/test/resources/9999999/fileLocation/patch-aws-expected.json +14 -0
- package/test/resources/9999999/fileLocation/patch-exsftp-expected.json +12 -0
- package/test/resources/9999999/fileLocation/template-expected.json +14 -0
- package/test/resources/9999999/interaction/v1/interactions/get-response-status=Published.json +40 -0
- package/test/type.automation.test.js +14 -14
- package/test/type.dataFilter.test.js +10 -2
- package/test/type.fileLocation.test.js +279 -0
- package/test/type.fileTransfer.test.js +4 -4
- package/test/type.filter.test.js +9 -2
- package/test/type.importFile.test.js +5 -5
- package/test/type.journey.test.js +26 -0
- package/test/type.query.test.js +2 -2
- package/test/type.script.test.js +1 -1
- package/tsconfig.json +1 -1
- package/tsconfig.npmScripts.json +1 -1
- package/tsconfig.precommit.json +1 -1
|
@@ -50,6 +50,11 @@ class Journey extends MetadataType {
|
|
|
50
50
|
|
|
51
51
|
let singleKey = '';
|
|
52
52
|
let mode = 'all';
|
|
53
|
+
const additionalParams = new URLSearchParams();
|
|
54
|
+
if (Util.OPTIONS.onlyPublished) {
|
|
55
|
+
Util.logger.info(' - Retrieving only published versions of journeys');
|
|
56
|
+
additionalParams.set('status', 'Published');
|
|
57
|
+
}
|
|
53
58
|
if (key) {
|
|
54
59
|
if (key.startsWith('%23')) {
|
|
55
60
|
// correct the format
|
|
@@ -74,7 +79,7 @@ class Journey extends MetadataType {
|
|
|
74
79
|
}
|
|
75
80
|
mode = 'id';
|
|
76
81
|
} else if (key.startsWith('name:')) {
|
|
77
|
-
|
|
82
|
+
additionalParams.set('nameOrDescription', key.slice(5));
|
|
78
83
|
mode = 'name';
|
|
79
84
|
} else {
|
|
80
85
|
// assume actual key was provided
|
|
@@ -85,7 +90,10 @@ class Journey extends MetadataType {
|
|
|
85
90
|
|
|
86
91
|
try {
|
|
87
92
|
const uri = `/interaction/v1/interactions/`;
|
|
88
|
-
if (
|
|
93
|
+
if (
|
|
94
|
+
(!Util.OPTIONS.onlyPublished && singleKey && (mode === 'key' || mode === 'id')) ||
|
|
95
|
+
!retrieveDir
|
|
96
|
+
) {
|
|
89
97
|
// full details for retrieve, only base data for caching; reduces caching time from minutes to seconds
|
|
90
98
|
const extras = retrieveDir && singleKey ? extrasDefault : '';
|
|
91
99
|
|
|
@@ -97,13 +105,12 @@ class Journey extends MetadataType {
|
|
|
97
105
|
key
|
|
98
106
|
);
|
|
99
107
|
} else {
|
|
108
|
+
const url =
|
|
109
|
+
uri + (additionalParams.size > 0 ? '?' + additionalParams.toString() : '');
|
|
100
110
|
// retrieve all
|
|
101
111
|
const results = this.definition.restPagination
|
|
102
|
-
? await this.client.rest.getBulk(
|
|
103
|
-
|
|
104
|
-
this.definition.restPageSize || 500
|
|
105
|
-
)
|
|
106
|
-
: await this.client.rest.get(uri + (mode === 'name' ? singleKey : ''));
|
|
112
|
+
? await this.client.rest.getBulk(url, this.definition.restPageSize || 500)
|
|
113
|
+
: await this.client.rest.get(url);
|
|
107
114
|
|
|
108
115
|
if (results.items?.length) {
|
|
109
116
|
// empty results will come back without "items" defined
|
|
@@ -115,51 +122,71 @@ class Journey extends MetadataType {
|
|
|
115
122
|
}
|
|
116
123
|
// full details for retrieve
|
|
117
124
|
const extras = extrasDefault;
|
|
125
|
+
let details = [];
|
|
118
126
|
let parsed;
|
|
119
127
|
if (retrieveDir) {
|
|
120
128
|
const searchName = mode === 'name' ? key.slice(5) : null;
|
|
121
129
|
const foundKey = [];
|
|
130
|
+
let journey;
|
|
122
131
|
// get extra details for saving this
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
+
if (Util.OPTIONS.onlyPublished && mode === 'key') {
|
|
133
|
+
journey = results.items.find(
|
|
134
|
+
(a) => a[this.definition.keyField] === singleKey.slice(4)
|
|
135
|
+
);
|
|
136
|
+
} else if (Util.OPTIONS.onlyPublished && mode === 'id') {
|
|
137
|
+
journey = results.items.find(
|
|
138
|
+
(a) => a[this.definition.idField] === singleKey
|
|
139
|
+
);
|
|
140
|
+
} else {
|
|
141
|
+
details = results.items
|
|
142
|
+
? await Promise.all(
|
|
143
|
+
results.items.map(async (a) => {
|
|
144
|
+
if (mode === 'name') {
|
|
145
|
+
// when filtering by name, the API in fact does a LIKE search with placeholders left and right of the search term - and also searches the description field.
|
|
146
|
+
if (searchName === a[this.definition.nameField]) {
|
|
147
|
+
foundKey.push(a[this.definition.keyField]);
|
|
148
|
+
} else {
|
|
149
|
+
// skip because the name does not match
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
return await this.client.rest.get(
|
|
155
|
+
`${uri}key:${a[this.definition.keyField]}?extras=${extras}` +
|
|
156
|
+
`&versionNumber=${a.version}`
|
|
157
|
+
);
|
|
158
|
+
} catch (ex) {
|
|
159
|
+
// if we do get here, we should log the error and continue instead of failing to download all automations
|
|
160
|
+
Util.logger.warn(
|
|
161
|
+
` ☇ skipping ${this.definition.type} ${
|
|
162
|
+
a[this.definition.nameField]
|
|
163
|
+
} (${a[this.definition.keyField]}): ${ex.message} (${
|
|
164
|
+
ex.code
|
|
165
|
+
})${
|
|
166
|
+
ex.endpoint
|
|
167
|
+
? Util.getGrayMsg(
|
|
168
|
+
' - ' +
|
|
169
|
+
ex.endpoint.split(
|
|
170
|
+
'rest.marketingcloudapis.com'
|
|
171
|
+
)[1]
|
|
172
|
+
)
|
|
173
|
+
: ''
|
|
174
|
+
}`
|
|
175
|
+
);
|
|
132
176
|
return null;
|
|
133
177
|
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
ex.code
|
|
147
|
-
})${
|
|
148
|
-
ex.endpoint
|
|
149
|
-
? Util.getGrayMsg(
|
|
150
|
-
' - ' +
|
|
151
|
-
ex.endpoint.split(
|
|
152
|
-
'rest.marketingcloudapis.com'
|
|
153
|
-
)[1]
|
|
154
|
-
)
|
|
155
|
-
: ''
|
|
156
|
-
}`
|
|
157
|
-
);
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
})
|
|
161
|
-
)
|
|
162
|
-
: [];
|
|
178
|
+
})
|
|
179
|
+
)
|
|
180
|
+
: [];
|
|
181
|
+
}
|
|
182
|
+
if (Util.OPTIONS.onlyPublished && journey) {
|
|
183
|
+
details.push(
|
|
184
|
+
await this.client.rest.get(
|
|
185
|
+
`${uri}${singleKey}?extras=${extras}` +
|
|
186
|
+
`&versionNumber=${journey.version}`
|
|
187
|
+
)
|
|
188
|
+
);
|
|
189
|
+
}
|
|
163
190
|
parsed = this.parseResponseBody({ items: details.filter(Boolean) });
|
|
164
191
|
// * retrieveDir is mandatory in this method as it is not used for caching (there is a seperate method for that)
|
|
165
192
|
const savedMetadata = await this.saveResults(parsed, retrieveDir, null, null);
|
|
@@ -1296,7 +1323,7 @@ class Journey extends MetadataType {
|
|
|
1296
1323
|
}
|
|
1297
1324
|
|
|
1298
1325
|
// apply sorting by activity key to work around the API shuffling activities around
|
|
1299
|
-
metadata.activities = metadata.activities.
|
|
1326
|
+
metadata.activities = metadata.activities.toSorted((a, b) => a.key.localeCompare(b.key));
|
|
1300
1327
|
}
|
|
1301
1328
|
|
|
1302
1329
|
/**
|
|
@@ -2335,7 +2362,7 @@ class Journey extends MetadataType {
|
|
|
2335
2362
|
}
|
|
2336
2363
|
|
|
2337
2364
|
// good practice to return the published keys in alphabetical order
|
|
2338
|
-
return executedKeyArr.filter(Boolean).
|
|
2365
|
+
return executedKeyArr.filter(Boolean).toSorted();
|
|
2339
2366
|
}
|
|
2340
2367
|
|
|
2341
2368
|
/**
|
|
@@ -92,7 +92,7 @@ class List extends MetadataType {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
|
-
* helper for @link retrieveForCache and @link retrieve
|
|
95
|
+
* helper for {@link retrieveForCache} and {@link retrieve}
|
|
96
96
|
*
|
|
97
97
|
* @private
|
|
98
98
|
* @param {MetadataTypeMapObj} results metadata from retrieve for current BU
|
|
@@ -108,7 +108,7 @@ class MetadataType {
|
|
|
108
108
|
} catch (ex) {
|
|
109
109
|
// this will catch issues with readdirSync
|
|
110
110
|
Util.metadataLogger('debug', this.definition.type, 'getJsonFromFS', ex);
|
|
111
|
-
throw
|
|
111
|
+
throw ex;
|
|
112
112
|
}
|
|
113
113
|
return fileName2FileContent;
|
|
114
114
|
}
|
|
@@ -229,7 +229,8 @@ class MetadataType {
|
|
|
229
229
|
// postRetrieveTasks will be run automatically on this via super.saveResult
|
|
230
230
|
} catch (ex) {
|
|
231
231
|
throw new Error(
|
|
232
|
-
`Could not get details for new ${this.definition.type} ${id} from server (${ex.message})
|
|
232
|
+
`Could not get details for new ${this.definition.type} ${id} from server (${ex.message})`,
|
|
233
|
+
{ cause: ex }
|
|
233
234
|
);
|
|
234
235
|
}
|
|
235
236
|
}
|
|
@@ -451,7 +452,7 @@ class MetadataType {
|
|
|
451
452
|
|
|
452
453
|
return { metadata: metadata, type: this.definition.type };
|
|
453
454
|
} catch (ex) {
|
|
454
|
-
throw new Error(`${this.definition.type}:: ${ex.message}
|
|
455
|
+
throw new Error(`${this.definition.type}:: ${ex.message}`, { cause: ex });
|
|
455
456
|
}
|
|
456
457
|
}
|
|
457
458
|
|
|
@@ -845,8 +846,7 @@ class MetadataType {
|
|
|
845
846
|
|
|
846
847
|
// make this newly created item available in cache for other itmes that might reference it
|
|
847
848
|
/** @type {MetadataTypeMap} */
|
|
848
|
-
const newObject = {};
|
|
849
|
-
newObject[metadataKey] = metadataMap[metadataKey];
|
|
849
|
+
const newObject = { [metadataKey]: metadataMap[metadataKey] };
|
|
850
850
|
cache.mergeMetadata(this.definition.type, newObject);
|
|
851
851
|
}
|
|
852
852
|
} else if (action === 'update' && !Util.OPTIONS.noUpdate) {
|
|
@@ -872,8 +872,10 @@ class MetadataType {
|
|
|
872
872
|
updateResults.push(result);
|
|
873
873
|
|
|
874
874
|
// make this newly created item available in cache for other itmes that might reference it
|
|
875
|
-
const newObject = {
|
|
876
|
-
|
|
875
|
+
const newObject = {
|
|
876
|
+
[metadataKey]: structuredClone(metadataMap[metadataKey]),
|
|
877
|
+
};
|
|
878
|
+
|
|
877
879
|
if (result.objectID) {
|
|
878
880
|
// required for assets
|
|
879
881
|
newObject[metadataKey].objectID = result.objectID;
|
|
@@ -1704,8 +1706,7 @@ class MetadataType {
|
|
|
1704
1706
|
(typeof singleRetrieve === 'string' || typeof singleRetrieve === 'number')
|
|
1705
1707
|
) {
|
|
1706
1708
|
// in case we really just wanted one entry but couldnt do so in the api call, filter it here
|
|
1707
|
-
const single = {};
|
|
1708
|
-
single[singleRetrieve] = metadataStructure[singleRetrieve];
|
|
1709
|
+
const single = { [singleRetrieve]: metadataStructure[singleRetrieve] };
|
|
1709
1710
|
return single;
|
|
1710
1711
|
} else if (singleRetrieve) {
|
|
1711
1712
|
return {};
|
|
@@ -2547,8 +2548,7 @@ class MetadataType {
|
|
|
2547
2548
|
* @returns {Promise.<boolean>} deletion success flag
|
|
2548
2549
|
*/
|
|
2549
2550
|
static async deleteByKeySOAP(key, overrideKeyField, codeNotFound, handleOutside) {
|
|
2550
|
-
const metadata = {};
|
|
2551
|
-
metadata[overrideKeyField || this.definition.keyField] = key;
|
|
2551
|
+
const metadata = { [overrideKeyField || this.definition.keyField]: key };
|
|
2552
2552
|
const soapType = this.definition.soapType || this.definition.type;
|
|
2553
2553
|
try {
|
|
2554
2554
|
await this.client.soap.delete(Util.capitalizeFirstLetter(soapType), metadata, null);
|
|
@@ -2639,16 +2639,12 @@ class MetadataType {
|
|
|
2639
2639
|
static async readBUMetadataForType(readDir, listBadKeys, buMetadata) {
|
|
2640
2640
|
buMetadata ||= {};
|
|
2641
2641
|
readDir = File.normalizePath([readDir, this.definition.type]);
|
|
2642
|
-
|
|
2643
|
-
if
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
}
|
|
2648
|
-
throw new Error(`Directory '${readDir}' does not exist.`);
|
|
2649
|
-
}
|
|
2650
|
-
} catch (ex) {
|
|
2651
|
-
throw new Error(ex.message);
|
|
2642
|
+
if (await File.pathExists(readDir)) {
|
|
2643
|
+
// check if folder name is a valid metadataType, then check if the user limited to a certain type in the command params
|
|
2644
|
+
buMetadata[this.definition.type] = await this.getJsonFromFS(readDir, listBadKeys);
|
|
2645
|
+
return buMetadata;
|
|
2646
|
+
} else {
|
|
2647
|
+
throw new Error(`Directory '${readDir}' does not exist.`);
|
|
2652
2648
|
}
|
|
2653
2649
|
}
|
|
2654
2650
|
|
|
@@ -83,6 +83,22 @@ class Script extends MetadataType {
|
|
|
83
83
|
return super.updateREST(script, '/automation/v1/scripts/' + script.ssjsActivityId);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
/**
|
|
87
|
+
* helper for {@link MetadataType.updateREST} and {@link MetadataType.updateSOAP}
|
|
88
|
+
*
|
|
89
|
+
* @param {MetadataTypeItem} metadataEntry a single metadata Entry
|
|
90
|
+
* @param {object} apiResponse varies depending on the API call
|
|
91
|
+
* @returns {Promise.<object>} apiResponse, potentially modified
|
|
92
|
+
*/
|
|
93
|
+
static async postUpdateTasks(metadataEntry, apiResponse) {
|
|
94
|
+
// script update endpoint returns wrong values for createdDate and modifiedDate - need to re-retrieve to get correct values
|
|
95
|
+
const ssjsActivityId = apiResponse?.ssjsActivityId;
|
|
96
|
+
if (ssjsActivityId) {
|
|
97
|
+
apiResponse = await this.client.rest.get('/automation/v1/scripts/' + ssjsActivityId);
|
|
98
|
+
}
|
|
99
|
+
return apiResponse;
|
|
100
|
+
}
|
|
101
|
+
|
|
86
102
|
/**
|
|
87
103
|
* Creates a single Script
|
|
88
104
|
*
|
|
@@ -1055,7 +1055,7 @@ class User extends MetadataType {
|
|
|
1055
1055
|
associatedBus = [
|
|
1056
1056
|
...new Set(user.c__AssociatedBusinessUnits.map((mid) => this._getBuName(mid))),
|
|
1057
1057
|
]
|
|
1058
|
-
.
|
|
1058
|
+
.toSorted((a, b) => (a < b ? -1 : a > b ? 1 : 0))
|
|
1059
1059
|
.join(',<br> ');
|
|
1060
1060
|
}
|
|
1061
1061
|
const defaultBUName = this._getBuName(user.DefaultBusinessUnit);
|
|
@@ -1215,7 +1215,7 @@ class User extends MetadataType {
|
|
|
1215
1215
|
// individual role (which are not manageable nor visible in the GUI)
|
|
1216
1216
|
(roleName) => !roleName.startsWith('Individual role for ')
|
|
1217
1217
|
)
|
|
1218
|
-
.
|
|
1218
|
+
.toSorted((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
1219
1219
|
} else {
|
|
1220
1220
|
// set to empty array
|
|
1221
1221
|
roles = [];
|
|
@@ -23,6 +23,7 @@ export default {
|
|
|
23
23
|
soapType: 'FilterDefinition',
|
|
24
24
|
typeDescription: 'Defines an audience based on specified rules. Used by Filter Activities.',
|
|
25
25
|
typeRetrieveByDefault: true,
|
|
26
|
+
typeCdpByDefault: true,
|
|
26
27
|
typeName: 'Data Filter',
|
|
27
28
|
fields: {
|
|
28
29
|
// the GUI seems to ONLY send fields during update that are actually changed. It has yet to be tested if that also works with sending other fiedls as well
|
|
@@ -24,6 +24,7 @@ export default {
|
|
|
24
24
|
typeDescription:
|
|
25
25
|
'Defines an audience based on specified rules. Auto-generated by filtered DEs and filtered Lists.',
|
|
26
26
|
typeRetrieveByDefault: false,
|
|
27
|
+
typeCdpByDefault: false,
|
|
27
28
|
typeName: 'Data Filter (auto-generated)',
|
|
28
29
|
fields: {
|
|
29
30
|
// the GUI seems to ONLY send fields during update that are actually changed. It has yet to be tested if that also works with sending other fiedls as well
|
|
@@ -5,13 +5,14 @@ export default {
|
|
|
5
5
|
hasExtended: false,
|
|
6
6
|
idField: 'id',
|
|
7
7
|
keyIsFixed: true,
|
|
8
|
-
keyField: '
|
|
8
|
+
keyField: 'customerKey',
|
|
9
9
|
nameField: 'name',
|
|
10
10
|
createdDateField: null,
|
|
11
11
|
createdNameField: null,
|
|
12
12
|
lastmodDateField: null,
|
|
13
13
|
lastmodNameField: null,
|
|
14
14
|
restPagination: false,
|
|
15
|
+
maxKeyLength: 36, // confirmed max length
|
|
15
16
|
type: 'fileLocation',
|
|
16
17
|
typeDescription:
|
|
17
18
|
'Used for export or import of files to/from Marketing Cloud. Previously this was labeled ftpLocation.',
|
|
@@ -32,37 +33,81 @@ export default {
|
|
|
32
33
|
'Azure Blob Storage': 15,
|
|
33
34
|
'Google Cloud Storage': 16,
|
|
34
35
|
},
|
|
36
|
+
locationTypeMappingDeployable: {
|
|
37
|
+
'External SFTP Site': 'ExternalSftp',
|
|
38
|
+
'Amazon Simple Storage Service': 'AmazonSimpleStorage',
|
|
39
|
+
'Azure Blob Storage': 'AzureBlobStorage',
|
|
40
|
+
'Google Cloud Storage': 'GcpBlobStorage',
|
|
41
|
+
},
|
|
42
|
+
locationTypeIdMappingDeployable: {
|
|
43
|
+
2: 'ExternalSftp',
|
|
44
|
+
13: 'AmazonSimpleStorage',
|
|
45
|
+
15: 'AzureBlobStorage',
|
|
46
|
+
16: 'GcpBlobStorage',
|
|
47
|
+
},
|
|
35
48
|
fields: {
|
|
36
49
|
id: {
|
|
37
50
|
isCreateable: false,
|
|
38
51
|
isUpdateable: false,
|
|
39
|
-
retrieving:
|
|
52
|
+
retrieving: false,
|
|
40
53
|
template: false,
|
|
41
54
|
},
|
|
42
55
|
locationTypeId: {
|
|
56
|
+
// automation endpoint
|
|
43
57
|
isCreateable: false,
|
|
44
58
|
isUpdateable: false,
|
|
45
|
-
retrieving:
|
|
59
|
+
retrieving: false,
|
|
60
|
+
template: false,
|
|
61
|
+
},
|
|
62
|
+
locationType: {
|
|
63
|
+
// data endpoint
|
|
64
|
+
isCreateable: true,
|
|
65
|
+
isUpdateable: true,
|
|
66
|
+
retrieving: false,
|
|
46
67
|
template: false,
|
|
47
68
|
},
|
|
48
69
|
locationUrl: {
|
|
49
70
|
isCreateable: false,
|
|
50
71
|
isUpdateable: false,
|
|
72
|
+
retrieving: false,
|
|
73
|
+
template: false,
|
|
74
|
+
},
|
|
75
|
+
name: {
|
|
76
|
+
isCreateable: true,
|
|
77
|
+
isUpdateable: true,
|
|
51
78
|
retrieving: true,
|
|
52
79
|
template: true,
|
|
53
80
|
},
|
|
54
|
-
|
|
55
|
-
isCreateable:
|
|
56
|
-
isUpdateable:
|
|
81
|
+
customerKey: {
|
|
82
|
+
isCreateable: true,
|
|
83
|
+
isUpdateable: true,
|
|
57
84
|
retrieving: true,
|
|
58
85
|
template: true,
|
|
59
86
|
},
|
|
60
|
-
|
|
87
|
+
description: {
|
|
61
88
|
isCreateable: true,
|
|
62
89
|
isUpdateable: true,
|
|
63
90
|
retrieving: true,
|
|
64
91
|
template: true,
|
|
65
92
|
},
|
|
93
|
+
relPath: {
|
|
94
|
+
isCreateable: false,
|
|
95
|
+
isUpdateable: false,
|
|
96
|
+
retrieving: false,
|
|
97
|
+
template: false,
|
|
98
|
+
},
|
|
99
|
+
awsFileTransferLocation: {
|
|
100
|
+
skipValidation: true,
|
|
101
|
+
},
|
|
102
|
+
azureFileTransferLocation: {
|
|
103
|
+
skipValidation: true,
|
|
104
|
+
},
|
|
105
|
+
gcpFileTransferLocation: {
|
|
106
|
+
skipValidation: true,
|
|
107
|
+
},
|
|
108
|
+
sFtpFileTransferLocation: {
|
|
109
|
+
skipValidation: true,
|
|
110
|
+
},
|
|
66
111
|
c__locationType: {
|
|
67
112
|
isCreateable: false,
|
|
68
113
|
isUpdateable: false,
|
|
@@ -1188,6 +1188,12 @@ export default {
|
|
|
1188
1188
|
retrieving: true,
|
|
1189
1189
|
template: true,
|
|
1190
1190
|
},
|
|
1191
|
+
campaigns: {
|
|
1192
|
+
isCreateable: true,
|
|
1193
|
+
isUpdateable: true,
|
|
1194
|
+
retrieving: true,
|
|
1195
|
+
template: true,
|
|
1196
|
+
},
|
|
1191
1197
|
metaData: {
|
|
1192
1198
|
skipValidation: true,
|
|
1193
1199
|
},
|
|
@@ -100,6 +100,12 @@ export default {
|
|
|
100
100
|
retrieving: false,
|
|
101
101
|
template: false,
|
|
102
102
|
},
|
|
103
|
+
parentCategoryId: {
|
|
104
|
+
isCreateable: false,
|
|
105
|
+
isUpdateable: false,
|
|
106
|
+
retrieving: false,
|
|
107
|
+
template: false,
|
|
108
|
+
},
|
|
103
109
|
r__folder_Path: { skipValidation: true },
|
|
104
110
|
},
|
|
105
111
|
};
|
package/lib/util/auth.js
CHANGED
|
@@ -40,32 +40,28 @@ const Auth = {
|
|
|
40
40
|
*/
|
|
41
41
|
async saveCredential(authObject, credential) {
|
|
42
42
|
const sdk = setupSDK(credential, authObject);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
.filter((element) => !test.scope.includes(element));
|
|
43
|
+
// check credentials to allow clear log output and stop execution
|
|
44
|
+
const test = await sdk.auth.getAccessToken();
|
|
45
|
+
if (test.error) {
|
|
46
|
+
throw new Error(test.error_description);
|
|
47
|
+
} else if (test.scope) {
|
|
48
|
+
// find missing rights
|
|
49
|
+
const missingAccess = sdk.auth
|
|
50
|
+
.getSupportedScopes()
|
|
51
|
+
.filter((element) => !test.scope.includes(element));
|
|
53
52
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
const existingAuth = (await File.pathExists(Util.authFileName))
|
|
61
|
-
? await File.readJSON(Util.authFileName)
|
|
62
|
-
: {};
|
|
63
|
-
existingAuth[credential] = authObject;
|
|
64
|
-
await File.writeJSONToFile('./', Util.authFileName.split('.json')[0], existingAuth);
|
|
65
|
-
authfile = existingAuth;
|
|
53
|
+
if (missingAccess.length) {
|
|
54
|
+
Util.logger.warn(
|
|
55
|
+
'Installed package has insufficient access. You might encounter malfunctions!'
|
|
56
|
+
);
|
|
57
|
+
Util.logger.warn('Missing scope: ' + missingAccess.join(', '));
|
|
66
58
|
}
|
|
67
|
-
|
|
68
|
-
|
|
59
|
+
const existingAuth = (await File.pathExists(Util.authFileName))
|
|
60
|
+
? await File.readJSON(Util.authFileName)
|
|
61
|
+
: {};
|
|
62
|
+
existingAuth[credential] = authObject;
|
|
63
|
+
await File.writeJSONToFile('./', Util.authFileName.split('.json')[0], existingAuth);
|
|
64
|
+
authfile = existingAuth;
|
|
69
65
|
}
|
|
70
66
|
},
|
|
71
67
|
|
package/lib/util/businessUnit.js
CHANGED
package/lib/util/cli.js
CHANGED
|
@@ -304,7 +304,8 @@ const Cli = {
|
|
|
304
304
|
async _setCredential(properties, credName, refreshBUs = true) {
|
|
305
305
|
const skipInteraction = Util.skipInteraction;
|
|
306
306
|
// Get user input
|
|
307
|
-
|
|
307
|
+
/** @type {boolean} */
|
|
308
|
+
let credentialsGood;
|
|
308
309
|
let inputData;
|
|
309
310
|
do {
|
|
310
311
|
if (skipInteraction) {
|
package/lib/util/file.js
CHANGED
|
@@ -271,7 +271,8 @@ const File = {
|
|
|
271
271
|
*/
|
|
272
272
|
_beautify_prettier: async function (directory, filename, filetype, content) {
|
|
273
273
|
const properties = await config.getProperties();
|
|
274
|
-
|
|
274
|
+
/** @type {string} */
|
|
275
|
+
let formatted;
|
|
275
276
|
try {
|
|
276
277
|
if (!FileFs.prettierConfig) {
|
|
277
278
|
// either no prettier config in project directory or initPrettier was not run before this
|
package/lib/util/init.config.js
CHANGED
|
@@ -444,13 +444,15 @@ const Init = {
|
|
|
444
444
|
);
|
|
445
445
|
boilerplateFileContent ||= await File.readFile(boilerplateFileName, 'utf8');
|
|
446
446
|
|
|
447
|
-
|
|
447
|
+
/** @type {string} */
|
|
448
|
+
let todo;
|
|
448
449
|
|
|
449
450
|
if (await File.pathExists(fileName)) {
|
|
450
451
|
if (relevantForced.deletes.includes(path.normalize(fileName))) {
|
|
451
452
|
Util.logger.info(
|
|
452
453
|
`- ✋ ${fileName} found but it is required to delete it. Commencing rename instead for your convenience:`
|
|
453
454
|
);
|
|
455
|
+
// eslint-disable-next-line no-useless-assignment
|
|
454
456
|
todo = 'delete';
|
|
455
457
|
} else {
|
|
456
458
|
const existingFileContent = await File.readFile(fileName, 'utf8');
|
package/lib/util/init.git.js
CHANGED
|
@@ -30,7 +30,8 @@ const Init = {
|
|
|
30
30
|
}
|
|
31
31
|
// 3. test if in git repo
|
|
32
32
|
const gitRepoFoundInCWD = await File.pathExists('.git');
|
|
33
|
-
|
|
33
|
+
/** @type {boolean} */
|
|
34
|
+
let newRepoInitialized;
|
|
34
35
|
if (gitRepoFoundInCWD) {
|
|
35
36
|
Util.logger.info(`✔️ Git repository found`);
|
|
36
37
|
newRepoInitialized = false;
|
package/lib/util/init.js
CHANGED
|
@@ -91,6 +91,7 @@ const Init = {
|
|
|
91
91
|
}
|
|
92
92
|
} while (error && !skipInteraction);
|
|
93
93
|
Util.logger.debug('reloading config');
|
|
94
|
+
// eslint-disable-next-line no-useless-assignment
|
|
94
95
|
properties = await config.getProperties(true);
|
|
95
96
|
} else if (missingCredentials.length) {
|
|
96
97
|
// forced update-credential mode - user likely cloned repo and is missing mcdev-auth.json
|
|
@@ -284,7 +285,7 @@ const Init = {
|
|
|
284
285
|
},
|
|
285
286
|
|
|
286
287
|
/**
|
|
287
|
-
* helper for @initProject that optionally creates markets and market lists for all BUs
|
|
288
|
+
* helper for {@link Init.initProject} that optionally creates markets and market lists for all BUs
|
|
288
289
|
*/
|
|
289
290
|
async _initMarkets() {
|
|
290
291
|
const skipInteraction = Util.skipInteraction;
|
package/lib/util/util.js
CHANGED
|
@@ -1018,7 +1018,7 @@ export const Util = {
|
|
|
1018
1018
|
if (subTypeArr && subTypeArr.length > 0) {
|
|
1019
1019
|
Util.logger.info(
|
|
1020
1020
|
Util.getGrayMsg(
|
|
1021
|
-
`${indent} - Subtype${subTypeArr.length > 1 ? 's' : ''}: ${[...subTypeArr].
|
|
1021
|
+
`${indent} - Subtype${subTypeArr.length > 1 ? 's' : ''}: ${[...subTypeArr].toSorted().join(', ')}`
|
|
1022
1022
|
)
|
|
1023
1023
|
);
|
|
1024
1024
|
}
|
|
@@ -1341,7 +1341,7 @@ export const Util = {
|
|
|
1341
1341
|
values.push(...this.findLeafVals(object[k], key));
|
|
1342
1342
|
}
|
|
1343
1343
|
});
|
|
1344
|
-
return [...new Set(values.
|
|
1344
|
+
return [...new Set(values.toSorted())];
|
|
1345
1345
|
},
|
|
1346
1346
|
/**
|
|
1347
1347
|
* helper that returns a new object with sorted attributes of the given object
|
|
@@ -1351,7 +1351,7 @@ export const Util = {
|
|
|
1351
1351
|
*/
|
|
1352
1352
|
sortObjectAttributes(obj) {
|
|
1353
1353
|
return Object.keys(obj)
|
|
1354
|
-
.
|
|
1354
|
+
.toSorted()
|
|
1355
1355
|
.reduce((acc, key) => {
|
|
1356
1356
|
acc[key] = obj[key];
|
|
1357
1357
|
return acc;
|