mcdev 4.3.1 → 4.3.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.
@@ -0,0 +1,14 @@
1
+ [
2
+ {
3
+ "name": "Create PR to develop branch",
4
+ "target": "ref",
5
+ "refTargets": [
6
+ "localbranch",
7
+ "remotebranch"
8
+ ],
9
+ "action": {
10
+ "type": "url",
11
+ "url": "https://github.com/Accenture/sfmc-devtools/compare/develop...$shortname?expand=1"
12
+ }
13
+ }
14
+ ]
@@ -39,6 +39,7 @@ body:
39
39
  label: Version
40
40
  description: What version of our software are you running? (mcdev --version)
41
41
  options:
42
+ - 4.3.2
42
43
  - 4.3.1
43
44
  - 4.3.0
44
45
  - 4.2.1
@@ -7,3 +7,4 @@ node_modules/
7
7
  /template/
8
8
  **/QueryStudioResults at*
9
9
  .mcdev-auth.json
10
+ *.error.log
@@ -736,10 +736,7 @@ MessageSendActivity MetadataType
736
736
  * [AccountUser](#AccountUser) ⇐ [<code>MetadataType</code>](#MetadataType)
737
737
  * [.retrieve(retrieveDir, _, [__], [key])](#AccountUser.retrieve) ⇒ <code>Promise.&lt;TYPE.MetadataTypeMapObj&gt;</code>
738
738
  * [.retrieveChangelog()](#AccountUser.retrieveChangelog) ⇒ <code>Promise.&lt;TYPE.MetadataTypeMapObj&gt;</code>
739
- * [.timeSinceDate(date)](#AccountUser.timeSinceDate) ⇒ <code>number</code>
740
- * [.getBuName(id)](#AccountUser.getBuName) ⇒ <code>string</code>
741
739
  * [.document([metadata])](#AccountUser.document) ⇒ <code>Promise.&lt;void&gt;</code>
742
- * [._generateDocMd(users, type, columnsToPrint)](#AccountUser._generateDocMd) ⇒ <code>string</code>
743
740
  * [.postRetrieveTasks(metadata)](#AccountUser.postRetrieveTasks) ⇒ <code>TYPE.MetadataTypeItem</code>
744
741
  * [.parseMetadata(metadata)](#AccountUser.parseMetadata) ⇒ <code>TYPE.MetadataTypeItem</code>
745
742
 
@@ -765,28 +762,6 @@ Retrieves SOAP based metadata of metadata type into local filesystem. executes c
765
762
 
766
763
  **Kind**: static method of [<code>AccountUser</code>](#AccountUser)
767
764
  **Returns**: <code>Promise.&lt;TYPE.MetadataTypeMapObj&gt;</code> - Promise of metadata
768
- <a name="AccountUser.timeSinceDate"></a>
769
-
770
- ### AccountUser.timeSinceDate(date) ⇒ <code>number</code>
771
- **Kind**: static method of [<code>AccountUser</code>](#AccountUser)
772
- **Returns**: <code>number</code> - time difference
773
-
774
- | Param | Type | Description |
775
- | --- | --- | --- |
776
- | date | <code>string</code> | first date |
777
-
778
- <a name="AccountUser.getBuName"></a>
779
-
780
- ### AccountUser.getBuName(id) ⇒ <code>string</code>
781
- helper to print bu names
782
-
783
- **Kind**: static method of [<code>AccountUser</code>](#AccountUser)
784
- **Returns**: <code>string</code> - "bu name (bu id)""
785
-
786
- | Param | Type | Description |
787
- | --- | --- | --- |
788
- | id | <code>number</code> | bu id |
789
-
790
765
  <a name="AccountUser.document"></a>
791
766
 
792
767
  ### AccountUser.document([metadata]) ⇒ <code>Promise.&lt;void&gt;</code>
@@ -799,18 +774,6 @@ Creates markdown documentation of all roles
799
774
  | --- | --- | --- |
800
775
  | [metadata] | <code>TYPE.MetadataTypeMap</code> | user list |
801
776
 
802
- <a name="AccountUser._generateDocMd"></a>
803
-
804
- ### AccountUser.\_generateDocMd(users, type, columnsToPrint) ⇒ <code>string</code>
805
- **Kind**: static method of [<code>AccountUser</code>](#AccountUser)
806
- **Returns**: <code>string</code> - markdown
807
-
808
- | Param | Type | Description |
809
- | --- | --- | --- |
810
- | users | <code>Array.&lt;object&gt;</code> | list of users and installed package |
811
- | type | <code>&#x27;Installed Package&#x27;</code> \| <code>&#x27;User&#x27;</code> | choose what sub type to print |
812
- | columnsToPrint | <code>Array.&lt;Array&gt;</code> | helper array |
813
-
814
777
  <a name="AccountUser.postRetrieveTasks"></a>
815
778
 
816
779
  ### AccountUser.postRetrieveTasks(metadata) ⇒ <code>TYPE.MetadataTypeItem</code>
@@ -60,10 +60,17 @@ class AccountUser extends MetadataType {
60
60
  for (const item of resultsBatch) {
61
61
  this.userIdBuMap[item.AccountUser.AccountUserID] =
62
62
  this.userIdBuMap[item.AccountUser.AccountUserID] || [];
63
- this.userIdBuMap[item.AccountUser.AccountUserID].push({
64
- ID: item.Account.ID,
65
- Name: item.Account.Name,
66
- });
63
+ // push to array if not already in array
64
+ if (
65
+ !this.userIdBuMap[item.AccountUser.AccountUserID].some(
66
+ (bu) => bu.ID === item.Account.ID
67
+ )
68
+ ) {
69
+ this.userIdBuMap[item.AccountUser.AccountUserID].push({
70
+ ID: item.Account.ID,
71
+ Name: item.Account.Name,
72
+ });
73
+ }
67
74
  }
68
75
  // get actual user details
69
76
  /** @type {TYPE.SoapRequestParams} */
@@ -114,10 +121,11 @@ class AccountUser extends MetadataType {
114
121
  }
115
122
  /**
116
123
  *
124
+ * @private
117
125
  * @param {string} date first date
118
126
  * @returns {number} time difference
119
127
  */
120
- static timeSinceDate(date) {
128
+ static _timeSinceDate(date) {
121
129
  const interval = 'days';
122
130
  const second = 1000,
123
131
  minute = second * 60,
@@ -172,13 +180,28 @@ class AccountUser extends MetadataType {
172
180
  /**
173
181
  * helper to print bu names
174
182
  *
183
+ * @private
175
184
  * @param {number} id bu id
176
185
  * @returns {string} "bu name (bu id)""
177
186
  */
178
- static getBuName(id) {
187
+ static _getBuName(id) {
179
188
  const name = this.buObject.eid == id ? '_ParentBU_' : this.buIdName[id];
180
189
  return `<nobr>${name} (${id})</nobr>`;
181
190
  }
191
+
192
+ /**
193
+ * helper that gets BU names from config
194
+ *
195
+ * @private
196
+ */
197
+ static _getBuNames() {
198
+ this.buIdName = {};
199
+ for (const cred in this.properties.credentials) {
200
+ for (const buName in this.properties.credentials[cred].businessUnits) {
201
+ this.buIdName[this.properties.credentials[cred].businessUnits[buName]] = buName;
202
+ }
203
+ }
204
+ }
182
205
  /**
183
206
  * Creates markdown documentation of all roles
184
207
  *
@@ -209,7 +232,7 @@ class AccountUser extends MetadataType {
209
232
  }
210
233
  }
211
234
  // init map of BU Ids > BU Name
212
- this.buIdName = {};
235
+ this._getBuNames();
213
236
 
214
237
  // initialize permission object
215
238
  this.allPermissions = {};
@@ -244,14 +267,19 @@ class AccountUser extends MetadataType {
244
267
  }
245
268
  let associatedBus = '';
246
269
  if (user.AssociatedBusinessUnits__c) {
247
- associatedBus = user.AssociatedBusinessUnits__c.map((item) => {
248
- this.buIdName[item.ID] = item.Name;
249
- return this.getBuName(item.ID);
250
- })
270
+ user.AssociatedBusinessUnits__c.push({
271
+ ID: user.DefaultBusinessUnit,
272
+ });
273
+ // ensure associatedBus have no duplicates
274
+ associatedBus = [
275
+ ...new Set(
276
+ user.AssociatedBusinessUnits__c.map((item) => this._getBuName(item.ID))
277
+ ),
278
+ ]
251
279
  .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))
252
280
  .join(',<br> ');
253
281
  }
254
- const defaultBUName = this.getBuName(user.DefaultBusinessUnit);
282
+ const defaultBUName = this._getBuName(user.DefaultBusinessUnit);
255
283
  users.push({
256
284
  TYPE: user.type__c,
257
285
  UserID: user.UserID,
@@ -267,7 +295,7 @@ class AccountUser extends MetadataType {
267
295
  AssociatedBusinessUnits__c: associatedBus,
268
296
  Roles: roles,
269
297
  UserPermissions: userPermissions,
270
- LastSuccessfulLogin: this.timeSinceDate(user.LastSuccessfulLogin),
298
+ LastSuccessfulLogin: this._timeSinceDate(user.LastSuccessfulLogin),
271
299
  CreatedDate: user.CreatedDate.split('T').join(' '),
272
300
  ModifiedDate: user.ModifiedDate.split('T').join(' '),
273
301
  });
@@ -325,6 +353,7 @@ class AccountUser extends MetadataType {
325
353
  }
326
354
  /**
327
355
  *
356
+ * @private
328
357
  * @param {object[]} users list of users and installed package
329
358
  * @param {'Installed Package'|'User'} type choose what sub type to print
330
359
  * @param {Array[]} columnsToPrint helper array
@@ -317,22 +317,30 @@ class Asset extends MetadataType {
317
317
  } catch (ex) {
318
318
  failed.push({ item: item, error: ex });
319
319
  }
320
+ // even if the extended file failed, still save the metadata
320
321
  metadata[item.customerKey] = item;
321
322
  }
322
323
  // this is a complex type which stores data in the asset itself
323
324
  else if (!completed.includes(item.id)) {
324
- const extendedItem = await this.client.rest.get(
325
- 'asset/v1/content/assets/' + item.id
326
- );
327
- metadata[item.customerKey] = extendedItem;
325
+ try {
326
+ const extendedItem = await this.client.rest.get(
327
+ 'asset/v1/content/assets/' + item.id
328
+ );
329
+ // only save the metadata if we have extended content
330
+ metadata[item.customerKey] = extendedItem;
331
+ } catch (ex) {
332
+ failed.push({ item: item, error: ex });
333
+ }
328
334
  }
329
335
  completed.push(item.id);
330
- await this.saveResults(
331
- metadata,
332
- retrieveDir,
333
- 'asset-' + subType,
334
- templateVariables
335
- );
336
+ if (metadata[item.customerKey]) {
337
+ await this.saveResults(
338
+ metadata,
339
+ retrieveDir,
340
+ 'asset-' + subType,
341
+ templateVariables
342
+ );
343
+ }
336
344
  // update the current value in your application..
337
345
  extendedBar.increment();
338
346
  })
@@ -346,7 +354,7 @@ class Asset extends MetadataType {
346
354
  } catch (ex) {
347
355
  extendedBar.stop();
348
356
  Asset._resetLogLevel(loggerLevelBak, failed);
349
- // timeouts should be retried, others can be retried
357
+ // timeouts should be retried, others can be exited
350
358
  if (ex.code !== 'ETIMEDOUT') {
351
359
  throw ex;
352
360
  }
@@ -394,11 +402,17 @@ class Asset extends MetadataType {
394
402
  );
395
403
  for (const fail of failed) {
396
404
  Util.logger.warn(
397
- ` - "${fail.item.name}" (${fail.item.customerKey}) in ${fail.item.r__folder_Path}: ${fail.error.message}`
405
+ ` - "${fail.item.name}" (${fail.item.customerKey}): ${fail.error.message} (${
406
+ fail.error.code
407
+ })${
408
+ fail.error.endpoint
409
+ ? Util.getGrayMsg(
410
+ ' - ' +
411
+ fail.error.endpoint.split('rest.marketingcloudapis.com')[1]
412
+ )
413
+ : ''
414
+ }`
398
415
  );
399
- Util.logger.debug(`-- Error: ${fail.error.message}`);
400
- Util.logger.debug(`-- AssetType: ${fail.item.assetType.name}`);
401
- Util.logger.debug(`-- fileProperties: ${JSON.stringify(fail.item.fileProperties)}`);
402
416
  }
403
417
  Util.logger.info(
404
418
  ' - You will still find a JSON file for each of these in the download directory.'
@@ -242,9 +242,11 @@ class DataExtensionField extends MetadataType {
242
242
  }
243
243
 
244
244
  Util.logger.info(
245
- ` - Found ${deployColumns.length} added/updated Fields for ${deKey}${
246
- deployColumns.length ? ': ' : ''
247
- }` + deployColumns.map((item) => item.Name).join(', ')
245
+ Util.getGrayMsg(
246
+ ` - Found ${deployColumns.length} added/updated Fields for ${deKey}${
247
+ deployColumns.length ? ': ' : ''
248
+ }` + deployColumns.map((item) => item.Name).join(', ')
249
+ )
248
250
  );
249
251
  return existingFieldByName;
250
252
  }
@@ -104,15 +104,33 @@ class EmailSendDefinition extends MetadataType {
104
104
  delete metadata.r__email_Name;
105
105
  } else if (metadata.r__assetMessage_Key) {
106
106
  // content builder
107
- // * this ignores r__assetMessage_Name on purpose as that is only unique per parent folder but useful during PR reviews
108
- metadata.Email.ID = cache.searchForField(
109
- 'asset',
110
- metadata.r__assetMessage_Key,
111
- 'customerKey',
112
- 'legacyData.legacyId'
113
- );
114
- delete metadata.r__assetMessage_Key;
115
- delete metadata.r__assetMessage_Name;
107
+ // * this ignores r__assetMessage_Name on purpose\ as that is only unique per parent folder but useful during PR reviews
108
+ // will try to find the key with the bu mid at the end, if unable, will try to find the key without it
109
+ try {
110
+ // check asset key as provided
111
+ metadata.Email.ID = cache.searchForField(
112
+ 'asset',
113
+ metadata.r__assetMessage_Key,
114
+ 'customerKey',
115
+ 'legacyData.legacyId'
116
+ );
117
+ delete metadata.r__assetMessage_Key;
118
+ delete metadata.r__assetMessage_Name;
119
+ } catch {
120
+ // if we deploy to another BU, try applying the BU's MID to the end, which we do in preDeployTasks for assets
121
+
122
+ // get suffix to update customer key at the end
123
+ const suffix = '-' + this.buObject.mid;
124
+
125
+ metadata.Email.ID = cache.searchForField(
126
+ 'asset',
127
+ metadata.r__assetMessage_Key.slice(0, Math.max(0, 36 - suffix.length)) + suffix,
128
+ 'customerKey',
129
+ 'legacyData.legacyId'
130
+ );
131
+ delete metadata.r__assetMessage_Key;
132
+ delete metadata.r__assetMessage_Name;
133
+ }
116
134
  }
117
135
  // Target Audience DataExtension
118
136
  // normalize first because this can be an array
@@ -129,7 +147,7 @@ class EmailSendDefinition extends MetadataType {
129
147
  if (sdl.r__dataExtension_Key) {
130
148
  if (sdl.DataSourceTypeID !== 'CustomObject') {
131
149
  throw new Error(
132
- `Skipping ${metadata.Name} (${metadata.CustomerKey}): Expecting DataSourceTypeID to equal 'CustomObject' when r__dataExtension_Key is defined; Found '${sdl.DataSourceTypeID}'`
150
+ ` ☇ skipping ${this.definition.type} ${metadata.Name} (${metadata.CustomerKey}): Expecting DataSourceTypeID to equal 'CustomObject' when r__dataExtension_Key is defined; Found '${sdl.DataSourceTypeID}'`
133
151
  );
134
152
  }
135
153
  sdl.CustomObjectID = cache.searchForField(
@@ -141,9 +159,13 @@ class EmailSendDefinition extends MetadataType {
141
159
  delete sdl.r__dataExtension_Key;
142
160
  } else if (sdl.DataSourceTypeID === 'CustomObject') {
143
161
  throw new Error(
144
- `Skipping ${metadata.Name} (${metadata.CustomerKey}): Expecting r__dataExtension_Key to be defined if DataSourceTypeID='CustomObject'`
162
+ ` ☇ skipping ${this.definition.type} ${metadata.Name} (${metadata.CustomerKey}): Expecting r__dataExtension_Key to be defined if DataSourceTypeID='CustomObject'`
145
163
  );
146
164
  }
165
+ if (!sdl.SalesForceObjectID || sdl.SalesForceObjectID === '') {
166
+ // otherwise this causes error 42117 / invalid ObjectID
167
+ delete sdl.SalesForceObjectID;
168
+ }
147
169
  // get List (required)
148
170
  if (sdl.r__list_PathName) {
149
171
  sdl.List = {
@@ -152,7 +174,7 @@ class EmailSendDefinition extends MetadataType {
152
174
  delete sdl.r__list_PathName;
153
175
  } else {
154
176
  throw new Error(
155
- `Field SendDefinitionList.r__list_PathName was not defined. Please try re-retrieving this ESD from your source BU.`
177
+ ` ☇ skipping ${this.definition.type} ${metadata.Name} (${metadata.CustomerKey}) Field SendDefinitionList.r__list_PathName was not defined. Please try re-retrieving this ESD from your source BU.`
156
178
  );
157
179
  }
158
180
  }
@@ -208,7 +230,11 @@ class EmailSendDefinition extends MetadataType {
208
230
  delete metadata.Email;
209
231
  } catch {
210
232
  Util.logger.warn(
211
- ` - ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': Could not find email with ID ${metadata.Email.ID} in Classic nor in Content Builder.`
233
+ ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${
234
+ metadata[this.definition.keyField]
235
+ }): Could not find email with ID ${
236
+ metadata.Email.ID
237
+ } in Classic nor in Content Builder.`
212
238
  );
213
239
  }
214
240
  }
@@ -234,7 +260,11 @@ class EmailSendDefinition extends MetadataType {
234
260
  delete sdl.CustomObjectID;
235
261
  } catch {
236
262
  Util.logger.warn(
237
- ` - ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': Could not find Target Audience (DataExtension) with ObjectID ${sdl.CustomObjectID}.`
263
+ ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${
264
+ metadata[this.definition.keyField]
265
+ }): Could not find Target Audience (DataExtension) with ObjectID ${
266
+ sdl.CustomObjectID
267
+ }.`
238
268
  );
239
269
  }
240
270
  }
@@ -245,10 +275,16 @@ class EmailSendDefinition extends MetadataType {
245
275
  delete sdl.List;
246
276
  } catch (ex) {
247
277
  Util.logger.warn(
248
- ` - ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': ${ex.message}`
278
+ ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${
279
+ metadata[this.definition.keyField]
280
+ }): ${ex.message}`
249
281
  );
250
282
  }
251
283
  }
284
+ if (!sdl.SalesForceObjectID) {
285
+ // otherwise this causes error 42117 / invalid ObjectID
286
+ delete sdl.SalesForceObjectID;
287
+ }
252
288
  }
253
289
 
254
290
  return metadata;
@@ -53,21 +53,23 @@ class List extends MetadataType {
53
53
  */
54
54
  static async retrieveForCache() {
55
55
  const results = await this.retrieve();
56
- if (!cache.getCache()?.folder) {
57
- const subTypeArr = [
58
- 'list',
59
- 'mysubs',
60
- 'suppression_list',
61
- 'publication',
62
- 'contextual_suppression_list',
63
- ];
64
- Util.logger.debug('folders not cached but required for list');
65
- Util.logger.info(' - Caching dependent Metadata: folder');
66
- Util.logSubtypes(subTypeArr);
67
- Folder.client = this.client;
68
- Folder.buObject = this.buObject;
69
- Folder.properties = this.properties;
70
- const result = await Folder.retrieveForCache(null, subTypeArr);
56
+ const subTypeArr = [
57
+ 'list',
58
+ 'mysubs',
59
+ 'suppression_list',
60
+ 'publication',
61
+ 'contextual_suppression_list',
62
+ ];
63
+ Util.logger.debug('folders not cached but required for list');
64
+ Util.logger.info(' - Caching dependent Metadata: folder');
65
+ Util.logSubtypes(subTypeArr);
66
+ Folder.client = this.client;
67
+ Folder.buObject = this.buObject;
68
+ Folder.properties = this.properties;
69
+ const result = await Folder.retrieveForCache(null, subTypeArr);
70
+ if (cache.getCache()?.folder) {
71
+ cache.mergeMetadata('folder', result.metadata);
72
+ } else {
71
73
  cache.setMetadata('folder', result.metadata);
72
74
  }
73
75
  for (const metadataEntry in results.metadata) {
@@ -725,7 +725,7 @@ class MetadataType {
725
725
  Util.logger.info(
726
726
  ` - updated ${this.definition.type}: ${
727
727
  metadataEntry[this.definition.keyField]
728
- } / ${metadataEntry[this.definition.nameField]}`
728
+ } / ${metadataEntry[this.definition.nameField]}`
729
729
  );
730
730
  }
731
731
  return response;
@@ -132,8 +132,9 @@ class TriggeredSendDefinition extends MetadataType {
132
132
  this.setFolderPath(metadata);
133
133
  } catch {
134
134
  Util.logger.verbose(
135
- ` - skipping ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': Could not find folder.`
135
+ ` skipping ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': Could not find folder.`
136
136
  );
137
+ // do not save this TSD because it would not be visible in the user interface
137
138
  return;
138
139
  }
139
140
 
@@ -163,9 +164,9 @@ class TriggeredSendDefinition extends MetadataType {
163
164
  delete metadata.Email;
164
165
  } catch {
165
166
  Util.logger.verbose(
166
- ` - skipping ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': Could not find email with ID ${metadata.Email.ID} in Classic nor in Content Builder.`
167
+ ` - ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': Could not find email with ID ${metadata.Email.ID} in Classic nor in Content Builder. This TSD cannot be replublished but potentially restarted with its cached version of the email.`
167
168
  );
168
- return;
169
+ // save this TSD because it could be fixed by the user or potentially restarted without a fix; also, it might be used by a journey
169
170
  }
170
171
  }
171
172
  // List (optional)
@@ -175,9 +176,9 @@ class TriggeredSendDefinition extends MetadataType {
175
176
  delete metadata.List;
176
177
  } catch (ex) {
177
178
  Util.logger.verbose(
178
- ` - skipping ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': ${ex.message}`
179
+ ` - ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': ${ex.message}`
179
180
  );
180
- return;
181
+ // save this TSD because it could be fixed by the user
181
182
  }
182
183
  }
183
184
 
@@ -1,6 +1,6 @@
1
1
  module.exports = {
2
2
  bodyIteratorField: 'Results',
3
- dependencies: ['folder-userinitiatedsends', 'email', 'asset-message', 'dataExtension', 'list'], // filter, SendClassification, SenderProfile, DeliveryProfile
3
+ dependencies: ['folder-userinitiatedsends', 'email', 'asset-message', 'dataExtension', 'list'], // filter(+), sendClassification(+), SenderProfile(n/a), DeliveryProfile(n/a)
4
4
  folderType: 'userinitiatedsends',
5
5
  hasExtended: false,
6
6
  idField: 'ObjectID',
@@ -22,31 +22,31 @@ module.exports = {
22
22
  isCreateable: true,
23
23
  isUpdateable: true,
24
24
  retrieving: true,
25
- templating: false,
25
+ templating: true,
26
26
  },
27
27
  AutoBccEmail: {
28
28
  isCreateable: true,
29
29
  isUpdateable: true,
30
30
  retrieving: true,
31
- templating: false,
31
+ templating: true,
32
32
  },
33
33
  BccEmail: {
34
34
  isCreateable: true,
35
35
  isUpdateable: true,
36
36
  retrieving: true,
37
- templating: false,
37
+ templating: true,
38
38
  },
39
39
  CategoryID: {
40
40
  isCreateable: true,
41
41
  isUpdateable: true,
42
42
  retrieving: true,
43
- templating: false,
43
+ templating: true,
44
44
  },
45
45
  CCEmail: {
46
46
  isCreateable: true,
47
47
  isUpdateable: true,
48
48
  retrieving: true,
49
- templating: false,
49
+ templating: true,
50
50
  },
51
51
  'Client.ID': {
52
52
  isCreateable: false,
@@ -61,8 +61,8 @@ module.exports = {
61
61
  templating: false,
62
62
  },
63
63
  CreatedDate: {
64
- isCreateable: true,
65
- isUpdateable: true,
64
+ isCreateable: false,
65
+ isUpdateable: false,
66
66
  retrieving: true,
67
67
  templating: false,
68
68
  },
@@ -70,19 +70,19 @@ module.exports = {
70
70
  isCreateable: true,
71
71
  isUpdateable: true,
72
72
  retrieving: true,
73
- templating: false,
73
+ templating: true,
74
74
  },
75
75
  DeduplicateByEmail: {
76
76
  isCreateable: true,
77
77
  isUpdateable: true,
78
78
  retrieving: true,
79
- templating: false,
79
+ templating: true,
80
80
  },
81
81
  'DeliveryProfile.CustomerKey': {
82
82
  isCreateable: true,
83
83
  isUpdateable: true,
84
84
  retrieving: true,
85
- templating: false,
85
+ templating: true,
86
86
  },
87
87
  'DeliveryProfile.DomainType': {
88
88
  isCreateable: false,
@@ -130,13 +130,13 @@ module.exports = {
130
130
  isCreateable: true,
131
131
  isUpdateable: true,
132
132
  retrieving: true,
133
- templating: false,
133
+ templating: true,
134
134
  },
135
135
  'DeliveryProfile.PrivateIP': {
136
136
  isCreateable: true,
137
137
  isUpdateable: true,
138
138
  retrieving: true,
139
- templating: false,
139
+ templating: true,
140
140
  },
141
141
  'DeliveryProfile.SourceAddressType': {
142
142
  isCreateable: false,
@@ -154,7 +154,7 @@ module.exports = {
154
154
  isCreateable: true,
155
155
  isUpdateable: true,
156
156
  retrieving: true,
157
- templating: false,
157
+ templating: true,
158
158
  },
159
159
  DomainType: {
160
160
  isCreateable: false,
@@ -166,13 +166,13 @@ module.exports = {
166
166
  isCreateable: true,
167
167
  isUpdateable: true,
168
168
  retrieving: true,
169
- templating: false,
169
+ templating: true,
170
170
  },
171
171
  'Email.ID': {
172
172
  isCreateable: true,
173
173
  isUpdateable: true,
174
174
  retrieving: true,
175
- templating: false,
175
+ templating: true,
176
176
  },
177
177
  'Email.ObjectID': {
178
178
  isCreateable: false,
@@ -186,17 +186,35 @@ module.exports = {
186
186
  retrieving: false,
187
187
  templating: false,
188
188
  },
189
+ 'Email.Name': {
190
+ isCreateable: true,
191
+ isUpdateable: true,
192
+ retrieving: false,
193
+ templating: false,
194
+ },
195
+ 'Email.Subject': {
196
+ isCreateable: true,
197
+ isUpdateable: true,
198
+ retrieving: false,
199
+ templating: false,
200
+ },
201
+ 'Email.Status': {
202
+ isCreateable: true,
203
+ isUpdateable: true,
204
+ retrieving: false,
205
+ templating: false,
206
+ },
189
207
  EmailSubject: {
190
208
  isCreateable: true,
191
209
  isUpdateable: true,
192
210
  retrieving: true,
193
- templating: false,
211
+ templating: true,
194
212
  },
195
213
  ExclusionFilter: {
196
214
  isCreateable: true,
197
215
  isUpdateable: true,
198
216
  retrieving: true,
199
- templating: false,
217
+ templating: true,
200
218
  },
201
219
  FooterContentArea: {
202
220
  isCreateable: false,
@@ -256,7 +274,7 @@ module.exports = {
256
274
  isCreateable: true,
257
275
  isUpdateable: true,
258
276
  retrieving: true,
259
- templating: false,
277
+ templating: true,
260
278
  },
261
279
  IsPlatformObject: {
262
280
  isCreateable: true,
@@ -280,7 +298,7 @@ module.exports = {
280
298
  isCreateable: true,
281
299
  isUpdateable: true,
282
300
  retrieving: true,
283
- templating: false,
301
+ templating: true,
284
302
  },
285
303
  Keyword: {
286
304
  isCreateable: false,
@@ -295,8 +313,8 @@ module.exports = {
295
313
  templating: false,
296
314
  },
297
315
  ModifiedDate: {
298
- isCreateable: true,
299
- isUpdateable: true,
316
+ isCreateable: false,
317
+ isUpdateable: false,
300
318
  retrieving: true,
301
319
  templating: false,
302
320
  },
@@ -304,7 +322,7 @@ module.exports = {
304
322
  isCreateable: true,
305
323
  isUpdateable: true,
306
324
  retrieving: true,
307
- templating: false,
325
+ templating: true,
308
326
  },
309
327
  ObjectID: {
310
328
  isCreateable: false,
@@ -376,7 +394,7 @@ module.exports = {
376
394
  isCreateable: true,
377
395
  isUpdateable: true,
378
396
  retrieving: true,
379
- templating: false,
397
+ templating: true,
380
398
  },
381
399
  'SendClassification.ObjectID': {
382
400
  isCreateable: false,
@@ -400,7 +418,7 @@ module.exports = {
400
418
  isCreateable: true,
401
419
  isUpdateable: true,
402
420
  retrieving: true,
403
- templating: false,
421
+ templating: true,
404
422
  },
405
423
  'SendDefinitionList[].ObjectID': {
406
424
  isCreateable: false,
@@ -414,6 +432,9 @@ module.exports = {
414
432
  retrieving: false,
415
433
  templating: false,
416
434
  },
435
+ 'SendDefinitionList[].CustomObjectID': {
436
+ skipValidation: true, // cannot be asked for but will be retrieved
437
+ },
417
438
  'SendDefinitionList[].SendDefinitionListType': {
418
439
  skipValidation: true, // cannot be asked for but will be retrieved
419
440
  },
@@ -448,7 +469,7 @@ module.exports = {
448
469
  isCreateable: true,
449
470
  isUpdateable: true,
450
471
  retrieving: true,
451
- templating: false,
472
+ templating: true,
452
473
  },
453
474
  'SenderProfile.FromAddress': {
454
475
  isCreateable: false,
@@ -478,13 +499,13 @@ module.exports = {
478
499
  isCreateable: true,
479
500
  isUpdateable: true,
480
501
  retrieving: true,
481
- templating: false,
502
+ templating: true,
482
503
  },
483
504
  SendWindowClose: {
484
505
  isCreateable: true,
485
506
  isUpdateable: true,
486
507
  retrieving: true,
487
- templating: false,
508
+ templating: true,
488
509
  },
489
510
  SendWindowDelete: {
490
511
  isCreateable: false,
@@ -496,7 +517,7 @@ module.exports = {
496
517
  isCreateable: true,
497
518
  isUpdateable: true,
498
519
  retrieving: true,
499
- templating: false,
520
+ templating: true,
500
521
  },
501
522
  SourceAddressType: {
502
523
  isCreateable: false,
@@ -508,13 +529,13 @@ module.exports = {
508
529
  isCreateable: true,
509
530
  isUpdateable: true,
510
531
  retrieving: true,
511
- templating: false,
532
+ templating: true,
512
533
  },
513
534
  TestEmailAddr: {
514
535
  isCreateable: true,
515
536
  isUpdateable: true,
516
537
  retrieving: true,
517
- templating: false,
538
+ templating: true,
518
539
  },
519
540
  TimeZone: {
520
541
  isCreateable: false,
package/lib/util/auth.js CHANGED
@@ -101,7 +101,7 @@ function setupSDK(sessionKey, authObject) {
101
101
  credentialStore.set(sessionKey, authObject);
102
102
  },
103
103
  onConnectionError: (ex, remainingAttempts) => {
104
- Util.logger.warn(
104
+ Util.logger.info(
105
105
  ` - Connection problem (Code: ${ex.code}). Retrying ${remainingAttempts} time${
106
106
  remainingAttempts > 1 ? 's' : ''
107
107
  }${
@@ -111,6 +111,8 @@ const DevOps = {
111
111
  // Filter to only handle files that start with at least one of the passed filterPaths.
112
112
  // ! Filter happens after initial parse, because if file was moved its new path has to be calculated
113
113
  .filter((file) => filterPaths.some((path) => file.file.startsWith(path)))
114
+ .filter((file) => !file.file.endsWith('.error.log'))
115
+ .filter((file) => !file.file.endsWith('.md'))
114
116
  // ensure badly named files on unsupported metadata types are not in our subset
115
117
  .filter((/** @type {TYPE.DeltaPkgItem} */ file) => {
116
118
  if (MetadataType[file.type]) {
@@ -210,10 +212,12 @@ const DevOps = {
210
212
  const copied = delta.map((file) =>
211
213
  File.copyFile(
212
214
  file.file,
213
- file.file.replace(
214
- path.normalize(properties.directories.retrieve),
215
- path.normalize(properties.directories.deploy)
216
- )
215
+ path
216
+ .normalize(file.file)
217
+ .replace(
218
+ path.normalize(properties.directories.retrieve),
219
+ path.normalize(properties.directories.deploy)
220
+ )
217
221
  )
218
222
  );
219
223
  const results = await Promise.all(copied);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcdev",
3
- "version": "4.3.1",
3
+ "version": "4.3.2",
4
4
  "description": "Accenture Salesforce Marketing Cloud DevTools",
5
5
  "author": "Accenture: joern.berkefeld, douglas.midgley, robert.zimmermann, maciej.barnas",
6
6
  "license": "MIT",
@@ -74,5 +74,5 @@
74
74
  "triggeredSendDefinition"
75
75
  ]
76
76
  },
77
- "version": "4.3.1"
77
+ "version": "4.3.2"
78
78
  }