Repository: Accenture/sfmc-devtools
Pull request #699 (2023-02-02 11:06:07)

4.3.1
release


Patch:
diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml
index 5fa0ac66..0c559c08 100644
--- a/.github/ISSUE_TEMPLATE/bug.yml
+++ b/.github/ISSUE_TEMPLATE/bug.yml
@@ -39,6 +39,7 @@ body:
           label: Version
           description: What version of our software are you running? (mcdev --version)
           options:
+              - 4.3.1
               - 4.3.0
               - 4.2.1
               - 4.2.0
diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md
index 90e848d8..89ec2875 100644
--- a/docs/dist/documentation.md
+++ b/docs/dist/documentation.md
@@ -849,7 +849,7 @@ FileTransfer MetadataType
     * [.retrieveAsTemplate(templateDir, name, templateVariables, [selectedSubType])](#Asset.retrieveAsTemplate) ⇒ <code>Promise.&lt;{metadata: TYPE.AssetItem, type: string}&gt;</code>
     * [.create(metadata)](#Asset.create) ⇒ <code>Promise</code>
     * [.update(metadata)](#Asset.update) ⇒ <code>Promise</code>
-    * [.requestSubType(subType, subTypeArray, [retrieveDir], [templateName], [templateVariables], key)](#Asset.requestSubType) ⇒ <code>Promise</code>
+    * [.requestSubType(subType, [retrieveDir], [templateName], [templateVariables], key)](#Asset.requestSubType) ⇒ <code>Promise</code>
     * [.requestAndSaveExtended(items, subType, retrieveDir, [templateVariables])](#Asset.requestAndSaveExtended) ⇒ <code>Promise</code>
     * [._retrieveExtendedFile(metadata, subType, retrieveDir)](#Asset._retrieveExtendedFile) ⇒ <code>Promise.&lt;void&gt;</code>
     * [._readExtendedFileFromFS(metadata, subType, deployDir, [pathOnly])](#Asset._readExtendedFileFromFS) ⇒ <code>Promise.&lt;string&gt;</code>
@@ -939,7 +939,7 @@ Updates a single asset
 
 <a name="Asset.requestSubType"></a>
 
-### Asset.requestSubType(subType, subTypeArray, [retrieveDir], [templateName], [templateVariables], key) ⇒ <code>Promise</code>
+### Asset.requestSubType(subType, [retrieveDir], [templateName], [templateVariables], key) ⇒ <code>Promise</code>
 Retrieves Metadata of a specific asset type
 
 **Kind**: static method of [<code>Asset</code>](#Asset)  
@@ -948,7 +948,6 @@ Retrieves Metadata of a specific asset type
 | Param | Type | Description |
 | --- | --- | --- |
 | subType | <code>TYPE.AssetSubType</code> | group of similar assets to put in a folder (ie. images) |
-| subTypeArray | <code>Array.&lt;TYPE.AssetSubType&gt;</code> | list of all asset types within this subtype |
 | [retrieveDir] | <code>string</code> | target directory for saving assets |
 | [templateName] | <code>string</code> | name of the metadata file |
 | [templateVariables] | <code>TYPE.TemplateMap</code> | variables to be replaced in the metadata |
diff --git a/lib/Retriever.js b/lib/Retriever.js
index e8b5243e..de10173e 100644
--- a/lib/Retriever.js
+++ b/lib/Retriever.js
@@ -62,29 +62,33 @@ class Retriever {
         // ensure we know which real dependencies we have to ensure we cache those completely
         const dependencies = this._getTypeDependencies(metadataTypes);
         const deployOrder = Util.getMetadataHierachy(metadataTypes);
-        for (const metadataType in deployOrder) {
-            const type = metadataType;
-            const subTypeArr = deployOrder[metadataType];
+        for (const type in deployOrder) {
+            const subTypeArr = deployOrder[type];
             // if types were added by getMetadataHierachy() for caching, make sure the key-list is set to [null] for them which will retrieve all
-            typeKeyMap[metadataType] = typeKeyMap[metadataType] || [null];
+            typeKeyMap[type] = typeKeyMap[type] || [null];
             // add client to metadata process class instead of passing every time
             MetadataTypeInfo[type].client = auth.getSDK(this.buObject);
             MetadataTypeInfo[type].properties = this.properties;
             MetadataTypeInfo[type].buObject = this.buObject;
             try {
                 let result;
-                if (!metadataTypes.includes(type) && !metadataTypes.includes(metadataType)) {
+                if (
+                    !metadataTypes.includes(type) &&
+                    (!Array.isArray(subTypeArr) ||
+                        (Array.isArray(subTypeArr) &&
+                            !metadataTypes.includes(`${type}-${subTypeArr?.[0]}`)))
+                ) {
                     // type not in list of types to retrieve, but is a dependency of one of them
                     if (changelogOnly && type !== 'folder') {
                         // no extra caching needed for list view except for folders
                         continue;
                     }
-                    Util.logger.info(`Caching dependent Metadata: ${metadataType}`);
+                    Util.logger.info(`Caching dependent Metadata: ${type}`);
                     Util.logSubtypes(subTypeArr);
                     result = await MetadataTypeInfo[type].retrieveForCache(null, subTypeArr);
                 } else if (templateVariables) {
                     // type is in list of types to retrieve and we have template variables
-                    Util.logger.info(`Retrieving as Template: ${metadataType}`);
+                    Util.logger.info(`Retrieving as Template: ${type}`);
                     if (subTypeArr?.length > 1) {
                         Util.logger.warn(
                             `retrieveAsTemplate only works with one subtype, ignoring all but first subtype from your list: ${subTypeArr.join(
@@ -93,7 +97,7 @@ class Retriever {
                         );
                     }
                     result = await Promise.all(
-                        typeKeyMap[metadataType].map((name) =>
+                        typeKeyMap[type].map((name) =>
                             MetadataTypeInfo[type].retrieveAsTemplate(
                                 this.templateDir,
                                 name,
@@ -106,12 +110,11 @@ class Retriever {
                     // type is in list of types to retrieve and we don't have template variables
                     let cacheResult = null;
                     if (
-                        (typeKeyMap[metadataType].length > 1 ||
-                            typeKeyMap[metadataType][0] !== null) &&
-                        (dependencies.includes(type) || dependencies.includes(metadataType))
+                        (typeKeyMap[type].length > 1 || typeKeyMap[type][0] !== null) &&
+                        (dependencies.includes(type) || dependencies.includes(type))
                     ) {
                         // if we have a key-list and the type is a dependency, we need to cache the whole type
-                        Util.logger.info(`Caching dependent Metadata: ${metadataType}`);
+                        Util.logger.info(`Caching dependent Metadata: ${type}`);
                         Util.logSubtypes(subTypeArr);
                         cacheResult = await MetadataTypeInfo[type].retrieveForCache(
                             null,
@@ -119,15 +122,15 @@ class Retriever {
                         );
                     }
                     Util.logger.info(
-                        `Retrieving: ${metadataType}` +
-                            (typeKeyMap[metadataType][0] === null
+                        `Retrieving: ${type}` +
+                            (typeKeyMap[type][0] === null
                                 ? ''
-                                : Util.getKeysString(typeKeyMap[metadataType]))
+                                : Util.getKeysString(typeKeyMap[type]))
                     );
                     result = await (changelogOnly
                         ? MetadataTypeInfo[type].retrieveChangelog(null, subTypeArr)
                         : Promise.all(
-                              typeKeyMap[metadataType].map((key) =>
+                              typeKeyMap[type].map((key) =>
                                   MetadataTypeInfo[type].retrieve(
                                       this.savePath,
                                       null,
@@ -154,14 +157,14 @@ class Retriever {
                                 cache.mergeMetadata(type, result_i.metadata);
                             }
                         }
-                        if (metadataTypes.includes(type) || metadataTypes.includes(metadataType)) {
+                        if (metadataTypes.includes(type) || metadataTypes.includes(type)) {
                             retrieveChangelog[type] = result
                                 .filter((el) => !!el)
                                 .map((element) => element.metadata);
                         }
                     } else {
                         cache.setMetadata(type, result.metadata);
-                        if (metadataTypes.includes(type) || metadataTypes.includes(metadataType)) {
+                        if (metadataTypes.includes(type) || metadataTypes.includes(type)) {
                             retrieveChangelog[type] = result.metadata;
                         }
                     }
@@ -175,7 +178,7 @@ class Retriever {
                     Util.logger.error(ex.message);
                     break;
                 } else {
-                    Util.logger.errorStack(ex, ` - Retrieving ${metadataType} failed`);
+                    Util.logger.errorStack(ex, ` - Retrieving ${type} failed`);
                 }
             }
         }
diff --git a/lib/metadataTypes/Asset.js b/lib/metadataTypes/Asset.js
index 6734173d..c96c9f1e 100644
--- a/lib/metadataTypes/Asset.js
+++ b/lib/metadataTypes/Asset.js
@@ -31,16 +31,7 @@ class Asset extends MetadataType {
         for (const subType of subTypeArr) {
             // each subtype contains multiple different specific types (images contains jpg and png for example)
             // we use await here to limit the risk of too many concurrent api requests at time
-            items.push(
-                ...(await this.requestSubType(
-                    subType,
-                    this.definition.extendedSubTypes[subType],
-                    retrieveDir,
-                    null,
-                    null,
-                    key
-                ))
-            );
+            items.push(...(await this.requestSubType(subType, retrieveDir, null, null, key)));
         }
         const metadata = this.parseResponseBody({ items: items });
         if (retrieveDir) {
@@ -81,13 +72,7 @@ class Asset extends MetadataType {
             // each subtype contains multiple different specific types (images contains jpg and png for example)
             // we use await here to limit the risk of too many concurrent api requests at time
             items.push(
-                ...(await this.requestSubType(
-                    subType,
-                    this.definition.extendedSubTypes[subType],
-                    templateDir,
-                    name,
-                    templateVariables
-                ))
+                ...(await this.requestSubType(subType, templateDir, name, templateVariables))
             );
         }
         const metadata = this.parseResponseBody({ items: items });
@@ -146,27 +131,21 @@ class Asset extends MetadataType {
      * Retrieves Metadata of a specific asset type
      *
      * @param {TYPE.AssetSubType} subType group of similar assets to put in a folder (ie. images)
-     * @param {TYPE.AssetSubType[]} subTypeArray list of all asset types within this subtype
      * @param {string} [retrieveDir] target directory for saving assets
      * @param {string} [templateName] name of the metadata file
      * @param {TYPE.TemplateMap} [templateVariables] variables to be replaced in the metadata
      * @param {string} key customer key to filter by
      * @returns {Promise} Promise
      */
-    static async requestSubType(
-        subType,
-        subTypeArray,
-        retrieveDir,
-        templateName,
-        templateVariables,
-        key
-    ) {
+    static async requestSubType(subType, retrieveDir, templateName, templateVariables, key) {
         if (retrieveDir) {
             Util.logger.info(`- Retrieving Subtype: ${subType}`);
         } else {
             Util.logger.info(` - Caching Subtype: ${subType}`);
         }
-        const subtypeIds = subTypeArray?.map(
+        /** @type {TYPE.AssetSubType[]} */
+        const extendedSubTypeArr = this.definition.extendedSubTypes[subType];
+        const subtypeIds = extendedSubTypeArr?.map(
             (subTypeItemName) => Asset.definition.typeMapping[subTypeItemName]
         );
         const uri = '/asset/v1/content/assets/query';
@@ -977,6 +956,7 @@ class Asset extends MetadataType {
 
                 break;
             }
+            case 'template': // template-template
             case 'buttonblock': // block - Button Block
             case 'freeformblock': // block
             case 'htmlblock': // block
@@ -1198,6 +1178,7 @@ class Asset extends MetadataType {
                     subFolder: [customerKey],
                 };
             }
+            case 'template': // template-template
             case 'buttonblock': // block - Button Block
             case 'freeformblock': // block
             case 'htmlblock': // block
diff --git a/lib/metadataTypes/Automation.js b/lib/metadataTypes/Automation.js
index 9565985e..cc031c2d 100644
--- a/lib/metadataTypes/Automation.js
+++ b/lib/metadataTypes/Automation.js
@@ -63,7 +63,7 @@ class Automation extends MetadataType {
                           }
                           // if we do get here, we should log the error and continue instead of failing to download all automations
                           Util.logger.error(
-                              ` - skipping retrieving Automation ${a.ObjectID}: ${ex.message} ${ex.code}`
+                              ` ☇ skipping Automation ${a.ObjectID}: ${ex.message} ${ex.code}`
                           );
                           return null;
                       }
diff --git a/lib/metadataTypes/EmailSendDefinition.js b/lib/metadataTypes/EmailSendDefinition.js
index 8a868cd8..d60e4574 100644
--- a/lib/metadataTypes/EmailSendDefinition.js
+++ b/lib/metadataTypes/EmailSendDefinition.js
@@ -182,33 +182,35 @@ class EmailSendDefinition extends MetadataType {
         super.setFolderPath(metadata);
 
         // email
-        try {
-            // classic
-            const classicEmail = cache.searchForField('email', metadata.Email.ID, 'ID', 'Name');
-            metadata.r__email_Name = classicEmail;
-            delete metadata.Email;
-        } catch {
+        if (metadata.Email?.ID) {
             try {
-                // content builder
-                const contentBuilderEmailName = cache.searchForField(
-                    'asset',
-                    metadata.Email.ID,
-                    'legacyData.legacyId',
-                    'name'
-                );
-                metadata.r__assetMessage_Name = contentBuilderEmailName;
-                const contentBuilderEmailKey = cache.searchForField(
-                    'asset',
-                    metadata.Email.ID,
-                    'legacyData.legacyId',
-                    'customerKey'
-                );
-                metadata.r__assetMessage_Key = contentBuilderEmailKey;
+                // classic
+                const classicEmail = cache.searchForField('email', metadata.Email.ID, 'ID', 'Name');
+                metadata.r__email_Name = classicEmail;
                 delete metadata.Email;
             } catch {
-                Util.logger.warn(
-                    ` - ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': Could not find email with ID ${metadata.Email.ID} in Classic nor in Content Builder.`
-                );
+                try {
+                    // content builder
+                    const contentBuilderEmailName = cache.searchForField(
+                        'asset',
+                        metadata.Email.ID,
+                        'legacyData.legacyId',
+                        'name'
+                    );
+                    metadata.r__assetMessage_Name = contentBuilderEmailName;
+                    const contentBuilderEmailKey = cache.searchForField(
+                        'asset',
+                        metadata.Email.ID,
+                        'legacyData.legacyId',
+                        'customerKey'
+                    );
+                    metadata.r__assetMessage_Key = contentBuilderEmailKey;
+                    delete metadata.Email;
+                } catch {
+                    Util.logger.warn(
+                        ` - ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': Could not find email with ID ${metadata.Email.ID} in Classic nor in Content Builder.`
+                    );
+                }
             }
         }
         // Target Audience DataExtension
@@ -237,13 +239,15 @@ class EmailSendDefinition extends MetadataType {
                 }
             }
             // List
-            try {
-                sdl.r__list_PathName = cache.getListPathName(sdl.List.ID, 'ID');
-                delete sdl.List;
-            } catch (ex) {
-                Util.logger.warn(
-                    ` - ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': ${ex.message}`
-                );
+            if (sdl.List?.ID) {
+                try {
+                    sdl.r__list_PathName = cache.getListPathName(sdl.List.ID, 'ID');
+                    delete sdl.List;
+                } catch (ex) {
+                    Util.logger.warn(
+                        ` - ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': ${ex.message}`
+                    );
+                }
             }
         }
 
diff --git a/lib/metadataTypes/Interaction.js b/lib/metadataTypes/Interaction.js
index a36cb9fb..7157723c 100644
--- a/lib/metadataTypes/Interaction.js
+++ b/lib/metadataTypes/Interaction.js
@@ -27,6 +27,7 @@ class Interaction extends MetadataType {
      * @returns {Promise.<TYPE.MetadataTypeMapObj>} Promise
      */
     static async retrieve(retrieveDir, _, __, key) {
+        const extrasDefault = 'activities';
         if (retrieveDir) {
             // only print this during retrieve, not during retrieveForCache
             Util.logBeta(this.definition.type);
@@ -59,17 +60,81 @@ class Interaction extends MetadataType {
             }
             /* eslint-enable unicorn/prefer-ternary */
         }
-        // full details for retrieve, only base data for caching; reduces caching time from minutes to seconds
-        const extras = retrieveDir ? 'all' : '';
 
         try {
-            return await super.retrieveREST(
-                retrieveDir,
-                `/interaction/v1/interactions/${singleKey}?extras=${extras}`,
-                null,
-                null,
-                key
-            );
+            const uri = `/interaction/v1/interactions/`;
+            if (singleKey || !retrieveDir) {
+                // full details for retrieve, only base data for caching; reduces caching time from minutes to seconds
+                const extras = retrieveDir && singleKey ? extrasDefault : '';
+
+                // caching or single retrieve
+                return await super.retrieveREST(
+                    retrieveDir,
+                    `${uri}${singleKey}?extras=${extras}`,
+                    null,
+                    null,
+                    key
+                );
+            } else {
+                // retrieve all
+                const results = this.definition.restPagination
+                    ? await this.client.rest.getBulk(uri, this.definition.restPageSize || 500)
+                    : await this.client.rest.get(uri);
+                // const results = this.parseResponseBody(response);
+                if (results.items?.length) {
+                    // empty results will come back without "items" defined
+                    Util.logger.info(
+                        Util.getGrayMsg(
+                            ` - ${results.items?.length} ${this.definition.type}s found. Retrieving details...`
+                        )
+                    );
+                }
+                // full details for retrieve
+                const extras = extrasDefault;
+
+                const details = results.items
+                    ? await Promise.all(
+                          results.items.map(async (a) => {
+                              try {
+                                  return await this.client.rest.get(
+                                      `${uri}key:${a[this.definition.keyField]}?extras=${extras}`
+                                  );
+                              } catch (ex) {
+                                  // if we do get here, we should log the error and continue instead of failing to download all automations
+                                  Util.logger.error(
+                                      ` ☇ skipping ${this.definition.type} ${
+                                          a[this.definition.nameField]
+                                      } (${a[this.definition.keyField]}): ${ex.message} (${
+                                          ex.code
+                                      })${
+                                          ex.endpoint
+                                              ? Util.getGrayMsg(
+                                                    ' - ' +
+                                                        ex.endpoint.split(
+                                                            'rest.marketingcloudapis.com'
+                                                        )[1]
+                                                )
+                                              : ''
+                                      }`
+                                  );
+                                  return null;
+                              }
+                          })
+                      )
+                    : [];
+                const parsed = this.parseResponseBody({ items: details.filter(Boolean) });
+
+                // * retrieveDir is mandatory in this method as it is not used for caching (there is a seperate method for that)
+                const savedMetadata = await this.saveResults(parsed, retrieveDir, null, null);
+                Util.logger.info(
+                    `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` +
+                        Util.getKeysString(key)
+                );
+                return {
+                    metadata: parsed,
+                    type: this.definition.type,
+                };
+            }
         } catch (ex) {
             // if the interaction does not exist, the API returns an error code which would otherwise bring execution to a hold
             if (
diff --git a/lib/metadataTypes/definitions/Interaction.definition.js b/lib/metadataTypes/definitions/Interaction.definition.js
index 574da5c3..1afc769b 100644
--- a/lib/metadataTypes/definitions/Interaction.definition.js
+++ b/lib/metadataTypes/definitions/Interaction.definition.js
@@ -15,7 +15,7 @@ module.exports = {
     lastmodDateField: 'modifiedDate',
     lastmodNameField: null,
     restPagination: true,
-    restPageSize: 250,
+    restPageSize: 500,
     type: 'interaction',
     typeDescription: 'Journey (internally called "Interaction").',
     typeRetrieveByDefault: true,
diff --git a/lib/metadataTypes/definitions/TransactionalEmail.definition.js b/lib/metadataTypes/definitions/TransactionalEmail.definition.js
index 3eafcbc9..98b03766 100644
--- a/lib/metadataTypes/definitions/TransactionalEmail.definition.js
+++ b/lib/metadataTypes/definitions/TransactionalEmail.definition.js
@@ -41,8 +41,8 @@ module.exports = {
             template: false,
         },
         definitionId: {
-            isCreateable: true,
-            isUpdateable: true,
+            isCreateable: false,
+            isUpdateable: false,
             retrieving: false,
             template: false,
         },
diff --git a/lib/metadataTypes/definitions/TransactionalPush.definition.js b/lib/metadataTypes/definitions/TransactionalPush.definition.js
index 9102abcb..66e66337 100644
--- a/lib/metadataTypes/definitions/TransactionalPush.definition.js
+++ b/lib/metadataTypes/definitions/TransactionalPush.definition.js
@@ -41,8 +41,8 @@ module.exports = {
             template: false,
         },
         definitionId: {
-            isCreateable: true,
-            isUpdateable: true,
+            isCreateable: false,
+            isUpdateable: false,
             retrieving: false,
             template: false,
         },
diff --git a/lib/metadataTypes/definitions/TransactionalSMS.definition.js b/lib/metadataTypes/definitions/TransactionalSMS.definition.js
index d018f1ec..2cb1c7af 100644
--- a/lib/metadataTypes/definitions/TransactionalSMS.definition.js
+++ b/lib/metadataTypes/definitions/TransactionalSMS.definition.js
@@ -41,8 +41,8 @@ module.exports = {
             template: false,
         },
         definitionId: {
-            isCreateable: true,
-            isUpdateable: true,
+            isCreateable: false,
+            isUpdateable: false,
             retrieving: false,
             template: false,
         },
diff --git a/lib/util/auth.js b/lib/util/auth.js
index d9aabe6b..1344531a 100644
--- a/lib/util/auth.js
+++ b/lib/util/auth.js
@@ -114,8 +114,13 @@ function setupSDK(sessionKey, authObject) {
                 );
                 Util.logger.errorStack(ex);
             },
-            // logRequest: (req) => console.log('request', req), // uncomment to log requests
-            // logResponse: (res) => console.log('response:', res), // uncomment to log responses
+            // logRequest: (req) =>
+            //     console.log(`${Util.color.fgMagenta}request${Util.color.reset}`, req), // uncomment to log requests
+            // logResponse: (res) =>
+            //     console.log(
+            //         `${Util.color.bgMagenta}response${Util.color.reset}:`,
+            //         typeof res.data == 'string' && res.data.length > 10000 ? res.data : res
+            //     ), // uncomment to log responses
         },
         requestAttempts: 4,
         retryOnConnectionError: true,
diff --git a/lib/util/util.js b/lib/util/util.js
index be2ca7cc..50aacad4 100644
--- a/lib/util/util.js
+++ b/lib/util/util.js
@@ -364,21 +364,38 @@ const Util = {
         // group subtypes per type
         for (const type of flatList) {
             if (type.includes('-')) {
-                const key = type.split('-')[0];
-                if (finalList[key]) {
+                const [key, subKey] = Util.getTypeAndSubType(type);
+                if (finalList[key] === null) {
+                    // if main type is already required, then don't filter by subtypes
                     continue;
+                } else if (finalList[key] && subKey) {
+                    // add another subtype to the set
+                    finalList[key].add(subKey);
+                    continue;
+                } else {
+                    // create a new set with the first subtype; subKey will be always set here
+                    finalList[key] = new Set([subKey]);
+                }
+                if (subTypeDeps[key]) {
+                    // check if there are depndent subtypes that need to be added
+                    const temp = [...subTypeDeps[key]].map((a) => {
+                        const temp = a.split('-');
+                        temp.shift(); // remove first item which is the main type
+                        return temp.join('-'); // subType can include "-"
+                    });
+                    finalList[key].add(...temp);
                 }
-                finalList[key] = subTypeDeps[key]
-                    ? [...subTypeDeps[key]].map((a) => {
-                          const temp = a.split('-');
-                          temp.shift(); // remove first item which is the main type
-                          return temp.join('-'); // subType can include "-"
-                      })
-                    : null;
             } else {
                 finalList[type] = null;
             }
         }
+        // convert sets into arrays
+        for (const type of Object.keys(finalList)) {
+            if (finalList[type] instanceof Set) {
+                finalList[type] = [...finalList[type]];
+            }
+        }
+
         return finalList;
     },
 
@@ -487,6 +504,31 @@ const Util = {
     color: {
         reset: '\x1B[0m',
         dim: '\x1B[2m',
+        bright: '\x1B[1m',
+        underscore: '\x1B[4m',
+        blink: '\x1B[5m',
+        reverse: '\x1B[7m',
+        hidden: '\x1B[8m',
+
+        fgBlack: '\x1B[30m',
+        fgRed: '\x1B[31m',
+        fgGreen: '\x1B[32m',
+        fgYellow: '\x1B[33m',
+        fgBlue: '\x1B[34m',
+        fgMagenta: '\x1B[35m',
+        fgCyan: '\x1B[36m',
+        fgWhite: '\x1B[37m',
+        fgGray: '\x1B[90m',
+
+        bgBlack: '\x1B[40m',
+        bgRed: '\x1B[41m',
+        bgGreen: '\x1B[42m',
+        bgYellow: '\x1B[43m',
+        bgBlue: '\x1B[44m',
+        bgMagenta: '\x1B[45m',
+        bgCyan: '\x1B[46m',
+        bgWhite: '\x1B[47m',
+        bgGray: '\x1B[100m',
     },
     /**
      * helper that wraps a message in the correct color codes to have them printed gray
diff --git a/package-lock.json b/package-lock.json
index 92c1ee67..9b32a3e9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
     "name": "mcdev",
-    "version": "4.3.0",
+    "version": "4.3.1",
     "lockfileVersion": 2,
     "requires": true,
     "packages": {
         "": {
             "name": "mcdev",
-            "version": "4.3.0",
+            "version": "4.3.1",
             "license": "MIT",
             "dependencies": {
                 "beauty-amp-core": "0.3.7",
diff --git a/package.json b/package.json
index acab6139..e62b3ac2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
     "name": "mcdev",
-    "version": "4.3.0",
+    "version": "4.3.1",
     "description": "Accenture Salesforce Marketing Cloud DevTools",
     "author": "Accenture: joern.berkefeld, douglas.midgley, robert.zimmermann, maciej.barnas",
     "license": "MIT",
@@ -50,6 +50,7 @@
         "lint-test": "eslint test/**/*.js",
         "upgrade": "npm-check --update",
         "manual-prepare": "husky install",
+        "lint-and-test": "run-s lint test",
         "test": "mocha",
         "coverage": "nyc mocha",
         "version:major": "npm version --no-commit-hooks major",


Test patch:
diff --git a/test/interaction.test.js b/test/interaction.test.js
index 9e26c2f8..667d3b6a 100644
--- a/test/interaction.test.js
+++ b/test/interaction.test.js
@@ -33,7 +33,7 @@ describe('interaction', () => {
             );
             assert.equal(
                 testUtils.getAPIHistoryLength(),
-                7,
+                9,
                 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests'
             );
             return;
@@ -114,7 +114,7 @@ describe('interaction', () => {
 
             assert.equal(
                 testUtils.getAPIHistoryLength(),
-                7,
+                9,
                 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests'
             );
             return;
diff --git a/test/mockRoot/.mcdevrc.json b/test/mockRoot/.mcdevrc.json
index 8ad5c016..3d436c01 100644
--- a/test/mockRoot/.mcdevrc.json
+++ b/test/mockRoot/.mcdevrc.json
@@ -74,5 +74,5 @@
             "triggeredSendDefinition"
         ]
     },
-    "version": "4.3.0"
+    "version": "4.3.1"
 }
diff --git a/test/resourceFactory.js b/test/resourceFactory.js
index 14bbc794..d7468791 100644
--- a/test/resourceFactory.js
+++ b/test/resourceFactory.js
@@ -1,6 +1,7 @@
 const fs = require('fs-extra');
 const path = require('node:path');
 const { XMLParser } = require('fast-xml-parser');
+const { color } = require('../lib/util/util');
 const parser = new XMLParser();
 const attributeParser = new XMLParser({ ignoreAttributes: false });
 /**
@@ -25,7 +26,11 @@ exports.loadSOAPRecords = async (mcdevAction, type, mid) => {
             encoding: 'utf8',
         });
     }
-    console.log(`error: Please create file ${testPath}`); // eslint-disable-line no-console
+    /* eslint-disable no-console */
+    console.log(
+        `${color.bgRed}${color.fgBlack}test-error${color.reset}: Please create file ${testPath}`
+    );
+    /* eslint-enable no-console */
 
     return fs.readFile(path.join('test', 'resources', mcdevAction + '-response.xml'), {
         encoding: 'utf8',
@@ -98,13 +103,15 @@ exports.handleRESTRequest = async (config) => {
         if (urlObj.searchParams.get('$filter')) {
             filterName = urlObj.searchParams.get('$filter').split(' eq ')[1];
         }
-        const testPath = path.join(
-            'test',
-            'resources',
-            config.headers.Authorization.replace('Bearer ', ''),
-            urlObj.pathname,
-            config.method + '-response.json'
-        );
+        const testPath = path
+            .join(
+                'test',
+                'resources',
+                config.headers.Authorization.replace('Bearer ', ''),
+                urlObj.pathname,
+                config.method + '-response.json'
+            )
+            .replace(':', '_'); // replace : with _ for Windows
 
         if (await fs.pathExists(testPath)) {
             // build filter logic to ensure templating works
@@ -126,7 +133,11 @@ exports.handleRESTRequest = async (config) => {
                 ];
             }
         } else {
-            console.log(`error: Please create file ${testPath}`); // eslint-disable-line no-console
+            /* eslint-disable no-console */
+            console.log(
+                `${color.bgRed}${color.fgBlack}test-error${color.reset}: Please create file ${testPath}`
+            );
+            /* eslint-enable no-console */
 
             return [
                 404,
diff --git a/test/resources/9999999/interaction/v1/interactions/key_0b76dccf-594c-b6dc-1acf-10c4493dcb84/get-response.json b/test/resources/9999999/interaction/v1/interactions/key_0b76dccf-594c-b6dc-1acf-10c4493dcb84/get-response.json
new file mode 100644
index 00000000..b3411e45
--- /dev/null
+++ b/test/resources/9999999/interaction/v1/interactions/key_0b76dccf-594c-b6dc-1acf-10c4493dcb84/get-response.json
@@ -0,0 +1,219 @@
+{
+    "id": "dsfdsafdsa-922c-4568-85a5-e5cc77efc3be",
+    "key": "0b76dccf-594c-b6dc-1acf-10c4493dcb84",
+    "name": "testExisting_temail",
+    "lastPublishedDate": "0001-01-01T00:00:00",
+    "description": "",
+    "version": 1,
+    "workflowApiVersion": 1,
+    "createdDate": "2022-03-24T02:20:32.74",
+    "modifiedDate": "2022-03-24T02:20:40.45",
+    "activities": [
+        {
+            "id": "9606bcb0-9246-4610-9800-963bc77fc20a",
+            "key": "EMAILV2-4",
+            "name": "testExisting_temail",
+            "description": "",
+            "type": "EMAILV2",
+            "outcomes": [
+                {
+                    "key": "1f44021a-74ac-49be-a07c-67862228214d",
+                    "arguments": {},
+                    "metaData": {
+                        "invalid": false
+                    }
+                }
+            ],
+            "arguments": {},
+            "configurationArguments": {
+                "applicationExtensionKey": "jb-email-activity",
+                "isModified": true,
+                "triggeredSend": {
+                    "autoAddSubscribers": true,
+                    "autoUpdateSubscribers": true,
+                    "bccEmail": "",
+                    "ccEmail": "",
+                    "created": {},
+                    "domainExclusions": [],
+                    "dynamicEmailSubject": "test email",
+                    "emailId": 25923,
+                    "emailSubject": "test email",
+                    "exclusionFilter": "",
+                    "isSalesforceTracking": true,
+                    "isMultipart": true,
+                    "isSendLogging": false,
+                    "isStoppedOnJobError": false,
+                    "modified": {},
+                    "preHeader": "",
+                    "priority": 4,
+                    "sendClassificationId": "1284e9b2-a7b8-e711-80cf-1402ec721c9d",
+                    "throttleOpens": "1/1/0001 12:00:00 AM",
+                    "throttleCloses": "1/1/0001 12:00:00 AM",
+                    "deliveryProfileId": "1084e9b2-a7b8-e711-80cf-1402ec721c9d",
+                    "senderProfileId": "0f84e9b2-a7b8-e711-80cf-1402ec721c9d",
+                    "isTrackingClicks": true,
+                    "publicationListId": 15
+                },
+                "triggeredSendId": "0a650d90-755e-ed11-b852-48df37d1df5b",
+                "triggeredSendKey": "testExisting_temail"
+            },
+            "metaData": {
+                "highThroughput": {
+                    "definitionKey": "testExisting_temail",
+                    "dataExtensionId": "21711373-72c1-ec11-b83b-48df37d1deb7"
+                },
+                "sections": {},
+                "isConfigured": true
+            },
+            "schema": {
+                "arguments": {
+                    "requestID": {
+                        "dataType": "Text",
+                        "isNullable": true,
+                        "direction": "Out",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "messageKey": {
+                        "dataType": "Text",
+                        "isNullable": true,
+                        "direction": "Out",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "activityId": {
+                        "dataType": "Text",
+                        "isNullable": true,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "definitionId": {
+                        "dataType": "Text",
+                        "isNullable": true,
+                        "direction": "In",
+                        "readOnly": true,
+                        "access": "Hidden"
+                    },
+                    "emailSubjectDataBound": {
+                        "dataType": "Text",
+                        "isNullable": true,
+                        "direction": "In",
+                        "readOnly": true,
+                        "access": "Hidden"
+                    },
+                    "contactId": {
+                        "dataType": "Number",
+                        "isNullable": true,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "contactKey": {
+                        "dataType": "Text",
+                        "isNullable": false,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "emailAddress": {
+                        "dataType": "Text",
+                        "isNullable": false,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "sourceCustomObjectId": {
+                        "dataType": "Text",
+                        "isNullable": true,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "sourceCustomObjectKey": {
+                        "dataType": "LongNumber",
+                        "isNullable": true,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "fieldType": {
+                        "dataType": "Text",
+                        "isNullable": true,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "eventData": {
+                        "dataType": "Text",
+                        "isNullable": true,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "obfuscationProperties": {
+                        "dataType": "Text",
+                        "isNullable": true,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "customObjectKey": {
+                        "dataType": "LongNumber",
+                        "isNullable": true,
+                        "direction": "In",
+                        "readOnly": true,
+                        "access": "Hidden"
+                    },
+                    "definitionInstanceId": {
+                        "dataType": "Text",
+                        "isNullable": false,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    }
+                }
+            }
+        }
+    ],
+    "triggers": [
+        {
+            "key": "TRIGGER",
+            "name": "TRIGGER",
+            "description": "",
+            "type": "transactional-api",
+            "outcomes": [],
+            "arguments": {},
+            "configurationArguments": {},
+            "metaData": {
+                "chainType": "none",
+                "configurationRequired": false,
+                "iconUrl": "/images/icon_journeyBuilder-event-transactional-blue.svg",
+                "title": "Transactional API Event",
+                "category": "Transactional",
+                "entrySourceGroupConfigUrl": "null"
+            }
+        }
+    ],
+    "goals": [],
+    "exits": [],
+    "notifiers": [],
+    "entryMode": "MultipleEntries",
+    "definitionType": "Transactional",
+    "channel": "email",
+    "defaults": {
+        "properties": {
+            "analyticsTracking": {
+                "enabled": false,
+                "analyticsType": "google",
+                "urlDomainsToTrack": []
+            }
+        }
+    },
+    "metaData": {},
+    "executionMode": "Production",
+    "categoryId": 6298,
+    "status": "Published",
+    "scheduledStatus": "Draft",
+    "definitionId": "dsfdsafdsa-922c-4568-85a5-e5cc77efc3be"
+}
diff --git a/test/resources/9999999/interaction/v1/interactions/key_testExisting_interaction/get-response.json b/test/resources/9999999/interaction/v1/interactions/key_testExisting_interaction/get-response.json
new file mode 100644
index 00000000..d29bdfb1
--- /dev/null
+++ b/test/resources/9999999/interaction/v1/interactions/key_testExisting_interaction/get-response.json
@@ -0,0 +1,280 @@
+{
+    "id": "233d4413-922c-4568-85a5-e5cc77efc3be",
+    "key": "testExisting_interaction",
+    "name": "testExisting_interaction",
+    "lastPublishedDate": "2017-04-12T08:07:48",
+    "description": "bla bla",
+    "version": 1,
+    "workflowApiVersion": 1,
+    "createdDate": "2017-04-12T05:38:05.837",
+    "modifiedDate": "2017-04-12T08:07:48.333",
+    "activities": [
+        {
+            "id": "69213026-bd2c-433b-8332-5f52d3e87ca5",
+            "key": "WAIT-1",
+            "name": "",
+            "description": "",
+            "type": "WAIT",
+            "outcomes": [
+                {
+                    "key": "bd3dff6b-565c-4b56-b9cb-60cd5b6d080b",
+                    "next": "DATAEXTENSIONUPDATE-1",
+                    "arguments": {},
+                    "metaData": {}
+                }
+            ],
+            "arguments": {
+                "waitDefinitionId": "546704c0-9384-4a41-b20e-97615cc6cc6a",
+                "waitForEventId": "",
+                "executionMode": "{{Context.ExecutionMode}}",
+                "startActivityKey": "{{Context.StartActivityKey}}",
+                "waitQueueId": "{{Context.WaitQueueId}}"
+            },
+            "configurationArguments": {
+                "waitDuration": 1,
+                "waitUnit": "DAYS",
+                "specifiedTime": "",
+                "timeZone": "",
+                "description": "",
+                "waitForEventKey": ""
+            },
+            "metaData": { "waitType": "duration" },
+            "schema": {
+                "arguments": {
+                    "endDate": {
+                        "dataType": "Date",
+                        "isNullable": false,
+                        "direction": "Out",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "waitEndDateAttributeDataBound": {
+                        "dataType": "Text",
+                        "isNullable": true,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "waitDefinitionId": {
+                        "dataType": "Text",
+                        "isNullable": false,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "waitForEventId": {
+                        "dataType": "Text",
+                        "isNullable": true,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "executionMode": {
+                        "dataType": "Text",
+                        "isNullable": false,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "startActivityKey": {
+                        "dataType": "Text",
+                        "isNullable": true,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "waitQueueId": {
+                        "dataType": "LongNumber",
+                        "isNullable": true,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    }
+                }
+            }
+        },
+        {
+            "id": "5d93cda9-2015-4c07-a1db-0d5853d25bf6",
+            "key": "WAIT-2",
+            "name": "",
+            "description": "",
+            "type": "WAIT",
+            "outcomes": [
+                {
+                    "key": "e3d3258a-891b-4838-b5a6-af37f8cb769a",
+                    "arguments": {},
+                    "metaData": {}
+                }
+            ],
+            "arguments": {
+                "waitDefinitionId": "deed4c9d-f487-4371-bfd9-76fca44ec49b",
+                "waitForEventId": "",
+                "executionMode": "{{Context.ExecutionMode}}",
+                "startActivityKey": "{{Context.StartActivityKey}}",
+                "waitQueueId": "{{Context.WaitQueueId}}"
+            },
+            "configurationArguments": {
+                "waitDuration": 1,
+                "waitUnit": "DAYS",
+                "specifiedTime": "",
+                "timeZone": "",
+                "description": "",
+                "waitForEventKey": ""
+            },
+            "metaData": { "waitType": "duration" },
+            "schema": {
+                "arguments": {
+                    "endDate": {
+                        "dataType": "Date",
+                        "isNullable": false,
+                        "direction": "Out",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "waitEndDateAttributeDataBound": {
+                        "dataType": "Text",
+                        "isNullable": true,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "waitDefinitionId": {
+                        "dataType": "Text",
+                        "isNullable": false,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "waitForEventId": {
+                        "dataType": "Text",
+                        "isNullable": true,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "executionMode": {
+                        "dataType": "Text",
+                        "isNullable": false,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "startActivityKey": {
+                        "dataType": "Text",
+                        "isNullable": true,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "waitQueueId": {
+                        "dataType": "LongNumber",
+                        "isNullable": true,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    }
+                }
+            }
+        },
+        {
+            "id": "ef4db13e-83f0-4d41-981d-4bf5810c0daa",
+            "key": "DATAEXTENSIONUPDATE-1",
+            "name": "",
+            "description": "",
+            "type": "DATAEXTENSIONUPDATE",
+            "outcomes": [
+                {
+                    "key": "a49ecf04-4612-4582-85fe-d7193f872fa8",
+                    "next": "WAIT-2",
+                    "arguments": {},
+                    "metaData": {}
+                }
+            ],
+            "arguments": { "contactKey": "{{Contact.Key}}", "value": "{{DateTime.Now}}" },
+            "configurationArguments": {
+                "dataExtensionId": "ace267f0-b81d-e711-80cc-1402ec7222b4",
+                "field": "4e875b26-0317-4525-bfa0-50c8d1b7a7b5"
+            },
+            "metaData": {
+                "dataExtensionName": "test_TestData",
+                "cachedFieldName": "JourneyConfirmed",
+                "cachedDisplayValue": ""
+            },
+            "schema": {
+                "arguments": {
+                    "contactKey": {
+                        "dataType": "Text",
+                        "isNullable": false,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    },
+                    "value": {
+                        "dataType": "Text",
+                        "isNullable": false,
+                        "direction": "In",
+                        "readOnly": false,
+                        "access": "Hidden"
+                    }
+                }
+            }
+        }
+    ],
+    "triggers": [
+        {
+            "id": "a7f8cd93-e0a6-40a5-8557-5bd6fa76c0ea",
+            "key": "TRIGGER",
+            "name": "test_TESTDataentry",
+            "description": "",
+            "type": "APIEvent",
+            "outcomes": [],
+            "arguments": {
+                "filterResult": "{{Contact.FilterId.e4ddb887-6f2d-46b1-9f64-9a020f217339}}"
+            },
+            "configurationArguments": {
+                "schemaVersionId": 133,
+                "criteria": "<FilterDefinition><ConditionSet Operator=\"AND\" ConditionSetName=\"Individual Filter Grouping\"></ConditionSet></FilterDefinition>",
+                "filterDefinitionId": "e4ddb887-6f2d-46b1-9f64-9a020f217339"
+            },
+            "metaData": {
+                "scheduleState": "No Schedule",
+                "sourceInteractionId": "00000000-0000-0000-0000-000000000000",
+                "eventDefinitionId": "33b4dbc5-4b58-4a54-ab57-24388f1eefe4",
+                "eventDefinitionKey": "APIEvent-60130566-e2fe-eb29-4140-15c27093a80b",
+                "chainType": "None",
+                "configurationRequired": false,
+                "iconUrl": "/events/images/icon_journeyBuilder-event-api-blue.svg",
+                "title": "",
+                "category": "Event"
+            }
+        }
+    ],
+    "goals": [],
+    "exits": [],
+    "notifiers": [],
+    "stats": {
+        "currentPopulation": 5848,
+        "cumulativePopulation": 3019,
+        "metGoal": 0,
+        "metExitCriteria": 0,
+        "goalPerformance": 0
+    },
+    "healthStats": {
+        "currentlyInCount": 0,
+        "delayedContactCount": 0,
+        "delayedMinuteInterval": 60
+    },
+    "entryMode": "OnceAndDone",
+    "definitionType": "Multistep",
+    "channel": "",
+    "defaults": {
+        "email": ["{{Event.APIEvent-60130566-e2fe-eb29-4140-15c27093a80b.\"emailAddress\"}}"],
+        "properties": {}
+    },
+    "metaData": {},
+    "executionMode": "Production",
+    "categoryId": 6298,
+    "status": "Draft",
+    "definitionId": "233d4413-922c-4568-85a5-e5cc77efc3be",
+    "scheduledStatus": "Draft"
+}
diff --git a/test/resources/9999999/triggeredSendDefinition/retrieve-response.xml b/test/resources/9999999/triggeredSendDefinition/retrieve-response.xml
new file mode 100644
index 00000000..d2c1eebd
--- /dev/null
+++ b/test/resources/9999999/triggeredSendDefinition/retrieve-response.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+    xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
+    xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
+    xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
+    <soap:Header>
+        <wsa:Action>RetrieveResponse</wsa:Action>
+        <wsa:MessageID>urn:uuid:8fe86c01-56d5-4c56-8ac2-991578c79485</wsa:MessageID>
+        <wsa:RelatesTo>urn:uuid:592f71ac-78a0-45a2-8cfe-37a91fc6cfd5</wsa:RelatesTo>
+        <wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
+        <wsse:Security>
+            <wsu:Timestamp wsu:Id="Timestamp-675af14a-b6a1-44a5-b7e7-4a76c6d18424">
+                <wsu:Created>2023-02-01T10:29:21Z</wsu:Created>
+                <wsu:Expires>2023-02-01T10:34:21Z</wsu:Expires>
+            </wsu:Timestamp>
+        </wsse:Security>
+    </soap:Header>
+    <soap:Body>
+        <RetrieveResponseMsg xmlns="http://exacttarget.com/wsdl/partnerAPI">
+            <OverallStatus>OK</OverallStatus>
+            <RequestID>a90dda5e-aeba-440a-82ce-74fb2abf5eb2</RequestID>
+            <Results xsi:type="TriggeredSendDefinition">
+                <PartnerKey xsi:nil="true" />
+                <PartnerProperties>
+                    <Name>TestEmailAddr</Name>
+                    <Value />
+                </PartnerProperties>
+                <CreatedDate>2018-06-25T05:58:00</CreatedDate>
+                <ModifiedDate>2018-06-25T05:58:00</ModifiedDate>
+                <ObjectID>b3150cf0-6e78-e811-80d4-1402ec721c9d</ObjectID>
+                <CustomerKey>2621</CustomerKey>
+                <IsPlatformObject>false</IsPlatformObject>
+                <Name>Unsubscribe_Email</Name>
+                <Description>Unsubscribe_Email</Description>
+                <Keyword />
+                <CategoryID>5654</CategoryID>
+                <SendClassification>
+                    <PartnerKey xsi:nil="true" />
+                    <ObjectID xsi:nil="true" />
+                    <CustomerKey>Default Transactional</CustomerKey>
+                </SendClassification>
+                <FromName>unsubscribe</FromName>
+                <FromAddress>unsubscribe@emails.mcdev.accenture.com</FromAddress>
+                <SuppressTracking>false</SuppressTracking>
+                <TriggeredSendType>Continuous</TriggeredSendType>
+                <TriggeredSendStatus>New</TriggeredSendStatus>
+                <Email>
+                    <PartnerKey xsi:nil="true" />
+                    <ID>1960</ID>
+                    <ObjectID xsi:nil="true" />
+                </Email>
+                <AutoAddSubscribers>false</AutoAddSubscribers>
+                <AutoUpdateSubscribers>false</AutoUpdateSubscribers>
+                <BatchInterval>0</BatchInterval>
+                <BccEmail />
+                <EmailSubject>You are successfully unsubscribed</EmailSubject>
+                <DynamicEmailSubject>You are successfully unsubscribed</DynamicEmailSubject>
+                <IsMultipart>false</IsMultipart>
+                <IsWrapped>true</IsWrapped>
+                <AllowedSlots>0</AllowedSlots>
+                <NewSlotTrigger>0</NewSlotTrigger>
+                <Priority>4</Priority>
+            </Results>
+        </RetrieveResponseMsg>
+    </soap:Body>
+</soap:Envelope>