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.
Files changed (68) hide show
  1. package/.eslintrc.json +1 -0
  2. package/.github/ISSUE_TEMPLATE/bug.yml +2 -0
  3. package/.husky/commit-msg +1 -1
  4. package/.husky/post-checkout +34 -3
  5. package/.husky/post-merge +21 -0
  6. package/.husky/pre-commit +1 -0
  7. package/README.md +1 -1
  8. package/docs/dist/documentation.md +60 -15
  9. package/lib/Deployer.js +0 -1
  10. package/lib/cli.js +8 -8
  11. package/lib/index.js +2 -1
  12. package/lib/metadataTypes/Asset.js +4 -2
  13. package/lib/metadataTypes/Automation.js +39 -28
  14. package/lib/metadataTypes/DataExtension.js +1 -3
  15. package/lib/metadataTypes/DataExtensionField.js +5 -5
  16. package/lib/metadataTypes/Journey.js +11 -10
  17. package/lib/metadataTypes/MetadataType.js +34 -7
  18. package/lib/metadataTypes/MobileKeyword.js +165 -20
  19. package/lib/metadataTypes/MobileMessage.js +20 -28
  20. package/lib/metadataTypes/Role.js +2 -3
  21. package/lib/metadataTypes/TransactionalSMS.js +5 -5
  22. package/lib/metadataTypes/User.js +16 -22
  23. package/lib/metadataTypes/definitions/MobileKeyword.definition.js +19 -7
  24. package/lib/metadataTypes/definitions/MobileMessage.definition.js +50 -8
  25. package/lib/metadataTypes/definitions/User.definition.js +5 -4
  26. package/lib/util/auth.js +13 -1
  27. package/lib/util/cli.js +1 -1
  28. package/lib/util/file.js +5 -3
  29. package/lib/util/util.js +1 -0
  30. package/package.json +9 -9
  31. package/test/mockRoot/.mcdevrc.json +3 -1
  32. package/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/{testNew_keyword.mobileKeyword-meta.json → 4912312345678.TESTNEW_KEYWORD.mobileKeyword-meta.json} +1 -4
  33. package/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/{testNew_keyword_blocked.mobileKeyword-meta.json → 4912312345678.TESTNEW_KEYWORD_BLOCKED.mobileKeyword-meta.json} +1 -4
  34. package/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.json +1 -1
  35. package/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.json +1 -1
  36. package/test/resources/1111111/user/retrieve-expected.md +19 -0
  37. package/test/resources/9999999/dataExtension/retrieve-expected.md +18 -0
  38. package/test/resources/9999999/journey/build-expected.json +1 -1
  39. package/test/resources/9999999/journey/get-expected.json +1 -1
  40. package/test/resources/9999999/journey/put-expected.json +1 -1
  41. package/test/resources/9999999/journey/template-expected.json +1 -1
  42. package/test/resources/9999999/legacy/v1/beta/mobile/keyword/NXV4ZFMwTEFwRVczd3RaLUF5X3p5dzo4Njow/get-response.json +1 -1
  43. package/test/resources/9999999/legacy/v1/beta/mobile/keyword/get-response.json +1 -1
  44. package/test/resources/9999999/legacy/v1/beta/mobile/message/NTIzOjc4OjA/get-response.json +1 -1
  45. package/test/resources/9999999/legacy/v1/beta/mobile/message/get-response.json +1 -1
  46. package/test/resources/9999999/messaging/v1/sms/definitions/post-response.json +1 -1
  47. package/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/get-response.json +1 -1
  48. package/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/patch-response.json +1 -1
  49. package/test/resources/9999999/mobileKeyword/build-expected.json +1 -4
  50. package/test/resources/9999999/mobileKeyword/get-expected.json +1 -4
  51. package/test/resources/9999999/mobileKeyword/post-create-expected.json +1 -4
  52. package/test/resources/9999999/mobileKeyword/template-expected.json +1 -4
  53. package/test/resources/9999999/query/build-expected.sql +1 -1
  54. package/test/resources/9999999/query/get-expected.sql +1 -1
  55. package/test/resources/9999999/query/patch-expected.sql +1 -1
  56. package/test/resources/9999999/query/post-expected.sql +1 -1
  57. package/test/resources/9999999/query/template-expected.sql +1 -1
  58. package/test/resources/9999999/transactionalSMS/build-expected.json +1 -1
  59. package/test/resources/9999999/transactionalSMS/get-expected.json +1 -1
  60. package/test/resources/9999999/transactionalSMS/patch-expected.json +1 -1
  61. package/test/resources/9999999/transactionalSMS/post-expected.json +1 -1
  62. package/test/resources/9999999/transactionalSMS/template-expected.json +1 -1
  63. package/test/type.dataExtension.test.js +13 -1
  64. package/test/type.mobileKeyword.test.js +57 -19
  65. package/test/type.user.test.js +30 -1
  66. package/test/utils.js +10 -0
  67. /package/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/{testNew_keyword.mobileKeyword-meta.amp → 4912312345678.TESTNEW_KEYWORD.mobileKeyword-meta.amp} +0 -0
  68. /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
