mcdev 4.3.0 → 4.3.1

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.
@@ -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.1
42
43
  - 4.3.0
43
44
  - 4.2.1
44
45
  - 4.2.0
@@ -849,7 +849,7 @@ FileTransfer MetadataType
849
849
  * [.retrieveAsTemplate(templateDir, name, templateVariables, [selectedSubType])](#Asset.retrieveAsTemplate) ⇒ <code>Promise.&lt;{metadata: TYPE.AssetItem, type: string}&gt;</code>
850
850
  * [.create(metadata)](#Asset.create) ⇒ <code>Promise</code>
851
851
  * [.update(metadata)](#Asset.update) ⇒ <code>Promise</code>
852
- * [.requestSubType(subType, subTypeArray, [retrieveDir], [templateName], [templateVariables], key)](#Asset.requestSubType) ⇒ <code>Promise</code>
852
+ * [.requestSubType(subType, [retrieveDir], [templateName], [templateVariables], key)](#Asset.requestSubType) ⇒ <code>Promise</code>
853
853
  * [.requestAndSaveExtended(items, subType, retrieveDir, [templateVariables])](#Asset.requestAndSaveExtended) ⇒ <code>Promise</code>
854
854
  * [._retrieveExtendedFile(metadata, subType, retrieveDir)](#Asset._retrieveExtendedFile) ⇒ <code>Promise.&lt;void&gt;</code>
855
855
  * [._readExtendedFileFromFS(metadata, subType, deployDir, [pathOnly])](#Asset._readExtendedFileFromFS) ⇒ <code>Promise.&lt;string&gt;</code>
@@ -939,7 +939,7 @@ Updates a single asset
939
939
 
940
940
  <a name="Asset.requestSubType"></a>
941
941
 
942
- ### Asset.requestSubType(subType, subTypeArray, [retrieveDir], [templateName], [templateVariables], key) ⇒ <code>Promise</code>
942
+ ### Asset.requestSubType(subType, [retrieveDir], [templateName], [templateVariables], key) ⇒ <code>Promise</code>
943
943
  Retrieves Metadata of a specific asset type
944
944
 
945
945
  **Kind**: static method of [<code>Asset</code>](#Asset)
@@ -948,7 +948,6 @@ Retrieves Metadata of a specific asset type
948
948
  | Param | Type | Description |
949
949
  | --- | --- | --- |
950
950
  | subType | <code>TYPE.AssetSubType</code> | group of similar assets to put in a folder (ie. images) |
951
- | subTypeArray | <code>Array.&lt;TYPE.AssetSubType&gt;</code> | list of all asset types within this subtype |
952
951
  | [retrieveDir] | <code>string</code> | target directory for saving assets |
953
952
  | [templateName] | <code>string</code> | name of the metadata file |
954
953
  | [templateVariables] | <code>TYPE.TemplateMap</code> | variables to be replaced in the metadata |
package/lib/Retriever.js CHANGED
@@ -62,29 +62,33 @@ class Retriever {
62
62
  // ensure we know which real dependencies we have to ensure we cache those completely
63
63
  const dependencies = this._getTypeDependencies(metadataTypes);
64
64
  const deployOrder = Util.getMetadataHierachy(metadataTypes);
65
- for (const metadataType in deployOrder) {
66
- const type = metadataType;
67
- const subTypeArr = deployOrder[metadataType];
65
+ for (const type in deployOrder) {
66
+ const subTypeArr = deployOrder[type];
68
67
  // if types were added by getMetadataHierachy() for caching, make sure the key-list is set to [null] for them which will retrieve all
69
- typeKeyMap[metadataType] = typeKeyMap[metadataType] || [null];
68
+ typeKeyMap[type] = typeKeyMap[type] || [null];
70
69
  // add client to metadata process class instead of passing every time
71
70
  MetadataTypeInfo[type].client = auth.getSDK(this.buObject);
72
71
  MetadataTypeInfo[type].properties = this.properties;
73
72
  MetadataTypeInfo[type].buObject = this.buObject;
74
73
  try {
75
74
  let result;
76
- if (!metadataTypes.includes(type) && !metadataTypes.includes(metadataType)) {
75
+ if (
76
+ !metadataTypes.includes(type) &&
77
+ (!Array.isArray(subTypeArr) ||
78
+ (Array.isArray(subTypeArr) &&
79
+ !metadataTypes.includes(`${type}-${subTypeArr?.[0]}`)))
80
+ ) {
77
81
  // type not in list of types to retrieve, but is a dependency of one of them
78
82
  if (changelogOnly && type !== 'folder') {
79
83
  // no extra caching needed for list view except for folders
80
84
  continue;
81
85
  }
82
- Util.logger.info(`Caching dependent Metadata: ${metadataType}`);
86
+ Util.logger.info(`Caching dependent Metadata: ${type}`);
83
87
  Util.logSubtypes(subTypeArr);
84
88
  result = await MetadataTypeInfo[type].retrieveForCache(null, subTypeArr);
85
89
  } else if (templateVariables) {
86
90
  // type is in list of types to retrieve and we have template variables
87
- Util.logger.info(`Retrieving as Template: ${metadataType}`);
91
+ Util.logger.info(`Retrieving as Template: ${type}`);
88
92
  if (subTypeArr?.length > 1) {
89
93
  Util.logger.warn(
90
94
  `retrieveAsTemplate only works with one subtype, ignoring all but first subtype from your list: ${subTypeArr.join(
@@ -93,7 +97,7 @@ class Retriever {
93
97
  );
94
98
  }
95
99
  result = await Promise.all(
96
- typeKeyMap[metadataType].map((name) =>
100
+ typeKeyMap[type].map((name) =>
97
101
  MetadataTypeInfo[type].retrieveAsTemplate(
98
102
  this.templateDir,
99
103
  name,
@@ -106,12 +110,11 @@ class Retriever {
106
110
  // type is in list of types to retrieve and we don't have template variables
107
111
  let cacheResult = null;
108
112
  if (
109
- (typeKeyMap[metadataType].length > 1 ||
110
- typeKeyMap[metadataType][0] !== null) &&
111
- (dependencies.includes(type) || dependencies.includes(metadataType))
113
+ (typeKeyMap[type].length > 1 || typeKeyMap[type][0] !== null) &&
114
+ (dependencies.includes(type) || dependencies.includes(type))
112
115
  ) {
113
116
  // if we have a key-list and the type is a dependency, we need to cache the whole type
114
- Util.logger.info(`Caching dependent Metadata: ${metadataType}`);
117
+ Util.logger.info(`Caching dependent Metadata: ${type}`);
115
118
  Util.logSubtypes(subTypeArr);
116
119
  cacheResult = await MetadataTypeInfo[type].retrieveForCache(
117
120
  null,
@@ -119,15 +122,15 @@ class Retriever {
119
122
  );
120
123
  }
121
124
  Util.logger.info(
122
- `Retrieving: ${metadataType}` +
123
- (typeKeyMap[metadataType][0] === null
125
+ `Retrieving: ${type}` +
126
+ (typeKeyMap[type][0] === null
124
127
  ? ''
125
- : Util.getKeysString(typeKeyMap[metadataType]))
128
+ : Util.getKeysString(typeKeyMap[type]))
126
129
  );
127
130
  result = await (changelogOnly
128
131
  ? MetadataTypeInfo[type].retrieveChangelog(null, subTypeArr)
129
132
  : Promise.all(
130
- typeKeyMap[metadataType].map((key) =>
133
+ typeKeyMap[type].map((key) =>
131
134
  MetadataTypeInfo[type].retrieve(
132
135
  this.savePath,
133
136
  null,
@@ -154,14 +157,14 @@ class Retriever {
154
157
  cache.mergeMetadata(type, result_i.metadata);
155
158
  }
156
159
  }
157
- if (metadataTypes.includes(type) || metadataTypes.includes(metadataType)) {
160
+ if (metadataTypes.includes(type) || metadataTypes.includes(type)) {
158
161
  retrieveChangelog[type] = result
159
162
  .filter((el) => !!el)
160
163
  .map((element) => element.metadata);
161
164
  }
162
165
  } else {
163
166
  cache.setMetadata(type, result.metadata);
164
- if (metadataTypes.includes(type) || metadataTypes.includes(metadataType)) {
167
+ if (metadataTypes.includes(type) || metadataTypes.includes(type)) {
165
168
  retrieveChangelog[type] = result.metadata;
166
169
  }
167
170
  }
@@ -175,7 +178,7 @@ class Retriever {
175
178
  Util.logger.error(ex.message);
176
179
  break;
177
180
  } else {
178
- Util.logger.errorStack(ex, ` - Retrieving ${metadataType} failed`);
181
+ Util.logger.errorStack(ex, ` - Retrieving ${type} failed`);
179
182
  }
180
183
  }
181
184
  }
@@ -31,16 +31,7 @@ class Asset extends MetadataType {
31
31
  for (const subType of subTypeArr) {
32
32
  // each subtype contains multiple different specific types (images contains jpg and png for example)
33
33
  // we use await here to limit the risk of too many concurrent api requests at time
34
- items.push(
35
- ...(await this.requestSubType(
36
- subType,
37
- this.definition.extendedSubTypes[subType],
38
- retrieveDir,
39
- null,
40
- null,
41
- key
42
- ))
43
- );
34
+ items.push(...(await this.requestSubType(subType, retrieveDir, null, null, key)));
44
35
  }
45
36
  const metadata = this.parseResponseBody({ items: items });
46
37
  if (retrieveDir) {
@@ -81,13 +72,7 @@ class Asset extends MetadataType {
81
72
  // each subtype contains multiple different specific types (images contains jpg and png for example)
82
73
  // we use await here to limit the risk of too many concurrent api requests at time
83
74
  items.push(
84
- ...(await this.requestSubType(
85
- subType,
86
- this.definition.extendedSubTypes[subType],
87
- templateDir,
88
- name,
89
- templateVariables
90
- ))
75
+ ...(await this.requestSubType(subType, templateDir, name, templateVariables))
91
76
  );
92
77
  }
93
78
  const metadata = this.parseResponseBody({ items: items });
@@ -146,27 +131,21 @@ class Asset extends MetadataType {
146
131
  * Retrieves Metadata of a specific asset type
147
132
  *
148
133
  * @param {TYPE.AssetSubType} subType group of similar assets to put in a folder (ie. images)
149
- * @param {TYPE.AssetSubType[]} subTypeArray list of all asset types within this subtype
150
134
  * @param {string} [retrieveDir] target directory for saving assets
151
135
  * @param {string} [templateName] name of the metadata file
152
136
  * @param {TYPE.TemplateMap} [templateVariables] variables to be replaced in the metadata
153
137
  * @param {string} key customer key to filter by
154
138
  * @returns {Promise} Promise
155
139
  */
156
- static async requestSubType(
157
- subType,
158
- subTypeArray,
159
- retrieveDir,
160
- templateName,
161
- templateVariables,
162
- key
163
- ) {
140
+ static async requestSubType(subType, retrieveDir, templateName, templateVariables, key) {
164
141
  if (retrieveDir) {
165
142
  Util.logger.info(`- Retrieving Subtype: ${subType}`);
166
143
  } else {
167
144
  Util.logger.info(` - Caching Subtype: ${subType}`);
168
145
  }
169
- const subtypeIds = subTypeArray?.map(
146
+ /** @type {TYPE.AssetSubType[]} */
147
+ const extendedSubTypeArr = this.definition.extendedSubTypes[subType];
148
+ const subtypeIds = extendedSubTypeArr?.map(
170
149
  (subTypeItemName) => Asset.definition.typeMapping[subTypeItemName]
171
150
  );
172
151
  const uri = '/asset/v1/content/assets/query';
@@ -977,6 +956,7 @@ class Asset extends MetadataType {
977
956
 
978
957
  break;
979
958
  }
959
+ case 'template': // template-template
980
960
  case 'buttonblock': // block - Button Block
981
961
  case 'freeformblock': // block
982
962
  case 'htmlblock': // block
@@ -1198,6 +1178,7 @@ class Asset extends MetadataType {
1198
1178
  subFolder: [customerKey],
1199
1179
  };
1200
1180
  }
1181
+ case 'template': // template-template
1201
1182
  case 'buttonblock': // block - Button Block
1202
1183
  case 'freeformblock': // block
1203
1184
  case 'htmlblock': // block
@@ -63,7 +63,7 @@ class Automation extends MetadataType {
63
63
  }
64
64
  // if we do get here, we should log the error and continue instead of failing to download all automations
65
65
  Util.logger.error(
66
- ` - skipping retrieving Automation ${a.ObjectID}: ${ex.message} ${ex.code}`
66
+ ` skipping Automation ${a.ObjectID}: ${ex.message} ${ex.code}`
67
67
  );
68
68
  return null;
69
69
  }
@@ -182,33 +182,35 @@ class EmailSendDefinition extends MetadataType {
182
182
  super.setFolderPath(metadata);
183
183
 
184
184
  // email
185
- try {
186
- // classic
187
- const classicEmail = cache.searchForField('email', metadata.Email.ID, 'ID', 'Name');
188
- metadata.r__email_Name = classicEmail;
189
- delete metadata.Email;
190
- } catch {
185
+ if (metadata.Email?.ID) {
191
186
  try {
192
- // content builder
193
- const contentBuilderEmailName = cache.searchForField(
194
- 'asset',
195
- metadata.Email.ID,
196
- 'legacyData.legacyId',
197
- 'name'
198
- );
199
- metadata.r__assetMessage_Name = contentBuilderEmailName;
200
- const contentBuilderEmailKey = cache.searchForField(
201
- 'asset',
202
- metadata.Email.ID,
203
- 'legacyData.legacyId',
204
- 'customerKey'
205
- );
206
- metadata.r__assetMessage_Key = contentBuilderEmailKey;
187
+ // classic
188
+ const classicEmail = cache.searchForField('email', metadata.Email.ID, 'ID', 'Name');
189
+ metadata.r__email_Name = classicEmail;
207
190
  delete metadata.Email;
208
191
  } catch {
209
- Util.logger.warn(
210
- ` - ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': Could not find email with ID ${metadata.Email.ID} in Classic nor in Content Builder.`
211
- );
192
+ try {
193
+ // content builder
194
+ const contentBuilderEmailName = cache.searchForField(
195
+ 'asset',
196
+ metadata.Email.ID,
197
+ 'legacyData.legacyId',
198
+ 'name'
199
+ );
200
+ metadata.r__assetMessage_Name = contentBuilderEmailName;
201
+ const contentBuilderEmailKey = cache.searchForField(
202
+ 'asset',
203
+ metadata.Email.ID,
204
+ 'legacyData.legacyId',
205
+ 'customerKey'
206
+ );
207
+ metadata.r__assetMessage_Key = contentBuilderEmailKey;
208
+ delete metadata.Email;
209
+ } catch {
210
+ 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.`
212
+ );
213
+ }
212
214
  }
213
215
  }
214
216
  // Target Audience DataExtension
@@ -237,13 +239,15 @@ class EmailSendDefinition extends MetadataType {
237
239
  }
238
240
  }
239
241
  // List
240
- try {
241
- sdl.r__list_PathName = cache.getListPathName(sdl.List.ID, 'ID');
242
- delete sdl.List;
243
- } catch (ex) {
244
- Util.logger.warn(
245
- ` - ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': ${ex.message}`
246
- );
242
+ if (sdl.List?.ID) {
243
+ try {
244
+ sdl.r__list_PathName = cache.getListPathName(sdl.List.ID, 'ID');
245
+ delete sdl.List;
246
+ } catch (ex) {
247
+ Util.logger.warn(
248
+ ` - ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': ${ex.message}`
249
+ );
250
+ }
247
251
  }
248
252
  }
249
253
 
@@ -27,6 +27,7 @@ class Interaction extends MetadataType {
27
27
  * @returns {Promise.<TYPE.MetadataTypeMapObj>} Promise
28
28
  */
29
29
  static async retrieve(retrieveDir, _, __, key) {
30
+ const extrasDefault = 'activities';
30
31
  if (retrieveDir) {
31
32
  // only print this during retrieve, not during retrieveForCache
32
33
  Util.logBeta(this.definition.type);
@@ -59,17 +60,81 @@ class Interaction extends MetadataType {
59
60
  }
60
61
  /* eslint-enable unicorn/prefer-ternary */
61
62
  }
62
- // full details for retrieve, only base data for caching; reduces caching time from minutes to seconds
63
- const extras = retrieveDir ? 'all' : '';
64
63
 
65
64
  try {
66
- return await super.retrieveREST(
67
- retrieveDir,
68
- `/interaction/v1/interactions/${singleKey}?extras=${extras}`,
69
- null,
70
- null,
71
- key
72
- );
65
+ const uri = `/interaction/v1/interactions/`;
66
+ if (singleKey || !retrieveDir) {
67
+ // full details for retrieve, only base data for caching; reduces caching time from minutes to seconds
68
+ const extras = retrieveDir && singleKey ? extrasDefault : '';
69
+
70
+ // caching or single retrieve
71
+ return await super.retrieveREST(
72
+ retrieveDir,
73
+ `${uri}${singleKey}?extras=${extras}`,
74
+ null,
75
+ null,
76
+ key
77
+ );
78
+ } else {
79
+ // retrieve all
80
+ const results = this.definition.restPagination
81
+ ? await this.client.rest.getBulk(uri, this.definition.restPageSize || 500)
82
+ : await this.client.rest.get(uri);
83
+ // const results = this.parseResponseBody(response);
84
+ if (results.items?.length) {
85
+ // empty results will come back without "items" defined
86
+ Util.logger.info(
87
+ Util.getGrayMsg(
88
+ ` - ${results.items?.length} ${this.definition.type}s found. Retrieving details...`
89
+ )
90
+ );
91
+ }
92
+ // full details for retrieve
93
+ const extras = extrasDefault;
94
+
95
+ const details = results.items
96
+ ? await Promise.all(
97
+ results.items.map(async (a) => {
98
+ try {
99
+ return await this.client.rest.get(
100
+ `${uri}key:${a[this.definition.keyField]}?extras=${extras}`
101
+ );
102
+ } catch (ex) {
103
+ // if we do get here, we should log the error and continue instead of failing to download all automations
104
+ Util.logger.error(
105
+ ` ☇ skipping ${this.definition.type} ${
106
+ a[this.definition.nameField]
107
+ } (${a[this.definition.keyField]}): ${ex.message} (${
108
+ ex.code
109
+ })${
110
+ ex.endpoint
111
+ ? Util.getGrayMsg(
112
+ ' - ' +
113
+ ex.endpoint.split(
114
+ 'rest.marketingcloudapis.com'
115
+ )[1]
116
+ )
117
+ : ''
118
+ }`
119
+ );
120
+ return null;
121
+ }
122
+ })
123
+ )
124
+ : [];
125
+ const parsed = this.parseResponseBody({ items: details.filter(Boolean) });
126
+
127
+ // * retrieveDir is mandatory in this method as it is not used for caching (there is a seperate method for that)
128
+ const savedMetadata = await this.saveResults(parsed, retrieveDir, null, null);
129
+ Util.logger.info(
130
+ `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` +
131
+ Util.getKeysString(key)
132
+ );
133
+ return {
134
+ metadata: parsed,
135
+ type: this.definition.type,
136
+ };
137
+ }
73
138
  } catch (ex) {
74
139
  // if the interaction does not exist, the API returns an error code which would otherwise bring execution to a hold
75
140
  if (
@@ -15,7 +15,7 @@ module.exports = {
15
15
  lastmodDateField: 'modifiedDate',
16
16
  lastmodNameField: null,
17
17
  restPagination: true,
18
- restPageSize: 250,
18
+ restPageSize: 500,
19
19
  type: 'interaction',
20
20
  typeDescription: 'Journey (internally called "Interaction").',
21
21
  typeRetrieveByDefault: true,
@@ -41,8 +41,8 @@ module.exports = {
41
41
  template: false,
42
42
  },
43
43
  definitionId: {
44
- isCreateable: true,
45
- isUpdateable: true,
44
+ isCreateable: false,
45
+ isUpdateable: false,
46
46
  retrieving: false,
47
47
  template: false,
48
48
  },
@@ -41,8 +41,8 @@ module.exports = {
41
41
  template: false,
42
42
  },
43
43
  definitionId: {
44
- isCreateable: true,
45
- isUpdateable: true,
44
+ isCreateable: false,
45
+ isUpdateable: false,
46
46
  retrieving: false,
47
47
  template: false,
48
48
  },
@@ -41,8 +41,8 @@ module.exports = {
41
41
  template: false,
42
42
  },
43
43
  definitionId: {
44
- isCreateable: true,
45
- isUpdateable: true,
44
+ isCreateable: false,
45
+ isUpdateable: false,
46
46
  retrieving: false,
47
47
  template: false,
48
48
  },
package/lib/util/auth.js CHANGED
@@ -114,8 +114,13 @@ function setupSDK(sessionKey, authObject) {
114
114
  );
115
115
  Util.logger.errorStack(ex);
116
116
  },
117
- // logRequest: (req) => console.log('request', req), // uncomment to log requests
118
- // logResponse: (res) => console.log('response:', res), // uncomment to log responses
117
+ // logRequest: (req) =>
118
+ // console.log(`${Util.color.fgMagenta}request${Util.color.reset}`, req), // uncomment to log requests
119
+ // logResponse: (res) =>
120
+ // console.log(
121
+ // `${Util.color.bgMagenta}response${Util.color.reset}:`,
122
+ // typeof res.data == 'string' && res.data.length > 10000 ? res.data : res
123
+ // ), // uncomment to log responses
119
124
  },
120
125
  requestAttempts: 4,
121
126
  retryOnConnectionError: true,
package/lib/util/util.js CHANGED
@@ -364,21 +364,38 @@ const Util = {
364
364
  // group subtypes per type
365
365
  for (const type of flatList) {
366
366
  if (type.includes('-')) {
367
- const key = type.split('-')[0];
368
- if (finalList[key]) {
367
+ const [key, subKey] = Util.getTypeAndSubType(type);
368
+ if (finalList[key] === null) {
369
+ // if main type is already required, then don't filter by subtypes
369
370
  continue;
371
+ } else if (finalList[key] && subKey) {
372
+ // add another subtype to the set
373
+ finalList[key].add(subKey);
374
+ continue;
375
+ } else {
376
+ // create a new set with the first subtype; subKey will be always set here
377
+ finalList[key] = new Set([subKey]);
378
+ }
379
+ if (subTypeDeps[key]) {
380
+ // check if there are depndent subtypes that need to be added
381
+ const temp = [...subTypeDeps[key]].map((a) => {
382
+ const temp = a.split('-');
383
+ temp.shift(); // remove first item which is the main type
384
+ return temp.join('-'); // subType can include "-"
385
+ });
386
+ finalList[key].add(...temp);
370
387
  }
371
- finalList[key] = subTypeDeps[key]
372
- ? [...subTypeDeps[key]].map((a) => {
373
- const temp = a.split('-');
374
- temp.shift(); // remove first item which is the main type
375
- return temp.join('-'); // subType can include "-"
376
- })
377
- : null;
378
388
  } else {
379
389
  finalList[type] = null;
380
390
  }
381
391
  }
392
+ // convert sets into arrays
393
+ for (const type of Object.keys(finalList)) {
394
+ if (finalList[type] instanceof Set) {
395
+ finalList[type] = [...finalList[type]];
396
+ }
397
+ }
398
+
382
399
  return finalList;
383
400
  },
384
401
 
@@ -487,6 +504,31 @@ const Util = {
487
504
  color: {
488
505
  reset: '\x1B[0m',
489
506
  dim: '\x1B[2m',
507
+ bright: '\x1B[1m',
508
+ underscore: '\x1B[4m',
509
+ blink: '\x1B[5m',
510
+ reverse: '\x1B[7m',
511
+ hidden: '\x1B[8m',
512
+
513
+ fgBlack: '\x1B[30m',
514
+ fgRed: '\x1B[31m',
515
+ fgGreen: '\x1B[32m',
516
+ fgYellow: '\x1B[33m',
517
+ fgBlue: '\x1B[34m',
518
+ fgMagenta: '\x1B[35m',
519
+ fgCyan: '\x1B[36m',
520
+ fgWhite: '\x1B[37m',
521
+ fgGray: '\x1B[90m',
522
+
523
+ bgBlack: '\x1B[40m',
524
+ bgRed: '\x1B[41m',
525
+ bgGreen: '\x1B[42m',
526
+ bgYellow: '\x1B[43m',
527
+ bgBlue: '\x1B[44m',
528
+ bgMagenta: '\x1B[45m',
529
+ bgCyan: '\x1B[46m',
530
+ bgWhite: '\x1B[47m',
531
+ bgGray: '\x1B[100m',
490
532
  },
491
533
  /**
492
534
  * helper that wraps a message in the correct color codes to have them printed gray
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcdev",
3
- "version": "4.3.0",
3
+ "version": "4.3.1",
4
4
  "description": "Accenture Salesforce Marketing Cloud DevTools",
5
5
  "author": "Accenture: joern.berkefeld, douglas.midgley, robert.zimmermann, maciej.barnas",
6
6
  "license": "MIT",
@@ -50,6 +50,7 @@
50
50
  "lint-test": "eslint test/**/*.js",
51
51
  "upgrade": "npm-check --update",
52
52
  "manual-prepare": "husky install",
53
+ "lint-and-test": "run-s lint test",
53
54
  "test": "mocha",
54
55
  "coverage": "nyc mocha",
55
56
  "version:major": "npm version --no-commit-hooks major",
@@ -33,7 +33,7 @@ describe('interaction', () => {
33
33
  );
34
34
  assert.equal(
35
35
  testUtils.getAPIHistoryLength(),
36
- 7,
36
+ 9,
37
37
  'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests'
38
38
  );
39
39
  return;
@@ -114,7 +114,7 @@ describe('interaction', () => {
114
114
 
115
115
  assert.equal(
116
116
  testUtils.getAPIHistoryLength(),
117
- 7,
117
+ 9,
118
118
  'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests'
119
119
  );
120
120
  return;
@@ -74,5 +74,5 @@
74
74
  "triggeredSendDefinition"
75
75
  ]
76
76
  },
77
- "version": "4.3.0"
77
+ "version": "4.3.1"
78
78
  }
@@ -1,6 +1,7 @@
1
1
  const fs = require('fs-extra');
2
2
  const path = require('node:path');
3
3
  const { XMLParser } = require('fast-xml-parser');
4
+ const { color } = require('../lib/util/util');
4
5
  const parser = new XMLParser();
5
6
  const attributeParser = new XMLParser({ ignoreAttributes: false });
6
7
  /**
@@ -25,7 +26,11 @@ exports.loadSOAPRecords = async (mcdevAction, type, mid) => {
25
26
  encoding: 'utf8',
26
27
  });
27
28
  }
28
- console.log(`error: Please create file ${testPath}`); // eslint-disable-line no-console
29
+ /* eslint-disable no-console */
30
+ console.log(
31
+ `${color.bgRed}${color.fgBlack}test-error${color.reset}: Please create file ${testPath}`
32
+ );
33
+ /* eslint-enable no-console */
29
34
 
30
35
  return fs.readFile(path.join('test', 'resources', mcdevAction + '-response.xml'), {
31
36
  encoding: 'utf8',
@@ -98,13 +103,15 @@ exports.handleRESTRequest = async (config) => {
98
103
  if (urlObj.searchParams.get('$filter')) {
99
104
  filterName = urlObj.searchParams.get('$filter').split(' eq ')[1];
100
105
  }
101
- const testPath = path.join(
102
- 'test',
103
- 'resources',
104
- config.headers.Authorization.replace('Bearer ', ''),
105
- urlObj.pathname,
106
- config.method + '-response.json'
107
- );
106
+ const testPath = path
107
+ .join(
108
+ 'test',
109
+ 'resources',
110
+ config.headers.Authorization.replace('Bearer ', ''),
111
+ urlObj.pathname,
112
+ config.method + '-response.json'
113
+ )
114
+ .replace(':', '_'); // replace : with _ for Windows
108
115
 
109
116
  if (await fs.pathExists(testPath)) {
110
117
  // build filter logic to ensure templating works
@@ -126,7 +133,11 @@ exports.handleRESTRequest = async (config) => {
126
133
  ];
127
134
  }
128
135
  } else {
129
- console.log(`error: Please create file ${testPath}`); // eslint-disable-line no-console
136
+ /* eslint-disable no-console */
137
+ console.log(
138
+ `${color.bgRed}${color.fgBlack}test-error${color.reset}: Please create file ${testPath}`
139
+ );
140
+ /* eslint-enable no-console */
130
141
 
131
142
  return [
132
143
  404,
@@ -0,0 +1,219 @@
1
+ {
2
+ "id": "dsfdsafdsa-922c-4568-85a5-e5cc77efc3be",
3
+ "key": "0b76dccf-594c-b6dc-1acf-10c4493dcb84",
4
+ "name": "testExisting_temail",
5
+ "lastPublishedDate": "0001-01-01T00:00:00",
6
+ "description": "",
7
+ "version": 1,
8
+ "workflowApiVersion": 1,
9
+ "createdDate": "2022-03-24T02:20:32.74",
10
+ "modifiedDate": "2022-03-24T02:20:40.45",
11
+ "activities": [
12
+ {
13
+ "id": "9606bcb0-9246-4610-9800-963bc77fc20a",
14
+ "key": "EMAILV2-4",
15
+ "name": "testExisting_temail",
16
+ "description": "",
17
+ "type": "EMAILV2",
18
+ "outcomes": [
19
+ {
20
+ "key": "1f44021a-74ac-49be-a07c-67862228214d",
21
+ "arguments": {},
22
+ "metaData": {
23
+ "invalid": false
24
+ }
25
+ }
26
+ ],
27
+ "arguments": {},
28
+ "configurationArguments": {
29
+ "applicationExtensionKey": "jb-email-activity",
30
+ "isModified": true,
31
+ "triggeredSend": {
32
+ "autoAddSubscribers": true,
33
+ "autoUpdateSubscribers": true,
34
+ "bccEmail": "",
35
+ "ccEmail": "",
36
+ "created": {},
37
+ "domainExclusions": [],
38
+ "dynamicEmailSubject": "test email",
39
+ "emailId": 25923,
40
+ "emailSubject": "test email",
41
+ "exclusionFilter": "",
42
+ "isSalesforceTracking": true,
43
+ "isMultipart": true,
44
+ "isSendLogging": false,
45
+ "isStoppedOnJobError": false,
46
+ "modified": {},
47
+ "preHeader": "",
48
+ "priority": 4,
49
+ "sendClassificationId": "1284e9b2-a7b8-e711-80cf-1402ec721c9d",
50
+ "throttleOpens": "1/1/0001 12:00:00 AM",
51
+ "throttleCloses": "1/1/0001 12:00:00 AM",
52
+ "deliveryProfileId": "1084e9b2-a7b8-e711-80cf-1402ec721c9d",
53
+ "senderProfileId": "0f84e9b2-a7b8-e711-80cf-1402ec721c9d",
54
+ "isTrackingClicks": true,
55
+ "publicationListId": 15
56
+ },
57
+ "triggeredSendId": "0a650d90-755e-ed11-b852-48df37d1df5b",
58
+ "triggeredSendKey": "testExisting_temail"
59
+ },
60
+ "metaData": {
61
+ "highThroughput": {
62
+ "definitionKey": "testExisting_temail",
63
+ "dataExtensionId": "21711373-72c1-ec11-b83b-48df37d1deb7"
64
+ },
65
+ "sections": {},
66
+ "isConfigured": true
67
+ },
68
+ "schema": {
69
+ "arguments": {
70
+ "requestID": {
71
+ "dataType": "Text",
72
+ "isNullable": true,
73
+ "direction": "Out",
74
+ "readOnly": false,
75
+ "access": "Hidden"
76
+ },
77
+ "messageKey": {
78
+ "dataType": "Text",
79
+ "isNullable": true,
80
+ "direction": "Out",
81
+ "readOnly": false,
82
+ "access": "Hidden"
83
+ },
84
+ "activityId": {
85
+ "dataType": "Text",
86
+ "isNullable": true,
87
+ "direction": "In",
88
+ "readOnly": false,
89
+ "access": "Hidden"
90
+ },
91
+ "definitionId": {
92
+ "dataType": "Text",
93
+ "isNullable": true,
94
+ "direction": "In",
95
+ "readOnly": true,
96
+ "access": "Hidden"
97
+ },
98
+ "emailSubjectDataBound": {
99
+ "dataType": "Text",
100
+ "isNullable": true,
101
+ "direction": "In",
102
+ "readOnly": true,
103
+ "access": "Hidden"
104
+ },
105
+ "contactId": {
106
+ "dataType": "Number",
107
+ "isNullable": true,
108
+ "direction": "In",
109
+ "readOnly": false,
110
+ "access": "Hidden"
111
+ },
112
+ "contactKey": {
113
+ "dataType": "Text",
114
+ "isNullable": false,
115
+ "direction": "In",
116
+ "readOnly": false,
117
+ "access": "Hidden"
118
+ },
119
+ "emailAddress": {
120
+ "dataType": "Text",
121
+ "isNullable": false,
122
+ "direction": "In",
123
+ "readOnly": false,
124
+ "access": "Hidden"
125
+ },
126
+ "sourceCustomObjectId": {
127
+ "dataType": "Text",
128
+ "isNullable": true,
129
+ "direction": "In",
130
+ "readOnly": false,
131
+ "access": "Hidden"
132
+ },
133
+ "sourceCustomObjectKey": {
134
+ "dataType": "LongNumber",
135
+ "isNullable": true,
136
+ "direction": "In",
137
+ "readOnly": false,
138
+ "access": "Hidden"
139
+ },
140
+ "fieldType": {
141
+ "dataType": "Text",
142
+ "isNullable": true,
143
+ "direction": "In",
144
+ "readOnly": false,
145
+ "access": "Hidden"
146
+ },
147
+ "eventData": {
148
+ "dataType": "Text",
149
+ "isNullable": true,
150
+ "direction": "In",
151
+ "readOnly": false,
152
+ "access": "Hidden"
153
+ },
154
+ "obfuscationProperties": {
155
+ "dataType": "Text",
156
+ "isNullable": true,
157
+ "direction": "In",
158
+ "readOnly": false,
159
+ "access": "Hidden"
160
+ },
161
+ "customObjectKey": {
162
+ "dataType": "LongNumber",
163
+ "isNullable": true,
164
+ "direction": "In",
165
+ "readOnly": true,
166
+ "access": "Hidden"
167
+ },
168
+ "definitionInstanceId": {
169
+ "dataType": "Text",
170
+ "isNullable": false,
171
+ "direction": "In",
172
+ "readOnly": false,
173
+ "access": "Hidden"
174
+ }
175
+ }
176
+ }
177
+ }
178
+ ],
179
+ "triggers": [
180
+ {
181
+ "key": "TRIGGER",
182
+ "name": "TRIGGER",
183
+ "description": "",
184
+ "type": "transactional-api",
185
+ "outcomes": [],
186
+ "arguments": {},
187
+ "configurationArguments": {},
188
+ "metaData": {
189
+ "chainType": "none",
190
+ "configurationRequired": false,
191
+ "iconUrl": "/images/icon_journeyBuilder-event-transactional-blue.svg",
192
+ "title": "Transactional API Event",
193
+ "category": "Transactional",
194
+ "entrySourceGroupConfigUrl": "null"
195
+ }
196
+ }
197
+ ],
198
+ "goals": [],
199
+ "exits": [],
200
+ "notifiers": [],
201
+ "entryMode": "MultipleEntries",
202
+ "definitionType": "Transactional",
203
+ "channel": "email",
204
+ "defaults": {
205
+ "properties": {
206
+ "analyticsTracking": {
207
+ "enabled": false,
208
+ "analyticsType": "google",
209
+ "urlDomainsToTrack": []
210
+ }
211
+ }
212
+ },
213
+ "metaData": {},
214
+ "executionMode": "Production",
215
+ "categoryId": 6298,
216
+ "status": "Published",
217
+ "scheduledStatus": "Draft",
218
+ "definitionId": "dsfdsafdsa-922c-4568-85a5-e5cc77efc3be"
219
+ }
@@ -0,0 +1,280 @@
1
+ {
2
+ "id": "233d4413-922c-4568-85a5-e5cc77efc3be",
3
+ "key": "testExisting_interaction",
4
+ "name": "testExisting_interaction",
5
+ "lastPublishedDate": "2017-04-12T08:07:48",
6
+ "description": "bla bla",
7
+ "version": 1,
8
+ "workflowApiVersion": 1,
9
+ "createdDate": "2017-04-12T05:38:05.837",
10
+ "modifiedDate": "2017-04-12T08:07:48.333",
11
+ "activities": [
12
+ {
13
+ "id": "69213026-bd2c-433b-8332-5f52d3e87ca5",
14
+ "key": "WAIT-1",
15
+ "name": "",
16
+ "description": "",
17
+ "type": "WAIT",
18
+ "outcomes": [
19
+ {
20
+ "key": "bd3dff6b-565c-4b56-b9cb-60cd5b6d080b",
21
+ "next": "DATAEXTENSIONUPDATE-1",
22
+ "arguments": {},
23
+ "metaData": {}
24
+ }
25
+ ],
26
+ "arguments": {
27
+ "waitDefinitionId": "546704c0-9384-4a41-b20e-97615cc6cc6a",
28
+ "waitForEventId": "",
29
+ "executionMode": "{{Context.ExecutionMode}}",
30
+ "startActivityKey": "{{Context.StartActivityKey}}",
31
+ "waitQueueId": "{{Context.WaitQueueId}}"
32
+ },
33
+ "configurationArguments": {
34
+ "waitDuration": 1,
35
+ "waitUnit": "DAYS",
36
+ "specifiedTime": "",
37
+ "timeZone": "",
38
+ "description": "",
39
+ "waitForEventKey": ""
40
+ },
41
+ "metaData": { "waitType": "duration" },
42
+ "schema": {
43
+ "arguments": {
44
+ "endDate": {
45
+ "dataType": "Date",
46
+ "isNullable": false,
47
+ "direction": "Out",
48
+ "readOnly": false,
49
+ "access": "Hidden"
50
+ },
51
+ "waitEndDateAttributeDataBound": {
52
+ "dataType": "Text",
53
+ "isNullable": true,
54
+ "direction": "In",
55
+ "readOnly": false,
56
+ "access": "Hidden"
57
+ },
58
+ "waitDefinitionId": {
59
+ "dataType": "Text",
60
+ "isNullable": false,
61
+ "direction": "In",
62
+ "readOnly": false,
63
+ "access": "Hidden"
64
+ },
65
+ "waitForEventId": {
66
+ "dataType": "Text",
67
+ "isNullable": true,
68
+ "direction": "In",
69
+ "readOnly": false,
70
+ "access": "Hidden"
71
+ },
72
+ "executionMode": {
73
+ "dataType": "Text",
74
+ "isNullable": false,
75
+ "direction": "In",
76
+ "readOnly": false,
77
+ "access": "Hidden"
78
+ },
79
+ "startActivityKey": {
80
+ "dataType": "Text",
81
+ "isNullable": true,
82
+ "direction": "In",
83
+ "readOnly": false,
84
+ "access": "Hidden"
85
+ },
86
+ "waitQueueId": {
87
+ "dataType": "LongNumber",
88
+ "isNullable": true,
89
+ "direction": "In",
90
+ "readOnly": false,
91
+ "access": "Hidden"
92
+ }
93
+ }
94
+ }
95
+ },
96
+ {
97
+ "id": "5d93cda9-2015-4c07-a1db-0d5853d25bf6",
98
+ "key": "WAIT-2",
99
+ "name": "",
100
+ "description": "",
101
+ "type": "WAIT",
102
+ "outcomes": [
103
+ {
104
+ "key": "e3d3258a-891b-4838-b5a6-af37f8cb769a",
105
+ "arguments": {},
106
+ "metaData": {}
107
+ }
108
+ ],
109
+ "arguments": {
110
+ "waitDefinitionId": "deed4c9d-f487-4371-bfd9-76fca44ec49b",
111
+ "waitForEventId": "",
112
+ "executionMode": "{{Context.ExecutionMode}}",
113
+ "startActivityKey": "{{Context.StartActivityKey}}",
114
+ "waitQueueId": "{{Context.WaitQueueId}}"
115
+ },
116
+ "configurationArguments": {
117
+ "waitDuration": 1,
118
+ "waitUnit": "DAYS",
119
+ "specifiedTime": "",
120
+ "timeZone": "",
121
+ "description": "",
122
+ "waitForEventKey": ""
123
+ },
124
+ "metaData": { "waitType": "duration" },
125
+ "schema": {
126
+ "arguments": {
127
+ "endDate": {
128
+ "dataType": "Date",
129
+ "isNullable": false,
130
+ "direction": "Out",
131
+ "readOnly": false,
132
+ "access": "Hidden"
133
+ },
134
+ "waitEndDateAttributeDataBound": {
135
+ "dataType": "Text",
136
+ "isNullable": true,
137
+ "direction": "In",
138
+ "readOnly": false,
139
+ "access": "Hidden"
140
+ },
141
+ "waitDefinitionId": {
142
+ "dataType": "Text",
143
+ "isNullable": false,
144
+ "direction": "In",
145
+ "readOnly": false,
146
+ "access": "Hidden"
147
+ },
148
+ "waitForEventId": {
149
+ "dataType": "Text",
150
+ "isNullable": true,
151
+ "direction": "In",
152
+ "readOnly": false,
153
+ "access": "Hidden"
154
+ },
155
+ "executionMode": {
156
+ "dataType": "Text",
157
+ "isNullable": false,
158
+ "direction": "In",
159
+ "readOnly": false,
160
+ "access": "Hidden"
161
+ },
162
+ "startActivityKey": {
163
+ "dataType": "Text",
164
+ "isNullable": true,
165
+ "direction": "In",
166
+ "readOnly": false,
167
+ "access": "Hidden"
168
+ },
169
+ "waitQueueId": {
170
+ "dataType": "LongNumber",
171
+ "isNullable": true,
172
+ "direction": "In",
173
+ "readOnly": false,
174
+ "access": "Hidden"
175
+ }
176
+ }
177
+ }
178
+ },
179
+ {
180
+ "id": "ef4db13e-83f0-4d41-981d-4bf5810c0daa",
181
+ "key": "DATAEXTENSIONUPDATE-1",
182
+ "name": "",
183
+ "description": "",
184
+ "type": "DATAEXTENSIONUPDATE",
185
+ "outcomes": [
186
+ {
187
+ "key": "a49ecf04-4612-4582-85fe-d7193f872fa8",
188
+ "next": "WAIT-2",
189
+ "arguments": {},
190
+ "metaData": {}
191
+ }
192
+ ],
193
+ "arguments": { "contactKey": "{{Contact.Key}}", "value": "{{DateTime.Now}}" },
194
+ "configurationArguments": {
195
+ "dataExtensionId": "ace267f0-b81d-e711-80cc-1402ec7222b4",
196
+ "field": "4e875b26-0317-4525-bfa0-50c8d1b7a7b5"
197
+ },
198
+ "metaData": {
199
+ "dataExtensionName": "test_TestData",
200
+ "cachedFieldName": "JourneyConfirmed",
201
+ "cachedDisplayValue": ""
202
+ },
203
+ "schema": {
204
+ "arguments": {
205
+ "contactKey": {
206
+ "dataType": "Text",
207
+ "isNullable": false,
208
+ "direction": "In",
209
+ "readOnly": false,
210
+ "access": "Hidden"
211
+ },
212
+ "value": {
213
+ "dataType": "Text",
214
+ "isNullable": false,
215
+ "direction": "In",
216
+ "readOnly": false,
217
+ "access": "Hidden"
218
+ }
219
+ }
220
+ }
221
+ }
222
+ ],
223
+ "triggers": [
224
+ {
225
+ "id": "a7f8cd93-e0a6-40a5-8557-5bd6fa76c0ea",
226
+ "key": "TRIGGER",
227
+ "name": "test_TESTDataentry",
228
+ "description": "",
229
+ "type": "APIEvent",
230
+ "outcomes": [],
231
+ "arguments": {
232
+ "filterResult": "{{Contact.FilterId.e4ddb887-6f2d-46b1-9f64-9a020f217339}}"
233
+ },
234
+ "configurationArguments": {
235
+ "schemaVersionId": 133,
236
+ "criteria": "<FilterDefinition><ConditionSet Operator=\"AND\" ConditionSetName=\"Individual Filter Grouping\"></ConditionSet></FilterDefinition>",
237
+ "filterDefinitionId": "e4ddb887-6f2d-46b1-9f64-9a020f217339"
238
+ },
239
+ "metaData": {
240
+ "scheduleState": "No Schedule",
241
+ "sourceInteractionId": "00000000-0000-0000-0000-000000000000",
242
+ "eventDefinitionId": "33b4dbc5-4b58-4a54-ab57-24388f1eefe4",
243
+ "eventDefinitionKey": "APIEvent-60130566-e2fe-eb29-4140-15c27093a80b",
244
+ "chainType": "None",
245
+ "configurationRequired": false,
246
+ "iconUrl": "/events/images/icon_journeyBuilder-event-api-blue.svg",
247
+ "title": "",
248
+ "category": "Event"
249
+ }
250
+ }
251
+ ],
252
+ "goals": [],
253
+ "exits": [],
254
+ "notifiers": [],
255
+ "stats": {
256
+ "currentPopulation": 5848,
257
+ "cumulativePopulation": 3019,
258
+ "metGoal": 0,
259
+ "metExitCriteria": 0,
260
+ "goalPerformance": 0
261
+ },
262
+ "healthStats": {
263
+ "currentlyInCount": 0,
264
+ "delayedContactCount": 0,
265
+ "delayedMinuteInterval": 60
266
+ },
267
+ "entryMode": "OnceAndDone",
268
+ "definitionType": "Multistep",
269
+ "channel": "",
270
+ "defaults": {
271
+ "email": ["{{Event.APIEvent-60130566-e2fe-eb29-4140-15c27093a80b.\"emailAddress\"}}"],
272
+ "properties": {}
273
+ },
274
+ "metaData": {},
275
+ "executionMode": "Production",
276
+ "categoryId": 6298,
277
+ "status": "Draft",
278
+ "definitionId": "233d4413-922c-4568-85a5-e5cc77efc3be",
279
+ "scheduledStatus": "Draft"
280
+ }
@@ -0,0 +1,68 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
5
+ xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
6
+ xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
7
+ xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
8
+ <soap:Header>
9
+ <wsa:Action>RetrieveResponse</wsa:Action>
10
+ <wsa:MessageID>urn:uuid:8fe86c01-56d5-4c56-8ac2-991578c79485</wsa:MessageID>
11
+ <wsa:RelatesTo>urn:uuid:592f71ac-78a0-45a2-8cfe-37a91fc6cfd5</wsa:RelatesTo>
12
+ <wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
13
+ <wsse:Security>
14
+ <wsu:Timestamp wsu:Id="Timestamp-675af14a-b6a1-44a5-b7e7-4a76c6d18424">
15
+ <wsu:Created>2023-02-01T10:29:21Z</wsu:Created>
16
+ <wsu:Expires>2023-02-01T10:34:21Z</wsu:Expires>
17
+ </wsu:Timestamp>
18
+ </wsse:Security>
19
+ </soap:Header>
20
+ <soap:Body>
21
+ <RetrieveResponseMsg xmlns="http://exacttarget.com/wsdl/partnerAPI">
22
+ <OverallStatus>OK</OverallStatus>
23
+ <RequestID>a90dda5e-aeba-440a-82ce-74fb2abf5eb2</RequestID>
24
+ <Results xsi:type="TriggeredSendDefinition">
25
+ <PartnerKey xsi:nil="true" />
26
+ <PartnerProperties>
27
+ <Name>TestEmailAddr</Name>
28
+ <Value />
29
+ </PartnerProperties>
30
+ <CreatedDate>2018-06-25T05:58:00</CreatedDate>
31
+ <ModifiedDate>2018-06-25T05:58:00</ModifiedDate>
32
+ <ObjectID>b3150cf0-6e78-e811-80d4-1402ec721c9d</ObjectID>
33
+ <CustomerKey>2621</CustomerKey>
34
+ <IsPlatformObject>false</IsPlatformObject>
35
+ <Name>Unsubscribe_Email</Name>
36
+ <Description>Unsubscribe_Email</Description>
37
+ <Keyword />
38
+ <CategoryID>5654</CategoryID>
39
+ <SendClassification>
40
+ <PartnerKey xsi:nil="true" />
41
+ <ObjectID xsi:nil="true" />
42
+ <CustomerKey>Default Transactional</CustomerKey>
43
+ </SendClassification>
44
+ <FromName>unsubscribe</FromName>
45
+ <FromAddress>unsubscribe@emails.mcdev.accenture.com</FromAddress>
46
+ <SuppressTracking>false</SuppressTracking>
47
+ <TriggeredSendType>Continuous</TriggeredSendType>
48
+ <TriggeredSendStatus>New</TriggeredSendStatus>
49
+ <Email>
50
+ <PartnerKey xsi:nil="true" />
51
+ <ID>1960</ID>
52
+ <ObjectID xsi:nil="true" />
53
+ </Email>
54
+ <AutoAddSubscribers>false</AutoAddSubscribers>
55
+ <AutoUpdateSubscribers>false</AutoUpdateSubscribers>
56
+ <BatchInterval>0</BatchInterval>
57
+ <BccEmail />
58
+ <EmailSubject>You are successfully unsubscribed</EmailSubject>
59
+ <DynamicEmailSubject>You are successfully unsubscribed</DynamicEmailSubject>
60
+ <IsMultipart>false</IsMultipart>
61
+ <IsWrapped>true</IsWrapped>
62
+ <AllowedSlots>0</AllowedSlots>
63
+ <NewSlotTrigger>0</NewSlotTrigger>
64
+ <Priority>4</Priority>
65
+ </Results>
66
+ </RetrieveResponseMsg>
67
+ </soap:Body>
68
+ </soap:Envelope>