mcdev 5.0.0 → 5.0.2
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 -0
- package/.github/ISSUE_TEMPLATE/bug.yml +2 -0
- package/.husky/commit-msg +1 -1
- package/.husky/post-checkout +34 -3
- package/.husky/post-merge +21 -0
- package/.husky/pre-commit +1 -0
- package/README.md +1 -1
- package/docs/dist/documentation.md +60 -15
- package/lib/Deployer.js +0 -1
- package/lib/cli.js +8 -8
- package/lib/index.js +2 -1
- package/lib/metadataTypes/Asset.js +4 -2
- package/lib/metadataTypes/Automation.js +39 -28
- package/lib/metadataTypes/DataExtension.js +1 -3
- package/lib/metadataTypes/DataExtensionField.js +5 -5
- package/lib/metadataTypes/Journey.js +11 -10
- package/lib/metadataTypes/MetadataType.js +34 -7
- package/lib/metadataTypes/MobileKeyword.js +165 -20
- package/lib/metadataTypes/MobileMessage.js +20 -28
- package/lib/metadataTypes/Role.js +2 -3
- package/lib/metadataTypes/TransactionalSMS.js +5 -5
- package/lib/metadataTypes/User.js +16 -22
- package/lib/metadataTypes/definitions/MobileKeyword.definition.js +19 -7
- package/lib/metadataTypes/definitions/MobileMessage.definition.js +50 -8
- package/lib/metadataTypes/definitions/User.definition.js +5 -4
- package/lib/util/auth.js +13 -1
- package/lib/util/cli.js +1 -1
- package/lib/util/file.js +5 -3
- package/lib/util/util.js +1 -0
- package/package.json +9 -9
- package/test/mockRoot/.mcdevrc.json +3 -1
- package/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/{testNew_keyword.mobileKeyword-meta.json → 4912312345678.TESTNEW_KEYWORD.mobileKeyword-meta.json} +1 -4
- package/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/{testNew_keyword_blocked.mobileKeyword-meta.json → 4912312345678.TESTNEW_KEYWORD_BLOCKED.mobileKeyword-meta.json} +1 -4
- package/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.json +1 -1
- package/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.json +1 -1
- package/test/resources/1111111/user/retrieve-expected.md +19 -0
- package/test/resources/9999999/dataExtension/retrieve-expected.md +18 -0
- package/test/resources/9999999/journey/build-expected.json +1 -1
- package/test/resources/9999999/journey/get-expected.json +1 -1
- package/test/resources/9999999/journey/put-expected.json +1 -1
- package/test/resources/9999999/journey/template-expected.json +1 -1
- package/test/resources/9999999/legacy/v1/beta/mobile/keyword/NXV4ZFMwTEFwRVczd3RaLUF5X3p5dzo4Njow/get-response.json +1 -1
- package/test/resources/9999999/legacy/v1/beta/mobile/keyword/get-response.json +1 -1
- package/test/resources/9999999/legacy/v1/beta/mobile/message/NTIzOjc4OjA/get-response.json +1 -1
- package/test/resources/9999999/legacy/v1/beta/mobile/message/get-response.json +1 -1
- package/test/resources/9999999/messaging/v1/sms/definitions/post-response.json +1 -1
- package/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/get-response.json +1 -1
- package/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/patch-response.json +1 -1
- package/test/resources/9999999/mobileKeyword/build-expected.json +1 -4
- package/test/resources/9999999/mobileKeyword/get-expected.json +1 -4
- package/test/resources/9999999/mobileKeyword/post-create-expected.json +1 -4
- package/test/resources/9999999/mobileKeyword/template-expected.json +1 -4
- package/test/resources/9999999/query/build-expected.sql +1 -1
- package/test/resources/9999999/query/get-expected.sql +1 -1
- package/test/resources/9999999/query/patch-expected.sql +1 -1
- package/test/resources/9999999/query/post-expected.sql +1 -1
- package/test/resources/9999999/query/template-expected.sql +1 -1
- package/test/resources/9999999/transactionalSMS/build-expected.json +1 -1
- package/test/resources/9999999/transactionalSMS/get-expected.json +1 -1
- package/test/resources/9999999/transactionalSMS/patch-expected.json +1 -1
- package/test/resources/9999999/transactionalSMS/post-expected.json +1 -1
- package/test/resources/9999999/transactionalSMS/template-expected.json +1 -1
- package/test/type.dataExtension.test.js +13 -1
- package/test/type.mobileKeyword.test.js +57 -19
- package/test/type.user.test.js +30 -1
- package/test/utils.js +10 -0
- /package/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/{testNew_keyword.mobileKeyword-meta.amp → 4912312345678.TESTNEW_KEYWORD.mobileKeyword-meta.amp} +0 -0
- /package/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/{testNew_keyword_blocked.mobileKeyword-meta.amp → 4912312345678.TESTNEW_KEYWORD_BLOCKED.mobileKeyword-meta.amp} +0 -0
|
@@ -24,14 +24,12 @@ class MobileKeyword extends MetadataType {
|
|
|
24
24
|
*/
|
|
25
25
|
static retrieve(retrieveDir, _, __, key) {
|
|
26
26
|
try {
|
|
27
|
+
let queryParams;
|
|
28
|
+
[key, queryParams] = this.#getRetrieveKeyAndUrl(key);
|
|
29
|
+
|
|
27
30
|
return super.retrieveREST(
|
|
28
31
|
retrieveDir,
|
|
29
|
-
'/legacy/v1/beta/mobile/keyword/' +
|
|
30
|
-
(key
|
|
31
|
-
? key.startsWith('id:')
|
|
32
|
-
? key.slice(3)
|
|
33
|
-
: `?view=simple&$where=keyword%20eq%20%27${key}%27%20`
|
|
34
|
-
: '?view=simple'),
|
|
32
|
+
'/legacy/v1/beta/mobile/keyword/' + queryParams,
|
|
35
33
|
null,
|
|
36
34
|
key
|
|
37
35
|
);
|
|
@@ -47,6 +45,109 @@ class MobileKeyword extends MetadataType {
|
|
|
47
45
|
}
|
|
48
46
|
}
|
|
49
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Builds map of metadata entries mapped to their keyfields
|
|
50
|
+
*
|
|
51
|
+
* @param {object} body json of response body
|
|
52
|
+
* @param {string|number} [singleRetrieve] key of single item to filter by
|
|
53
|
+
* @returns {TYPE.MetadataTypeMap} keyField => metadata map
|
|
54
|
+
*/
|
|
55
|
+
static parseResponseBody(body, singleRetrieve) {
|
|
56
|
+
const bodyIteratorField = this.definition.bodyIteratorField;
|
|
57
|
+
const keyField = this.definition.keyField;
|
|
58
|
+
const metadataStructure = {};
|
|
59
|
+
if (body !== null) {
|
|
60
|
+
if (Array.isArray(body)) {
|
|
61
|
+
// in some cases data is just an array
|
|
62
|
+
for (const item of body) {
|
|
63
|
+
this.#createCustomKeyField(item);
|
|
64
|
+
const key = item[keyField];
|
|
65
|
+
metadataStructure[key] = item;
|
|
66
|
+
}
|
|
67
|
+
} else if (body[bodyIteratorField]) {
|
|
68
|
+
for (const item of body[bodyIteratorField]) {
|
|
69
|
+
this.#createCustomKeyField(item);
|
|
70
|
+
const key = item[keyField];
|
|
71
|
+
metadataStructure[key] = item;
|
|
72
|
+
}
|
|
73
|
+
} else if (singleRetrieve) {
|
|
74
|
+
// some types will return a single item intead of an array if the key is supported by their api
|
|
75
|
+
this.#createCustomKeyField(body);
|
|
76
|
+
// ! currently, the id: prefix is only supported by journey (interaction)
|
|
77
|
+
if (singleRetrieve.startsWith('id:')) {
|
|
78
|
+
singleRetrieve = body[keyField];
|
|
79
|
+
}
|
|
80
|
+
metadataStructure[singleRetrieve] = body;
|
|
81
|
+
return metadataStructure;
|
|
82
|
+
}
|
|
83
|
+
if (
|
|
84
|
+
metadataStructure[singleRetrieve] &&
|
|
85
|
+
(typeof singleRetrieve === 'string' || typeof singleRetrieve === 'number')
|
|
86
|
+
) {
|
|
87
|
+
// in case we really just wanted one entry but couldnt do so in the api call, filter it here
|
|
88
|
+
const single = {};
|
|
89
|
+
single[singleRetrieve] = metadataStructure[singleRetrieve];
|
|
90
|
+
return single;
|
|
91
|
+
} else if (singleRetrieve) {
|
|
92
|
+
return {};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return metadataStructure;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* helper for {@link parseResponseBody} that creates a custom key field for this type based on mobileCode and keyword
|
|
100
|
+
*
|
|
101
|
+
* @private
|
|
102
|
+
* @param {TYPE.MetadataType} metadata single item
|
|
103
|
+
*/
|
|
104
|
+
static #createCustomKeyField(metadata) {
|
|
105
|
+
metadata.c__codeKeyword = metadata.code.code + '.' + metadata.keyword;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* helper for {@link preDeployTasks} and {@link createOrUpdate} to ensure we have code & keyword properly set
|
|
110
|
+
*
|
|
111
|
+
* @private
|
|
112
|
+
* @param {TYPE.MetadataType} metadata single item
|
|
113
|
+
*/
|
|
114
|
+
static #setCodeAndKeyword(metadata) {
|
|
115
|
+
const [code, keyword] = metadata.c__codeKeyword.split('.');
|
|
116
|
+
|
|
117
|
+
// mobileCode
|
|
118
|
+
metadata.code = {
|
|
119
|
+
id: cache.searchForField('mobileCode', code, 'code', 'id'),
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// keyword
|
|
123
|
+
metadata.keyword = keyword;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* helper for {@link MetadataType.upsert}
|
|
128
|
+
*
|
|
129
|
+
* @param {TYPE.MetadataTypeMap} metadataMap list of metadata
|
|
130
|
+
* @param {string} metadataKey key of item we are looking at
|
|
131
|
+
* @param {boolean} hasError error flag from previous code
|
|
132
|
+
* @param {TYPE.MetadataTypeItemDiff[]} metadataToUpdate list of items to update
|
|
133
|
+
* @param {TYPE.MetadataTypeItem[]} metadataToCreate list of items to create
|
|
134
|
+
* @returns {'create' | 'update' | 'skip'} action to take
|
|
135
|
+
*/
|
|
136
|
+
static createOrUpdate(metadataMap, metadataKey, hasError, metadataToUpdate, metadataToCreate) {
|
|
137
|
+
const createOrUpdateAction = super.createOrUpdate(
|
|
138
|
+
metadataMap,
|
|
139
|
+
metadataKey,
|
|
140
|
+
hasError,
|
|
141
|
+
metadataToUpdate,
|
|
142
|
+
metadataToCreate
|
|
143
|
+
);
|
|
144
|
+
if (createOrUpdateAction === 'update') {
|
|
145
|
+
// in case --changeKeyField or --changeKeyValue was used, let's ensure we set code & keyword here again
|
|
146
|
+
this.#setCodeAndKeyword(metadataMap[metadataKey]);
|
|
147
|
+
}
|
|
148
|
+
return createOrUpdateAction;
|
|
149
|
+
}
|
|
150
|
+
|
|
50
151
|
/**
|
|
51
152
|
* Retrieves event definition metadata for caching
|
|
52
153
|
*
|
|
@@ -60,26 +161,29 @@ class MobileKeyword extends MetadataType {
|
|
|
60
161
|
}
|
|
61
162
|
|
|
62
163
|
/**
|
|
63
|
-
*
|
|
164
|
+
* retrieve an item and create a template from it
|
|
64
165
|
*
|
|
65
166
|
* @param {string} templateDir Directory where retrieved metadata directory will be saved
|
|
66
|
-
* @param {string}
|
|
167
|
+
* @param {string} key name of the metadata file
|
|
67
168
|
* @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
|
|
68
169
|
* @returns {Promise.<TYPE.MetadataTypeItemObj>} Promise of metadata
|
|
69
170
|
*/
|
|
70
|
-
static async retrieveAsTemplate(templateDir,
|
|
171
|
+
static async retrieveAsTemplate(templateDir, key, templateVariables) {
|
|
71
172
|
try {
|
|
173
|
+
let queryParams;
|
|
174
|
+
[key, queryParams] = this.#getRetrieveKeyAndUrl(key);
|
|
175
|
+
|
|
72
176
|
return super.retrieveTemplateREST(
|
|
73
177
|
templateDir,
|
|
74
|
-
`/legacy/v1/beta/mobile/keyword
|
|
178
|
+
`/legacy/v1/beta/mobile/keyword/` + queryParams,
|
|
75
179
|
templateVariables,
|
|
76
|
-
|
|
180
|
+
key
|
|
77
181
|
);
|
|
78
182
|
} catch (ex) {
|
|
79
183
|
// if the mobileMessage does not exist, the API returns the error "Request failed with status code 400 (ERR_BAD_REQUEST)" which would otherwise bring execution to a hold
|
|
80
|
-
if (
|
|
184
|
+
if (key && ex.code === 'ERR_BAD_REQUEST') {
|
|
81
185
|
Util.logger.info(
|
|
82
|
-
`Downloaded: ${this.definition.type} (0)${Util.getKeysString(
|
|
186
|
+
`Downloaded: ${this.definition.type} (0)${Util.getKeysString(key)}`
|
|
83
187
|
);
|
|
84
188
|
} else {
|
|
85
189
|
throw ex;
|
|
@@ -88,13 +192,43 @@ class MobileKeyword extends MetadataType {
|
|
|
88
192
|
}
|
|
89
193
|
|
|
90
194
|
/**
|
|
91
|
-
*
|
|
195
|
+
* helper for {@link retrieve} and {@link retrieveAsTemplate}
|
|
196
|
+
*
|
|
197
|
+
* @private
|
|
198
|
+
* @param {string} key customer key of single item to retrieve / name of the metadata file
|
|
199
|
+
* @returns {Array} key, queryParams
|
|
200
|
+
*/
|
|
201
|
+
static #getRetrieveKeyAndUrl(key) {
|
|
202
|
+
let queryParams;
|
|
203
|
+
if (key) {
|
|
204
|
+
if (key.startsWith('id:')) {
|
|
205
|
+
// overwrite queryParams
|
|
206
|
+
queryParams = key.slice(3);
|
|
207
|
+
} else if (key.includes('.')) {
|
|
208
|
+
// keywords are always uppercased
|
|
209
|
+
key = key.toUpperCase();
|
|
210
|
+
// format: code.keyword
|
|
211
|
+
const [code, keyword] = key.split('.');
|
|
212
|
+
queryParams = `?view=simple&$where=keyword%20eq%20%27${keyword}%27%20and%code%20eq%20%27${code}%27%20`;
|
|
213
|
+
} else {
|
|
214
|
+
throw new Error(
|
|
215
|
+
`key ${key} has unexpected format. Expected 'code.keyword' or 'id:yourId'`
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
queryParams = '?view=simple';
|
|
220
|
+
}
|
|
221
|
+
return [key, queryParams];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Creates a single item
|
|
92
226
|
*
|
|
93
|
-
* @param {TYPE.MetadataTypeItem}
|
|
227
|
+
* @param {TYPE.MetadataTypeItem} metadata a single item
|
|
94
228
|
* @returns {Promise} Promise
|
|
95
229
|
*/
|
|
96
|
-
static create(
|
|
97
|
-
return super.createREST(
|
|
230
|
+
static create(metadata) {
|
|
231
|
+
return super.createREST(metadata, '/legacy/v1/beta/mobile/keyword/');
|
|
98
232
|
}
|
|
99
233
|
/**
|
|
100
234
|
* Updates a single item
|
|
@@ -114,9 +248,21 @@ class MobileKeyword extends MetadataType {
|
|
|
114
248
|
* manages post retrieve steps
|
|
115
249
|
*
|
|
116
250
|
* @param {TYPE.MetadataTypeItem} metadata a single item
|
|
117
|
-
* @returns {TYPE.CodeExtractItem | TYPE.MetadataTypeItem} Array with one metadata object and one ssjs string
|
|
251
|
+
* @returns {TYPE.CodeExtractItem | TYPE.MetadataTypeItem | void} Array with one metadata object and one ssjs string; or single metadata object; nothing if filtered
|
|
118
252
|
*/
|
|
119
253
|
static postRetrieveTasks(metadata) {
|
|
254
|
+
try {
|
|
255
|
+
cache.searchForField('mobileCode', metadata.code.code, 'code', 'id');
|
|
256
|
+
} catch {
|
|
257
|
+
// in case the the mobileCode cannot be found, do not save this keyword as its no longer accessible in the UI either
|
|
258
|
+
Util.logger.debug(
|
|
259
|
+
` - skipping ${this.definition.type} ${
|
|
260
|
+
metadata[this.definition.keyField]
|
|
261
|
+
}. Could not find parent mobileCode ${metadata.code.code}`
|
|
262
|
+
);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
120
266
|
if (metadata.responseMessage) {
|
|
121
267
|
// extract message body
|
|
122
268
|
const codeArr = [];
|
|
@@ -295,8 +441,7 @@ class MobileKeyword extends MetadataType {
|
|
|
295
441
|
);
|
|
296
442
|
}
|
|
297
443
|
|
|
298
|
-
|
|
299
|
-
metadata.code.id = cache.searchForField('mobileCode', metadata.code.code, 'code', 'id');
|
|
444
|
+
this.#setCodeAndKeyword(metadata);
|
|
300
445
|
return metadata;
|
|
301
446
|
}
|
|
302
447
|
/**
|
|
@@ -163,21 +163,17 @@ class MobileMessage extends MetadataType {
|
|
|
163
163
|
|
|
164
164
|
// mobileKeyword
|
|
165
165
|
try {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
metadata.subscriptionKeyword.keyword,
|
|
178
|
-
'keyword',
|
|
179
|
-
'keyword'
|
|
180
|
-
);
|
|
166
|
+
for (const attr of ['keyword', 'subscriptionKeyword', 'nextKeyword']) {
|
|
167
|
+
if (metadata[attr]?.id) {
|
|
168
|
+
metadata[attr] = {
|
|
169
|
+
c__codeKeyword: cache.searchForField(
|
|
170
|
+
'mobileKeyword',
|
|
171
|
+
metadata[attr].id,
|
|
172
|
+
'id',
|
|
173
|
+
'c__codeKeyword'
|
|
174
|
+
),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
181
177
|
}
|
|
182
178
|
} catch (ex) {
|
|
183
179
|
Util.logger.warn(
|
|
@@ -265,20 +261,16 @@ class MobileMessage extends MetadataType {
|
|
|
265
261
|
metadata.code = code;
|
|
266
262
|
|
|
267
263
|
// mobileKeyword
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (!keyword) {
|
|
278
|
-
throw new Error(`mobileKeyword ${metadata.keyword.keyword} not found in cache`);
|
|
264
|
+
for (const attr of ['keyword', 'subscriptionKeyword', 'nextKeyword']) {
|
|
265
|
+
if (metadata[attr]?.c__codeKeyword) {
|
|
266
|
+
const keywordObj = cache.getByKey('mobileKeyword', metadata[attr].c__codeKeyword);
|
|
267
|
+
if (!keywordObj) {
|
|
268
|
+
throw new Error(
|
|
269
|
+
`mobileKeyword ${metadata[attr].c__codeKeyword} not found in cache`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
metadata[attr] = keywordObj;
|
|
279
273
|
}
|
|
280
|
-
|
|
281
|
-
metadata.subscriptionKeyword.keyword = keyword;
|
|
282
274
|
}
|
|
283
275
|
|
|
284
276
|
// campaign
|
|
@@ -102,9 +102,8 @@ class Role extends MetadataType {
|
|
|
102
102
|
`Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` +
|
|
103
103
|
Util.getKeysString(key)
|
|
104
104
|
);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
105
|
+
|
|
106
|
+
await this.runDocumentOnRetrieve(key, savedMetadata);
|
|
108
107
|
}
|
|
109
108
|
return { metadata: parsed, type: this.definition.type };
|
|
110
109
|
}
|
|
@@ -58,9 +58,9 @@ class TransactionalSMS extends TransactionalMessage {
|
|
|
58
58
|
// we merely want to be able to show an error if it does not exist
|
|
59
59
|
cache.searchForField(
|
|
60
60
|
'mobileKeyword',
|
|
61
|
-
metadata.subscriptions.keyword,
|
|
62
|
-
'
|
|
63
|
-
'
|
|
61
|
+
metadata.subscriptions.shortCode + '.' + metadata.subscriptions.keyword,
|
|
62
|
+
'c__codeKeyword',
|
|
63
|
+
'id'
|
|
64
64
|
);
|
|
65
65
|
}
|
|
66
66
|
return metadata;
|
|
@@ -142,9 +142,9 @@ class TransactionalSMS extends TransactionalMessage {
|
|
|
142
142
|
// we merely want to be able to show a warning if it does not exist
|
|
143
143
|
cache.searchForField(
|
|
144
144
|
'mobileKeyword',
|
|
145
|
-
metadata.subscriptions.keyword,
|
|
145
|
+
metadata.subscriptions.shortCode + '.' + metadata.subscriptions.keyword,
|
|
146
146
|
'keyword',
|
|
147
|
-
'
|
|
147
|
+
'id'
|
|
148
148
|
);
|
|
149
149
|
} catch {
|
|
150
150
|
Util.logger.warn(
|
|
@@ -149,25 +149,33 @@ class User extends MetadataType {
|
|
|
149
149
|
// convert SSO / Federation Token into API compliant format
|
|
150
150
|
if (metadata.SsoIdentity || metadata.SsoIdentities) {
|
|
151
151
|
const ssoIdentity = {};
|
|
152
|
+
let error = false;
|
|
152
153
|
if (metadata.SsoIdentity) {
|
|
153
154
|
// assume metadata.SsoIdentity is an object
|
|
154
155
|
ssoIdentity.IsActive = metadata.SsoIdentity.IsActive;
|
|
155
|
-
ssoIdentity.
|
|
156
|
+
ssoIdentity.FederatedID = metadata.SsoIdentity.FederatedID;
|
|
156
157
|
delete metadata.SsoIdentity;
|
|
157
158
|
} else if (Array.isArray(metadata.SsoIdentities)) {
|
|
158
159
|
// be nice and allow SsoIdentities as an alternative if its an array of objects
|
|
159
160
|
ssoIdentity.IsActive = metadata.SsoIdentities[0].IsActive;
|
|
160
|
-
ssoIdentity.
|
|
161
|
+
ssoIdentity.FederatedID = metadata.SsoIdentities[0].FederatedID;
|
|
161
162
|
} else if (
|
|
162
163
|
Array.isArray(metadata.SsoIdentities?.SsoIdentity) &&
|
|
163
164
|
metadata.SsoIdentities?.SsoIdentity.length
|
|
164
165
|
) {
|
|
165
166
|
// API-compliant format already provided; just use it
|
|
166
167
|
ssoIdentity.IsActive = metadata.SsoIdentities.SsoIdentity[0]?.IsActive;
|
|
167
|
-
ssoIdentity.
|
|
168
|
+
ssoIdentity.FederatedID = metadata.SsoIdentities.SsoIdentity[0]?.FederatedID;
|
|
168
169
|
} else {
|
|
170
|
+
error = true;
|
|
171
|
+
}
|
|
172
|
+
if (
|
|
173
|
+
(ssoIdentity.IsActive !== true && ssoIdentity.IsActive !== false) ||
|
|
174
|
+
!ssoIdentity.FederatedID ||
|
|
175
|
+
error
|
|
176
|
+
) {
|
|
169
177
|
throw new TypeError(
|
|
170
|
-
'SsoIdentity should be an object with IsActive and
|
|
178
|
+
'SsoIdentity should be an object with IsActive and FederatedID properties.'
|
|
171
179
|
);
|
|
172
180
|
}
|
|
173
181
|
// if SsoIdentity is set, assume this was on purpose and bring it
|
|
@@ -175,7 +183,7 @@ class User extends MetadataType {
|
|
|
175
183
|
SsoIdentity: [
|
|
176
184
|
{
|
|
177
185
|
IsActive: ssoIdentity.IsActive,
|
|
178
|
-
|
|
186
|
+
FederatedID: ssoIdentity.FederatedID,
|
|
179
187
|
},
|
|
180
188
|
],
|
|
181
189
|
};
|
|
@@ -207,12 +215,12 @@ class User extends MetadataType {
|
|
|
207
215
|
);
|
|
208
216
|
|
|
209
217
|
if (action === 'create') {
|
|
210
|
-
const createItem = metadataToCreate
|
|
218
|
+
const createItem = metadataToCreate.at(-1);
|
|
211
219
|
User._setPasswordForNewUser(createItem);
|
|
212
220
|
User._prepareRoleAssignments({ after: createItem });
|
|
213
221
|
User._prepareBuAssignments(metadata[metadataKey], null, createItem);
|
|
214
222
|
} else if (action === 'update') {
|
|
215
|
-
const updateItem = metadataToUpdate
|
|
223
|
+
const updateItem = metadataToUpdate.at(-1);
|
|
216
224
|
User._prepareRoleAssignments(updateItem);
|
|
217
225
|
User._prepareBuAssignments(metadata[metadataKey], updateItem, null);
|
|
218
226
|
}
|
|
@@ -723,13 +731,7 @@ class User extends MetadataType {
|
|
|
723
731
|
)
|
|
724
732
|
);
|
|
725
733
|
}
|
|
726
|
-
|
|
727
|
-
!singleRetrieve &&
|
|
728
|
-
this.buObject &&
|
|
729
|
-
this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)
|
|
730
|
-
) {
|
|
731
|
-
await this.document(savedMetadata);
|
|
732
|
-
}
|
|
734
|
+
await this.runDocumentOnRetrieve(singleRetrieve, savedMetadata);
|
|
733
735
|
}
|
|
734
736
|
return { metadata: metadata, type: this.definition.type };
|
|
735
737
|
}
|
|
@@ -936,14 +938,6 @@ class User extends MetadataType {
|
|
|
936
938
|
return;
|
|
937
939
|
}
|
|
938
940
|
|
|
939
|
-
// if ran as part of retrieve/deploy with key, exit here
|
|
940
|
-
if (metadata && Object.keys(metadata).length === 1) {
|
|
941
|
-
Util.logger.debug(
|
|
942
|
-
'Only 1 user found. Skipping documentation, assuming we ran retrieve-by-key.'
|
|
943
|
-
);
|
|
944
|
-
return;
|
|
945
|
-
}
|
|
946
|
-
|
|
947
941
|
if (!metadata) {
|
|
948
942
|
// load users from disk if document was called directly and not part of a retrieve
|
|
949
943
|
try {
|
|
@@ -3,8 +3,8 @@ module.exports = {
|
|
|
3
3
|
dependencies: ['mobileCode'],
|
|
4
4
|
hasExtended: false,
|
|
5
5
|
idField: 'id',
|
|
6
|
-
keyIsFixed: false,
|
|
7
|
-
keyField: '
|
|
6
|
+
keyIsFixed: false, // custom field but mapped to normal fields
|
|
7
|
+
keyField: 'c__codeKeyword',
|
|
8
8
|
nameField: 'keyword',
|
|
9
9
|
createdDateField: 'createdDate',
|
|
10
10
|
createdNameField: 'createdBy.name',
|
|
@@ -21,14 +21,20 @@ module.exports = {
|
|
|
21
21
|
// irregular format: NXV4ZFMwTEFwRVczd3RaLUF5X3p5dzo4Njow
|
|
22
22
|
isCreateable: false,
|
|
23
23
|
isUpdateable: true,
|
|
24
|
-
retrieving:
|
|
24
|
+
retrieving: false,
|
|
25
25
|
template: false,
|
|
26
26
|
},
|
|
27
|
+
c__codeKeyword: {
|
|
28
|
+
isCreateable: false,
|
|
29
|
+
isUpdateable: false,
|
|
30
|
+
retrieving: true,
|
|
31
|
+
template: true,
|
|
32
|
+
},
|
|
27
33
|
keyword: {
|
|
28
34
|
isCreateable: true,
|
|
29
35
|
isUpdateable: true,
|
|
30
|
-
retrieving:
|
|
31
|
-
template:
|
|
36
|
+
retrieving: false,
|
|
37
|
+
template: false,
|
|
32
38
|
},
|
|
33
39
|
createdDate: {
|
|
34
40
|
isCreateable: false,
|
|
@@ -133,11 +139,17 @@ module.exports = {
|
|
|
133
139
|
retrieving: true,
|
|
134
140
|
template: false,
|
|
135
141
|
},
|
|
142
|
+
code: {
|
|
143
|
+
isCreateable: true,
|
|
144
|
+
isUpdateable: true,
|
|
145
|
+
retrieving: false,
|
|
146
|
+
template: false,
|
|
147
|
+
},
|
|
136
148
|
'code.code': {
|
|
137
149
|
isCreateable: false,
|
|
138
150
|
isUpdateable: false,
|
|
139
|
-
retrieving:
|
|
140
|
-
template:
|
|
151
|
+
retrieving: false,
|
|
152
|
+
template: false,
|
|
141
153
|
},
|
|
142
154
|
'code.id': {
|
|
143
155
|
isCreateable: true,
|
|
@@ -316,6 +316,12 @@ module.exports = {
|
|
|
316
316
|
'keyword.keyword': {
|
|
317
317
|
isCreateable: true,
|
|
318
318
|
isUpdateable: true,
|
|
319
|
+
retrieving: false,
|
|
320
|
+
template: false,
|
|
321
|
+
},
|
|
322
|
+
'keyword.c__codeKeyword': {
|
|
323
|
+
isCreateable: false,
|
|
324
|
+
isUpdateable: false,
|
|
319
325
|
retrieving: true,
|
|
320
326
|
template: true,
|
|
321
327
|
},
|
|
@@ -620,27 +626,63 @@ module.exports = {
|
|
|
620
626
|
'subscriptionKeyword.id': {
|
|
621
627
|
isCreatable: true,
|
|
622
628
|
isUpdatable: true,
|
|
623
|
-
retrieving:
|
|
624
|
-
template:
|
|
629
|
+
retrieving: false,
|
|
630
|
+
template: false,
|
|
625
631
|
},
|
|
626
632
|
'subscriptionKeyword.keyword': {
|
|
627
|
-
isCreatable:
|
|
628
|
-
isUpdatable:
|
|
633
|
+
isCreatable: false,
|
|
634
|
+
isUpdatable: false,
|
|
635
|
+
retrieving: false,
|
|
636
|
+
template: false,
|
|
637
|
+
},
|
|
638
|
+
'subscriptionKeyword.c__codeKeyword': {
|
|
639
|
+
isCreateable: false,
|
|
640
|
+
isUpdateable: false,
|
|
629
641
|
retrieving: true,
|
|
630
642
|
template: true,
|
|
631
643
|
},
|
|
632
644
|
'subscriptionKeyword.restriction': {
|
|
633
|
-
isCreatable:
|
|
634
|
-
isUpdatable:
|
|
635
|
-
retrieving:
|
|
636
|
-
template:
|
|
645
|
+
isCreatable: false,
|
|
646
|
+
isUpdatable: false,
|
|
647
|
+
retrieving: false,
|
|
648
|
+
template: false,
|
|
637
649
|
},
|
|
638
650
|
'subscriptionKeyword.isInherited': {
|
|
651
|
+
isCreatable: false,
|
|
652
|
+
isUpdatable: false,
|
|
653
|
+
retrieving: false,
|
|
654
|
+
template: false,
|
|
655
|
+
},
|
|
656
|
+
'nextKeyword.id': {
|
|
639
657
|
isCreatable: true,
|
|
640
658
|
isUpdatable: true,
|
|
659
|
+
retrieving: false,
|
|
660
|
+
template: false,
|
|
661
|
+
},
|
|
662
|
+
'nextKeyword.keyword': {
|
|
663
|
+
isCreatable: false,
|
|
664
|
+
isUpdatable: false,
|
|
665
|
+
retrieving: false,
|
|
666
|
+
template: false,
|
|
667
|
+
},
|
|
668
|
+
'nextKeyword.c__codeKeyword': {
|
|
669
|
+
isCreateable: false,
|
|
670
|
+
isUpdateable: false,
|
|
641
671
|
retrieving: true,
|
|
642
672
|
template: true,
|
|
643
673
|
},
|
|
674
|
+
'nextKeyword.restriction': {
|
|
675
|
+
isCreatable: false,
|
|
676
|
+
isUpdatable: false,
|
|
677
|
+
retrieving: false,
|
|
678
|
+
template: false,
|
|
679
|
+
},
|
|
680
|
+
'nextKeyword.isInherited': {
|
|
681
|
+
isCreatable: false,
|
|
682
|
+
isUpdatable: false,
|
|
683
|
+
retrieving: false,
|
|
684
|
+
template: false,
|
|
685
|
+
},
|
|
644
686
|
subscriberResponseMessage: {
|
|
645
687
|
isCreatable: true,
|
|
646
688
|
isUpdatable: true,
|
|
@@ -17,6 +17,7 @@ module.exports = {
|
|
|
17
17
|
typeDescription: 'Marketing Cloud users',
|
|
18
18
|
typeName: 'User',
|
|
19
19
|
typeRetrieveByDefault: false,
|
|
20
|
+
documentInOneFile: true,
|
|
20
21
|
stringifyFieldsBeforeTemplate: ['DefaultBusinessUnit', 'c__AssociatedBusinessUnits'],
|
|
21
22
|
fields: {
|
|
22
23
|
AccountUserID: {
|
|
@@ -196,19 +197,19 @@ module.exports = {
|
|
|
196
197
|
retrieving: false, // retrieve not supported by API
|
|
197
198
|
template: false,
|
|
198
199
|
},
|
|
199
|
-
'SsoIdentities
|
|
200
|
+
'SsoIdentities.SsoIdentity': {
|
|
200
201
|
isCreateable: true,
|
|
201
202
|
isUpdateable: true,
|
|
202
|
-
retrieving: false,
|
|
203
|
+
retrieving: false, // retrieve not supported by API
|
|
203
204
|
template: false,
|
|
204
205
|
},
|
|
205
|
-
'SsoIdentities[].IsActive': {
|
|
206
|
+
'SsoIdentities.SsoIdentity[].IsActive': {
|
|
206
207
|
isCreateable: true,
|
|
207
208
|
isUpdateable: true,
|
|
208
209
|
retrieving: false,
|
|
209
210
|
template: false,
|
|
210
211
|
},
|
|
211
|
-
'SsoIdentities[].FederatedID': {
|
|
212
|
+
'SsoIdentities.SsoIdentity[].FederatedID': {
|
|
212
213
|
isCreateable: true,
|
|
213
214
|
isUpdateable: true,
|
|
214
215
|
retrieving: false,
|
package/lib/util/auth.js
CHANGED
|
@@ -129,7 +129,19 @@ function setupSDK(sessionKey, authObject) {
|
|
|
129
129
|
if (Util.OPTIONS.api === 'cli') {
|
|
130
130
|
console.log(`${Util.color.fgMagenta}API REQUEST >>${Util.color.reset}`, msg); // eslint-disable-line no-console
|
|
131
131
|
} else if (Util.OPTIONS.api === 'log') {
|
|
132
|
+
let data;
|
|
133
|
+
if (msg.data) {
|
|
134
|
+
data = msg.data;
|
|
135
|
+
delete msg.data;
|
|
136
|
+
}
|
|
132
137
|
Util.logger.debug('API REQUEST >> ' + JSON.stringify(msg, null, 2));
|
|
138
|
+
if (data) {
|
|
139
|
+
// printing it separately leads to better formatting
|
|
140
|
+
Util.logger.debug(
|
|
141
|
+
'API REQUEST body >> \n ' +
|
|
142
|
+
(typeof data === 'string' ? data : JSON.stringify(data, null, 2))
|
|
143
|
+
);
|
|
144
|
+
}
|
|
133
145
|
}
|
|
134
146
|
},
|
|
135
147
|
logResponse: (res) => {
|
|
@@ -138,7 +150,7 @@ function setupSDK(sessionKey, authObject) {
|
|
|
138
150
|
if (Util.OPTIONS.api === 'cli') {
|
|
139
151
|
console.log(`${Util.color.fgMagenta}API RESPONSE <<${Util.color.reset}`, msg); // eslint-disable-line no-console
|
|
140
152
|
} else if (Util.OPTIONS.api === 'log') {
|
|
141
|
-
Util.logger.debug('API RESPONSE << ' + msg);
|
|
153
|
+
Util.logger.debug('API RESPONSE body << \n ' + msg);
|
|
142
154
|
}
|
|
143
155
|
},
|
|
144
156
|
},
|
package/lib/util/cli.js
CHANGED
|
@@ -570,7 +570,7 @@ const Cli = {
|
|
|
570
570
|
}
|
|
571
571
|
if (Util.OPTIONS.json) {
|
|
572
572
|
if (Util.OPTIONS.loggerLevel !== 'error') {
|
|
573
|
-
console.log(json); // eslint-disable-line no-console
|
|
573
|
+
console.log(JSON.stringify(json, null, 2)); // eslint-disable-line no-console
|
|
574
574
|
}
|
|
575
575
|
return json;
|
|
576
576
|
}
|
package/lib/util/file.js
CHANGED
|
@@ -517,9 +517,10 @@ const File = {
|
|
|
517
517
|
* @param {string} [filetype='html'] filetype ie. JSON or SSJS
|
|
518
518
|
* @returns {Promise.<boolean>} success of config load
|
|
519
519
|
*/
|
|
520
|
-
async initPrettier(filetype) {
|
|
521
|
-
if (FileFs.prettierConfig === null) {
|
|
522
|
-
filetype
|
|
520
|
+
async initPrettier(filetype = 'html') {
|
|
521
|
+
if (FileFs.prettierConfig === null || FileFs.prettierConfigFileType !== filetype) {
|
|
522
|
+
// run this if no config was yet found or if the filetype previously used to initialize it differs (because it results in a potentially different config!)
|
|
523
|
+
FileFs.prettierConfigFileType = filetype;
|
|
523
524
|
try {
|
|
524
525
|
// pass in project dir with fake index.html to avoid "no parser" error
|
|
525
526
|
// by using process.cwd we are limiting ourselves to a config in the project root
|
|
@@ -547,5 +548,6 @@ const File = {
|
|
|
547
548
|
};
|
|
548
549
|
const FileFs = { ...fs, ...File };
|
|
549
550
|
FileFs.prettierConfig = null;
|
|
551
|
+
FileFs.prettierConfigFileType = null;
|
|
550
552
|
|
|
551
553
|
module.exports = FileFs;
|