- * Retrieve a specific keyword
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} name name of the metadata file
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, name, templateVariables) {
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/?view=simple&$where=keyword%20eq%20%27${name}%27%20`,
178
+ `/legacy/v1/beta/mobile/keyword/` + queryParams,
75
179
  templateVariables,
76
- name
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 (name && ex.code === 'ERR_BAD_REQUEST') {
184
+ if (key && ex.code === 'ERR_BAD_REQUEST') {
81
185
  Util.logger.info(
82
- `Downloaded: ${this.definition.type} (0)${Util.getKeysString(name)}`
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
- * Creates a single Event Definition
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} MobileKeyword a single Event Definition
227
+ * @param {TYPE.MetadataTypeItem} metadata a single item
94
228
  * @returns {Promise} Promise
95
229
  */
96
- static create(MobileKeyword) {
97
- return super.createREST(MobileKeyword, '/legacy/v1/beta/mobile/keyword/');
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
- // mobileCode
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
- if (metadata.keyword?.keyword) {
167
- cache.searchForField(
168
- 'mobileKeyword',
169
- metadata.keyword.keyword,
170
- 'keyword',
171
- 'keyword'
172
- );
173
- }
174
- if (metadata.subscriptionKeyword?.keyword) {
175
- cache.searchForField(
176
- 'mobileKeyword',
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
- if (metadata.keyword?.keyword) {
269
- const keyword = cache.getByKey('mobileKeyword', metadata.keyword.keyword);
270
- if (!keyword) {
271
- throw new Error(`mobileKeyword ${metadata.keyword.keyword} not found in cache`);
272
- }
273
- metadata.keyword = keyword;
274
- }
275
- if (metadata.subscriptionKeyword?.keyword) {
276
- const keyword = cache.getByKey('mobileKeyword', metadata.subscriptionKeyword.keyword);
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
- if (this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)) {
106
- await this.document(savedMetadata);
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
- 'keyword',
63
- 'keyword'
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
- 'keyword'
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.FederatedId = metadata.SsoIdentity.FederatedId;
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.FederatedId = metadata.SsoIdentities[0].FederatedId;
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.FederatedId = metadata.SsoIdentities.SsoIdentity[0]?.FederatedId;
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 FederatedId properties.'
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
- FederatedId: ssoIdentity.FederatedId,
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[metadataToCreate.length - 1];
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[metadataToUpdate.length - 1];
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
- if (
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: 'keyword',
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: true,
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: true,
31
- template: true,
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: true,
140
- template: true,
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: true,
624
- template: true,
629
+ retrieving: false,
630
+ template: false,
625
631
  },
626
632
  'subscriptionKeyword.keyword': {
627
- isCreatable: true,
628
- isUpdatable: true,
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: true,
634
- isUpdatable: true,
635
- retrieving: true,
636
- template: true,
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 ||= 'html';
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;