From 35438e9412a09d7073eee984ef4c17ead142bb47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 10 Aug 2023 13:15:17 +0200 Subject: [PATCH 01/70] #940: basic concept for fixing shared DEs incl new --fixShared option --- docs/dist/documentation.md | 16 +++++++++++ lib/cli.js | 8 +++++- lib/index.js | 1 + lib/metadataTypes/DataExtension.js | 43 +++++++++++++++++++++++++++++- 4 files changed, 66 insertions(+), 2 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 8a2189b07..e2b5ed302 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -219,6 +219,9 @@ Provides default functionality that can be overwritten by child metadata type cl
Automation.(metadataMap, originalMetadataMap, key, [oldKey])Promise.<{key:string, response:object}>

helper for postDeployTasks

+
DataExtension.(upsertedMetadata, originalMetadata, createdUpdated)void
+

helper for postDeployTasks

+
getUserName(userList, item, fieldname)string
setupSDK(sessionKey, authObject)SDK
@@ -8539,6 +8542,19 @@ helper for [postDeployTasks](#Automation.postDeployTasks) | key | string | current customer key | | [oldKey] | string | old customer key before fixKey / changeKeyValue / changeKeyField | + + +## DataExtension.(upsertedMetadata, originalMetadata, createdUpdated) ⇒ void +helper for [postDeployTasks](#DataExtension.postDeployTasks) + +**Kind**: global function + +| Param | Type | Description | +| --- | --- | --- | +| upsertedMetadata | TYPE.DataExtensionMap | metadata mapped by their keyField | +| originalMetadata | TYPE.DataExtensionMap | metadata to be updated (contains additioanl fields) | +| createdUpdated | Object | counter representing successful creates/updates | + ## getUserName(userList, item, fieldname) ⇒ string diff --git a/lib/cli.js b/lib/cli.js index 8c198cf97..81c9b59c1 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -45,7 +45,7 @@ yargs }, }) .command({ - command: 'deploy [BU] [TYPE] [KEY] [--fromRetrieve] [--refresh]', + command: 'deploy [BU] [TYPE] [KEY]', aliases: ['d'], desc: 'deploys local metadata to a business unit', builder: (yargs) => { @@ -97,6 +97,12 @@ yargs group: 'Options for deploy:', describe: 'optionally start existing schedule instead of running item once immediately (only works for automations)', + }) + .option('fixShared', { + type: 'boolean', + group: 'Options for deploy:', + describe: + "optionally ensure that updates to shared DataExtensions become visible in child BU's data designer (SF Known issue W-11031095)", }); }, handler: (argv) => { diff --git a/lib/index.js b/lib/index.js index b34ae4102..bcc150061 100644 --- a/lib/index.js +++ b/lib/index.js @@ -56,6 +56,7 @@ class Mcdev { 'commitHistory', 'execute', 'filter', + 'fixShared', 'fromRetrieve', 'json', 'like', diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index 7210095a6..45508b44d 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -283,7 +283,7 @@ class DataExtension extends MetadataType { * @param {{created: number, updated: number}} createdUpdated counter representing successful creates/updates * @returns {void} */ - static postDeployTasks(upsertedMetadata, originalMetadata, createdUpdated) { + static async postDeployTasks(upsertedMetadata, originalMetadata, createdUpdated) { for (const key in upsertedMetadata) { const item = upsertedMetadata[key]; @@ -354,6 +354,47 @@ class DataExtension extends MetadataType { DataExtensionField.postRetrieveTasks(field, true); } } + await this.#postDeployFixShared(upsertedMetadata, originalMetadata); + } + + /** + * helper for {@link DataExtension.postDeployTasks} + * + * @param {TYPE.DataExtensionMap} upsertedMetadata metadata mapped by their keyField + * @param {TYPE.DataExtensionMap} originalMetadata metadata to be updated (contains additioanl fields) + * @param {{created: number, updated: number}} createdUpdated counter representing successful creates/updates + * @returns {void} + */ + static async #postDeployFixShared(upsertedMetadata, originalMetadata, createdUpdated) { + if (this.buObject.eid !== this.buObject.mid || createdUpdated.updated === 0) { + // only if we were executing a deploy on parent bu could we be deploying shared data extensions + // only if updates were made could the issue in https://issues.salesforce.com/#q=W-11031095 affect data designer + return; + } + // find all shared data extensions + const sharedDataExtensions = []; + for (const key in upsertedMetadata) { + const item = upsertedMetadata[key]; + const originalItem = originalMetadata[key]; + if ( + item.r__folder_ContentType === 'shared_dataextension' || + originalItem.r__folder_ContentType === 'shared_dataextension' + ) { + sharedDataExtensions.push(item); + } + } + if (sharedDataExtensions.length > 0 && !Util.OPTIONS.fixShared) { + Util.logger.warn( + 'Shared Data Extensions were updated but fixShared option is not set. This can result in your changes not being visible on child BUs.' + ); + // TODO replace with 2 inquirer questions to 1) ask if child BUs should be fixed if needed and 2) select which BUs to run this for + } + if (sharedDataExtensions.length > 0 && Util.OPTIONS.fixShared) { + // TODO initiate actual search & update logic + } else { + // no shared dataExtensions or fixShared option not enabled + return; + } } /** From 82aaa6cbc631da7647d872bbfcb75a3dad1d22b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 10 Aug 2023 16:03:11 +0200 Subject: [PATCH 02/70] #940: refactoring --- docs/dist/documentation.md | 26 ++++++ lib/metadataTypes/DataExtension.js | 137 ++++++++++++++++------------- 2 files changed, 102 insertions(+), 61 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index e2b5ed302..3c0415605 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -1301,6 +1301,7 @@ AttributeSet MetadataType * [AttributeSet](#AttributeSet) ⇐ [MetadataType](#MetadataType) * [.retrieve(retrieveDir, [_], [__], [key])](#AttributeSet.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.retrieveForCache()](#AttributeSet.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.retrieveForSharedDEs([deKeys])](#AttributeSet.retrieveForSharedDEs) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.parseResponseBody(body, [singleRetrieve])](#AttributeSet.parseResponseBody) ⇒ TYPE.MetadataTypeMap * [.postRetrieveTasks(metadata)](#AttributeSet.postRetrieveTasks) ⇒ TYPE.MetadataTypeItem * [._getSystemValueDefinitions()](#AttributeSet._getSystemValueDefinitions) ⇒ Array.<object> @@ -1327,6 +1328,18 @@ Retrieves Metadata of schema set definitions for caching. **Kind**: static method of [AttributeSet](#AttributeSet) **Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise + + +### AttributeSet.retrieveForSharedDEs([deKeys]) ⇒ Promise.<TYPE.MetadataTypeMapObj> +Retrieves Metadata of schema set definitions for caching. + +**Kind**: static method of [AttributeSet](#AttributeSet) +**Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise + +| Param | Type | Description | +| --- | --- | --- | +| [deKeys] | Array.<string> | if provided, only shared DEs with these keys will be returned | + ### AttributeSet.parseResponseBody(body, [singleRetrieve]) ⇒ TYPE.MetadataTypeMap @@ -1784,6 +1797,7 @@ DataExtension MetadataType * [.update(metadata)](#DataExtension.update) ⇒ Promise * [.postDeployTasks(upsertedMetadata, originalMetadata, createdUpdated)](#DataExtension.postDeployTasks) ⇒ void * [.retrieve(retrieveDir, [additionalFields], [_], [key])](#DataExtension.retrieve) ⇒ Promise.<{metadata: TYPE.DataExtensionMap, type: string}> + * [.retrieveSharedForCache([additionalFields])](#DataExtension.retrieveSharedForCache) ⇒ Promise.<TYPE.DataExtensionMap> * [.retrieveChangelog([additionalFields])](#DataExtension.retrieveChangelog) ⇒ Promise.<{metadata: TYPE.DataExtensionMap, type: string}> * [.postRetrieveTasks(metadata)](#DataExtension.postRetrieveTasks) ⇒ TYPE.DataExtensionItem * [.preDeployTasks(metadata)](#DataExtension.preDeployTasks) ⇒ Promise.<TYPE.DataExtensionItem> @@ -1876,6 +1890,18 @@ Retrieves dataExtension metadata. Afterwards starts retrieval of dataExtensionCo | [_] | void | unused parameter | | [key] | string | customer key of single item to retrieve | + + +### DataExtension.retrieveSharedForCache([additionalFields]) ⇒ Promise.<TYPE.DataExtensionMap> +helper for [retrieve](#DataExtension.retrieve) and for AttributeSet.retrieveForSharedDEs + +**Kind**: static method of [DataExtension](#DataExtension) +**Returns**: Promise.<TYPE.DataExtensionMap> - keyField => metadata map + +| Param | Type | Description | +| --- | --- | --- | +| [additionalFields] | Array.<string> | Returns specified fields even if their retrieve definition is not set to true | + ### DataExtension.retrieveChangelog([additionalFields]) ⇒ Promise.<{metadata: TYPE.DataExtensionMap, type: string}> diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index 45508b44d..3c8f5664e 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -434,67 +434,7 @@ class DataExtension extends MetadataType { await this._attachFields(metadata, fieldOptions, additionalFields); } if (!retrieveDir && this.buObject.eid !== this.buObject.mid) { - // for caching, we want to retrieve shared DEs as well from the instance parent BU - Util.logger.info( - ' - Caching dependent Metadata: dataExtension (shared via _ParentBU_)' - ); - /** @type {TYPE.BuObject} */ - const buObjectParentBu = { - eid: this.properties.credentials[this.buObject.credential].eid, - mid: this.properties.credentials[this.buObject.credential].eid, - businessUnit: Util.parentBuName, - credential: this.buObject.credential, - }; - try { - this.client = auth.getSDK(buObjectParentBu); - } catch (ex) { - Util.logger.error(ex.message); - return; - } - const metadataParentBu = await this._retrieveAll(additionalFields); - - // get shared folders to match our shared / synched Data Extensions - const subTypeArr = this.definition.dependencies - .filter((item) => item.startsWith('folder-')) - .map((item) => item.slice(7)); - Util.logger.info(' - Caching dependent Metadata: folder (shared via _ParentBU_)'); - Util.logSubtypes(subTypeArr); - Folder.client = this.client; - Folder.buObject = buObjectParentBu; - Folder.properties = this.properties; - const result = await Folder.retrieveForCache(null, subTypeArr); - cache.mergeMetadata('folder', result.metadata, this.buObject.eid); - - // get the types and clean out non-shared ones - const folderTypesFromParent = require('../MetadataTypeDefinitions').folder - .folderTypesFromParent; - for (const metadataEntry in metadataParentBu) { - try { - // get the data extension type from the folder - const folderContentType = cache.searchForField( - 'folder', - metadataParentBu[metadataEntry].CategoryID, - 'ID', - 'ContentType', - this.buObject.eid - ); - if (!folderTypesFromParent.includes(folderContentType)) { - Util.logger.verbose( - `removing ${metadataEntry} because r__folder_ContentType '${folderContentType}' identifies this DE as not being shared` - ); - delete metadataParentBu[metadataEntry]; - } - } catch (ex) { - Util.logger.debug( - `removing ${metadataEntry} because of error while retrieving r__folder_ContentType: ${ex.message}` - ); - delete metadataParentBu[metadataEntry]; - } - } - - // revert client to current default - this.client = auth.getSDK(this.buObject); - Folder.client = auth.getSDK(this.buObject); + const metadataParentBu = await this.retrieveSharedForCache(additionalFields); // make sure to overwrite parent bu DEs with local ones metadata = { ...metadataParentBu, ...metadata }; @@ -510,6 +450,81 @@ class DataExtension extends MetadataType { return { metadata: metadata, type: 'dataExtension' }; } + /** + * helper for {@link DataExtension.retrieve} and for AttributeSet.retrieveForSharedDEs + * + * @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true + * @returns {Promise.} keyField => metadata map + */ + static async retrieveSharedForCache(additionalFields = []) { + // for caching, we want to retrieve shared DEs as well from the instance parent BU + Util.logger.info(' - Caching dependent Metadata: dataExtension (shared via _ParentBU_)'); + const buObjectBak = this.buObject; + const clientBak = this.client; + /** @type {TYPE.BuObject} */ + const buObjectParentBu = { + eid: this.properties.credentials[this.buObject.credential].eid, + mid: this.properties.credentials[this.buObject.credential].eid, + businessUnit: Util.parentBuName, + credential: this.buObject.credential, + }; + try { + this.buObject = buObjectParentBu; + this.client = auth.getSDK(buObjectParentBu); + } catch (ex) { + Util.logger.error(ex.message); + return; + } + const metadataParentBu = await this._retrieveAll(additionalFields); + + // get shared folders to match our shared / synched Data Extensions + const subTypeArr = this.definition.dependencies + .filter((item) => item.startsWith('folder-')) + .map((item) => item.slice(7)); + Util.logger.info(' - Caching dependent Metadata: folder (shared via _ParentBU_)'); + Util.logSubtypes(subTypeArr); + Folder.client = this.client; + Folder.buObject = this.buObject; + Folder.properties = this.properties; + const result = await Folder.retrieveForCache(null, subTypeArr); + cache.mergeMetadata('folder', result.metadata, this.buObject.eid); + + // get the types and clean out non-shared ones + const folderTypesFromParent = require('../MetadataTypeDefinitions').folder + .folderTypesFromParent; + for (const metadataEntry in metadataParentBu) { + try { + // get the data extension type from the folder + const folderContentType = cache.searchForField( + 'folder', + metadataParentBu[metadataEntry].CategoryID, + 'ID', + 'ContentType', + this.buObject.eid + ); + if (!folderTypesFromParent.includes(folderContentType)) { + Util.logger.verbose( + `removing ${metadataEntry} because r__folder_ContentType '${folderContentType}' identifies this DE as not being shared` + ); + delete metadataParentBu[metadataEntry]; + } + } catch (ex) { + Util.logger.debug( + `removing ${metadataEntry} because of error while retrieving r__folder_ContentType: ${ex.message}` + ); + delete metadataParentBu[metadataEntry]; + } + } + + // revert client to current default + this.client = clientBak; + this.buObject = buObjectBak; + Folder.client = clientBak; + Folder.buObject = buObjectBak; + + return metadataParentBu; + } + /** * helper to retrieve all dataExtension fields and attach them to the dataExtension metadata * From 8eaeb04a60e1cf72805c98a4ed6f413c52d4d2ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 11 Aug 2023 12:11:04 +0200 Subject: [PATCH 03/70] #940: fix data model for shared dataextensions postDeploy --- docs/dist/documentation.md | 110 ++++++++- lib/cli.js | 1 - lib/metadataTypes/AttributeSet.js | 32 +++ lib/metadataTypes/DataExtension.js | 307 ++++++++++++++++++++++-- lib/metadataTypes/DataExtensionField.js | 32 ++- 5 files changed, 446 insertions(+), 36 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 3c0415605..f15b793b0 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -222,6 +222,22 @@ Provides default functionality that can be overwritten by child metadata type cl
DataExtension.(upsertedMetadata, originalMetadata, createdUpdated)void

helper for postDeployTasks

+
DataExtension.(childBuName, buObjectParent, clientParent, sharedDataExtensions)Promise.<Array.<string>>
+

helper for DataExtension.#postDeployFixShared

+
+
DataExtension.(deId, deKey, buObjectChildBu, clientChildBu, buObjectParent, clientParent)Promise
+
+
DataExtension.(randomSuffix, buObjectChildBu, clientChildBu, deKey, fieldObjectID)Promise
+
+
DataExtension.(buObjectChildBu, clientChildBu, deKey, deId)Promise.<string>
+

helper for DataExtension.#fixShared_item

+
+
DataExtension.(randomSuffix, buObjectParent, clientParent, deKey)Promise.<string>
+

helper for DataExtension.#fixShared_item

+
+
DataExtension.()Array.<string>
+

helper for DataExtension.#postDeployFixShared

+
getUserName(userList, item, fieldname)string
setupSDK(sessionKey, authObject)SDK
@@ -1301,7 +1317,7 @@ AttributeSet MetadataType * [AttributeSet](#AttributeSet) ⇐ [MetadataType](#MetadataType) * [.retrieve(retrieveDir, [_], [__], [key])](#AttributeSet.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.retrieveForCache()](#AttributeSet.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> - * [.retrieveForSharedDEs([deKeys])](#AttributeSet.retrieveForSharedDEs) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.retrieveForSharedDEs(sharedDataExtensions)](#AttributeSet.retrieveForSharedDEs) ⇒ Promise.<Array.<string>> * [.parseResponseBody(body, [singleRetrieve])](#AttributeSet.parseResponseBody) ⇒ TYPE.MetadataTypeMap * [.postRetrieveTasks(metadata)](#AttributeSet.postRetrieveTasks) ⇒ TYPE.MetadataTypeItem * [._getSystemValueDefinitions()](#AttributeSet._getSystemValueDefinitions) ⇒ Array.<object> @@ -1330,15 +1346,15 @@ Retrieves Metadata of schema set definitions for caching. **Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise -### AttributeSet.retrieveForSharedDEs([deKeys]) ⇒ Promise.<TYPE.MetadataTypeMapObj> +### AttributeSet.retrieveForSharedDEs(sharedDataExtensions) ⇒ Promise.<Array.<string>> Retrieves Metadata of schema set definitions for caching. **Kind**: static method of [AttributeSet](#AttributeSet) -**Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise +**Returns**: Promise.<Array.<string>> - Promise of list of shared dataExtension IDs | Param | Type | Description | | --- | --- | --- | -| [deKeys] | Array.<string> | if provided, only shared DEs with these keys will be returned | +| sharedDataExtensions | object | list of IDs of shared data extensions | @@ -2036,7 +2052,7 @@ DataExtensionField MetadataType * [.postRetrieveTasks(metadata, forDataExtension)](#DataExtensionField.postRetrieveTasks) ⇒ TYPE.DataExtensionFieldItem * [.prepareDeployColumnsOnUpdate(deployColumns, deKey)](#DataExtensionField.prepareDeployColumnsOnUpdate) ⇒ Promise.<Object.<string, TYPE.DataExtensionFieldItem>> * [.deleteByKey(customerKey)](#DataExtensionField.deleteByKey) ⇒ Promise.<boolean> - * [.deleteByKeySOAP(customerKey)](#DataExtensionField.deleteByKeySOAP) ⇒ boolean + * [.deleteByKeySOAP(customerKey, [fieldId])](#DataExtensionField.deleteByKeySOAP) ⇒ boolean * [.postDeleteTasks(customerKey)](#DataExtensionField.postDeleteTasks) ⇒ void @@ -2131,7 +2147,7 @@ Delete a metadata item from the specified business unit -### DataExtensionField.deleteByKeySOAP(customerKey) ⇒ boolean +### DataExtensionField.deleteByKeySOAP(customerKey, [fieldId]) ⇒ boolean Delete a data extension from the specified business unit **Kind**: static method of [DataExtensionField](#DataExtensionField) @@ -2140,6 +2156,7 @@ Delete a data extension from the specified business unit | Param | Type | Description | | --- | --- | --- | | customerKey | string | Identifier of metadata | +| [fieldId] | string | for programmatic deletes only one can pass in the ID directly | @@ -8581,6 +8598,87 @@ helper for [postDeployTasks](#DataExtension.postDeployTasks) | originalMetadata | TYPE.DataExtensionMap | metadata to be updated (contains additioanl fields) | | createdUpdated | Object | counter representing successful creates/updates | + + +## DataExtension.(childBuName, buObjectParent, clientParent, sharedDataExtensions) ⇒ Promise.<Array.<string>> +helper for [DataExtension.#postDeployFixShared](DataExtension.#postDeployFixShared) + +**Kind**: global function +**Returns**: Promise.<Array.<string>> - updated shared DE keys on BU + +| Param | Type | Description | +| --- | --- | --- | +| childBuName | string | name of child BU to fix | +| buObjectParent | TYPE.BuObject | bu object for parent BU | +| clientParent | object | SDK for parent BU | +| sharedDataExtensions | object | list of IDs of shared data extensions | + + + +## DataExtension.(deId, deKey, buObjectChildBu, clientChildBu, buObjectParent, clientParent) ⇒ Promise +**Kind**: global function +**Returns**: Promise - - + +| Param | Type | Description | +| --- | --- | --- | +| deId | string | data extension ObjectID | +| deKey | string | dataExtension key | +| buObjectChildBu | TYPE.BuObject | BU object for Child BU | +| clientChildBu | object | SDK for child BU | +| buObjectParent | TYPE.BuObject | BU object for Parent BU | +| clientParent | object | SDK for parent BU | + + + +## DataExtension.(randomSuffix, buObjectChildBu, clientChildBu, deKey, fieldObjectID) ⇒ Promise +**Kind**: global function +**Returns**: Promise - - + +| Param | Type | Description | +| --- | --- | --- | +| randomSuffix | string | - | +| buObjectChildBu | TYPE.BuObject | BU object for Child BU | +| clientChildBu | object | SDK for child BU | +| deKey | string | dataExtension key | +| fieldObjectID | string | field ObjectID | + + + +## DataExtension.(buObjectChildBu, clientChildBu, deKey, deId) ⇒ Promise.<string> +helper for [DataExtension.#fixShared_item](DataExtension.#fixShared_item) + +**Kind**: global function +**Returns**: Promise.<string> - randomSuffix + +| Param | Type | Description | +| --- | --- | --- | +| buObjectChildBu | TYPE.BuObject | BU object for Child BU | +| clientChildBu | object | SDK for child BU | +| deKey | string | dataExtension key | +| deId | string | dataExtension ObjectID | + + + +## DataExtension.(randomSuffix, buObjectParent, clientParent, deKey) ⇒ Promise.<string> +helper for [DataExtension.#fixShared_item](DataExtension.#fixShared_item) + +**Kind**: global function +**Returns**: Promise.<string> - fieldObjectID + +| Param | Type | Description | +| --- | --- | --- | +| randomSuffix | string | - | +| buObjectParent | TYPE.BuObject | BU object for Parent BU | +| clientParent | object | SDK for parent BU | +| deKey | string | dataExtension key | + + + +## DataExtension.() ⇒ Array.<string> +helper for [DataExtension.#postDeployFixShared](DataExtension.#postDeployFixShared) + +**Kind**: global function +**Returns**: Array.<string> - list of selected BU names ## getUserName(userList, item, fieldname) ⇒ string diff --git a/lib/cli.js b/lib/cli.js index 81c9b59c1..8decd9ece 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -99,7 +99,6 @@ yargs 'optionally start existing schedule instead of running item once immediately (only works for automations)', }) .option('fixShared', { - type: 'boolean', group: 'Options for deploy:', describe: "optionally ensure that updates to shared DataExtensions become visible in child BU's data designer (SF Known issue W-11031095)", diff --git a/lib/metadataTypes/AttributeSet.js b/lib/metadataTypes/AttributeSet.js index 1f9d82984..aac9cbfad 100644 --- a/lib/metadataTypes/AttributeSet.js +++ b/lib/metadataTypes/AttributeSet.js @@ -47,6 +47,38 @@ class AttributeSet extends MetadataType { static retrieveForCache() { return super.retrieveREST(null, '/hub/v1/contacts/schema/setDefinitions'); } + /** + * Retrieves Metadata of schema set definitions for caching. + * + * @param {object} sharedDataExtensions list of IDs of shared data extensions + * @returns {Promise.} Promise of list of shared dataExtension IDs + */ + static async retrieveForSharedDEs(sharedDataExtensions) { + if (!Object.keys(sharedDataExtensions).length) { + return []; + } + const result = await super.retrieveREST(null, '/hub/v1/contacts/schema/setDefinitions'); + const metadataMap = result?.metadata; + if (metadataMap && Object.keys(metadataMap).length) { + const sharedDEs = Object.keys(metadataMap) + .filter( + (key) => + metadataMap[key].storageLogicalType === 'ExactTargetSchema' || + metadataMap[key].storageLogicalType === 'DataExtension' + ) + .filter((key) => + Object.keys(sharedDataExtensions).includes( + metadataMap[key].storageReferenceID.value + ) + ) + .map((key) => metadataMap[key].storageReferenceID.value) + .filter(Boolean); + return sharedDEs; + } else { + // nothing to do - return empty array + return []; + } + } /** * Builds map of metadata entries mapped to their keyfields diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index 3c8f5664e..fa51522d4 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -5,11 +5,13 @@ const TYPE = require('../../types/mcdev.d'); const MetadataType = require('./MetadataType'); const DataExtensionField = require('./DataExtensionField'); const Folder = require('./Folder'); +const AttributeSet = require('./AttributeSet'); const Util = require('../util/util'); const File = require('../util/file'); const auth = require('../util/auth'); const cache = require('../util/cache'); const pLimit = require('p-limit'); +const inquirer = require('inquirer'); /** * DataExtension MetadataType @@ -354,7 +356,7 @@ class DataExtension extends MetadataType { DataExtensionField.postRetrieveTasks(field, true); } } - await this.#postDeployFixShared(upsertedMetadata, originalMetadata); + await this.#postDeployFixShared(upsertedMetadata, originalMetadata, createdUpdated); } /** @@ -369,34 +371,302 @@ class DataExtension extends MetadataType { if (this.buObject.eid !== this.buObject.mid || createdUpdated.updated === 0) { // only if we were executing a deploy on parent bu could we be deploying shared data extensions // only if updates were made could the issue in https://issues.salesforce.com/#q=W-11031095 affect data designer + Util.logger.debug(`EID != MID or nothing updated`); return; } + // find all shared data extensions - const sharedDataExtensions = []; - for (const key in upsertedMetadata) { - const item = upsertedMetadata[key]; - const originalItem = originalMetadata[key]; - if ( - item.r__folder_ContentType === 'shared_dataextension' || - originalItem.r__folder_ContentType === 'shared_dataextension' - ) { - sharedDataExtensions.push(item); - } - } - if (sharedDataExtensions.length > 0 && !Util.OPTIONS.fixShared) { + const sharedDataExtensionsKeys = this.deployedSharedKeys; + this.deployedSharedKeys = null; + + if (sharedDataExtensionsKeys.length > 0 && !Util.OPTIONS.fixShared) { Util.logger.warn( 'Shared Data Extensions were updated but fixShared option is not set. This can result in your changes not being visible on child BUs.' ); - // TODO replace with 2 inquirer questions to 1) ask if child BUs should be fixed if needed and 2) select which BUs to run this for + // TODO replace with 1 inquirer question to ask if child BUs should be fixed if needed } - if (sharedDataExtensions.length > 0 && Util.OPTIONS.fixShared) { - // TODO initiate actual search & update logic + if (sharedDataExtensionsKeys.length > 0 && Util.OPTIONS.fixShared) { + // select which BUs to run this for + const selectedBuNames = await this.#fixSharedGetBUs(); + + // backup settings + const buObjectBak = this.buObject; + const clientBak = this.client; + + // get dataExtension IDs + const sharedDataExtensions = {}; + for (const key of sharedDataExtensionsKeys) { + try { + const id = cache.searchForField( + 'dataExtension', + key, + 'CustomerKey', + 'ObjectID', + this.buObject.eid + ); + sharedDataExtensions[id] = key; + } catch { + continue; + } + } + + // run the fix-data-model logic + Util.logger.info( + `Fixing Shared Data Extensions details in data models of child BUs` + + Util.getKeysString(sharedDataExtensionsKeys) + ); + for (const buName of selectedBuNames) { + await this.#fixSharedOnBU( + buName, + buObjectBak, + clientBak, + + sharedDataExtensions + ); + } + Util.logger.info(`Finished fixing Shared Data Extensions details in data models`); + + // restore settings + this.buObject = buObjectBak; + this.client = clientBak; } else { // no shared dataExtensions or fixShared option not enabled return; } } + /** + * helper for {@link DataExtension.#postDeployFixShared} + * + * @param {string} childBuName name of child BU to fix + * @param {TYPE.BuObject} buObjectParent bu object for parent BU + * @param {object} clientParent SDK for parent BU + * @param {object} sharedDataExtensions list of IDs of shared data extensions + * @returns {Promise.} updated shared DE keys on BU + */ + static async #fixSharedOnBU(childBuName, buObjectParent, clientParent, sharedDataExtensions) { + /** @type {TYPE.BuObject} */ + const buObjectChildBu = { + eid: this.properties.credentials[buObjectParent.credential].eid, + mid: this.properties.credentials[buObjectParent.credential].businessUnits[childBuName], + businessUnit: childBuName, + credential: this.buObject.credential, + }; + const clientChildBu = auth.getSDK(buObjectChildBu); + + try { + // check if shared data Extension is used in an attributeSet on current BU + AttributeSet.properties = this.properties; + AttributeSet.buObject = buObjectChildBu; + AttributeSet.client = clientChildBu; + const sharedDeIdsUsedOnBU = await AttributeSet.retrieveForSharedDEs( + sharedDataExtensions + ); + if (sharedDeIdsUsedOnBU.length) { + Util.logger.info( + ` - Fixing dataExtensions on BU ${childBuName} ` + + Util.getKeysString( + sharedDeIdsUsedOnBU.map((deId) => sharedDataExtensions[deId]) + ) + ); + + for (const deId of sharedDeIdsUsedOnBU) { + // dont use Promise.all to ensure order of execution; otherwise, switched BU contexts in one step will affect the next + await this.#fixShared_item( + deId, + sharedDataExtensions[deId], + buObjectChildBu, + clientChildBu, + buObjectParent, + clientParent + ); + } + } else { + Util.logger.info( + Util.getGrayMsg( + ` - No matching attributeSet found for given Shared Data Extensions keys found on ${childBuName}` + ) + ); + } + return sharedDeIdsUsedOnBU; + } catch (ex) { + Util.logger.error(ex.message); + return; + } + } + + /** + * + * @param {string} deId data extension ObjectID + * @param {string} deKey dataExtension key + * @param {TYPE.BuObject} buObjectChildBu BU object for Child BU + * @param {object} clientChildBu SDK for child BU + * @param {TYPE.BuObject} buObjectParent BU object for Parent BU + * @param {object} clientParent SDK for parent BU + * @returns {Promise} - + */ + static async #fixShared_item( + deId, + deKey, + buObjectChildBu, + clientChildBu, + buObjectParent, + clientParent + ) { + // add field via child BU + const randomSuffix = await DataExtension.#fixShared_item_addField( + buObjectChildBu, + clientChildBu, + deKey, + deId + ); + + // get field ID from parent BU (it is not returned on child BU) + const fieldObjectID = await DataExtension.#fixShared_item_getFieldId( + randomSuffix, + buObjectParent, + clientParent, + deKey + ); + + // delete field via child BU + await DataExtension.#fixShared_item_deleteField( + randomSuffix, + buObjectChildBu, + clientChildBu, + deKey, + fieldObjectID + ); + + Util.logger.info(` - Fixed dataExtension ${deKey} on BU ${buObjectChildBu.businessUnit}`); + return true; + } + + /** + * + * @param {string} randomSuffix - + * @param {TYPE.BuObject} buObjectChildBu BU object for Child BU + * @param {object} clientChildBu SDK for child BU + * @param {string} deKey dataExtension key + * @param {string} fieldObjectID field ObjectID + * @returns {Promise} - + */ + static async #fixShared_item_deleteField( + randomSuffix, + buObjectChildBu, + clientChildBu, + deKey, + fieldObjectID + ) { + DataExtensionField.buObject = buObjectChildBu; + DataExtensionField.client = clientChildBu; + await DataExtensionField.deleteByKeySOAP( + deKey + '.TriggerUpdate' + randomSuffix, + fieldObjectID + ); + } + + /** + * helper for {@link DataExtension.#fixShared_item} + * + * @param {TYPE.BuObject} buObjectChildBu BU object for Child BU + * @param {object} clientChildBu SDK for child BU + * @param {string} deKey dataExtension key + * @param {string} deId dataExtension ObjectID + * @returns {Promise.} randomSuffix + */ + static async #fixShared_item_addField(buObjectChildBu, clientChildBu, deKey, deId) { + this.buObject = buObjectChildBu; + this.client = clientChildBu; + const randomSuffix = Math.floor(Math.random() * 9999999999); + // add a new field to the shared DE to trigger an update to the data model + const soapType = this.definition.soapType || this.definition.type; + await this.client.soap.update( + Util.capitalizeFirstLetter(soapType), + { + CustomerKey: deKey, + ObjectID: deId, + Fields: { + Field: [ + { + Name: 'TriggerUpdate' + randomSuffix, + IsRequired: false, + IsPrimaryKey: false, + FieldType: 'Boolean', + ObjectID: null, + }, + ], + }, + }, + null + ); + return randomSuffix; + } + + /** + * helper for {@link DataExtension.#fixShared_item} + * + * @param {string} randomSuffix - + * @param {TYPE.BuObject} buObjectParent BU object for Parent BU + * @param {object} clientParent SDK for parent BU + * @param {string} deKey dataExtension key + * @returns {Promise.} fieldObjectID + */ + static async #fixShared_item_getFieldId(randomSuffix, buObjectParent, clientParent, deKey) { + DataExtensionField.buObject = buObjectParent; + DataExtensionField.client = clientParent; + const fieldKey = `[${deKey}].[TriggerUpdate${randomSuffix}]`; + const fieldResponse = await DataExtensionField.retrieveForCache( + { + filter: { + leftOperand: 'CustomerKey', + operator: 'equals', + rightOperand: fieldKey, + }, + }, + ['Name', 'ObjectID'] + ); + const fieldObjectID = fieldResponse.metadata[fieldKey]?.ObjectID; + return fieldObjectID; + } + + /** + * helper for {@link DataExtension.#postDeployFixShared} + * + * @returns {string[]} list of selected BU names + */ + static async #fixSharedGetBUs() { + const buListObj = this.properties.credentials[this.buObject.credential].businessUnits; + const fixBuPreselected = []; + if (typeof Util.OPTIONS.fixShared === 'string') { + fixBuPreselected.push( + ...Util.OPTIONS.fixShared + .split(',') + .filter(Boolean) + .map((bu) => bu.trim()) + ); + } + + const buList = Object.keys(buListObj) + .map((name) => ({ name, value: name, checked: fixBuPreselected.includes(name) })) + .filter((bu) => bu.value !== Util.parentBuName); + const questions = { + type: 'checkbox', + name: 'businessUnits', + message: 'Please select BUs that have access to the updated Shared Data Extensions:', + pageSize: 10, + choices: buList, + }; + let responses = null; + + try { + responses = await inquirer.prompt(questions); + } catch (ex) { + Util.logger.info(ex); + } + return responses.businessUnits; + } + /** * Retrieves dataExtension metadata. Afterwards starts retrieval of dataExtensionColumn metadata retrieval * @@ -684,11 +954,16 @@ class DataExtension extends MetadataType { throw new Error(`Cannot Upsert Strongly Typed Data Extensions`); } if ( + !Util.OPTIONS._fixSharedOnBu && this.buObject.eid !== this.buObject.mid && metadata.r__folder_Path?.startsWith('Shared Items') ) { throw new Error(`Cannot Create/Update a Shared Data Extension from the Child BU`); } + if (metadata.r__folder_ContentType === 'shared_dataextension') { + this.deployedSharedKeys ||= []; + this.deployedSharedKeys.push(metadata.CustomerKey); + } if (metadata.r__folder_Path?.startsWith('Synchronized Data Extensions')) { throw new Error( `Cannot Create/Update a Synchronized Data Extension. Please use Contact Builder to maintain these` diff --git a/lib/metadataTypes/DataExtensionField.js b/lib/metadataTypes/DataExtensionField.js index 7af1ab604..9fc4c2071 100644 --- a/lib/metadataTypes/DataExtensionField.js +++ b/lib/metadataTypes/DataExtensionField.js @@ -265,24 +265,28 @@ class DataExtensionField extends MetadataType { * Delete a data extension from the specified business unit * * @param {string} customerKey Identifier of metadata + * @param {string} [fieldId] for programmatic deletes only one can pass in the ID directly * @returns {boolean} deletion success flag */ - static async deleteByKeySOAP(customerKey) { + static async deleteByKeySOAP(customerKey, fieldId) { const [deKey, fieldKey] = customerKey.split('.'); customerKey = `[${deKey}].[${fieldKey}]`; + let fieldObjectID = fieldId; // get the object id - const response = await this.retrieveForCache( - { - filter: { - leftOperand: 'CustomerKey', - operator: 'equals', - rightOperand: customerKey, + if (!fieldObjectID) { + const response = await this.retrieveForCache( + { + filter: { + leftOperand: 'CustomerKey', + operator: 'equals', + rightOperand: customerKey, + }, }, - }, - ['Name', 'ObjectID'] - ); - const fieldObjectID = response.metadata[customerKey].ObjectID; + ['Name', 'ObjectID'] + ); + fieldObjectID = response.metadata[customerKey]?.ObjectID; + } if (!fieldObjectID) { Util.logger.error(`Could not find ${customerKey} on your BU`); return false; @@ -304,8 +308,10 @@ class DataExtensionField extends MetadataType { null ); - Util.logger.info(`- deleted ${this.definition.type}: ${customerKey}`); - this.postDeleteTasks(customerKey); + if (!fieldId) { + Util.logger.info(` - deleted ${this.definition.type}: ${customerKey}`); + this.postDeleteTasks(customerKey); + } return true; } catch (ex) { const errorMsg = ex.results?.length From 11120cbd93084a4223f54e8a760834b1aee6c65f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 11 Aug 2023 12:23:09 +0200 Subject: [PATCH 04/70] #940: remove redundant fields --- lib/metadataTypes/AttributeSet.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/metadataTypes/AttributeSet.js b/lib/metadataTypes/AttributeSet.js index aac9cbfad..e3ca68caf 100644 --- a/lib/metadataTypes/AttributeSet.js +++ b/lib/metadataTypes/AttributeSet.js @@ -115,7 +115,7 @@ class AttributeSet extends MetadataType { switch (metadata.storageLogicalType) { case 'ExactTargetSchema': // synced / shared DEs case 'DataExtension': { - // local DEs + // shared / local DEs try { metadata.r__dataExtension_CustomerKey = cache.searchForField( 'dataExtension', @@ -258,6 +258,12 @@ class AttributeSet extends MetadataType { // Member ID delete metadata.customObjectOwnerMID; + // remove duplicate ID fields (main field is definitionID) + delete metadata.setDefinitionID; + if (metadata.dataRetentionProperties?.setDefinitionID) { + delete metadata.dataRetentionProperties?.setDefinitionID; + } + // connectingID.identifierType seems to be always set to 'FullyQualifiedName' - to be sure we check it here and remove it if it's the case if (metadata.connectingID?.identifierType === 'FullyQualifiedName') { // remove useless field From 32b0310765aa85737d5ea4e59d70f3b297fbca83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 11 Aug 2023 12:39:14 +0200 Subject: [PATCH 05/70] #940: allow skipping question for more BUs + ensure provided BUs are valid --- lib/metadataTypes/DataExtension.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index fa51522d4..6f0edfec7 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -639,13 +639,18 @@ class DataExtension extends MetadataType { const buListObj = this.properties.credentials[this.buObject.credential].businessUnits; const fixBuPreselected = []; if (typeof Util.OPTIONS.fixShared === 'string') { + const availableBuNames = Object.keys(buListObj); fixBuPreselected.push( ...Util.OPTIONS.fixShared .split(',') .filter(Boolean) .map((bu) => bu.trim()) + .filter((bu) => availableBuNames.includes(bu)) ); } + if (Util.skipInteraction && fixBuPreselected.length) { + return fixBuPreselected; + } const buList = Object.keys(buListObj) .map((name) => ({ name, value: name, checked: fixBuPreselected.includes(name) })) From a9ac14bb97186cdb4d0a3bc7cf6b64b6b4aef445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 11 Aug 2023 14:01:07 +0200 Subject: [PATCH 06/70] #940: fix attributeSet test --- test/resources/9999999/attributeSet/retrieve-expected.json | 4 +--- test/type.dataExtension.test.js | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/resources/9999999/attributeSet/retrieve-expected.json b/test/resources/9999999/attributeSet/retrieve-expected.json index cf01d6710..a81a4fe12 100644 --- a/test/resources/9999999/attributeSet/retrieve-expected.json +++ b/test/resources/9999999/attributeSet/retrieve-expected.json @@ -11,8 +11,7 @@ "dataRetentionProperties": { "isDeleteAtEndOfRetentionPeriod": false, "isResetRetentionPeriodOnImport": false, - "isRowBasedRetention": false, - "setDefinitionID": "3152de3f-31e2-e611-80cc-1402ec7222b4" + "isRowBasedRetention": false }, "definitionKey": "MobileSubscriptions", "isCustomObjectBacked": true, @@ -29,7 +28,6 @@ "parentID": "00000000-0000-0000-0000-000000000000", "r__folder_Path": "Data Extensions", "relationshipCount": 0, - "setDefinitionID": "3152de3f-31e2-e611-80cc-1402ec7222b4", "setDefinitionKey": "MobileSubscriptions", "storageLogicalType": "DataExtension", "storageName": "_MobileSubscription", diff --git a/test/type.dataExtension.test.js b/test/type.dataExtension.test.js index e9a8a6d93..f16b073fd 100644 --- a/test/type.dataExtension.test.js +++ b/test/type.dataExtension.test.js @@ -87,6 +87,8 @@ describe('type: dataExtension', () => { ); return; }); + it('Should create & update a shared dataExtension'); + it('Should create & update a shared dataExtension with --fixShared'); it('Should rename fields'); }); describe('Templating ================', () => { From 55b54ed38ddd6665741f12227ddfcf8b10cf208f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 11 Aug 2023 15:04:42 +0200 Subject: [PATCH 07/70] #940: add test for delete dataExtension, delete dataExtensionField, getFilesToCommit --- docs/dist/documentation.md | 4 +- lib/metadataTypes/DataExtension.js | 1 + .../9999999/dataExtension/delete-response.xml | 42 +++++++++ ...ing_dataExtension].[LastName]-response.xml | 44 ++++++++++ test/type.dataExtension.test.js | 88 +++++++++++-------- 5 files changed, 141 insertions(+), 38 deletions(-) create mode 100644 test/resources/9999999/dataExtension/delete-response.xml create mode 100644 test/resources/9999999/dataExtensionField/retrieve-CustomerKey=[testExisting_dataExtension].[LastName]-response.xml diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index f15b793b0..a27dc2ce2 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -220,7 +220,8 @@ Provides default functionality that can be overwritten by child metadata type cl

helper for postDeployTasks

DataExtension.(upsertedMetadata, originalMetadata, createdUpdated)void
-

helper for postDeployTasks

+

helper for postDeployTasks +fixes an issue where shared data extensions are not visible in data designer on child BU; SF known issue: https://issues.salesforce.com/#q=W-11031095

DataExtension.(childBuName, buObjectParent, clientParent, sharedDataExtensions)Promise.<Array.<string>>

helper for DataExtension.#postDeployFixShared

@@ -8589,6 +8590,7 @@ helper for [postDeployTasks](#Automation.postDeployTasks) ## DataExtension.(upsertedMetadata, originalMetadata, createdUpdated) ⇒ void helper for [postDeployTasks](#DataExtension.postDeployTasks) +fixes an issue where shared data extensions are not visible in data designer on child BU; SF known issue: https://issues.salesforce.com/#q=W-11031095 **Kind**: global function diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index 6f0edfec7..8eb73b4af 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -361,6 +361,7 @@ class DataExtension extends MetadataType { /** * helper for {@link DataExtension.postDeployTasks} + * fixes an issue where shared data extensions are not visible in data designer on child BU; SF known issue: https://issues.salesforce.com/#q=W-11031095 * * @param {TYPE.DataExtensionMap} upsertedMetadata metadata mapped by their keyField * @param {TYPE.DataExtensionMap} originalMetadata metadata to be updated (contains additioanl fields) diff --git a/test/resources/9999999/dataExtension/delete-response.xml b/test/resources/9999999/dataExtension/delete-response.xml new file mode 100644 index 000000000..3c5d3b961 --- /dev/null +++ b/test/resources/9999999/dataExtension/delete-response.xml @@ -0,0 +1,42 @@ + + + + DeleteResponse + urn:uuid:cbcad7ed-d768-4dbd-b2b9-230cc05dca1b + urn:uuid:545315fb-1e5b-4b6b-a17a-da1fcac3524f + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + + + 2023-08-11T12:34:31Z + 2023-08-11T12:39:31Z + + + + + + + OK + Data Extension deleted. / Data Extension Fields deleted + 0 + + + 21711373-72c1-ec11-b83b-48df37d1deb7 + testExisting_dataExtension + + + + 5c0faaa8-81db-4530-8b47-c56432f68543 + OK + + + diff --git a/test/resources/9999999/dataExtensionField/retrieve-CustomerKey=[testExisting_dataExtension].[LastName]-response.xml b/test/resources/9999999/dataExtensionField/retrieve-CustomerKey=[testExisting_dataExtension].[LastName]-response.xml new file mode 100644 index 000000000..d040f9e3b --- /dev/null +++ b/test/resources/9999999/dataExtensionField/retrieve-CustomerKey=[testExisting_dataExtension].[LastName]-response.xml @@ -0,0 +1,44 @@ + + + + RetrieveResponse + urn:uuid:6d2f81c8-80ab-44d5-8664-206753e7ac8d + urn:uuid:a7354389-079e-4844-93b5-af6b7bf1d535 + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + + + 2022-04-21T19:21:52Z + 2022-04-21T19:26:52Z + + + + + + OK + cfe51e71-6a2b-4cb4-aecd-3777076d63bc + + + bea0e308-5d45-4181-a673-da9972a7c674 + [testExisting_dataExtension].[LastName] + LastName + 0 + + 50 + false + 1 + false + Text + + + + testExisting_dataExtension + + + + + diff --git a/test/type.dataExtension.test.js b/test/type.dataExtension.test.js index f16b073fd..02031e6eb 100644 --- a/test/type.dataExtension.test.js +++ b/test/type.dataExtension.test.js @@ -197,46 +197,60 @@ describe('type: dataExtension', () => { }); describe('Delete ================', () => { // TODO: add this test - it('Should delete the item'); // , async () => { - // // WHEN - // const result = await handler.deleteByKey('testInstance/testBU', 'mobileKeyword', [ - // 'testExisting_keyword', - // ]); - // // THEN - // assert.equal(process.exitCode, false, 'delete should not have thrown an error'); + it('Should delete the dataExtension', async () => { + // WHEN + const result = await handler.deleteByKey( + 'testInstance/testBU', + 'dataExtension', + 'testExisting_dataExtension' + ); + // THEN + assert.equal(process.exitCode, false, 'delete should not have thrown an error'); - // assert.equal(result, true, 'should have deleted the item'); - // return; - // }); + assert.equal(result, true, 'should have deleted the item'); + return; + }); + it('Should delete the dataExtensionField', async () => { + // WHEN + const result = await handler.deleteByKey( + 'testInstance/testBU', + 'dataExtensionField', + 'testExisting_dataExtension.LastName' + ); + // THEN + assert.equal(process.exitCode, false, 'delete should not have thrown an error'); + + assert.equal(result, true, 'should have deleted the item'); + return; + }); }); describe('CI/CD ================', () => { - // TODO: add this test - it('Should return a list of files based on their type and key'); // , async () => { - // // WHEN - // const fileList = await handler.getFilesToCommit( - // 'testInstance/testBU', - // 'mobileKeyword', - // ['testExisting_keyword'] - // ); - // // THEN - // assert.equal( - // process.exitCode, - // false, - // 'getFilesToCommit should not have thrown an error' - // ); - // assert.equal(fileList.length, 2, 'expected only 2 file paths'); + it('Should return a list of files based on their type and key', async () => { + // WHEN + const fileList = await handler.getFilesToCommit( + 'testInstance/testBU', + 'dataExtension', + ['testExisting_dataExtension'] + ); + // THEN + assert.equal( + process.exitCode, + false, + 'getFilesToCommit should not have thrown an error' + ); + assert.equal(fileList.length, 2, 'expected only 2 file paths (json, md)'); - // assert.equal( - // fileList[0].split('\\').join('/'), - // 'retrieve/testInstance/testBU/mobileKeyword/testExisting_keyword.mobileKeyword-meta.json', - // 'wrong JSON path' - // ); - // assert.equal( - // fileList[1].split('\\').join('/'), - // 'retrieve/testInstance/testBU/mobileKeyword/testExisting_keyword.mobileKeyword-meta.amp', - // 'wrong AMP path' - // ); - // return; - // }); + assert.equal( + fileList[0].split('\\').join('/'), + 'retrieve/testInstance/testBU/dataExtension/testExisting_dataExtension.dataExtension-meta.json', + 'wrong JSON path' + ); + assert.equal( + fileList[1].split('\\').join('/'), + 'retrieve/testInstance/testBU/dataExtension/testExisting_dataExtension.dataExtension-doc.md', + 'wrong MD path' + ); + return; + }); }); }); From 52463a3f061946eea287a179d4e074e393702397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 11 Aug 2023 15:33:40 +0200 Subject: [PATCH 08/70] #940: add test for retrieving shared data extensions --- .../dataExtension/retrieve-expected.json | 55 +++ .../dataExtension/retrieve-expected.md | 18 + .../dataExtension/retrieve-response.xml | 28 +- .../dataExtensionField/retrieve-response.xml | 98 +++++ .../retrieve-response.xml | 303 ++++++++++++++ ...extensionORContentType=hidden-response.xml | 387 ++++++++++++++++++ .../1111111/dataFolder/retrieve-response.xml | 107 +++-- test/type.dataExtension.test.js | 46 ++- 8 files changed, 1013 insertions(+), 29 deletions(-) create mode 100644 test/resources/1111111/dataExtension/retrieve-expected.json create mode 100644 test/resources/1111111/dataExtension/retrieve-expected.md create mode 100644 test/resources/1111111/dataExtensionField/retrieve-response.xml create mode 100644 test/resources/1111111/dataExtensionTemplate/retrieve-response.xml create mode 100644 test/resources/1111111/dataFolder/retrieve-ContentType=synchronizeddataextensionORContentType=shared_salesforcedataextensionORContentType=shared_dataextensionORContentType=shared_dataORContentType=salesforcedataextensionORContentType=dataextensionORContentType=hidden-response.xml diff --git a/test/resources/1111111/dataExtension/retrieve-expected.json b/test/resources/1111111/dataExtension/retrieve-expected.json new file mode 100644 index 000000000..f64ad16df --- /dev/null +++ b/test/resources/1111111/dataExtension/retrieve-expected.json @@ -0,0 +1,55 @@ +{ + "CustomerKey": "testExisting_dataExtensionShared", + "DataRetentionPeriodLength": 6, + "DataRetentionPeriodUnitOfMeasure": 5, + "DeleteAtEndOfRetentionPeriod": false, + "Description": "bla bla", + "Fields": [ + { + "DefaultValue": "", + "FieldType": "Text", + "IsPrimaryKey": false, + "IsRequired": false, + "MaxLength": 50, + "Name": "FirstName" + }, + { + "DefaultValue": "", + "FieldType": "Text", + "IsPrimaryKey": false, + "IsRequired": false, + "MaxLength": 50, + "Name": "LastName" + }, + { + "DefaultValue": "", + "FieldType": "EmailAddress", + "IsPrimaryKey": false, + "IsRequired": true, + "MaxLength": 254, + "Name": "EmailAddress" + }, + { + "DefaultValue": "", + "FieldType": "Text", + "IsPrimaryKey": true, + "IsRequired": true, + "MaxLength": 50, + "Name": "ContactKey" + } + ], + "IsSendable": true, + "IsTestable": true, + "Name": "testExisting_dataExtensionShared", + "ResetRetentionPeriodOnImport": false, + "RetainUntil": "", + "RowBasedRetention": true, + "SendableDataExtensionField": { + "Name": "ContactKey" + }, + "SendableSubscriberField": { + "Name": "Subscriber Key" + }, + "r__folder_ContentType": "shared_dataextension", + "r__folder_Path": "Shared Items/Shared Data Extensions" +} diff --git a/test/resources/1111111/dataExtension/retrieve-expected.md b/test/resources/1111111/dataExtension/retrieve-expected.md new file mode 100644 index 000000000..7e9a1d2fa --- /dev/null +++ b/test/resources/1111111/dataExtension/retrieve-expected.md @@ -0,0 +1,18 @@ +## testExisting_dataExtensionShared + +**Description:** bla bla + +**Folder:** Shared Items/Shared Data Extensions/ + +**Fields in table:** 4 + +**Sendable:** Yes (`ContactKey` to `Subscriber Key`) + +**Testable:** Yes + +| Name | FieldType | MaxLength | IsPrimaryKey | IsNullable | DefaultValue | +| --- | --- | --- | --- | --- | --- | +| FirstName | Text | 50 | - | + | | +| LastName | Text | 50 | - | + | | +| EmailAddress | EmailAddress | 254 | - | - | | +| ContactKey | Text | 50 | + | - | | diff --git a/test/resources/1111111/dataExtension/retrieve-response.xml b/test/resources/1111111/dataExtension/retrieve-response.xml index 68ea2dd11..cad82d0b3 100644 --- a/test/resources/1111111/dataExtension/retrieve-response.xml +++ b/test/resources/1111111/dataExtension/retrieve-response.xml @@ -21,6 +21,32 @@ OK d175de6e-c8e4-4f5d-9c1d-ad64426ff4b7 + + + 2022-04-21T06:56:27.927 + 2022-04-21T06:56:27.927 + 21711373-72c1-ec11-b83b-shared + testExisting_dataExtensionShared + testExisting_dataExtensionShared + bla bla + true + true + + + + ContactKey + + + _SubscriberKey + + 6 + 5 + true + false + false + + 89356 + - \ No newline at end of file + diff --git a/test/resources/1111111/dataExtensionField/retrieve-response.xml b/test/resources/1111111/dataExtensionField/retrieve-response.xml new file mode 100644 index 000000000..c3810d2ca --- /dev/null +++ b/test/resources/1111111/dataExtensionField/retrieve-response.xml @@ -0,0 +1,98 @@ + + + + RetrieveResponse + urn:uuid:6d2f81c8-80ab-44d5-8664-206753e7ac8d + urn:uuid:a7354389-079e-4844-93b5-af6b7bf1d535 + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + + + 2022-04-21T19:21:52Z + 2022-04-21T19:26:52Z + + + + + + OK + cfe51e71-6a2b-4cb4-aecd-3777076d63bc + + + shared-8018397d-880d-4f88-940e-3b4eca098a0c + [testExisting_dataExtensionShared].[ContactKey] + ContactKey + 0 + + 50 + true + 3 + true + Text + + + + testExisting_dataExtensionShared + + + + + shared-bea0e308-5d45-4181-a673-da9972a7c674 + [testExisting_dataExtensionShared].[LastName] + LastName + 0 + + 50 + false + 1 + false + Text + + + + testExisting_dataExtensionShared + + + + + shared-2557b461-a699-4744-950d-e80a19afc2dc + [testExisting_dataExtensionShared].[EmailAddress] + EmailAddress + 0 + + 254 + true + 2 + false + EmailAddress + + + + testExisting_dataExtensionShared + + + + + shared-42760528-a8c5-44dd-8c1d-ff34e5daee54 + [testExisting_dataExtensionShared].[FirstName] + FirstName + 0 + + 50 + false + 0 + false + Text + + + + testExisting_dataExtensionShared + + + + + diff --git a/test/resources/1111111/dataExtensionTemplate/retrieve-response.xml b/test/resources/1111111/dataExtensionTemplate/retrieve-response.xml new file mode 100644 index 000000000..31b41f0b5 --- /dev/null +++ b/test/resources/1111111/dataExtensionTemplate/retrieve-response.xml @@ -0,0 +1,303 @@ + + + + RetrieveResponse + urn:uuid:110bbc72-d639-4e67-ab93-68e081bcf3a0 + urn:uuid:753eed05-f925-4113-8a98-e81ca69c96fd + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + + + 2022-04-21T19:21:49Z + 2022-04-21T19:26:49Z + + + + + + OK + 82fd5f74-ad8c-49ad-958f-867d4a4b53b0 + + + + IsSendable + False + + + IsTestable + False + + + SendableCustomObjectField + + + + SendableSubscriberField + + + + DataRetentionPeriodLength + + + + DataRetentionPeriodUnitOfMeasure + + + + RowBasedRetention + False + + + ResetRetentionPeriodOnImport + False + + + DeleteAtEndOfRetentionPeriod + False + + + RetainUntil + + + cfd6fc95-d594-ea11-a2e6-1402ec938a35 + 086D14D3-5057-462B-AF33-01CA8D1FE87A + DomainExclusion + Domain Exclusion Data Extension Template + + + + + IsSendable + True + + + IsTestable + False + + + SendableCustomObjectField + SubscriberKey + + + SendableSubscriberField + _SubscriberKey + + + DataRetentionPeriodLength + + + + DataRetentionPeriodUnitOfMeasure + + + + RowBasedRetention + False + + + ResetRetentionPeriodOnImport + False + + + DeleteAtEndOfRetentionPeriod + False + + + RetainUntil + + + 1fd7fc95-d594-ea11-a2e6-1402ec938a35 + B6E8AE4C-3D93-49B1-B299-E0AE734213DD + TriggeredSendDataExtension + Triggered Send Source Data Extension Template + + + + + IsSendable + True + + + IsTestable + False + + + SendableCustomObjectField + Email Address + + + SendableSubscriberField + _EmailAddress + + + DataRetentionPeriodLength + + + + DataRetentionPeriodUnitOfMeasure + + + + RowBasedRetention + False + + + ResetRetentionPeriodOnImport + False + + + DeleteAtEndOfRetentionPeriod + False + + + RetainUntil + + + bb1df59b-d594-ea11-a2e6-1402ec938a35 + 23471ECA-8710-4512-9296-040CA86FBD9E + CONTEXTUAL_SUPPRESSION_LISTS + Used to create new auto-suppression lists + + + + + IsSendable + True + + + IsTestable + False + + + SendableCustomObjectField + EmailAddress + + + SendableSubscriberField + _SubscriberKey + + + DataRetentionPeriodLength + + + + DataRetentionPeriodUnitOfMeasure + + + + RowBasedRetention + False + + + ResetRetentionPeriodOnImport + False + + + DeleteAtEndOfRetentionPeriod + False + + + RetainUntil + + + e61df59b-d594-ea11-a2e6-1402ec938a35 + 00A4369E-0B57-4EF2-BFFA-E3F23B4D1098 + SocialPages Default Template Extension + Required for contacts. Used by Smart Capture for Social Pages. + + + + + IsSendable + True + + + IsTestable + False + + + SendableCustomObjectField + SubscriberKey + + + SendableSubscriberField + _SubscriberKey + + + DataRetentionPeriodLength + 6 + + + DataRetentionPeriodUnitOfMeasure + 5 + + + RowBasedRetention + False + + + ResetRetentionPeriodOnImport + False + + + DeleteAtEndOfRetentionPeriod + False + + + RetainUntil + + + 37d8c7f2-ad19-4084-8d18-4dda1dc36772 + DAE95D91-762C-4124-B082-0433165ADD30 + AudienceBuilderResult + Used for creating audience builder result destinations. + + + + + IsSendable + True + + + IsTestable + False + + + SendableCustomObjectField + ContactKey + + + SendableSubscriberField + _SubscriberKey + + + DataRetentionPeriodLength + + + + DataRetentionPeriodUnitOfMeasure + 0 + + + RowBasedRetention + False + + + ResetRetentionPeriodOnImport + False + + + DeleteAtEndOfRetentionPeriod + False + + + RetainUntil + + + 941cf36b-f927-4674-8468-c9a3bed7cae4 + BE1B7591-BFA6-473F-A6CE-0E458204865B + Event DE Template + Event Data Extension Template + + + + \ No newline at end of file diff --git a/test/resources/1111111/dataFolder/retrieve-ContentType=synchronizeddataextensionORContentType=shared_salesforcedataextensionORContentType=shared_dataextensionORContentType=shared_dataORContentType=salesforcedataextensionORContentType=dataextensionORContentType=hidden-response.xml b/test/resources/1111111/dataFolder/retrieve-ContentType=synchronizeddataextensionORContentType=shared_salesforcedataextensionORContentType=shared_dataextensionORContentType=shared_dataORContentType=salesforcedataextensionORContentType=dataextensionORContentType=hidden-response.xml new file mode 100644 index 000000000..41bb03f59 --- /dev/null +++ b/test/resources/1111111/dataFolder/retrieve-ContentType=synchronizeddataextensionORContentType=shared_salesforcedataextensionORContentType=shared_dataextensionORContentType=shared_dataORContentType=salesforcedataextensionORContentType=dataextensionORContentType=hidden-response.xml @@ -0,0 +1,387 @@ + + + + RetrieveResponse + urn:uuid:4d209b2f-d7ce-4e6e-916c-c8642d368866 + urn:uuid:a850f043-1422-4d16-8443-702dd2f9f13a + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + + + 2023-08-11T13:15:46Z + 2023-08-11T13:20:46Z + + + + + + OK + ba1b0c59-78c4-4608-8423-35dda2248d4d + + + 1111111 + + + 2016-07-22T11:52:18.73 + 2016-07-22T11:52:19.603 + 89344 + + shared_data_default + + + 0 + + + Shared Items + + shared_data + true + false + false + + + + 1111111 + + + 2016-07-22T11:52:19.6 + 2016-07-22T11:52:19.6 + 89355 + + dataextension_default + + + 0 + + + Data Extensions + + dataextension + true + false + true + + + + 1111111 + + + 2016-07-22T11:52:19.603 + 2016-07-22T11:52:19.603 + 89356 + + shared_dataextension_default + + + 89344 + + shared_data_default + + Shared Data Extensions + + shared_dataextension + true + false + true + + + + 1111111 + + + 2016-07-29T12:13:27.28 + 2016-07-29T12:13:27.28 + 90889 + + HiddenCategory_default + + + 0 + + + HiddenCategory + Hidden folder to store Hidden items + Hidden + true + false + true + + + + 1111111 + + + 2016-08-23T09:22:27.54 + 2016-08-23T09:22:27.54 + 93698 + + salesforcedataextension_default + + + 0 + + + Salesforce Data Extensions + + salesforcedataextension + true + false + true + + + + 1111111 + + + 2016-08-23T09:22:27.55 + 2016-08-23T09:22:27.55 + 93699 + + shared_salesforcedataextension_defau + + + 89344 + + shared_data_default + + Shared Salesforce Data Extensions + + shared_salesforcedataextension + true + false + true + + + + 1111111 + + + 2020-01-27T10:50:46.573 + 2020-01-27T10:50:46.573 + 309082 + + + + + 89355 + + dataextension_default + + QueryStudioResults + + dataextension + true + true + true + + + + 1111111 + + + 2020-04-17T08:14:19.763 + 2020-04-17T08:14:19.763 + 587750 + + + + + 89356 + + shared_dataextension_default + + catalyst target 1 + + shared_dataextension + true + true + true + + + + 1111111 + + + 2020-07-09T02:30:12.38 + 2021-12-16T03:43:30.753 + 605618 + + dataextension_default + + + 89355 + + dataextension_default + + Audiences + + dataextension + true + true + true + + + + 1111111 + + + 2020-12-25T06:11:40.107 + 2021-12-23T10:51:24.393 + 633441 + + + + + 89355 + + dataextension_default + + System DEs + + dataextension + true + true + true + + + + 1111111 + + + 2021-01-30T11:54:36.11 + 2021-01-30T11:54:36.11 + 638814 + + 0b72bf27-0678-484d-ac0b-a8762b9bec33 + + + 89356 + + shared_dataextension_default + + Customer 360 Segments + All Customer 360 segments will be grouped here. Each sub-folder relates to an activation profile name. + shared_dataextension + true + false + true + + + + 1111111 + + + 2021-01-30T11:56:38.77 + 2021-01-30T11:56:38.77 + 638815 + + cedd206d-178e-41cb-8965-ce255975b046 + + + 638814 + + 0b72bf27-0678-484d-ac0b-a8762b9bec33 + + FirstAudience360 Segment + + shared_dataextension + true + false + false + + + + 1111111 + + + 2021-02-07T10:44:01.413 + 2021-12-16T03:43:33.38 + 639967 + + + + + 89355 + + dataextension_default + + TestAudiences + + dataextension + true + true + true + + + + 1111111 + + + 2021-08-04T15:17:18.533 + 2021-08-04T15:17:18.567 + 675203 + + A19F7E38-7369-497E-826F-D551F17FB0B4 + + + 0 + + + Synchronized Data Extensions + + synchronizeddataextension + true + false + true + + + + 1111111 + + + 2021-10-03T05:01:37.23 + 2021-10-03T05:01:37.37 + 688352 + + B80AE306-55BC-4C2E-A79F-8CCA486F0BA0 + + + 0 + + + Synchronized Data Extensions + + synchronizeddataextension + true + false + true + + + + 1111111 + + + 2022-10-26T09:48:38.293 + 2022-10-26T09:48:38.31 + 757145 + + 0ACB800B-AA8B-4F0F-9642-4907592C919C + + + 0 + + + Synchronized Data Extensions + + synchronizeddataextension + true + false + true + + + + diff --git a/test/resources/1111111/dataFolder/retrieve-response.xml b/test/resources/1111111/dataFolder/retrieve-response.xml index 30b16237c..73e3ec0c1 100644 --- a/test/resources/1111111/dataFolder/retrieve-response.xml +++ b/test/resources/1111111/dataFolder/retrieve-response.xml @@ -1,43 +1,98 @@ - + RetrieveResponse - urn:uuid:f36f3303-3b5a-4641-8109-b26447634d91 - urn:uuid:33983968-28c4-4379-bb5f-f80ae32eb988 + urn:uuid:6d2f81c8-80ab-44d5-8664-206753e7ac8d + urn:uuid:a7354389-079e-4844-93b5-af6b7bf1d535 http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous - - 2022-04-19T20:03:41Z - 2022-04-19T20:08:41Z + + 2022-04-21T19:21:52Z + 2022-04-21T19:26:52Z OK - 02cd5ccb-8f84-4651-826f-71169eeecf05 - - - 1111111 - + cfe51e71-6a2b-4cb4-aecd-3777076d63bc + - 2016-07-22T11:52:19.6 - 2016-07-22T11:52:19.6 - 1 - - dataextension_default - + 8018397d-880d-4f88-940e-3b4eca098a0c + [testExisting_dataExtension].[ContactKey] + ContactKey + 0 + + 50 + true + 3 + true + Text + - 0 - - Data Extensions - - dataextension - true - false - true + testExisting_dataExtension + + + + + bea0e308-5d45-4181-a673-da9972a7c674 + [testExisting_dataExtension].[LastName] + LastName + 0 + + 50 + false + 1 + false + Text + + + + testExisting_dataExtension + + + + + 2557b461-a699-4744-950d-e80a19afc2dc + [testExisting_dataExtension].[EmailAddress] + EmailAddress + 0 + + 254 + true + 2 + false + EmailAddress + + + + testExisting_dataExtension + + + + + 42760528-a8c5-44dd-8c1d-ff34e5daee54 + [testExisting_dataExtension].[FirstName] + FirstName + 0 + + 50 + false + 0 + false + Text + + + + testExisting_dataExtension + - \ No newline at end of file + diff --git a/test/type.dataExtension.test.js b/test/type.dataExtension.test.js index 02031e6eb..096c6f54d 100644 --- a/test/type.dataExtension.test.js +++ b/test/type.dataExtension.test.js @@ -50,6 +50,48 @@ describe('type: dataExtension', () => { ); return; }); + it('Should retrieve a shared dataExtension', async () => { + // WHEN + await handler.retrieve('testInstance/_ParentBU_', ['dataExtension']); + // THEN + assert.equal(process.exitCode, false, 'retrieve should not have thrown an error'); + // get results from cache + const result = cache.getCache(); + assert.equal( + result.dataExtension ? Object.keys(result.dataExtension).length : 0, + 1, + 'only one dataExtension expected' + ); + assert.deepEqual( + await testUtils.getActualJson( + 'testExisting_dataExtensionShared', + 'dataExtension', + '_ParentBU_' + ), + await testUtils.getExpectedJson('1111111', 'dataExtension', 'retrieve'), + + 'returned metadata was not equal expected' + ); + // check if MD file was created and equals expectations + expect( + file( + testUtils.getActualDoc( + 'testExisting_dataExtensionShared', + 'dataExtension', + '_ParentBU_' + ) + ) + ).to.equal( + file(testUtils.getExpectedFile('1111111', 'dataExtension', 'retrieve', 'md')) + ); + + assert.equal( + testUtils.getAPIHistoryLength(), + 4, + 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' + ); + return; + }); }); describe('Deploy ================', () => { beforeEach(() => { @@ -65,8 +107,8 @@ describe('type: dataExtension', () => { const result = cache.getCache(); assert.equal( result.dataExtension ? Object.keys(result.dataExtension).length : 0, - 2, - 'two dataExtensions expected' + 3, + 'three dataExtensions expected' ); // insert assert.deepEqual( From 3749ac15c46915df9c90512a56ad74de9a90529d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 11 Aug 2023 16:40:22 +0200 Subject: [PATCH 09/70] #940: add deploy test for shared dataExtensions with --fixShared --- lib/index.js | 1 + lib/metadataTypes/DataExtension.js | 4 +- ...ataExtensionShared.dataExtension-meta.json | 59 +++ ...ataExtensionShared.dataExtension-meta.json | 23 + .../rowset/get-response.json | 13 + .../dataExtension/create-expected.json | 23 + .../1111111/dataExtension/create-response.xml | 59 +++ .../dataExtension/update-expected.json | 55 +++ .../1111111/dataExtension/update-response.xml | 57 +++ ...[TriggerUpdate_randomNumber_]-response.xml | 45 ++ ...tExisting_dataExtensionShared-response.xml | 98 ++++ ...tExisting_dataExtensionShared-response.xml | 98 ++++ .../1111111/dataFolder/retrieve-response.xml | 443 ++++++++++++++--- .../schema/attributeGroups/get-response.json | 43 ++ .../schema/setDefinitions/get-response.json | 447 ++++++++++++++++++ test/type.attributeGroup.test.js | 8 +- test/type.attributeSet.test.js | 8 +- test/type.dataExtension.test.js | 110 ++++- 18 files changed, 1505 insertions(+), 89 deletions(-) create mode 100644 test/mockRoot/deploy/testInstance/_ParentBU_/dataExtension/testExisting_dataExtensionShared.dataExtension-meta.json create mode 100644 test/mockRoot/deploy/testInstance/_ParentBU_/dataExtension/testNew_dataExtensionShared.dataExtension-meta.json create mode 100644 test/resources/1111111/data/v1/customobjectdata/key/testExisting_dataExtensionShared/rowset/get-response.json create mode 100644 test/resources/1111111/dataExtension/create-expected.json create mode 100644 test/resources/1111111/dataExtension/create-response.xml create mode 100644 test/resources/1111111/dataExtension/update-expected.json create mode 100644 test/resources/1111111/dataExtension/update-response.xml create mode 100644 test/resources/1111111/dataExtensionField/retrieve-CustomerKey=[testExisting_dataExtensionShared].[TriggerUpdate_randomNumber_]-response.xml create mode 100644 test/resources/1111111/dataExtensionField/retrieve-DataExtension.CustomerKey=testExisting_dataExtensionShared-response.xml create mode 100644 test/resources/1111111/dataExtensionField/retrieve-DataExtension.CustomerKey=testNew_dataExtensionSharedORDataExtension.CustomerKey=testExisting_dataExtensionShared-response.xml diff --git a/lib/index.js b/lib/index.js index bcc150061..535fa4f08 100644 --- a/lib/index.js +++ b/lib/index.js @@ -63,6 +63,7 @@ class Mcdev { 'noLogColors', 'noLogFile', 'refresh', + '_runningTest', 'schedule', 'skipInteraction', ]; diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index 8eb73b4af..80b7174c0 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -579,7 +579,9 @@ class DataExtension extends MetadataType { static async #fixShared_item_addField(buObjectChildBu, clientChildBu, deKey, deId) { this.buObject = buObjectChildBu; this.client = clientChildBu; - const randomSuffix = Math.floor(Math.random() * 9999999999); + const randomSuffix = Util.OPTIONS._runningTest + ? '_randomNumber_' + : Math.floor(Math.random() * 9999999999); // add a new field to the shared DE to trigger an update to the data model const soapType = this.definition.soapType || this.definition.type; await this.client.soap.update( diff --git a/test/mockRoot/deploy/testInstance/_ParentBU_/dataExtension/testExisting_dataExtensionShared.dataExtension-meta.json b/test/mockRoot/deploy/testInstance/_ParentBU_/dataExtension/testExisting_dataExtensionShared.dataExtension-meta.json new file mode 100644 index 000000000..23a55d5a1 --- /dev/null +++ b/test/mockRoot/deploy/testInstance/_ParentBU_/dataExtension/testExisting_dataExtensionShared.dataExtension-meta.json @@ -0,0 +1,59 @@ +{ + "CustomerKey": "testExisting_dataExtensionShared", + "Name": "testExisting_dataExtensionShared", + "Description": "Container for my test emails", + "IsSendable": true, + "IsTestable": true, + "SendableDataExtensionField": { "Name": "ContactKey" }, + "SendableSubscriberField": { "Name": "Subscriber Key" }, + "DataRetentionPeriodLength": 6, + "DataRetentionPeriodUnitOfMeasure": 5, + "RowBasedRetention": true, + "ResetRetentionPeriodOnImport": false, + "DeleteAtEndOfRetentionPeriod": false, + "RetainUntil": "", + "Fields": [ + { + "Name": "FirstName", + "DefaultValue": "", + "MaxLength": 50, + "IsRequired": false, + "IsPrimaryKey": false, + "FieldType": "Text" + }, + { + "Name": "LastName", + "DefaultValue": "", + "MaxLength": 55, + "IsRequired": false, + "IsPrimaryKey": false, + "FieldType": "Text" + }, + { + "Name": "EmailAddress", + "DefaultValue": "", + "MaxLength": 254, + "IsRequired": true, + "IsPrimaryKey": false, + "FieldType": "EmailAddress" + }, + { + "Name": "testField", + "DefaultValue": "", + "MaxLength": 254, + "IsRequired": false, + "IsPrimaryKey": false, + "FieldType": "Text" + }, + { + "Name": "ContactKey", + "DefaultValue": "", + "MaxLength": 50, + "IsRequired": true, + "IsPrimaryKey": true, + "FieldType": "Text" + } + ], + "r__folder_ContentType": "shared_dataextension", + "r__folder_Path": "Shared Items/Shared Data Extensions" +} diff --git a/test/mockRoot/deploy/testInstance/_ParentBU_/dataExtension/testNew_dataExtensionShared.dataExtension-meta.json b/test/mockRoot/deploy/testInstance/_ParentBU_/dataExtension/testNew_dataExtensionShared.dataExtension-meta.json new file mode 100644 index 000000000..daf504e7c --- /dev/null +++ b/test/mockRoot/deploy/testInstance/_ParentBU_/dataExtension/testNew_dataExtensionShared.dataExtension-meta.json @@ -0,0 +1,23 @@ +{ + "CustomerKey": "testNew_dataExtensionShared", + "Name": "testNew_dataExtensionShared", + "Description": "", + "IsSendable": false, + "IsTestable": false, + "RowBasedRetention": false, + "ResetRetentionPeriodOnImport": false, + "DeleteAtEndOfRetentionPeriod": false, + "RetainUntil": "", + "Fields": [ + { + "Name": "testField", + "DefaultValue": "", + "MaxLength": 254, + "IsRequired": true, + "IsPrimaryKey": false, + "FieldType": "Text" + } + ], + "r__folder_ContentType": "shared_dataextension", + "r__folder_Path": "Shared Items/Shared Data Extensions" +} diff --git a/test/resources/1111111/data/v1/customobjectdata/key/testExisting_dataExtensionShared/rowset/get-response.json b/test/resources/1111111/data/v1/customobjectdata/key/testExisting_dataExtensionShared/rowset/get-response.json new file mode 100644 index 000000000..1156309d9 --- /dev/null +++ b/test/resources/1111111/data/v1/customobjectdata/key/testExisting_dataExtensionShared/rowset/get-response.json @@ -0,0 +1,13 @@ +{ + "links": { + "self": "/v1/customobjectdata/token/ea0a44dc-b679-4d7d-8b77-e5d3f106e854/rowset?$page=1" + }, + "requestToken": "ea0a44dc-b679-4d7d-8b77-e5d3f106e854", + "tokenExpireDateUtc": "2023-01-26T13:54:59.883", + "customObjectId": "30400c03-0ec4-ec11-b83c-48df37d1de8b", + "customObjectKey": "testExisting_dataExtensionShared", + "pageSize": 1, + "page": 1, + "count": 0, + "top": 0 +} diff --git a/test/resources/1111111/dataExtension/create-expected.json b/test/resources/1111111/dataExtension/create-expected.json new file mode 100644 index 000000000..222e46023 --- /dev/null +++ b/test/resources/1111111/dataExtension/create-expected.json @@ -0,0 +1,23 @@ +{ + "CustomerKey": "testNew_dataExtensionShared", + "DeleteAtEndOfRetentionPeriod": false, + "Description": "", + "Fields": [ + { + "DefaultValue": "", + "FieldType": "Text", + "IsPrimaryKey": false, + "IsRequired": true, + "MaxLength": 254, + "Name": "testField" + } + ], + "IsSendable": false, + "IsTestable": false, + "Name": "testNew_dataExtensionShared", + "ResetRetentionPeriodOnImport": false, + "RetainUntil": "", + "RowBasedRetention": false, + "r__folder_ContentType": "shared_dataextension", + "r__folder_Path": "Shared Items/Shared Data Extensions" +} diff --git a/test/resources/1111111/dataExtension/create-response.xml b/test/resources/1111111/dataExtension/create-response.xml new file mode 100644 index 000000000..207f5ca3c --- /dev/null +++ b/test/resources/1111111/dataExtension/create-response.xml @@ -0,0 +1,59 @@ + + + + CreateResponse + urn:uuid:3a5f1edf-dacb-4d19-a0d3-4e0dcc9f507d + urn:uuid:fb395987-bf45-42d6-af73-f51816c7b73f + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + + + 2022-04-24T20:35:10Z + 2022-04-24T20:40:10Z + + + + + + + OK + Data Extension created. + 0 + 0 + shared-0ec4-ec11-b83c-48df37d1de8a + + + shared-0ec4-ec11-b83c-48df37d1de8a + testNew_dataExtensionShared + testNew_dataExtensionShared + + false + false + false + false + false + + + + + + testField + + 254 + true + false + Text + + + 89356 + + + c41aa55a-90e3-4021-9f82-103796aae6da + OK + + + diff --git a/test/resources/1111111/dataExtension/update-expected.json b/test/resources/1111111/dataExtension/update-expected.json new file mode 100644 index 000000000..e889c1f87 --- /dev/null +++ b/test/resources/1111111/dataExtension/update-expected.json @@ -0,0 +1,55 @@ +{ + "CustomerKey": "testExisting_dataExtensionShared", + "DeleteAtEndOfRetentionPeriod": false, + "Description": "Container for my test emails", + "Fields": [ + { + "DefaultValue": "", + "FieldType": "Text", + "IsPrimaryKey": false, + "IsRequired": false, + "MaxLength": 50, + "Name": "FirstName" + }, + { + "DefaultValue": "", + "FieldType": "Text", + "IsPrimaryKey": false, + "IsRequired": false, + "MaxLength": 55, + "Name": "LastName" + }, + { + "DefaultValue": "", + "FieldType": "EmailAddress", + "IsPrimaryKey": false, + "IsRequired": true, + "MaxLength": 254, + "Name": "EmailAddress" + }, + { + "DefaultValue": "", + "FieldType": "Text", + "IsPrimaryKey": true, + "IsRequired": true, + "MaxLength": 50, + "Name": "ContactKey" + }, + { + "DefaultValue": "", + "FieldType": "Text", + "IsPrimaryKey": false, + "IsRequired": false, + "MaxLength": 254, + "Name": "testField" + } + ], + "IsSendable": false, + "IsTestable": false, + "Name": "testExisting_dataExtensionShared", + "ResetRetentionPeriodOnImport": false, + "RetainUntil": "", + "RowBasedRetention": true, + "r__folder_ContentType": "shared_dataextension", + "r__folder_Path": "Shared Items/Shared Data Extensions" +} diff --git a/test/resources/1111111/dataExtension/update-response.xml b/test/resources/1111111/dataExtension/update-response.xml new file mode 100644 index 000000000..f2122b7e1 --- /dev/null +++ b/test/resources/1111111/dataExtension/update-response.xml @@ -0,0 +1,57 @@ + + + + UpdateResponse + urn:uuid:994e213d-1125-450d-b187-32e3870147d1 + urn:uuid:f1fc86ef-c0c8-4c17-a901-a15fc2631f76 + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + + + 2022-04-26T20:49:03Z + 2022-04-26T20:54:03Z + + + + + + + OK + Data Extension updated. + 0 + + + 21711373-72c1-ec11-b83b-shared + testExisting_dataExtensionShared + testExisting_dataExtensionShared + Container for my test emails + false + false + false + false + false + + + + + + testField + + 254 + true + false + Text + + + 89356 + + + dbfedcb6-a809-4101-b314-a7b920c9fb1e + OK + + + diff --git a/test/resources/1111111/dataExtensionField/retrieve-CustomerKey=[testExisting_dataExtensionShared].[TriggerUpdate_randomNumber_]-response.xml b/test/resources/1111111/dataExtensionField/retrieve-CustomerKey=[testExisting_dataExtensionShared].[TriggerUpdate_randomNumber_]-response.xml new file mode 100644 index 000000000..ddb2cff52 --- /dev/null +++ b/test/resources/1111111/dataExtensionField/retrieve-CustomerKey=[testExisting_dataExtensionShared].[TriggerUpdate_randomNumber_]-response.xml @@ -0,0 +1,45 @@ + + + + RetrieveResponse + urn:uuid:6d2f81c8-80ab-44d5-8664-206753e7ac8d + urn:uuid:a7354389-079e-4844-93b5-af6b7bf1d535 + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + + + 2022-04-21T19:21:52Z + 2022-04-21T19:26:52Z + + + + + + OK + cfe51e71-6a2b-4cb4-aecd-3777076d63bc + + + TriggerUpdate-8018397d-880d-4f88-940e-3b4eca098a0c + [testExisting_dataExtensionShared].[TriggerUpdate_randomNumber_] + TriggerUpdate_randomNumber_ + 0 + + 50 + true + 3 + true + Text + + + + testExisting_dataExtensionShared + + + + + + diff --git a/test/resources/1111111/dataExtensionField/retrieve-DataExtension.CustomerKey=testExisting_dataExtensionShared-response.xml b/test/resources/1111111/dataExtensionField/retrieve-DataExtension.CustomerKey=testExisting_dataExtensionShared-response.xml new file mode 100644 index 000000000..c3810d2ca --- /dev/null +++ b/test/resources/1111111/dataExtensionField/retrieve-DataExtension.CustomerKey=testExisting_dataExtensionShared-response.xml @@ -0,0 +1,98 @@ + + + + RetrieveResponse + urn:uuid:6d2f81c8-80ab-44d5-8664-206753e7ac8d + urn:uuid:a7354389-079e-4844-93b5-af6b7bf1d535 + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + + + 2022-04-21T19:21:52Z + 2022-04-21T19:26:52Z + + + + + + OK + cfe51e71-6a2b-4cb4-aecd-3777076d63bc + + + shared-8018397d-880d-4f88-940e-3b4eca098a0c + [testExisting_dataExtensionShared].[ContactKey] + ContactKey + 0 + + 50 + true + 3 + true + Text + + + + testExisting_dataExtensionShared + + + + + shared-bea0e308-5d45-4181-a673-da9972a7c674 + [testExisting_dataExtensionShared].[LastName] + LastName + 0 + + 50 + false + 1 + false + Text + + + + testExisting_dataExtensionShared + + + + + shared-2557b461-a699-4744-950d-e80a19afc2dc + [testExisting_dataExtensionShared].[EmailAddress] + EmailAddress + 0 + + 254 + true + 2 + false + EmailAddress + + + + testExisting_dataExtensionShared + + + + + shared-42760528-a8c5-44dd-8c1d-ff34e5daee54 + [testExisting_dataExtensionShared].[FirstName] + FirstName + 0 + + 50 + false + 0 + false + Text + + + + testExisting_dataExtensionShared + + + + + diff --git a/test/resources/1111111/dataExtensionField/retrieve-DataExtension.CustomerKey=testNew_dataExtensionSharedORDataExtension.CustomerKey=testExisting_dataExtensionShared-response.xml b/test/resources/1111111/dataExtensionField/retrieve-DataExtension.CustomerKey=testNew_dataExtensionSharedORDataExtension.CustomerKey=testExisting_dataExtensionShared-response.xml new file mode 100644 index 000000000..c3810d2ca --- /dev/null +++ b/test/resources/1111111/dataExtensionField/retrieve-DataExtension.CustomerKey=testNew_dataExtensionSharedORDataExtension.CustomerKey=testExisting_dataExtensionShared-response.xml @@ -0,0 +1,98 @@ + + + + RetrieveResponse + urn:uuid:6d2f81c8-80ab-44d5-8664-206753e7ac8d + urn:uuid:a7354389-079e-4844-93b5-af6b7bf1d535 + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + + + 2022-04-21T19:21:52Z + 2022-04-21T19:26:52Z + + + + + + OK + cfe51e71-6a2b-4cb4-aecd-3777076d63bc + + + shared-8018397d-880d-4f88-940e-3b4eca098a0c + [testExisting_dataExtensionShared].[ContactKey] + ContactKey + 0 + + 50 + true + 3 + true + Text + + + + testExisting_dataExtensionShared + + + + + shared-bea0e308-5d45-4181-a673-da9972a7c674 + [testExisting_dataExtensionShared].[LastName] + LastName + 0 + + 50 + false + 1 + false + Text + + + + testExisting_dataExtensionShared + + + + + shared-2557b461-a699-4744-950d-e80a19afc2dc + [testExisting_dataExtensionShared].[EmailAddress] + EmailAddress + 0 + + 254 + true + 2 + false + EmailAddress + + + + testExisting_dataExtensionShared + + + + + shared-42760528-a8c5-44dd-8c1d-ff34e5daee54 + [testExisting_dataExtensionShared].[FirstName] + FirstName + 0 + + 50 + false + 0 + false + Text + + + + testExisting_dataExtensionShared + + + + + diff --git a/test/resources/1111111/dataFolder/retrieve-response.xml b/test/resources/1111111/dataFolder/retrieve-response.xml index 73e3ec0c1..41bb03f59 100644 --- a/test/resources/1111111/dataFolder/retrieve-response.xml +++ b/test/resources/1111111/dataFolder/retrieve-response.xml @@ -7,91 +7,380 @@ xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> RetrieveResponse - urn:uuid:6d2f81c8-80ab-44d5-8664-206753e7ac8d - urn:uuid:a7354389-079e-4844-93b5-af6b7bf1d535 + urn:uuid:4d209b2f-d7ce-4e6e-916c-c8642d368866 + urn:uuid:a850f043-1422-4d16-8443-702dd2f9f13a http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous - - 2022-04-21T19:21:52Z - 2022-04-21T19:26:52Z + + 2023-08-11T13:15:46Z + 2023-08-11T13:20:46Z OK - cfe51e71-6a2b-4cb4-aecd-3777076d63bc - - - 8018397d-880d-4f88-940e-3b4eca098a0c - [testExisting_dataExtension].[ContactKey] - ContactKey - 0 - - 50 - true - 3 - true - Text - - - - testExisting_dataExtension - - - - - bea0e308-5d45-4181-a673-da9972a7c674 - [testExisting_dataExtension].[LastName] - LastName - 0 - - 50 - false - 1 - false - Text - - - - testExisting_dataExtension - - - - - 2557b461-a699-4744-950d-e80a19afc2dc - [testExisting_dataExtension].[EmailAddress] - EmailAddress - 0 - - 254 - true - 2 - false - EmailAddress - - - - testExisting_dataExtension - - - - - 42760528-a8c5-44dd-8c1d-ff34e5daee54 - [testExisting_dataExtension].[FirstName] - FirstName - 0 - - 50 - false - 0 - false - Text - - - - testExisting_dataExtension - + ba1b0c59-78c4-4608-8423-35dda2248d4d + + + 1111111 + + + 2016-07-22T11:52:18.73 + 2016-07-22T11:52:19.603 + 89344 + + shared_data_default + + + 0 + + + Shared Items + + shared_data + true + false + false + + + + 1111111 + + + 2016-07-22T11:52:19.6 + 2016-07-22T11:52:19.6 + 89355 + + dataextension_default + + + 0 + + + Data Extensions + + dataextension + true + false + true + + + + 1111111 + + + 2016-07-22T11:52:19.603 + 2016-07-22T11:52:19.603 + 89356 + + shared_dataextension_default + + + 89344 + + shared_data_default + + Shared Data Extensions + + shared_dataextension + true + false + true + + + + 1111111 + + + 2016-07-29T12:13:27.28 + 2016-07-29T12:13:27.28 + 90889 + + HiddenCategory_default + + + 0 + + + HiddenCategory + Hidden folder to store Hidden items + Hidden + true + false + true + + + + 1111111 + + + 2016-08-23T09:22:27.54 + 2016-08-23T09:22:27.54 + 93698 + + salesforcedataextension_default + + + 0 + + + Salesforce Data Extensions + + salesforcedataextension + true + false + true + + + + 1111111 + + + 2016-08-23T09:22:27.55 + 2016-08-23T09:22:27.55 + 93699 + + shared_salesforcedataextension_defau + + + 89344 + + shared_data_default + + Shared Salesforce Data Extensions + + shared_salesforcedataextension + true + false + true + + + + 1111111 + + + 2020-01-27T10:50:46.573 + 2020-01-27T10:50:46.573 + 309082 + + + + + 89355 + + dataextension_default + + QueryStudioResults + + dataextension + true + true + true + + + + 1111111 + + + 2020-04-17T08:14:19.763 + 2020-04-17T08:14:19.763 + 587750 + + + + + 89356 + + shared_dataextension_default + + catalyst target 1 + + shared_dataextension + true + true + true + + + + 1111111 + + + 2020-07-09T02:30:12.38 + 2021-12-16T03:43:30.753 + 605618 + + dataextension_default + + + 89355 + + dataextension_default + + Audiences + + dataextension + true + true + true + + + + 1111111 + + + 2020-12-25T06:11:40.107 + 2021-12-23T10:51:24.393 + 633441 + + + + + 89355 + + dataextension_default + + System DEs + + dataextension + true + true + true + + + + 1111111 + + + 2021-01-30T11:54:36.11 + 2021-01-30T11:54:36.11 + 638814 + + 0b72bf27-0678-484d-ac0b-a8762b9bec33 + + + 89356 + + shared_dataextension_default + + Customer 360 Segments + All Customer 360 segments will be grouped here. Each sub-folder relates to an activation profile name. + shared_dataextension + true + false + true + + + + 1111111 + + + 2021-01-30T11:56:38.77 + 2021-01-30T11:56:38.77 + 638815 + + cedd206d-178e-41cb-8965-ce255975b046 + + + 638814 + + 0b72bf27-0678-484d-ac0b-a8762b9bec33 + + FirstAudience360 Segment + + shared_dataextension + true + false + false + + + + 1111111 + + + 2021-02-07T10:44:01.413 + 2021-12-16T03:43:33.38 + 639967 + + + + + 89355 + + dataextension_default + + TestAudiences + + dataextension + true + true + true + + + + 1111111 + + + 2021-08-04T15:17:18.533 + 2021-08-04T15:17:18.567 + 675203 + + A19F7E38-7369-497E-826F-D551F17FB0B4 + + + 0 + + + Synchronized Data Extensions + + synchronizeddataextension + true + false + true + + + + 1111111 + + + 2021-10-03T05:01:37.23 + 2021-10-03T05:01:37.37 + 688352 + + B80AE306-55BC-4C2E-A79F-8CCA486F0BA0 + + + 0 + + + Synchronized Data Extensions + + synchronizeddataextension + true + false + true + + + + 1111111 + + + 2022-10-26T09:48:38.293 + 2022-10-26T09:48:38.31 + 757145 + + 0ACB800B-AA8B-4F0F-9642-4907592C919C + + + 0 + + + Synchronized Data Extensions + + synchronizeddataextension + true + false + true diff --git a/test/resources/9999999/hub/v1/contacts/schema/attributeGroups/get-response.json b/test/resources/9999999/hub/v1/contacts/schema/attributeGroups/get-response.json index 078668f80..e0dbcb015 100644 --- a/test/resources/9999999/hub/v1/contacts/schema/attributeGroups/get-response.json +++ b/test/resources/9999999/hub/v1/contacts/schema/attributeGroups/get-response.json @@ -571,6 +571,49 @@ "identifierType": "FullyQualifiedName" }, "namespace": "" + }, + { + "mID": 7330928, + "objectState": "Created", + "attributeSetIdentifiers": [ + { + "definitionID": "528b787a-5238-ee11-b85a-48df37d1de8a", + "definitionName": { + "value": "testExisting_dataExtensionShared" + }, + "definitionKey": "testExisting_dataExtensionShared", + "connectingID": { + "identifierType": "FullyQualifiedName" + }, + "namespace": "" + } + ], + "isOwner": true, + "isPrimary": false, + "isSystemDefined": false, + "isHidden": false, + "canAddProperties": true, + "canAddRelationships": true, + "containsSchemaAttributes": false, + "canRemove": true, + "canChangeProperties": true, + "displayOrder": 8, + "requiredRelationships": [], + "canModify": true, + "attributeGroupIconKey": "et-icon-databas", + "fullyQualifiedName": "testExisting_attributeGroup", + "attributeGroupType": "Standard", + "localizedDescription": {}, + "attributeCount": 6, + "definitionID": "79846c6e-5238-ee11-b85a-48df37d1de8a", + "definitionName": { + "value": "testExisting_attributeGroup" + }, + "definitionKey": "testExisting_attributeGroup", + "connectingID": { + "identifierType": "FullyQualifiedName" + }, + "namespace": "" } ], "responseContext": { diff --git a/test/resources/9999999/hub/v1/contacts/schema/setDefinitions/get-response.json b/test/resources/9999999/hub/v1/contacts/schema/setDefinitions/get-response.json index 8e5dc97ff..150cb21ff 100644 --- a/test/resources/9999999/hub/v1/contacts/schema/setDefinitions/get-response.json +++ b/test/resources/9999999/hub/v1/contacts/schema/setDefinitions/get-response.json @@ -19793,6 +19793,453 @@ "setDefinitionID": "f452de3f-31e2-e611-80cc-1402ec7222b4", "setDefinitionKey": "PredictiveIntelProfiles", "name": "Predictive Intelligence Profiles" + }, + { + "definitionID": "528b787a-5238-ee11-b85a-48df37d1de8a", + "definitionKey": "testExisting_dataExtensionShared", + "definitionName": { + "value": "testExisting_dataExtensionShared" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + }, + "relationships": [ + { + "canModify": true, + "canRemove": true, + "isHidden": false, + "isSystemDefined": false, + "isGroupToSetRelationship": true, + "leftItem": { + "cardinality": "One", + "connectingID": { + "identifierType": "FullyQualifiedName" + }, + "identifier": "79846c6e-5238-ee11-b85a-48df37d1de8a", + "relationshipType": "AttributeGroup" + }, + "leftRelationshipIDs": [ + { + "type": "int16", + "value": "3" + } + ], + "leftRelationshipReferenceType": "CustomerData", + "relationshipAttributes": [ + { + "leftAttributeID": "37778523-13f7-e911-a2d8-1402ec938a35", + "leftConnectingID": { + "identifierType": "FullyQualifiedName" + }, + "rightAttributeID": "558b787a-5238-ee11-b85a-48df37d1de8a", + "rightConnectingID": { + "identifierType": "FullyQualifiedName" + } + } + ], + "relationshipID": "598b787a-5238-ee11-b85a-48df37d1de8a", + "rightItem": { + "cardinality": "One", + "connectingID": { + "identifierType": "FullyQualifiedName" + }, + "identifier": "528b787a-5238-ee11-b85a-48df37d1de8a", + "relationshipType": "AttributeSet" + } + } + ], + "valueDefinitions": [ + { + "baseType": "Numeric", + "dataSourceName": {}, + "dataType": "LongNumber", + "description": "", + "localizedDescription": { + "value": "" + }, + "definitionID": "548b787a-5238-ee11-b85a-48df37d1de8a", + "definitionKey": "CustomObjectKey", + "definitionName": { + "value": "Custom Object Key" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + }, + "fullyQualifiedName": "testExisting_dataExtensionShared.Custom Object Key", + "isHidden": true, + "isIdentityValue": true, + "isNullable": false, + "isPrimaryKey": false, + "isReadOnly": true, + "isSystemDefined": true, + "isUpdateable": true, + "parentDefinition": { + "definitionID": "528b787a-5238-ee11-b85a-48df37d1de8a", + "definitionKey": "testExisting_dataExtensionShared", + "definitionName": { + "value": "testExisting_dataExtensionShared" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + } + }, + "parentType": "Set", + "storageName": "_CustomObjectKey", + "valueDefinitionID": "548b787a-5238-ee11-b85a-48df37d1de8a", + "valueDefinitionKey": "CustomObjectKey", + "name": "Custom Object Key", + "setDefinitionID": "528b787a-5238-ee11-b85a-48df37d1de8a", + "setDefinitionKey": "testExisting_dataExtensionShared", + "setDefinitionName": { + "value": "testExisting_dataExtensionShared" + }, + "parentIdentifier": "528b787a-5238-ee11-b85a-48df37d1de8a" + }, + { + "baseType": "Text", + "dataSourceName": {}, + "dataType": "Text", + "description": "", + "localizedDescription": { + "value": "" + }, + "definitionID": "588b787a-5238-ee11-b85a-48df37d1de8a", + "definitionKey": "FirstName", + "definitionName": { + "value": "FirstName" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + }, + "displayOrder": 0, + "fullyQualifiedName": "testExisting_dataExtensionShared.FirstName", + "isHidden": false, + "isIdentityValue": false, + "isNullable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isSystemDefined": false, + "isUpdateable": true, + "length": 50, + "obfuscationProperties": { + "maskType": "None", + "maskTypeID": 0, + "storageTypeID": 1, + "storageType": "Plain", + "valueDefinitionID": "588b787a-5238-ee11-b85a-48df37d1de8a" + }, + "ordinal": 0, + "parentDefinition": { + "definitionID": "528b787a-5238-ee11-b85a-48df37d1de8a", + "definitionKey": "testExisting_dataExtensionShared", + "definitionName": { + "value": "testExisting_dataExtensionShared" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + } + }, + "parentType": "Set", + "storageName": "FirstName", + "storageFieldReferenceID": { + "type": "guid", + "value": "391bfc9e-ea85-4610-a24b-d8400a36cdfc" + }, + "valueDefinitionID": "588b787a-5238-ee11-b85a-48df37d1de8a", + "valueDefinitionKey": "FirstName", + "name": "FirstName", + "setDefinitionID": "528b787a-5238-ee11-b85a-48df37d1de8a", + "setDefinitionKey": "testExisting_dataExtensionShared", + "setDefinitionName": { + "value": "testExisting_dataExtensionShared" + }, + "parentIdentifier": "528b787a-5238-ee11-b85a-48df37d1de8a" + }, + { + "baseType": "Text", + "dataSourceName": {}, + "dataType": "Text", + "description": "", + "localizedDescription": { + "value": "" + }, + "definitionID": "578b787a-5238-ee11-b85a-48df37d1de8a", + "definitionKey": "LastName", + "definitionName": { + "value": "LastName" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + }, + "displayOrder": 1, + "fullyQualifiedName": "testExisting_dataExtensionShared.LastName", + "isHidden": false, + "isIdentityValue": false, + "isNullable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isSystemDefined": false, + "isUpdateable": true, + "length": 55, + "obfuscationProperties": { + "maskType": "None", + "maskTypeID": 0, + "storageTypeID": 1, + "storageType": "Plain", + "valueDefinitionID": "578b787a-5238-ee11-b85a-48df37d1de8a" + }, + "ordinal": 1, + "parentDefinition": { + "definitionID": "528b787a-5238-ee11-b85a-48df37d1de8a", + "definitionKey": "testExisting_dataExtensionShared", + "definitionName": { + "value": "testExisting_dataExtensionShared" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + } + }, + "parentType": "Set", + "storageName": "LastName", + "storageFieldReferenceID": { + "type": "guid", + "value": "3f80ba1f-f957-400f-88cb-a9303491026d" + }, + "valueDefinitionID": "578b787a-5238-ee11-b85a-48df37d1de8a", + "valueDefinitionKey": "LastName", + "name": "LastName", + "setDefinitionID": "528b787a-5238-ee11-b85a-48df37d1de8a", + "setDefinitionKey": "testExisting_dataExtensionShared", + "setDefinitionName": { + "value": "testExisting_dataExtensionShared" + }, + "parentIdentifier": "528b787a-5238-ee11-b85a-48df37d1de8a" + }, + { + "baseType": "Text", + "dataSourceName": {}, + "dataType": "EmailAddress", + "description": "", + "localizedDescription": { + "value": "" + }, + "definitionID": "568b787a-5238-ee11-b85a-48df37d1de8a", + "definitionKey": "EmailAddress", + "definitionName": { + "value": "EmailAddress" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + }, + "displayOrder": 2, + "fullyQualifiedName": "testExisting_dataExtensionShared.EmailAddress", + "isHidden": false, + "isIdentityValue": false, + "isNullable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isSystemDefined": false, + "isUpdateable": true, + "length": 254, + "obfuscationProperties": { + "maskType": "None", + "maskTypeID": 0, + "storageTypeID": 1, + "storageType": "Plain", + "valueDefinitionID": "568b787a-5238-ee11-b85a-48df37d1de8a" + }, + "ordinal": 2, + "parentDefinition": { + "definitionID": "528b787a-5238-ee11-b85a-48df37d1de8a", + "definitionKey": "testExisting_dataExtensionShared", + "definitionName": { + "value": "testExisting_dataExtensionShared" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + } + }, + "parentType": "Set", + "storageName": "EmailAddress", + "storageFieldReferenceID": { + "type": "guid", + "value": "41b9575b-da06-41ed-8551-f76868451a51" + }, + "valueDefinitionID": "568b787a-5238-ee11-b85a-48df37d1de8a", + "valueDefinitionKey": "EmailAddress", + "name": "EmailAddress", + "setDefinitionID": "528b787a-5238-ee11-b85a-48df37d1de8a", + "setDefinitionKey": "testExisting_dataExtensionShared", + "setDefinitionName": { + "value": "testExisting_dataExtensionShared" + }, + "parentIdentifier": "528b787a-5238-ee11-b85a-48df37d1de8a" + }, + { + "baseType": "Text", + "dataSourceName": {}, + "dataType": "Text", + "description": "", + "localizedDescription": { + "value": "" + }, + "definitionID": "538b787a-5238-ee11-b85a-48df37d1de8a", + "definitionKey": "testField", + "definitionName": { + "value": "testField" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + }, + "displayOrder": 3, + "fullyQualifiedName": "testExisting_dataExtensionShared.testField", + "isHidden": false, + "isIdentityValue": false, + "isNullable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isSystemDefined": false, + "isUpdateable": true, + "length": 254, + "obfuscationProperties": { + "maskType": "None", + "maskTypeID": 0, + "storageTypeID": 1, + "storageType": "Plain", + "valueDefinitionID": "538b787a-5238-ee11-b85a-48df37d1de8a" + }, + "ordinal": 3, + "parentDefinition": { + "definitionID": "528b787a-5238-ee11-b85a-48df37d1de8a", + "definitionKey": "testExisting_dataExtensionShared", + "definitionName": { + "value": "testExisting_dataExtensionShared" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + } + }, + "parentType": "Set", + "storageName": "testField", + "storageFieldReferenceID": { + "type": "guid", + "value": "ec7a606d-f38e-4c91-a5f2-02bf231a0954" + }, + "valueDefinitionID": "538b787a-5238-ee11-b85a-48df37d1de8a", + "valueDefinitionKey": "testField", + "name": "testField", + "setDefinitionID": "528b787a-5238-ee11-b85a-48df37d1de8a", + "setDefinitionKey": "testExisting_dataExtensionShared", + "setDefinitionName": { + "value": "testExisting_dataExtensionShared" + }, + "parentIdentifier": "528b787a-5238-ee11-b85a-48df37d1de8a" + }, + { + "baseType": "Text", + "dataSourceName": {}, + "dataType": "Text", + "description": "", + "localizedDescription": { + "value": "" + }, + "definitionID": "558b787a-5238-ee11-b85a-48df37d1de8a", + "definitionKey": "ContactKey", + "definitionName": { + "value": "ContactKey" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + }, + "displayOrder": 4, + "fullyQualifiedName": "testExisting_dataExtensionShared.ContactKey", + "isHidden": false, + "isIdentityValue": false, + "isNullable": false, + "isPrimaryKey": true, + "isReadOnly": false, + "isSystemDefined": false, + "isUpdateable": true, + "length": 50, + "obfuscationProperties": { + "maskType": "None", + "maskTypeID": 0, + "storageTypeID": 1, + "storageType": "Plain", + "valueDefinitionID": "558b787a-5238-ee11-b85a-48df37d1de8a" + }, + "ordinal": 4, + "parentDefinition": { + "definitionID": "528b787a-5238-ee11-b85a-48df37d1de8a", + "definitionKey": "testExisting_dataExtensionShared", + "definitionName": { + "value": "testExisting_dataExtensionShared" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + } + }, + "parentType": "Set", + "storageName": "ContactKey", + "storageFieldReferenceID": { + "type": "guid", + "value": "49d0db37-dff0-49d9-9d82-eb29b345f238" + }, + "valueDefinitionID": "558b787a-5238-ee11-b85a-48df37d1de8a", + "valueDefinitionKey": "ContactKey", + "name": "ContactKey", + "setDefinitionID": "528b787a-5238-ee11-b85a-48df37d1de8a", + "setDefinitionKey": "testExisting_dataExtensionShared", + "setDefinitionName": { + "value": "testExisting_dataExtensionShared" + }, + "parentIdentifier": "528b787a-5238-ee11-b85a-48df37d1de8a" + } + ], + "attributeCount": 0, + "canAddValues": true, + "canChangeValues": true, + "canModify": true, + "canRemove": true, + "categoryID": 89356, + "createdBy": 700301950, + "createDate": "2023-08-11T08:22:00", + "customObjectOwnerMID": 7281698, + "dataRetentionProperties": { + "isRowBasedRetention": true, + "isResetRetentionPeriodOnImport": false, + "isDeleteAtEndOfRetentionPeriod": false, + "periodLength": 6, + "periodUnitOfMeasure": 5, + "setDefinitionID": "528b787a-5238-ee11-b85a-48df37d1de8a" + }, + "localizedDescription": { + "value": "Container for my test emails" + }, + "fullyQualifiedName": "testExisting_dataExtensionShared", + "isCustomObjectBacked": true, + "isEvent": false, + "isHidden": false, + "isReadOnly": false, + "isRoot": false, + "isSendable": true, + "isShared": true, + "isSystemDefined": false, + "isTestaable": true, + "parentID": "00000000-0000-0000-0000-000000000000", + "relationshipCount": 1, + "sendAttributeStorageName": "ContactKey", + "sendContactKeyStorageName": "_SubscriberKey", + "storageLogicalType": "DataExtension", + "storageName": "testExisting_dataExtensionShared", + "storageObjectIDs": ["5b8b787a-5238-ee11-b85a-48df37d1de8a"], + "storageReferenceID": { + "type": "guid", + "value": "21711373-72c1-ec11-b83b-shared" + }, + "setDefinitionID": "528b787a-5238-ee11-b85a-48df37d1de8a", + "setDefinitionKey": "testExisting_dataExtensionShared", + "name": "testExisting_dataExtensionShared" } ], "responseContext": { diff --git a/test/type.attributeGroup.test.js b/test/type.attributeGroup.test.js index 49ff53a13..61bdaaeaa 100644 --- a/test/type.attributeGroup.test.js +++ b/test/type.attributeGroup.test.js @@ -26,16 +26,16 @@ describe('type: attributeGroup', () => { retrieve['testInstance/testBU'].attributeGroup ? Object.keys(retrieve['testInstance/testBU'].attributeGroup).length : 0, - 7, - 'only 7 attributeGroups expected in retrieve response' + 8, + 'only 8 attributeGroups expected in retrieve response' ); // get results from cache const result = cache.getCache(); assert.equal( result.attributeGroup ? Object.keys(result.attributeGroup).length : 0, - 7, - 'only 7 attributeGroups expected in cache' + 8, + 'only 8 attributeGroups expected in cache' ); assert.deepEqual( await testUtils.getActualJson('ETMobileConnect', 'attributeGroup'), diff --git a/test/type.attributeSet.test.js b/test/type.attributeSet.test.js index d05ea88a0..a894b7f09 100644 --- a/test/type.attributeSet.test.js +++ b/test/type.attributeSet.test.js @@ -26,15 +26,15 @@ describe('type: attributeSet', () => { retrieve['testInstance/testBU'].attributeSet ? Object.keys(retrieve['testInstance/testBU'].attributeSet).length : 0, - 27, - 'only 27 attributeSets expected in retrieve response' + 28, + 'only 28 attributeSets expected in retrieve response' ); // get results from cache const result = cache.getCache(); assert.equal( result.attributeSet ? Object.keys(result.attributeSet).length : 0, - 27, - 'only 27 attributeSets expected in cache' + 28, + 'only 28 attributeSets expected in cache' ); assert.deepEqual( diff --git a/test/type.dataExtension.test.js b/test/type.dataExtension.test.js index 096c6f54d..addbf820c 100644 --- a/test/type.dataExtension.test.js +++ b/test/type.dataExtension.test.js @@ -99,10 +99,18 @@ describe('type: dataExtension', () => { }); it('Should create & update a dataExtension', async () => { // WHEN - await handler.deploy('testInstance/testBU', ['dataExtension']); + const deployResult = await handler.deploy('testInstance/testBU', ['dataExtension']); // THEN assert.equal(process.exitCode, false, 'deploy should not have thrown an error'); + assert.equal( + deployResult['testInstance/testBU']?.dataExtension + ? Object.keys(deployResult['testInstance/testBU']?.dataExtension).length + : 0, + 2, + 'two dataExtensions to be deployed' + ); + // get results from cache const result = cache.getCache(); assert.equal( @@ -129,8 +137,104 @@ describe('type: dataExtension', () => { ); return; }); - it('Should create & update a shared dataExtension'); - it('Should create & update a shared dataExtension with --fixShared'); + it('Should create & update a shared dataExtension', async () => { + // WHEN + const deployResult = await handler.deploy('testInstance/_ParentBU_', ['dataExtension']); + // THEN + assert.equal(process.exitCode, false, 'deploy should not have thrown an error'); + + assert.equal( + deployResult['testInstance/_ParentBU_']?.dataExtension + ? Object.keys(deployResult['testInstance/_ParentBU_']?.dataExtension).length + : 0, + 2, + 'two dataExtensions to be deployed' + ); + + // get results from cache + const result = cache.getCache(); + assert.equal( + result.dataExtension ? Object.keys(result.dataExtension).length : 0, + 2, + 'two dataExtensions expected' + ); + // insert + assert.deepEqual( + await testUtils.getActualJson( + 'testNew_dataExtensionShared', + 'dataExtension', + '_ParentBU_' + ), + await testUtils.getExpectedJson('1111111', 'dataExtension', 'create'), + 'returned metadata was not equal expected for create' + ); + // update + assert.deepEqual( + await testUtils.getActualJson( + 'testExisting_dataExtensionShared', + 'dataExtension', + '_ParentBU_' + ), + await testUtils.getExpectedJson('1111111', 'dataExtension', 'update'), + 'returned metadata was not equal expected for update' + ); + assert.equal( + testUtils.getAPIHistoryLength(), + 8, + 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' + ); + return; + }); + it('Should create & update a shared dataExtension with --fixShared & --skipInteraction', async () => { + // WHEN + handler.setOptions({ fixShared: 'testBU', skipInteraction: true, _runningTest: true }); + + const deployResult = await handler.deploy('testInstance/_ParentBU_', ['dataExtension']); + // THEN + assert.equal(process.exitCode, false, 'deploy should not have thrown an error'); + + assert.equal( + deployResult['testInstance/_ParentBU_']?.dataExtension + ? Object.keys(deployResult['testInstance/_ParentBU_']?.dataExtension).length + : 0, + 2, + 'two dataExtensions to be deployed' + ); + + // get results from cache + const result = cache.getCache(); + assert.equal( + result.dataExtension ? Object.keys(result.dataExtension).length : 0, + 2, + 'two dataExtensions expected' + ); + // insert + assert.deepEqual( + await testUtils.getActualJson( + 'testNew_dataExtensionShared', + 'dataExtension', + '_ParentBU_' + ), + await testUtils.getExpectedJson('1111111', 'dataExtension', 'create'), + 'returned metadata was not equal expected for create' + ); + // update + assert.deepEqual( + await testUtils.getActualJson( + 'testExisting_dataExtensionShared', + 'dataExtension', + '_ParentBU_' + ), + await testUtils.getExpectedJson('1111111', 'dataExtension', 'update'), + 'returned metadata was not equal expected for update' + ); + assert.equal( + testUtils.getAPIHistoryLength(), + 12, + 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' + ); + return; + }); it('Should rename fields'); }); describe('Templating ================', () => { From a4bb88b2ef46f13bfaaff3b5b538ed70468353fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 11 Aug 2023 16:43:19 +0200 Subject: [PATCH 10/70] #940: improve CICD test for script --- test/type.script.test.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/type.script.test.js b/test/type.script.test.js index 84202cfec..e5582bb05 100644 --- a/test/type.script.test.js +++ b/test/type.script.test.js @@ -358,7 +358,12 @@ describe('type: script', () => { assert.equal( fileList[1].split('\\').join('/'), 'retrieve/testInstance/testBU/script/testExisting_script.script-meta.ssjs', - 'wrong JSON path' + 'wrong SSJS path' + ); + assert.equal( + fileList[2].split('\\').join('/'), + 'retrieve/testInstance/testBU/script/testExisting_script.script-meta.html', + 'wrong HTML path' ); return; }); From 938cf5d2922d502bf73d9c5957d26b5c0ad0abe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 11 Aug 2023 16:44:47 +0200 Subject: [PATCH 11/70] #940: improve logs --- lib/metadataTypes/DataExtension.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index 80b7174c0..e4320d5aa 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -382,7 +382,7 @@ class DataExtension extends MetadataType { if (sharedDataExtensionsKeys.length > 0 && !Util.OPTIONS.fixShared) { Util.logger.warn( - 'Shared Data Extensions were updated but fixShared option is not set. This can result in your changes not being visible on child BUs.' + 'Shared Data Extensions were updated but --fixShared option is not set. This can result in your changes not being visible on child BUs.' ); // TODO replace with 1 inquirer question to ask if child BUs should be fixed if needed } @@ -485,7 +485,7 @@ class DataExtension extends MetadataType { } else { Util.logger.info( Util.getGrayMsg( - ` - No matching attributeSet found for given Shared Data Extensions keys found on ${childBuName}` + ` - No matching attributeSet found for given Shared Data Extensions keys found on BU ${childBuName}` ) ); } From 1b605fa20ae9f3e49bac771c7727497d0a3d75ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 20:20:26 +0000 Subject: [PATCH 12/70] Bump eslint from 8.46.0 to 8.47.0 Bumps [eslint](https://github.com/eslint/eslint) from 8.46.0 to 8.47.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.46.0...v8.47.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 74 +++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2f84a7329..380e2af9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,7 @@ "axios-mock-adapter": "1.21.5", "chai": "4.3.7", "chai-files": "1.4.0", - "eslint": "8.46.0", + "eslint": "8.47.0", "eslint-config-prettier": "9.0.0", "eslint-config-ssjs": "1.1.11", "eslint-plugin-jsdoc": "46.4.6", @@ -554,9 +554,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.1.tgz", - "integrity": "sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -599,9 +599,9 @@ "dev": true }, "node_modules/@eslint/js": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.46.0.tgz", - "integrity": "sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", + "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3147,15 +3147,15 @@ } }, "node_modules/eslint": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.46.0.tgz", - "integrity": "sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", + "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.1", - "@eslint/js": "^8.46.0", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "^8.47.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -3166,7 +3166,7 @@ "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.2", + "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", @@ -3390,9 +3390,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz", - "integrity": "sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4292,9 +4292,9 @@ } }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -10338,9 +10338,9 @@ "dev": true }, "@eslint/eslintrc": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.1.tgz", - "integrity": "sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -10375,9 +10375,9 @@ } }, "@eslint/js": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.46.0.tgz", - "integrity": "sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", + "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", "dev": true }, "@humanwhocodes/config-array": { @@ -12314,15 +12314,15 @@ "dev": true }, "eslint": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.46.0.tgz", - "integrity": "sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", + "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.1", - "@eslint/js": "^8.46.0", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "^8.47.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -12333,7 +12333,7 @@ "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.2", + "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", @@ -12493,9 +12493,9 @@ } }, "eslint-visitor-keys": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz", - "integrity": "sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, "espree": { @@ -13116,9 +13116,9 @@ } }, "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "requires": { "type-fest": "^0.20.2" diff --git a/package.json b/package.json index c88c78bc6..35eb9fcc0 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "axios-mock-adapter": "1.21.5", "chai": "4.3.7", "chai-files": "1.4.0", - "eslint": "8.46.0", + "eslint": "8.47.0", "eslint-config-prettier": "9.0.0", "eslint-config-ssjs": "1.1.11", "eslint-plugin-jsdoc": "46.4.6", From dea24395d5bc87f23f66f9cdb78cfc7f49d2fcd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 16 Aug 2023 16:34:07 +0200 Subject: [PATCH 13/70] #940: evaluate fix-success; refactoring --- docs/dist/documentation.md | 72 +++++---- lib/metadataTypes/DataExtension.js | 249 +++++++++++++++++------------ 2 files changed, 184 insertions(+), 137 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index a27dc2ce2..c80e5c097 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -220,24 +220,28 @@ Provides default functionality that can be overwritten by child metadata type cl

helper for postDeployTasks

DataExtension.(upsertedMetadata, originalMetadata, createdUpdated)void
-

helper for postDeployTasks +

takes care of updating attribute groups on child BUs after an update to Shared DataExtensions +helper for postDeployTasks fixes an issue where shared data extensions are not visible in data designer on child BU; SF known issue: https://issues.salesforce.com/#q=W-11031095

-
DataExtension.(childBuName, buObjectParent, clientParent, sharedDataExtensions)Promise.<Array.<string>>
-

helper for DataExtension.#postDeployFixShared

+
DataExtension.()Array.<string>
+

helper for DataExtension.#fixShared

+
+
DataExtension.(childBuName, buObjectParent, clientParent, sharedDataExtensionMap)Promise.<Array.<string>>
+

helper for DataExtension.#fixShared

+
+
DataExtension.(deId, deKey, buObjectChildBu, clientChildBu, buObjectParent, clientParent)Promise.<boolean>
+

method that actually takes care of triggering the update for a particular BU-sharedDe combo +helper for DataExtension.#fixShared_onBU

-
DataExtension.(deId, deKey, buObjectChildBu, clientChildBu, buObjectParent, clientParent)Promise
-
-
DataExtension.(randomSuffix, buObjectChildBu, clientChildBu, deKey, fieldObjectID)Promise
-
DataExtension.(buObjectChildBu, clientChildBu, deKey, deId)Promise.<string>

helper for DataExtension.#fixShared_item

DataExtension.(randomSuffix, buObjectParent, clientParent, deKey)Promise.<string>

helper for DataExtension.#fixShared_item

-
DataExtension.()Array.<string>
-

helper for DataExtension.#postDeployFixShared

+
DataExtension.(randomSuffix, buObjectChildBu, clientChildBu, deKey, fieldObjectID)Promise
+

helper for DataExtension.#fixShared_item

getUserName(userList, item, fieldname)string
@@ -8589,6 +8593,7 @@ helper for [postDeployTasks](#Automation.postDeployTasks) ## DataExtension.(upsertedMetadata, originalMetadata, createdUpdated) ⇒ void +takes care of updating attribute groups on child BUs after an update to Shared DataExtensions helper for [postDeployTasks](#DataExtension.postDeployTasks) fixes an issue where shared data extensions are not visible in data designer on child BU; SF known issue: https://issues.salesforce.com/#q=W-11031095 @@ -8602,8 +8607,15 @@ fixes an issue where shared data extensions are not visible in data designer on -## DataExtension.(childBuName, buObjectParent, clientParent, sharedDataExtensions) ⇒ Promise.<Array.<string>> -helper for [DataExtension.#postDeployFixShared](DataExtension.#postDeployFixShared) +## DataExtension.() ⇒ Array.<string> +helper for [DataExtension.#fixShared](DataExtension.#fixShared) + +**Kind**: global function +**Returns**: Array.<string> - list of selected BU names + + +## DataExtension.(childBuName, buObjectParent, clientParent, sharedDataExtensionMap) ⇒ Promise.<Array.<string>> +helper for [DataExtension.#fixShared](DataExtension.#fixShared) **Kind**: global function **Returns**: Promise.<Array.<string>> - updated shared DE keys on BU @@ -8613,13 +8625,16 @@ helper for [DataExtension.#postDeployFixShared](DataExtension.#postDeployFixShar | childBuName | string | name of child BU to fix | | buObjectParent | TYPE.BuObject | bu object for parent BU | | clientParent | object | SDK for parent BU | -| sharedDataExtensions | object | list of IDs of shared data extensions | +| sharedDataExtensionMap | Object.<string, string> | ID-Key relationship of shared data extensions | -## DataExtension.(deId, deKey, buObjectChildBu, clientChildBu, buObjectParent, clientParent) ⇒ Promise +## DataExtension.(deId, deKey, buObjectChildBu, clientChildBu, buObjectParent, clientParent) ⇒ Promise.<boolean> +method that actually takes care of triggering the update for a particular BU-sharedDe combo +helper for [DataExtension.#fixShared_onBU](DataExtension.#fixShared_onBU) + **Kind**: global function -**Returns**: Promise - - +**Returns**: Promise.<boolean> - flag that signals if the fix was successful | Param | Type | Description | | --- | --- | --- | @@ -8632,20 +8647,6 @@ helper for [DataExtension.#postDeployFixShared](DataExtension.#postDeployFixShar -## DataExtension.(randomSuffix, buObjectChildBu, clientChildBu, deKey, fieldObjectID) ⇒ Promise -**Kind**: global function -**Returns**: Promise - - - -| Param | Type | Description | -| --- | --- | --- | -| randomSuffix | string | - | -| buObjectChildBu | TYPE.BuObject | BU object for Child BU | -| clientChildBu | object | SDK for child BU | -| deKey | string | dataExtension key | -| fieldObjectID | string | field ObjectID | - - - ## DataExtension.(buObjectChildBu, clientChildBu, deKey, deId) ⇒ Promise.<string> helper for [DataExtension.#fixShared_item](DataExtension.#fixShared_item) @@ -8676,11 +8677,20 @@ helper for [DataExtension.#fixShared_item](DataExtension.#fixShared_item) -## DataExtension.() ⇒ Array.<string> -helper for [DataExtension.#postDeployFixShared](DataExtension.#postDeployFixShared) +## DataExtension.(randomSuffix, buObjectChildBu, clientChildBu, deKey, fieldObjectID) ⇒ Promise +helper for [DataExtension.#fixShared_item](DataExtension.#fixShared_item) **Kind**: global function -**Returns**: Array.<string> - list of selected BU names +**Returns**: Promise - - + +| Param | Type | Description | +| --- | --- | --- | +| randomSuffix | string | - | +| buObjectChildBu | TYPE.BuObject | BU object for Child BU | +| clientChildBu | object | SDK for child BU | +| deKey | string | dataExtension key | +| fieldObjectID | string | field ObjectID | + ## getUserName(userList, item, fieldname) ⇒ string diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index e4320d5aa..a29244e35 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -356,10 +356,11 @@ class DataExtension extends MetadataType { DataExtensionField.postRetrieveTasks(field, true); } } - await this.#postDeployFixShared(upsertedMetadata, originalMetadata, createdUpdated); + await this.#fixShared(upsertedMetadata, originalMetadata, createdUpdated); } /** + * takes care of updating attribute groups on child BUs after an update to Shared DataExtensions * helper for {@link DataExtension.postDeployTasks} * fixes an issue where shared data extensions are not visible in data designer on child BU; SF known issue: https://issues.salesforce.com/#q=W-11031095 * @@ -368,7 +369,7 @@ class DataExtension extends MetadataType { * @param {{created: number, updated: number}} createdUpdated counter representing successful creates/updates * @returns {void} */ - static async #postDeployFixShared(upsertedMetadata, originalMetadata, createdUpdated) { + static async #fixShared(upsertedMetadata, originalMetadata, createdUpdated) { if (this.buObject.eid !== this.buObject.mid || createdUpdated.updated === 0) { // only if we were executing a deploy on parent bu could we be deploying shared data extensions // only if updates were made could the issue in https://issues.salesforce.com/#q=W-11031095 affect data designer @@ -382,20 +383,22 @@ class DataExtension extends MetadataType { if (sharedDataExtensionsKeys.length > 0 && !Util.OPTIONS.fixShared) { Util.logger.warn( - 'Shared Data Extensions were updated but --fixShared option is not set. This can result in your changes not being visible on child BUs.' + 'Shared Data Extensions were updated but --fixShared option is not set. This can result in your changes not being visible in attribute groups on child BUs.' + ); + Util.logger.info( + 'We recommend to re-run your deployment with the --fixShared option unless you are sure your Shared Data Extension is not used in attribute groups on any child BU.' ); - // TODO replace with 1 inquirer question to ask if child BUs should be fixed if needed } if (sharedDataExtensionsKeys.length > 0 && Util.OPTIONS.fixShared) { // select which BUs to run this for - const selectedBuNames = await this.#fixSharedGetBUs(); + const selectedBuNames = await this.#fixShared_getBUs(); // backup settings const buObjectBak = this.buObject; const clientBak = this.client; - // get dataExtension IDs - const sharedDataExtensions = {}; + // get dataExtension ID-Key relationship + const sharedDataExtensionMap = {}; for (const key of sharedDataExtensionsKeys) { try { const id = cache.searchForField( @@ -405,7 +408,7 @@ class DataExtension extends MetadataType { 'ObjectID', this.buObject.eid ); - sharedDataExtensions[id] = key; + sharedDataExtensionMap[id] = key; } catch { continue; } @@ -416,13 +419,14 @@ class DataExtension extends MetadataType { `Fixing Shared Data Extensions details in data models of child BUs` + Util.getKeysString(sharedDataExtensionsKeys) ); + for (const buName of selectedBuNames) { - await this.#fixSharedOnBU( + await this.#fixShared_onBU( buName, buObjectBak, clientBak, - sharedDataExtensions + sharedDataExtensionMap ); } Util.logger.info(`Finished fixing Shared Data Extensions details in data models`); @@ -437,15 +441,62 @@ class DataExtension extends MetadataType { } /** - * helper for {@link DataExtension.#postDeployFixShared} + * helper for {@link DataExtension.#fixShared} + * + * @returns {string[]} list of selected BU names + */ + static async #fixShared_getBUs() { + const buListObj = this.properties.credentials[this.buObject.credential].businessUnits; + const fixBuPreselected = []; + if (typeof Util.OPTIONS.fixShared === 'string') { + const availableBuNames = Object.keys(buListObj); + fixBuPreselected.push( + ...Util.OPTIONS.fixShared + .split(',') + .filter(Boolean) + .map((bu) => bu.trim()) + .filter((bu) => availableBuNames.includes(bu)) + ); + } + if (Util.skipInteraction && fixBuPreselected.length) { + return fixBuPreselected; + } + + const buList = Object.keys(buListObj) + .map((name) => ({ name, value: name, checked: fixBuPreselected.includes(name) })) + .filter((bu) => bu.value !== Util.parentBuName); + const questions = { + type: 'checkbox', + name: 'businessUnits', + message: 'Please select BUs that have access to the updated Shared Data Extensions:', + pageSize: 10, + choices: buList, + }; + let responses = null; + + try { + responses = await inquirer.prompt(questions); + } catch (ex) { + Util.logger.info(ex); + } + return responses.businessUnits; + } + + /** + * helper for {@link DataExtension.#fixShared} * * @param {string} childBuName name of child BU to fix * @param {TYPE.BuObject} buObjectParent bu object for parent BU * @param {object} clientParent SDK for parent BU - * @param {object} sharedDataExtensions list of IDs of shared data extensions + * @param {Object.} sharedDataExtensionMap ID-Key relationship of shared data extensions * @returns {Promise.} updated shared DE keys on BU */ - static async #fixSharedOnBU(childBuName, buObjectParent, clientParent, sharedDataExtensions) { + static async #fixShared_onBU( + childBuName, + buObjectParent, + clientParent, + sharedDataExtensionMap + ) { /** @type {TYPE.BuObject} */ const buObjectChildBu = { eid: this.properties.credentials[buObjectParent.credential].eid, @@ -461,42 +512,59 @@ class DataExtension extends MetadataType { AttributeSet.buObject = buObjectChildBu; AttributeSet.client = clientChildBu; const sharedDeIdsUsedOnBU = await AttributeSet.retrieveForSharedDEs( - sharedDataExtensions + sharedDataExtensionMap ); if (sharedDeIdsUsedOnBU.length) { + let sharedDataExtensionsKeys = sharedDeIdsUsedOnBU.map( + (deId) => sharedDataExtensionMap[deId] + ); Util.logger.info( ` - Fixing dataExtensions on BU ${childBuName} ` + - Util.getKeysString( - sharedDeIdsUsedOnBU.map((deId) => sharedDataExtensions[deId]) - ) + Util.getKeysString(sharedDataExtensionsKeys) ); for (const deId of sharedDeIdsUsedOnBU) { // dont use Promise.all to ensure order of execution; otherwise, switched BU contexts in one step will affect the next - await this.#fixShared_item( + const fixed = await this.#fixShared_item( deId, - sharedDataExtensions[deId], + sharedDataExtensionMap[deId], buObjectChildBu, clientChildBu, buObjectParent, clientParent ); + if (!fixed) { + // remove from list of shared DEs that were fixed + sharedDataExtensionsKeys = sharedDataExtensionsKeys.filter( + (key) => key !== sharedDataExtensionMap[deId] + ); + } } + if (sharedDataExtensionsKeys.length) { + Util.logger.debug( + ` - Fixed ${sharedDataExtensionsKeys.length}/${ + sharedDeIdsUsedOnBU.length + }: ${sharedDataExtensionsKeys.join(', ')}` + ); + } + return sharedDataExtensionsKeys; } else { Util.logger.info( Util.getGrayMsg( ` - No matching attributeSet found for given Shared Data Extensions keys found on BU ${childBuName}` ) ); + return []; } - return sharedDeIdsUsedOnBU; } catch (ex) { Util.logger.error(ex.message); - return; + return []; } } /** + * method that actually takes care of triggering the update for a particular BU-sharedDe combo + * helper for {@link DataExtension.#fixShared_onBU} * * @param {string} deId data extension ObjectID * @param {string} deKey dataExtension key @@ -504,7 +572,7 @@ class DataExtension extends MetadataType { * @param {object} clientChildBu SDK for child BU * @param {TYPE.BuObject} buObjectParent BU object for Parent BU * @param {object} clientParent SDK for parent BU - * @returns {Promise} - + * @returns {Promise.} flag that signals if the fix was successful */ static async #fixShared_item( deId, @@ -514,57 +582,43 @@ class DataExtension extends MetadataType { buObjectParent, clientParent ) { - // add field via child BU - const randomSuffix = await DataExtension.#fixShared_item_addField( - buObjectChildBu, - clientChildBu, - deKey, - deId - ); + try { + // add field via child BU + const randomSuffix = await DataExtension.#fixShared_item_addField( + buObjectChildBu, + clientChildBu, + deKey, + deId + ); - // get field ID from parent BU (it is not returned on child BU) - const fieldObjectID = await DataExtension.#fixShared_item_getFieldId( - randomSuffix, - buObjectParent, - clientParent, - deKey - ); + // get field ID from parent BU (it is not returned on child BU) + const fieldObjectID = await DataExtension.#fixShared_item_getFieldId( + randomSuffix, + buObjectParent, + clientParent, + deKey + ); - // delete field via child BU - await DataExtension.#fixShared_item_deleteField( - randomSuffix, - buObjectChildBu, - clientChildBu, - deKey, - fieldObjectID - ); + // delete field via child BU + await DataExtension.#fixShared_item_deleteField( + randomSuffix, + buObjectChildBu, + clientChildBu, + deKey, + fieldObjectID + ); - Util.logger.info(` - Fixed dataExtension ${deKey} on BU ${buObjectChildBu.businessUnit}`); - return true; - } + Util.logger.info( + ` - Fixed dataExtension ${deKey} on BU ${buObjectChildBu.businessUnit}` + ); - /** - * - * @param {string} randomSuffix - - * @param {TYPE.BuObject} buObjectChildBu BU object for Child BU - * @param {object} clientChildBu SDK for child BU - * @param {string} deKey dataExtension key - * @param {string} fieldObjectID field ObjectID - * @returns {Promise} - - */ - static async #fixShared_item_deleteField( - randomSuffix, - buObjectChildBu, - clientChildBu, - deKey, - fieldObjectID - ) { - DataExtensionField.buObject = buObjectChildBu; - DataExtensionField.client = clientChildBu; - await DataExtensionField.deleteByKeySOAP( - deKey + '.TriggerUpdate' + randomSuffix, - fieldObjectID - ); + return true; + } catch (ex) { + Util.logger.error( + `- error fixing dataExtension ${deKey} on BU ${buObjectChildBu.businessUnit}: ${ex.message}` + ); + return false; + } } /** @@ -634,45 +688,28 @@ class DataExtension extends MetadataType { } /** - * helper for {@link DataExtension.#postDeployFixShared} + * helper for {@link DataExtension.#fixShared_item} * - * @returns {string[]} list of selected BU names + * @param {string} randomSuffix - + * @param {TYPE.BuObject} buObjectChildBu BU object for Child BU + * @param {object} clientChildBu SDK for child BU + * @param {string} deKey dataExtension key + * @param {string} fieldObjectID field ObjectID + * @returns {Promise} - */ - static async #fixSharedGetBUs() { - const buListObj = this.properties.credentials[this.buObject.credential].businessUnits; - const fixBuPreselected = []; - if (typeof Util.OPTIONS.fixShared === 'string') { - const availableBuNames = Object.keys(buListObj); - fixBuPreselected.push( - ...Util.OPTIONS.fixShared - .split(',') - .filter(Boolean) - .map((bu) => bu.trim()) - .filter((bu) => availableBuNames.includes(bu)) - ); - } - if (Util.skipInteraction && fixBuPreselected.length) { - return fixBuPreselected; - } - - const buList = Object.keys(buListObj) - .map((name) => ({ name, value: name, checked: fixBuPreselected.includes(name) })) - .filter((bu) => bu.value !== Util.parentBuName); - const questions = { - type: 'checkbox', - name: 'businessUnits', - message: 'Please select BUs that have access to the updated Shared Data Extensions:', - pageSize: 10, - choices: buList, - }; - let responses = null; - - try { - responses = await inquirer.prompt(questions); - } catch (ex) { - Util.logger.info(ex); - } - return responses.businessUnits; + static async #fixShared_item_deleteField( + randomSuffix, + buObjectChildBu, + clientChildBu, + deKey, + fieldObjectID + ) { + DataExtensionField.buObject = buObjectChildBu; + DataExtensionField.client = clientChildBu; + await DataExtensionField.deleteByKeySOAP( + deKey + '.TriggerUpdate' + randomSuffix, + fieldObjectID + ); } /** From 13d6879b727d7c0bf782844473e9f85f6aec648a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 16 Aug 2023 16:52:35 +0200 Subject: [PATCH 14/70] #940: allow pre-selecting ALL BUs via --fixShared=* --- docs/dist/documentation.md | 13 +++++++++--- lib/metadataTypes/DataExtension.js | 33 ++++++++++++++++++++---------- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index c80e5c097..fe0313a62 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -235,13 +235,16 @@ fixes an issue where shared data extensions are not visible in data designer on helper for DataExtension.#fixShared_onBU

DataExtension.(buObjectChildBu, clientChildBu, deKey, deId)Promise.<string>
-

helper for DataExtension.#fixShared_item

+

add a new field to the shared DE to trigger an update to the data model +helper for DataExtension.#fixShared_item

DataExtension.(randomSuffix, buObjectParent, clientParent, deKey)Promise.<string>
-

helper for DataExtension.#fixShared_item

+

get ID of the field added by DataExtension.#fixShared_item_addField on the shared DE via parent BU +helper for DataExtension.#fixShared_item

DataExtension.(randomSuffix, buObjectChildBu, clientChildBu, deKey, fieldObjectID)Promise
-

helper for DataExtension.#fixShared_item

+

delete the field added by DataExtension.#fixShared_item_addField +helper for DataExtension.#fixShared_item

getUserName(userList, item, fieldname)string
@@ -1914,6 +1917,7 @@ Retrieves dataExtension metadata. Afterwards starts retrieval of dataExtensionCo ### DataExtension.retrieveSharedForCache([additionalFields]) ⇒ Promise.<TYPE.DataExtensionMap> +get shared dataExtensions from parent BU and merge them into the cache helper for [retrieve](#DataExtension.retrieve) and for AttributeSet.retrieveForSharedDEs **Kind**: static method of [DataExtension](#DataExtension) @@ -8648,6 +8652,7 @@ helper for [DataExtension.#fixShared_onBU](DataExtension.#fixShared_onBU) ## DataExtension.(buObjectChildBu, clientChildBu, deKey, deId) ⇒ Promise.<string> +add a new field to the shared DE to trigger an update to the data model helper for [DataExtension.#fixShared_item](DataExtension.#fixShared_item) **Kind**: global function @@ -8663,6 +8668,7 @@ helper for [DataExtension.#fixShared_item](DataExtension.#fixShared_item) ## DataExtension.(randomSuffix, buObjectParent, clientParent, deKey) ⇒ Promise.<string> +get ID of the field added by [DataExtension.#fixShared_item_addField](DataExtension.#fixShared_item_addField) on the shared DE via parent BU helper for [DataExtension.#fixShared_item](DataExtension.#fixShared_item) **Kind**: global function @@ -8678,6 +8684,7 @@ helper for [DataExtension.#fixShared_item](DataExtension.#fixShared_item) ## DataExtension.(randomSuffix, buObjectChildBu, clientChildBu, deKey, fieldObjectID) ⇒ Promise +delete the field added by [DataExtension.#fixShared_item_addField](DataExtension.#fixShared_item_addField) helper for [DataExtension.#fixShared_item](DataExtension.#fixShared_item) **Kind**: global function diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index a29244e35..4dd6e3ab5 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -448,23 +448,34 @@ class DataExtension extends MetadataType { static async #fixShared_getBUs() { const buListObj = this.properties.credentials[this.buObject.credential].businessUnits; const fixBuPreselected = []; + const availableBuNames = Object.keys(buListObj).filter( + (buName) => buName !== Util.parentBuName + ); if (typeof Util.OPTIONS.fixShared === 'string') { - const availableBuNames = Object.keys(buListObj); - fixBuPreselected.push( - ...Util.OPTIONS.fixShared - .split(',') - .filter(Boolean) - .map((bu) => bu.trim()) - .filter((bu) => availableBuNames.includes(bu)) - ); + if (Util.OPTIONS.fixShared === '*') { + // pre-select all BUs + fixBuPreselected.push(...availableBuNames); + } else { + // pre-select BUs from comma-separated list + fixBuPreselected.push( + ...Util.OPTIONS.fixShared + .split(',') + .filter(Boolean) + .map((bu) => bu.trim()) + .filter((bu) => availableBuNames.includes(bu)) + ); + } } if (Util.skipInteraction && fixBuPreselected.length) { + // assume programmatic use case or user that wants to skip the wizard. Use pre-selected BUs return fixBuPreselected; } - const buList = Object.keys(buListObj) - .map((name) => ({ name, value: name, checked: fixBuPreselected.includes(name) })) - .filter((bu) => bu.value !== Util.parentBuName); + const buList = availableBuNames.map((name) => ({ + name, + value: name, + checked: fixBuPreselected.includes(name), + })); const questions = { type: 'checkbox', name: 'businessUnits', From 87e6267cdac16f2fed330cf41508067c6b8e9d6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 16 Aug 2023 16:54:21 +0200 Subject: [PATCH 15/70] #940: refactoring & jsdoc --- lib/metadataTypes/DataExtension.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index 4dd6e3ab5..3600dfab8 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -47,7 +47,7 @@ class DataExtension extends MetadataType { }; } Util.logger.info(` - Caching dependent Metadata: dataExtensionField`); - await this._attachFields(metadataMap, fieldOptions); + await this.#attachFields(metadataMap, fieldOptions); /** @type {object[]} */ const metadataToCreate = []; @@ -633,6 +633,7 @@ class DataExtension extends MetadataType { } /** + * add a new field to the shared DE to trigger an update to the data model * helper for {@link DataExtension.#fixShared_item} * * @param {TYPE.BuObject} buObjectChildBu BU object for Child BU @@ -672,6 +673,7 @@ class DataExtension extends MetadataType { } /** + * get ID of the field added by {@link DataExtension.#fixShared_item_addField} on the shared DE via parent BU * helper for {@link DataExtension.#fixShared_item} * * @param {string} randomSuffix - @@ -699,6 +701,7 @@ class DataExtension extends MetadataType { } /** + * delete the field added by {@link DataExtension.#fixShared_item_addField} * helper for {@link DataExtension.#fixShared_item} * * @param {string} randomSuffix - @@ -757,7 +760,7 @@ class DataExtension extends MetadataType { // in case of cache dont get fields if (metadata && retrieveDir) { // get fields from API - await this._attachFields(metadata, fieldOptions, additionalFields); + await this.#attachFields(metadata, fieldOptions, additionalFields); } if (!retrieveDir && this.buObject.eid !== this.buObject.mid) { const metadataParentBu = await this.retrieveSharedForCache(additionalFields); @@ -777,6 +780,7 @@ class DataExtension extends MetadataType { } /** + * get shared dataExtensions from parent BU and merge them into the cache * helper for {@link DataExtension.retrieve} and for AttributeSet.retrieveForSharedDEs * * @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true @@ -860,7 +864,7 @@ class DataExtension extends MetadataType { * @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true * @returns {Promise.} - */ - static async _attachFields(metadata, fieldOptions, additionalFields) { + static async #attachFields(metadata, fieldOptions, additionalFields) { const fieldsObj = await this._retrieveFields(fieldOptions, additionalFields); const fieldKeys = Object.keys(fieldsObj); // add fields to corresponding DE From d2f72abe2633b01d698a02219bb121a8e1dd3776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 16 Aug 2023 21:30:45 +0200 Subject: [PATCH 16/70] #940: refactoring --- docs/dist/documentation.md | 6 +++--- lib/metadataTypes/AttributeSet.js | 10 ++++------ lib/metadataTypes/DataExtension.js | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index fe0313a62..666ecb716 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -1325,7 +1325,7 @@ AttributeSet MetadataType * [AttributeSet](#AttributeSet) ⇐ [MetadataType](#MetadataType) * [.retrieve(retrieveDir, [_], [__], [key])](#AttributeSet.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.retrieveForCache()](#AttributeSet.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> - * [.retrieveForSharedDEs(sharedDataExtensions)](#AttributeSet.retrieveForSharedDEs) ⇒ Promise.<Array.<string>> + * [.retrieveForSharedDEs(sharedDataExtensionIds)](#AttributeSet.retrieveForSharedDEs) ⇒ Promise.<Array.<string>> * [.parseResponseBody(body, [singleRetrieve])](#AttributeSet.parseResponseBody) ⇒ TYPE.MetadataTypeMap * [.postRetrieveTasks(metadata)](#AttributeSet.postRetrieveTasks) ⇒ TYPE.MetadataTypeItem * [._getSystemValueDefinitions()](#AttributeSet._getSystemValueDefinitions) ⇒ Array.<object> @@ -1354,7 +1354,7 @@ Retrieves Metadata of schema set definitions for caching. **Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise -### AttributeSet.retrieveForSharedDEs(sharedDataExtensions) ⇒ Promise.<Array.<string>> +### AttributeSet.retrieveForSharedDEs(sharedDataExtensionIds) ⇒ Promise.<Array.<string>> Retrieves Metadata of schema set definitions for caching. **Kind**: static method of [AttributeSet](#AttributeSet) @@ -1362,7 +1362,7 @@ Retrieves Metadata of schema set definitions for caching. | Param | Type | Description | | --- | --- | --- | -| sharedDataExtensions | object | list of IDs of shared data extensions | +| sharedDataExtensionIds | Array.<string> | ID array for shared data extensions | diff --git a/lib/metadataTypes/AttributeSet.js b/lib/metadataTypes/AttributeSet.js index e3ca68caf..a558587df 100644 --- a/lib/metadataTypes/AttributeSet.js +++ b/lib/metadataTypes/AttributeSet.js @@ -50,11 +50,11 @@ class AttributeSet extends MetadataType { /** * Retrieves Metadata of schema set definitions for caching. * - * @param {object} sharedDataExtensions list of IDs of shared data extensions + * @param {string[]} sharedDataExtensionIds ID array for shared data extensions * @returns {Promise.} Promise of list of shared dataExtension IDs */ - static async retrieveForSharedDEs(sharedDataExtensions) { - if (!Object.keys(sharedDataExtensions).length) { + static async retrieveForSharedDEs(sharedDataExtensionIds) { + if (!sharedDataExtensionIds.length) { return []; } const result = await super.retrieveREST(null, '/hub/v1/contacts/schema/setDefinitions'); @@ -67,9 +67,7 @@ class AttributeSet extends MetadataType { metadataMap[key].storageLogicalType === 'DataExtension' ) .filter((key) => - Object.keys(sharedDataExtensions).includes( - metadataMap[key].storageReferenceID.value - ) + sharedDataExtensionIds.includes(metadataMap[key].storageReferenceID.value) ) .map((key) => metadataMap[key].storageReferenceID.value) .filter(Boolean); diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index 3600dfab8..2875cffab 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -523,7 +523,7 @@ class DataExtension extends MetadataType { AttributeSet.buObject = buObjectChildBu; AttributeSet.client = clientChildBu; const sharedDeIdsUsedOnBU = await AttributeSet.retrieveForSharedDEs( - sharedDataExtensionMap + Object.keys(sharedDataExtensionMap) ); if (sharedDeIdsUsedOnBU.length) { let sharedDataExtensionsKeys = sharedDeIdsUsedOnBU.map( From 08068a8d7cc8dcf37367badfff9dd406191185aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 17 Aug 2023 10:50:32 +0200 Subject: [PATCH 17/70] #940: improve skip fixShared logic and logs --- lib/metadataTypes/DataExtension.js | 41 +++++++++++++++--------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index 2875cffab..4dd880959 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -370,26 +370,29 @@ class DataExtension extends MetadataType { * @returns {void} */ static async #fixShared(upsertedMetadata, originalMetadata, createdUpdated) { - if (this.buObject.eid !== this.buObject.mid || createdUpdated.updated === 0) { + if (this.buObject.eid !== this.buObject.mid) { // only if we were executing a deploy on parent bu could we be deploying shared data extensions + Util.logger.debug(`Skipping fixShared logic because we are not executing on Parent BU`); + return; + } + if (createdUpdated.updated === 0) { // only if updates were made could the issue in https://issues.salesforce.com/#q=W-11031095 affect data designer - Util.logger.debug(`EID != MID or nothing updated`); + Util.logger.debug(`Skipping fixShared logic because nothing was updated`); return; } // find all shared data extensions + if (!this.deployedSharedKeys.length) { + Util.logger.debug( + `Skipping fixShared logic because no Shared Data Extensions were updated` + ); + return; + } + const sharedDataExtensionsKeys = this.deployedSharedKeys; this.deployedSharedKeys = null; - if (sharedDataExtensionsKeys.length > 0 && !Util.OPTIONS.fixShared) { - Util.logger.warn( - 'Shared Data Extensions were updated but --fixShared option is not set. This can result in your changes not being visible in attribute groups on child BUs.' - ); - Util.logger.info( - 'We recommend to re-run your deployment with the --fixShared option unless you are sure your Shared Data Extension is not used in attribute groups on any child BU.' - ); - } - if (sharedDataExtensionsKeys.length > 0 && Util.OPTIONS.fixShared) { + if (Util.OPTIONS.fixShared) { // select which BUs to run this for const selectedBuNames = await this.#fixShared_getBUs(); @@ -421,13 +424,7 @@ class DataExtension extends MetadataType { ); for (const buName of selectedBuNames) { - await this.#fixShared_onBU( - buName, - buObjectBak, - clientBak, - - sharedDataExtensionMap - ); + await this.#fixShared_onBU(buName, buObjectBak, clientBak, sharedDataExtensionMap); } Util.logger.info(`Finished fixing Shared Data Extensions details in data models`); @@ -435,8 +432,12 @@ class DataExtension extends MetadataType { this.buObject = buObjectBak; this.client = clientBak; } else { - // no shared dataExtensions or fixShared option not enabled - return; + Util.logger.warn( + 'Shared Data Extensions were updated but --fixShared option is not set. This can result in your changes not being visible in attribute groups on child BUs.' + ); + Util.logger.info( + 'We recommend to re-run your deployment with the --fixShared option unless you are sure your Shared Data Extension is not used in attribute groups on any child BU.' + ); } } From 1076b7020e93430a40c759bf26e240f83e8e599e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 17 Aug 2023 10:54:29 +0200 Subject: [PATCH 18/70] #940: only run fixShared logic if fields of a shared data extension were added/changed --- lib/metadataTypes/DataExtension.js | 11 ++++++++++- lib/metadataTypes/DataExtensionField.js | 5 +++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index 4dd880959..acf737fe2 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -389,8 +389,17 @@ class DataExtension extends MetadataType { return; } - const sharedDataExtensionsKeys = this.deployedSharedKeys; + const sharedDataExtensionsKeys = this.deployedSharedKeys.filter((key) => + DataExtensionField.fixShared_fieldChange.includes(key) + ); this.deployedSharedKeys = null; + if (!sharedDataExtensionsKeys.length) { + // only if updates were made could the issue in https://issues.salesforce.com/#q=W-11031095 affect data designer + Util.logger.debug( + `Skipping fixShared logic because no fields were changed on updated Shared Data Extensions` + ); + return; + } if (Util.OPTIONS.fixShared) { // select which BUs to run this for diff --git a/lib/metadataTypes/DataExtensionField.js b/lib/metadataTypes/DataExtensionField.js index 9fc4c2071..c993c15f9 100644 --- a/lib/metadataTypes/DataExtensionField.js +++ b/lib/metadataTypes/DataExtensionField.js @@ -248,6 +248,11 @@ class DataExtensionField extends MetadataType { }` + deployColumns.map((item) => item.Name).join(', ') ) ); + // create list of DE keys that had changes to their fields to be able to use it as a filter in the --fixShared logic + this.fixShared_fieldChange ||= []; + if (deployColumns.length) { + this.fixShared_fieldChange.push(deKey); + } return existingFieldByName; } From 7947ce30ea74ec799c6b00d447d2e79d84ec90fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 17 Aug 2023 15:06:32 +0200 Subject: [PATCH 19/70] #940: check if attributeSet fields actually need fixing --- docs/dist/documentation.md | 11 +-- lib/metadataTypes/AttributeSet.js | 108 +++++++++++++++++++++--- lib/metadataTypes/DataExtension.js | 24 +++--- lib/metadataTypes/DataExtensionField.js | 17 +++- 4 files changed, 131 insertions(+), 29 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 666ecb716..f01ef5cf2 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -1325,7 +1325,7 @@ AttributeSet MetadataType * [AttributeSet](#AttributeSet) ⇐ [MetadataType](#MetadataType) * [.retrieve(retrieveDir, [_], [__], [key])](#AttributeSet.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.retrieveForCache()](#AttributeSet.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> - * [.retrieveForSharedDEs(sharedDataExtensionIds)](#AttributeSet.retrieveForSharedDEs) ⇒ Promise.<Array.<string>> + * [.fixShared_retrieve(sharedDataExtensionMap, fixShared_fieldChange)](#AttributeSet.fixShared_retrieve) ⇒ Promise.<Array.<string>> * [.parseResponseBody(body, [singleRetrieve])](#AttributeSet.parseResponseBody) ⇒ TYPE.MetadataTypeMap * [.postRetrieveTasks(metadata)](#AttributeSet.postRetrieveTasks) ⇒ TYPE.MetadataTypeItem * [._getSystemValueDefinitions()](#AttributeSet._getSystemValueDefinitions) ⇒ Array.<object> @@ -1352,9 +1352,9 @@ Retrieves Metadata of schema set definitions for caching. **Kind**: static method of [AttributeSet](#AttributeSet) **Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise - + -### AttributeSet.retrieveForSharedDEs(sharedDataExtensionIds) ⇒ Promise.<Array.<string>> +### AttributeSet.fixShared\_retrieve(sharedDataExtensionMap, fixShared_fieldChange) ⇒ Promise.<Array.<string>> Retrieves Metadata of schema set definitions for caching. **Kind**: static method of [AttributeSet](#AttributeSet) @@ -1362,7 +1362,8 @@ Retrieves Metadata of schema set definitions for caching. | Param | Type | Description | | --- | --- | --- | -| sharedDataExtensionIds | Array.<string> | ID array for shared data extensions | +| sharedDataExtensionMap | Object.<string, string> | ID-Key relationship of shared data extensions | +| fixShared_fieldChange | object | DataExtensionField.fixShared_fieldChange | @@ -1918,7 +1919,7 @@ Retrieves dataExtension metadata. Afterwards starts retrieval of dataExtensionCo ### DataExtension.retrieveSharedForCache([additionalFields]) ⇒ Promise.<TYPE.DataExtensionMap> get shared dataExtensions from parent BU and merge them into the cache -helper for [retrieve](#DataExtension.retrieve) and for AttributeSet.retrieveForSharedDEs +helper for [retrieve](#DataExtension.retrieve) and for AttributeSet.fixShared_retrieve **Kind**: static method of [DataExtension](#DataExtension) **Returns**: Promise.<TYPE.DataExtensionMap> - keyField => metadata map diff --git a/lib/metadataTypes/AttributeSet.js b/lib/metadataTypes/AttributeSet.js index a558587df..a076d514d 100644 --- a/lib/metadataTypes/AttributeSet.js +++ b/lib/metadataTypes/AttributeSet.js @@ -50,28 +50,114 @@ class AttributeSet extends MetadataType { /** * Retrieves Metadata of schema set definitions for caching. * - * @param {string[]} sharedDataExtensionIds ID array for shared data extensions + * @param {Object.} sharedDataExtensionMap ID-Key relationship of shared data extensions + * @param {object} fixShared_fieldChange DataExtensionField.fixShared_fieldChange * @returns {Promise.} Promise of list of shared dataExtension IDs */ - static async retrieveForSharedDEs(sharedDataExtensionIds) { - if (!sharedDataExtensionIds.length) { + static async fixShared_retrieve(sharedDataExtensionMap, fixShared_fieldChange) { + if (!Object.keys(sharedDataExtensionMap).length) { return []; } const result = await super.retrieveREST(null, '/hub/v1/contacts/schema/setDefinitions'); const metadataMap = result?.metadata; if (metadataMap && Object.keys(metadataMap).length) { - const sharedDEs = Object.keys(metadataMap) + const sharedDeIds = Object.keys(metadataMap) .filter( - (key) => - metadataMap[key].storageLogicalType === 'ExactTargetSchema' || - metadataMap[key].storageLogicalType === 'DataExtension' - ) - .filter((key) => - sharedDataExtensionIds.includes(metadataMap[key].storageReferenceID.value) + (asKey) => + metadataMap[asKey].storageLogicalType === 'ExactTargetSchema' || + metadataMap[asKey].storageLogicalType === 'DataExtension' ) + .filter((asKey) => { + // check if dataExtension ID is found on any attributeSet of this BU + if (sharedDataExtensionMap[metadataMap[asKey].storageReferenceID.value]) { + Util.logger.debug( + ` shared dataExtension ID ${metadataMap[asKey].storageReferenceID.value} found in attributeGroup ${asKey}` + ); + return true; + } else { + return false; + } + }) + .filter((asKey) => { + // check if any of the updated dataExtension fields dont exist on the attributeSet or are out of date + const deKey = + sharedDataExtensionMap[metadataMap[asKey].storageReferenceID.value]; + const asFields = metadataMap[asKey].valueDefinitions; + if (!fixShared_fieldChange[deKey]) { + Util.logger.debug( + ` - No changed fields found. Assuming re-deploy with --fixShared flag` + ); + return true; + } + const deFields = Object.values(fixShared_fieldChange[deKey]); + return deFields.some((deField) => { + const search = asFields.filter((asf) => asf.name === deField.Name); + if (!search.length) { + Util.logger.debug( + Util.getGrayMsg( + ` - Field ${deField.Name} not found in attributeSet` + ) + ); + return true; + } + const asField = search[0]; + if (asField.dataType !== deField.FieldType) { + Util.logger.debug( + Util.getGrayMsg( + ` - Field ${deField.Name} FieldType changed (old: ${asField.dataType}; new: ${deField.FieldType})` + ) + ); + return true; + } + if ( + (asField.defaultValue && deField.DefaultValue !== '') || + (deField.FieldType === 'Boolean' && + deField.DefaultValue !== '' && + deField.DefaultValue + ? 'True' + : 'False' !== asField.defaultValue) || + (deField.FieldType !== 'Boolean' && + deField.DefaultValue !== asField.defaultValue) + ) { + Util.logger.debug( + Util.getGrayMsg( + ` - Field ${deField.Name} DefaultValue changed (old: ${asField.defaultValue}; new: ${deField.DefaultValue})` + ) + ); + return true; + } + // some field types don't carry the length property. reset to 0 to ease comparison + asField.length ||= 0; + if (asField.length !== deField.MaxLength) { + Util.logger.debug( + Util.getGrayMsg( + ` - Field ${deField.Name} MaxLength changed (old: ${asField.length}; new: ${deField.MaxLength})` + ) + ); + return true; + } + if (asField.isNullable !== deField.IsRequired) { + Util.logger.debug( + Util.getGrayMsg( + ` - Field ${deField.Name} IsRequired changed (old: ${asField.isNullable}; new: ${deField.IsRequired})` + ) + ); + return true; + } + if (asField.isPrimaryKey !== deField.IsPrimaryKey) { + Util.logger.debug( + Util.getGrayMsg( + ` - Field ${deField.Name} IsPrimaryKey changed (old: ${asField.isPrimaryKey}; new: ${deField.IsPrimaryKey})` + ) + ); + return true; + } + return false; + }); + }) .map((key) => metadataMap[key].storageReferenceID.value) .filter(Boolean); - return sharedDEs; + return sharedDeIds; } else { // nothing to do - return empty array return []; diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index acf737fe2..9215ff409 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -389,16 +389,19 @@ class DataExtension extends MetadataType { return; } - const sharedDataExtensionsKeys = this.deployedSharedKeys.filter((key) => - DataExtensionField.fixShared_fieldChange.includes(key) - ); + const sharedDataExtensionsKeys = this.deployedSharedKeys; this.deployedSharedKeys = null; - if (!sharedDataExtensionsKeys.length) { - // only if updates were made could the issue in https://issues.salesforce.com/#q=W-11031095 affect data designer + if ( + !sharedDataExtensionsKeys.filter( + (deKey) => + DataExtensionField.fixShared_fieldChange[deKey] && + Object.keys(DataExtensionField.fixShared_fieldChange[deKey])?.length + ) + ) { Util.logger.debug( - `Skipping fixShared logic because no fields were changed on updated Shared Data Extensions` + ` - No changed fields found. Assuming re-deploy with --fixShared flag` ); - return; + return true; } if (Util.OPTIONS.fixShared) { @@ -532,8 +535,9 @@ class DataExtension extends MetadataType { AttributeSet.properties = this.properties; AttributeSet.buObject = buObjectChildBu; AttributeSet.client = clientChildBu; - const sharedDeIdsUsedOnBU = await AttributeSet.retrieveForSharedDEs( - Object.keys(sharedDataExtensionMap) + const sharedDeIdsUsedOnBU = await AttributeSet.fixShared_retrieve( + sharedDataExtensionMap, + DataExtensionField.fixShared_fieldChange ); if (sharedDeIdsUsedOnBU.length) { let sharedDataExtensionsKeys = sharedDeIdsUsedOnBU.map( @@ -791,7 +795,7 @@ class DataExtension extends MetadataType { /** * get shared dataExtensions from parent BU and merge them into the cache - * helper for {@link DataExtension.retrieve} and for AttributeSet.retrieveForSharedDEs + * helper for {@link DataExtension.retrieve} and for AttributeSet.fixShared_retrieve * * @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true * @returns {Promise.} keyField => metadata map diff --git a/lib/metadataTypes/DataExtensionField.js b/lib/metadataTypes/DataExtensionField.js index c993c15f9..4d81e45f1 100644 --- a/lib/metadataTypes/DataExtensionField.js +++ b/lib/metadataTypes/DataExtensionField.js @@ -99,6 +99,10 @@ class DataExtensionField extends MetadataType { * @returns {Promise.>} existing fields by their original name to allow re-adding FieldType after update */ static async prepareDeployColumnsOnUpdate(deployColumns, deKey) { + // create list of DE keys that had changes to their fields to be able to use it as a filter in the --fixShared logic + this.fixShared_fieldChange ||= {}; + this.fixShared_fieldChange[deKey] ||= {}; + // get row count to know which field restrictions apply let hasData = false; try { @@ -189,6 +193,9 @@ class DataExtensionField extends MetadataType { Util.logger.verbose(`no change - removed field [${deKey}].[${item.Name}]`); continue; } + // track name of changed field + this.fixShared_fieldChange[deKey][item.Name] = JSON.parse(JSON.stringify(item)); + this.fixShared_fieldChange[deKey][item.Name].FieldType = itemOld.FieldType; // set the ObjectId for clear identification during update item.ObjectID = itemOld.ObjectID; @@ -217,6 +224,8 @@ class DataExtensionField extends MetadataType { } // Field doesn't exist in target, therefore Remove ObjectID if present delete item.ObjectID; + + this.fixShared_fieldChange[deKey][item.Name] = JSON.parse(JSON.stringify(item)); } if (Util.isTrue(item.IsPrimaryKey) && Util.isFalse(item.IsRequired)) { // applicable: with or without data @@ -232,12 +241,14 @@ class DataExtensionField extends MetadataType { `- Invalid value for 'IsRequired' of [${deKey}].[${item.Name}]. Found '${item.IsRequired}' instead of 'true'/'false'. Removing field from deploy!` ); deployColumns.splice(i, 1); + delete this.fixShared_fieldChange[deKey][item.Name]; } if (!Util.isTrue(item.IsPrimaryKey) && !Util.isFalse(item.IsPrimaryKey)) { Util.logger.error( `- Invalid value for 'IsPrimaryKey' of [${deKey}].[${item.Name}]. Found '${item.IsPrimaryKey}' instead of 'true'/'false'. Removing field from deploy!` ); deployColumns.splice(i, 1); + delete this.fixShared_fieldChange[deKey][item.Name]; } } @@ -249,9 +260,9 @@ class DataExtensionField extends MetadataType { ) ); // create list of DE keys that had changes to their fields to be able to use it as a filter in the --fixShared logic - this.fixShared_fieldChange ||= []; - if (deployColumns.length) { - this.fixShared_fieldChange.push(deKey); + if (!deployColumns.length || !Object.keys(this.fixShared_fieldChange[deKey]).length) { + // no changed fields found. remove entry from list for easier processing + delete this.fixShared_fieldChange[deKey]; } return existingFieldByName; } From 0630c5b16810c20e3e7fa3b470cd6bed7a0f763f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 17 Aug 2023 15:30:29 +0200 Subject: [PATCH 20/70] #0: fix incorrect parameter count for retrieveREST --- lib/metadataTypes/AttributeSet.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/metadataTypes/AttributeSet.js b/lib/metadataTypes/AttributeSet.js index a076d514d..7aa260442 100644 --- a/lib/metadataTypes/AttributeSet.js +++ b/lib/metadataTypes/AttributeSet.js @@ -31,13 +31,7 @@ class AttributeSet extends MetadataType { const result = await AttributeGroup.retrieveForCache(); cache.setMetadata('attributeGroup', result.metadata); } - return super.retrieveREST( - retrieveDir, - '/hub/v1/contacts/schema/setDefinitions', - null, - null, - key - ); + return super.retrieveREST(retrieveDir, '/hub/v1/contacts/schema/setDefinitions', null, key); } /** * Retrieves Metadata of schema set definitions for caching. From 773ff9925fe407fb531ea14a5e7aaa0613bbb791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 17 Aug 2023 15:32:29 +0200 Subject: [PATCH 21/70] #0: add missing attributeSet fields --- .../definitions/AttributeSet.definition.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/metadataTypes/definitions/AttributeSet.definition.js b/lib/metadataTypes/definitions/AttributeSet.definition.js index 47e77cde4..22d196ba0 100644 --- a/lib/metadataTypes/definitions/AttributeSet.definition.js +++ b/lib/metadataTypes/definitions/AttributeSet.definition.js @@ -119,6 +119,12 @@ module.exports = { retrieving: true, template: null, }, + 'dataRetentionProperties.periodLength': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, definitionID: { isCreateable: null, isUpdateable: null, @@ -859,5 +865,11 @@ module.exports = { retrieving: true, template: null, }, + r__dataExtension_CustomerKey: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, }, }; From 9e6553900be109f6d24946d6de1a1eae23a15da4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 17 Aug 2023 15:48:11 +0200 Subject: [PATCH 22/70] #940: jsdoc --- docs/dist/documentation.md | 3 ++- lib/metadataTypes/AttributeSet.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index f01ef5cf2..223bfdeb4 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -1355,7 +1355,8 @@ Retrieves Metadata of schema set definitions for caching. ### AttributeSet.fixShared\_retrieve(sharedDataExtensionMap, fixShared_fieldChange) ⇒ Promise.<Array.<string>> -Retrieves Metadata of schema set definitions for caching. +used to identify updated shared data extensions that are used in attributeSets. +helper for DataExtension.#fixShared_onBU **Kind**: static method of [AttributeSet](#AttributeSet) **Returns**: Promise.<Array.<string>> - Promise of list of shared dataExtension IDs diff --git a/lib/metadataTypes/AttributeSet.js b/lib/metadataTypes/AttributeSet.js index 7aa260442..93fb43aa9 100644 --- a/lib/metadataTypes/AttributeSet.js +++ b/lib/metadataTypes/AttributeSet.js @@ -42,7 +42,8 @@ class AttributeSet extends MetadataType { return super.retrieveREST(null, '/hub/v1/contacts/schema/setDefinitions'); } /** - * Retrieves Metadata of schema set definitions for caching. + * used to identify updated shared data extensions that are used in attributeSets. + * helper for DataExtension.#fixShared_onBU * * @param {Object.} sharedDataExtensionMap ID-Key relationship of shared data extensions * @param {object} fixShared_fieldChange DataExtensionField.fixShared_fieldChange From e9342501a8cfbea2f7bf0c20a4c05fe53e167da6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 17 Aug 2023 16:13:51 +0200 Subject: [PATCH 23/70] #940: remove comments --- test/type.dataExtension.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/type.dataExtension.test.js b/test/type.dataExtension.test.js index addbf820c..17f30a354 100644 --- a/test/type.dataExtension.test.js +++ b/test/type.dataExtension.test.js @@ -342,7 +342,6 @@ describe('type: dataExtension', () => { }); }); describe('Delete ================', () => { - // TODO: add this test it('Should delete the dataExtension', async () => { // WHEN const result = await handler.deleteByKey( From 8cf5ae3743436bf0d81b89d55f65c28a09aabc13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 17 Aug 2023 17:10:10 +0200 Subject: [PATCH 24/70] #940: fix test for --fixShared with added fields --- lib/metadataTypes/AttributeSet.js | 33 ++++------ ...ataExtensionShared.dataExtension-meta.json | 2 +- .../dataExtension/update-expected.json | 2 +- .../schema/setDefinitions/get-response.json | 60 ------------------- 4 files changed, 15 insertions(+), 82 deletions(-) diff --git a/lib/metadataTypes/AttributeSet.js b/lib/metadataTypes/AttributeSet.js index 93fb43aa9..0c72a4dc1 100644 --- a/lib/metadataTypes/AttributeSet.js +++ b/lib/metadataTypes/AttributeSet.js @@ -66,7 +66,7 @@ class AttributeSet extends MetadataType { // check if dataExtension ID is found on any attributeSet of this BU if (sharedDataExtensionMap[metadataMap[asKey].storageReferenceID.value]) { Util.logger.debug( - ` shared dataExtension ID ${metadataMap[asKey].storageReferenceID.value} found in attributeGroup ${asKey}` + ` shared dataExtension ID ${metadataMap[asKey].storageReferenceID.value} found in attributeSet ${asKey}` ); return true; } else { @@ -90,7 +90,7 @@ class AttributeSet extends MetadataType { if (!search.length) { Util.logger.debug( Util.getGrayMsg( - ` - Field ${deField.Name} not found in attributeSet` + ` - Field ${deField.Name} not found in attributeSet; Note: only first recognized difference is printed to log` ) ); return true; @@ -99,25 +99,24 @@ class AttributeSet extends MetadataType { if (asField.dataType !== deField.FieldType) { Util.logger.debug( Util.getGrayMsg( - ` - Field ${deField.Name} FieldType changed (old: ${asField.dataType}; new: ${deField.FieldType})` + ` - Field ${deField.Name} FieldType changed (old: ${asField.dataType}; new: ${deField.FieldType}); Note: only first recognized difference is printed to log` ) ); return true; } + asField.defaultValue ||= ''; if ( - (asField.defaultValue && deField.DefaultValue !== '') || + (asField.defaultValue && deField.DefaultValue === '') || (deField.FieldType === 'Boolean' && - deField.DefaultValue !== '' && - deField.DefaultValue - ? 'True' - : 'False' !== asField.defaultValue) || + deField.DefaultValue !== '' && + (deField.DefaultValue + ? 'True' + : 'False' !== asField.defaultValue)) || (deField.FieldType !== 'Boolean' && deField.DefaultValue !== asField.defaultValue) ) { Util.logger.debug( - Util.getGrayMsg( - ` - Field ${deField.Name} DefaultValue changed (old: ${asField.defaultValue}; new: ${deField.DefaultValue})` - ) + ` - Field ${deField.Name} DefaultValue changed (old: ${asField.defaultValue}; new: ${deField.DefaultValue}); Note: only first recognized difference is printed to log` ); return true; } @@ -125,25 +124,19 @@ class AttributeSet extends MetadataType { asField.length ||= 0; if (asField.length !== deField.MaxLength) { Util.logger.debug( - Util.getGrayMsg( - ` - Field ${deField.Name} MaxLength changed (old: ${asField.length}; new: ${deField.MaxLength})` - ) + ` - Field ${deField.Name} MaxLength changed (old: ${asField.length}; new: ${deField.MaxLength}); Note: only first recognized difference is printed to log` ); return true; } if (asField.isNullable !== deField.IsRequired) { Util.logger.debug( - Util.getGrayMsg( - ` - Field ${deField.Name} IsRequired changed (old: ${asField.isNullable}; new: ${deField.IsRequired})` - ) + ` - Field ${deField.Name} IsRequired changed (old: ${asField.isNullable}; new: ${deField.IsRequired}); Note: only first recognized difference is printed to log` ); return true; } if (asField.isPrimaryKey !== deField.IsPrimaryKey) { Util.logger.debug( - Util.getGrayMsg( - ` - Field ${deField.Name} IsPrimaryKey changed (old: ${asField.isPrimaryKey}; new: ${deField.IsPrimaryKey})` - ) + ` - Field ${deField.Name} IsPrimaryKey changed (old: ${asField.isPrimaryKey}; new: ${deField.IsPrimaryKey}); Note: only first recognized difference is printed to log` ); return true; } diff --git a/test/mockRoot/deploy/testInstance/_ParentBU_/dataExtension/testExisting_dataExtensionShared.dataExtension-meta.json b/test/mockRoot/deploy/testInstance/_ParentBU_/dataExtension/testExisting_dataExtensionShared.dataExtension-meta.json index 23a55d5a1..9af73620a 100644 --- a/test/mockRoot/deploy/testInstance/_ParentBU_/dataExtension/testExisting_dataExtensionShared.dataExtension-meta.json +++ b/test/mockRoot/deploy/testInstance/_ParentBU_/dataExtension/testExisting_dataExtensionShared.dataExtension-meta.json @@ -38,7 +38,7 @@ "FieldType": "EmailAddress" }, { - "Name": "testField", + "Name": "newField", "DefaultValue": "", "MaxLength": 254, "IsRequired": false, diff --git a/test/resources/1111111/dataExtension/update-expected.json b/test/resources/1111111/dataExtension/update-expected.json index e889c1f87..bd074f2ad 100644 --- a/test/resources/1111111/dataExtension/update-expected.json +++ b/test/resources/1111111/dataExtension/update-expected.json @@ -41,7 +41,7 @@ "IsPrimaryKey": false, "IsRequired": false, "MaxLength": 254, - "Name": "testField" + "Name": "newField" } ], "IsSendable": false, diff --git a/test/resources/9999999/hub/v1/contacts/schema/setDefinitions/get-response.json b/test/resources/9999999/hub/v1/contacts/schema/setDefinitions/get-response.json index 150cb21ff..54e6f02ab 100644 --- a/test/resources/9999999/hub/v1/contacts/schema/setDefinitions/get-response.json +++ b/test/resources/9999999/hub/v1/contacts/schema/setDefinitions/get-response.json @@ -20075,66 +20075,6 @@ }, "parentIdentifier": "528b787a-5238-ee11-b85a-48df37d1de8a" }, - { - "baseType": "Text", - "dataSourceName": {}, - "dataType": "Text", - "description": "", - "localizedDescription": { - "value": "" - }, - "definitionID": "538b787a-5238-ee11-b85a-48df37d1de8a", - "definitionKey": "testField", - "definitionName": { - "value": "testField" - }, - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "displayOrder": 3, - "fullyQualifiedName": "testExisting_dataExtensionShared.testField", - "isHidden": false, - "isIdentityValue": false, - "isNullable": true, - "isPrimaryKey": false, - "isReadOnly": false, - "isSystemDefined": false, - "isUpdateable": true, - "length": 254, - "obfuscationProperties": { - "maskType": "None", - "maskTypeID": 0, - "storageTypeID": 1, - "storageType": "Plain", - "valueDefinitionID": "538b787a-5238-ee11-b85a-48df37d1de8a" - }, - "ordinal": 3, - "parentDefinition": { - "definitionID": "528b787a-5238-ee11-b85a-48df37d1de8a", - "definitionKey": "testExisting_dataExtensionShared", - "definitionName": { - "value": "testExisting_dataExtensionShared" - }, - "connectingID": { - "identifierType": "FullyQualifiedName" - } - }, - "parentType": "Set", - "storageName": "testField", - "storageFieldReferenceID": { - "type": "guid", - "value": "ec7a606d-f38e-4c91-a5f2-02bf231a0954" - }, - "valueDefinitionID": "538b787a-5238-ee11-b85a-48df37d1de8a", - "valueDefinitionKey": "testField", - "name": "testField", - "setDefinitionID": "528b787a-5238-ee11-b85a-48df37d1de8a", - "setDefinitionKey": "testExisting_dataExtensionShared", - "setDefinitionName": { - "value": "testExisting_dataExtensionShared" - }, - "parentIdentifier": "528b787a-5238-ee11-b85a-48df37d1de8a" - }, { "baseType": "Text", "dataSourceName": {}, From 550ebb4ffdb71a9bfdb362f8b5507d123fabdec0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Aug 2023 15:16:13 +0000 Subject: [PATCH 25/70] Bump lint-staged from 13.2.3 to 14.0.0 Bumps [lint-staged](https://github.com/okonet/lint-staged) from 13.2.3 to 14.0.0. - [Release notes](https://github.com/okonet/lint-staged/releases) - [Commits](https://github.com/okonet/lint-staged/compare/v13.2.3...v14.0.0) --- updated-dependencies: - dependency-name: lint-staged dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package-lock.json | 649 +++++++++++++++++++++++++++++----------------- package.json | 2 +- 2 files changed, 415 insertions(+), 236 deletions(-) diff --git a/package-lock.json b/package-lock.json index 380e2af9c..f418dd413 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,7 +48,7 @@ "fast-xml-parser": "4.2.7", "husky": "8.0.3", "jsdoc-to-markdown": "8.0.0", - "lint-staged": "13.2.3", + "lint-staged": "14.0.0", "mocha": "10.2.0", "mock-fs": "5.2.0", "npm-check": "6.0.1", @@ -1409,15 +1409,6 @@ "node": "*" } }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/async": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", @@ -2130,9 +2121,9 @@ } }, "node_modules/cli-truncate/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { "ansi-regex": "^6.0.1" @@ -2253,9 +2244,9 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "node_modules/colorette": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true }, "node_modules/colorspace": { @@ -2372,12 +2363,12 @@ } }, "node_modules/commander": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.0.tgz", - "integrity": "sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", "dev": true, "engines": { - "node": ">=14" + "node": ">=16" } }, "node_modules/comment-parser": { @@ -3501,10 +3492,16 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, "node_modules/execa": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.0.tgz", - "integrity": "sha512-T6nIJO3LHxUZ6ahVRaxXz9WLEruXLqdcluA+UuTptXmLM7nDAn9lx9IfkxPyzEL21583qSt4RmL44pO71EHaJQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", @@ -4653,9 +4650,9 @@ "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" }, "node_modules/human-signals": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.0.tgz", - "integrity": "sha512-zyzVyMjpGBX2+6cDVZeFPCdtOtdsxOeseRhB9tkQ6xXmGUNrcnBzdEKPy3VPNYz+4gy1oukVOXcrJCunSyc6QQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", "dev": true, "engines": { "node": ">=14.18.0" @@ -5727,39 +5724,36 @@ } }, "node_modules/lint-staged": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.2.3.tgz", - "integrity": "sha512-zVVEXLuQIhr1Y7R7YAWx4TZLdvuzk7DnmrsTNL0fax6Z3jrpFcas+vKbzxhhvp6TA55m1SQuWkpzI1qbfDZbAg==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-14.0.0.tgz", + "integrity": "sha512-0tLf0pqZYkar/wu3nTctk4rVIG+d7PanDYv4/IQR4qwdqfQkTDziLRFnqMcLuLBTuUqmcLwsHPD2EjQ18d/oaA==", "dev": true, "dependencies": { - "chalk": "5.2.0", - "cli-truncate": "^3.1.0", - "commander": "^10.0.0", - "debug": "^4.3.4", - "execa": "^7.0.0", + "chalk": "5.3.0", + "commander": "11.0.0", + "debug": "4.3.4", + "execa": "7.2.0", "lilconfig": "2.1.0", - "listr2": "^5.0.7", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-inspect": "^1.12.3", - "pidtree": "^0.6.0", - "string-argv": "^0.3.1", - "yaml": "^2.2.2" + "listr2": "6.6.1", + "micromatch": "4.0.5", + "pidtree": "0.6.0", + "string-argv": "0.3.2", + "yaml": "2.3.1" }, "bin": { "lint-staged": "bin/lint-staged.js" }, "engines": { - "node": "^14.13.1 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" }, "funding": { "url": "https://opencollective.com/lint-staged" } }, "node_modules/lint-staged/node_modules/chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -5769,22 +5763,20 @@ } }, "node_modules/listr2": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.8.tgz", - "integrity": "sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA==", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz", + "integrity": "sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==", "dev": true, "dependencies": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.19", - "log-update": "^4.0.0", - "p-map": "^4.0.0", + "cli-truncate": "^3.1.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^5.0.1", "rfdc": "^1.3.0", - "rxjs": "^7.8.0", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" + "wrap-ansi": "^8.1.0" }, "engines": { - "node": "^14.13.1 || >=16.0.0" + "node": ">=16.0.0" }, "peerDependencies": { "enquirer": ">= 2.3.0 < 3" @@ -5795,43 +5787,83 @@ } } }, - "node_modules/listr2/node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/listr2/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/listr2/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/listr2/node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/load-json-file": { @@ -5972,61 +6004,159 @@ } }, "node_modules/log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz", + "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==", "dev": true, "dependencies": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" + "ansi-escapes": "^5.0.0", + "cli-cursor": "^4.0.0", + "slice-ansi": "^5.0.0", + "strip-ansi": "^7.0.1", + "wrap-ansi": "^8.0.1" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", "dev": true, + "dependencies": { + "type-fest": "^1.0.2" + }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/slice-ansi": { + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/cli-cursor": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/log-update/node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/log-update/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/logform": { @@ -7491,21 +7621,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -8828,9 +8943,9 @@ } }, "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", - "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "engines": { "node": ">=12" @@ -9028,9 +9143,9 @@ } }, "node_modules/string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", "dev": true, "engines": { "node": ">=0.6.19" @@ -9876,9 +9991,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", - "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", "dev": true, "engines": { "node": ">= 14" @@ -10992,12 +11107,6 @@ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, "async": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", @@ -11501,9 +11610,9 @@ } }, "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "requires": { "ansi-regex": "^6.0.1" @@ -11603,9 +11712,9 @@ } }, "colorette": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true }, "colorspace": { @@ -11704,9 +11813,9 @@ } }, "commander": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.0.tgz", - "integrity": "sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", "dev": true }, "comment-parser": { @@ -12551,10 +12660,16 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, "execa": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.0.tgz", - "integrity": "sha512-T6nIJO3LHxUZ6ahVRaxXz9WLEruXLqdcluA+UuTptXmLM7nDAn9lx9IfkxPyzEL21583qSt4RmL44pO71EHaJQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", "dev": true, "requires": { "cross-spawn": "^7.0.3", @@ -13387,9 +13502,9 @@ "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" }, "human-signals": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.0.tgz", - "integrity": "sha512-zyzVyMjpGBX2+6cDVZeFPCdtOtdsxOeseRhB9tkQ6xXmGUNrcnBzdEKPy3VPNYz+4gy1oukVOXcrJCunSyc6QQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", "dev": true }, "husky": { @@ -14152,75 +14267,92 @@ } }, "lint-staged": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.2.3.tgz", - "integrity": "sha512-zVVEXLuQIhr1Y7R7YAWx4TZLdvuzk7DnmrsTNL0fax6Z3jrpFcas+vKbzxhhvp6TA55m1SQuWkpzI1qbfDZbAg==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-14.0.0.tgz", + "integrity": "sha512-0tLf0pqZYkar/wu3nTctk4rVIG+d7PanDYv4/IQR4qwdqfQkTDziLRFnqMcLuLBTuUqmcLwsHPD2EjQ18d/oaA==", "dev": true, "requires": { - "chalk": "5.2.0", - "cli-truncate": "^3.1.0", - "commander": "^10.0.0", - "debug": "^4.3.4", - "execa": "^7.0.0", + "chalk": "5.3.0", + "commander": "11.0.0", + "debug": "4.3.4", + "execa": "7.2.0", "lilconfig": "2.1.0", - "listr2": "^5.0.7", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-inspect": "^1.12.3", - "pidtree": "^0.6.0", - "string-argv": "^0.3.1", - "yaml": "^2.2.2" + "listr2": "6.6.1", + "micromatch": "4.0.5", + "pidtree": "0.6.0", + "string-argv": "0.3.2", + "yaml": "2.3.1" }, "dependencies": { "chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true } } }, "listr2": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.8.tgz", - "integrity": "sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA==", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz", + "integrity": "sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==", "dev": true, "requires": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.19", - "log-update": "^4.0.0", - "p-map": "^4.0.0", + "cli-truncate": "^3.1.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^5.0.1", "rfdc": "^1.3.0", - "rxjs": "^7.8.0", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" + "wrap-ansi": "^8.1.0" }, "dependencies": { - "cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "requires": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" } }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } }, - "slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" } } } @@ -14345,43 +14477,99 @@ } }, "log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz", + "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==", "dev": true, "requires": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" + "ansi-escapes": "^5.0.0", + "cli-cursor": "^4.0.0", + "slice-ansi": "^5.0.0", + "strip-ansi": "^7.0.1", + "wrap-ansi": "^8.0.1" }, "dependencies": { - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "ansi-escapes": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", + "dev": true, + "requires": { + "type-fest": "^1.0.2" + } + }, + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true }, - "slice-ansi": { + "cli-cursor": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", "dev": true, "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "restore-cursor": "^4.0.0" + } + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" } }, + "type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true + }, "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" } } } @@ -15498,15 +15686,6 @@ } } }, - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -16483,9 +16662,9 @@ }, "dependencies": { "ansi-styles": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", - "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true } } @@ -16647,9 +16826,9 @@ } }, "string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", "dev": true }, "string-width": { @@ -17292,9 +17471,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yaml": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", - "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", "dev": true }, "yargs": { diff --git a/package.json b/package.json index 35eb9fcc0..18c2ea3dc 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "fast-xml-parser": "4.2.7", "husky": "8.0.3", "jsdoc-to-markdown": "8.0.0", - "lint-staged": "13.2.3", + "lint-staged": "14.0.0", "mocha": "10.2.0", "mock-fs": "5.2.0", "npm-check": "6.0.1", From 3df6eae47adc7dcc3e0c9f7c4eadc1ed247b965f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Aug 2023 15:19:22 +0000 Subject: [PATCH 26/70] Bump inquirer from 8.2.5 to 8.2.6 Bumps [inquirer](https://github.com/SBoudrias/Inquirer.js) from 8.2.5 to 8.2.6. - [Release notes](https://github.com/SBoudrias/Inquirer.js/releases) - [Commits](https://github.com/SBoudrias/Inquirer.js/compare/inquirer@8.2.5...inquirer@8.2.6) --- updated-dependencies: - dependency-name: inquirer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 43 ++++++++++++++++++++++++++++++++++--------- package.json | 2 +- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index f418dd413..4e55a12d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "console.table": "0.10.0", "deep-equal": "2.2.2", "fs-extra": "11.1.0", - "inquirer": "8.2.5", + "inquirer": "8.2.6", "json-to-table": "4.2.1", "mustache": "4.2.0", "p-limit": "3.1.0", @@ -4780,9 +4780,9 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "node_modules/inquirer": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", - "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", "dependencies": { "ansi-escapes": "^4.2.1", "chalk": "^4.1.1", @@ -4798,12 +4798,25 @@ "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6", - "wrap-ansi": "^7.0.0" + "wrap-ansi": "^6.0.1" }, "engines": { "node": ">=12.0.0" } }, + "node_modules/inquirer/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/internal-slot": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", @@ -13585,9 +13598,9 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "inquirer": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", - "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", "requires": { "ansi-escapes": "^4.2.1", "chalk": "^4.1.1", @@ -13603,7 +13616,19 @@ "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6", - "wrap-ansi": "^7.0.0" + "wrap-ansi": "^6.0.1" + }, + "dependencies": { + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } } }, "internal-slot": { diff --git a/package.json b/package.json index 18c2ea3dc..ff158ff70 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "console.table": "0.10.0", "deep-equal": "2.2.2", "fs-extra": "11.1.0", - "inquirer": "8.2.5", + "inquirer": "8.2.6", "json-to-table": "4.2.1", "mustache": "4.2.0", "p-limit": "3.1.0", From a201068ecd38d5cf06efc9954b4d6c2daf4b5b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 17 Aug 2023 17:41:44 +0200 Subject: [PATCH 27/70] #940: make sure ALL fields on the DE are compared with the attributeSet; add test for field rename --- docs/dist/documentation.md | 6 +++--- lib/metadataTypes/AttributeSet.js | 10 +++++----- lib/metadataTypes/DataExtension.js | 2 +- lib/metadataTypes/DataExtensionField.js | 6 ++++++ .../testExisting_dataExtension.dataExtension-meta.json | 1 + .../9999999/dataExtension/update-expected.json | 2 +- test/type.dataExtension.test.js | 3 +-- 7 files changed, 18 insertions(+), 12 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 223bfdeb4..af33ea2e1 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -1325,7 +1325,7 @@ AttributeSet MetadataType * [AttributeSet](#AttributeSet) ⇐ [MetadataType](#MetadataType) * [.retrieve(retrieveDir, [_], [__], [key])](#AttributeSet.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.retrieveForCache()](#AttributeSet.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> - * [.fixShared_retrieve(sharedDataExtensionMap, fixShared_fieldChange)](#AttributeSet.fixShared_retrieve) ⇒ Promise.<Array.<string>> + * [.fixShared_retrieve(sharedDataExtensionMap, fixShared_fields)](#AttributeSet.fixShared_retrieve) ⇒ Promise.<Array.<string>> * [.parseResponseBody(body, [singleRetrieve])](#AttributeSet.parseResponseBody) ⇒ TYPE.MetadataTypeMap * [.postRetrieveTasks(metadata)](#AttributeSet.postRetrieveTasks) ⇒ TYPE.MetadataTypeItem * [._getSystemValueDefinitions()](#AttributeSet._getSystemValueDefinitions) ⇒ Array.<object> @@ -1354,7 +1354,7 @@ Retrieves Metadata of schema set definitions for caching. **Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise -### AttributeSet.fixShared\_retrieve(sharedDataExtensionMap, fixShared_fieldChange) ⇒ Promise.<Array.<string>> +### AttributeSet.fixShared\_retrieve(sharedDataExtensionMap, fixShared_fields) ⇒ Promise.<Array.<string>> used to identify updated shared data extensions that are used in attributeSets. helper for DataExtension.#fixShared_onBU @@ -1364,7 +1364,7 @@ helper for DataExtension.#fixShared_onBU | Param | Type | Description | | --- | --- | --- | | sharedDataExtensionMap | Object.<string, string> | ID-Key relationship of shared data extensions | -| fixShared_fieldChange | object | DataExtensionField.fixShared_fieldChange | +| fixShared_fields | object | DataExtensionField.fixShared_fields | diff --git a/lib/metadataTypes/AttributeSet.js b/lib/metadataTypes/AttributeSet.js index 0c72a4dc1..ef8bd19d5 100644 --- a/lib/metadataTypes/AttributeSet.js +++ b/lib/metadataTypes/AttributeSet.js @@ -46,10 +46,10 @@ class AttributeSet extends MetadataType { * helper for DataExtension.#fixShared_onBU * * @param {Object.} sharedDataExtensionMap ID-Key relationship of shared data extensions - * @param {object} fixShared_fieldChange DataExtensionField.fixShared_fieldChange + * @param {object} fixShared_fields DataExtensionField.fixShared_fields * @returns {Promise.} Promise of list of shared dataExtension IDs */ - static async fixShared_retrieve(sharedDataExtensionMap, fixShared_fieldChange) { + static async fixShared_retrieve(sharedDataExtensionMap, fixShared_fields) { if (!Object.keys(sharedDataExtensionMap).length) { return []; } @@ -74,17 +74,17 @@ class AttributeSet extends MetadataType { } }) .filter((asKey) => { - // check if any of the updated dataExtension fields dont exist on the attributeSet or are out of date + // check if any of the dataExtension fields dont exist on the attributeSet or are out of date const deKey = sharedDataExtensionMap[metadataMap[asKey].storageReferenceID.value]; const asFields = metadataMap[asKey].valueDefinitions; - if (!fixShared_fieldChange[deKey]) { + if (!fixShared_fields[deKey]) { Util.logger.debug( ` - No changed fields found. Assuming re-deploy with --fixShared flag` ); return true; } - const deFields = Object.values(fixShared_fieldChange[deKey]); + const deFields = Object.values(fixShared_fields[deKey]); return deFields.some((deField) => { const search = asFields.filter((asf) => asf.name === deField.Name); if (!search.length) { diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index 9215ff409..39a7e5601 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -537,7 +537,7 @@ class DataExtension extends MetadataType { AttributeSet.client = clientChildBu; const sharedDeIdsUsedOnBU = await AttributeSet.fixShared_retrieve( sharedDataExtensionMap, - DataExtensionField.fixShared_fieldChange + DataExtensionField.fixShared_fields ); if (sharedDeIdsUsedOnBU.length) { let sharedDataExtensionsKeys = sharedDeIdsUsedOnBU.map( diff --git a/lib/metadataTypes/DataExtensionField.js b/lib/metadataTypes/DataExtensionField.js index 4d81e45f1..c2755e073 100644 --- a/lib/metadataTypes/DataExtensionField.js +++ b/lib/metadataTypes/DataExtensionField.js @@ -100,6 +100,8 @@ class DataExtensionField extends MetadataType { */ static async prepareDeployColumnsOnUpdate(deployColumns, deKey) { // create list of DE keys that had changes to their fields to be able to use it as a filter in the --fixShared logic + this.fixShared_fields ||= {}; + this.fixShared_fields[deKey] ||= {}; this.fixShared_fieldChange ||= {}; this.fixShared_fieldChange[deKey] ||= {}; @@ -188,6 +190,9 @@ class DataExtensionField extends MetadataType { changeFound = true; } } + this.fixShared_fields[deKey][item.Name] = JSON.parse(JSON.stringify(item)); + this.fixShared_fields[deKey][item.Name].FieldType = itemOld.FieldType; + if (!changeFound) { deployColumns.splice(i, 1); Util.logger.verbose(`no change - removed field [${deKey}].[${item.Name}]`); @@ -225,6 +230,7 @@ class DataExtensionField extends MetadataType { // Field doesn't exist in target, therefore Remove ObjectID if present delete item.ObjectID; + this.fixShared_fields[deKey][item.Name] = JSON.parse(JSON.stringify(item)); this.fixShared_fieldChange[deKey][item.Name] = JSON.parse(JSON.stringify(item)); } if (Util.isTrue(item.IsPrimaryKey) && Util.isFalse(item.IsRequired)) { diff --git a/test/mockRoot/deploy/testInstance/testBU/dataExtension/testExisting_dataExtension.dataExtension-meta.json b/test/mockRoot/deploy/testInstance/testBU/dataExtension/testExisting_dataExtension.dataExtension-meta.json index f5c8c19af..ccd840273 100644 --- a/test/mockRoot/deploy/testInstance/testBU/dataExtension/testExisting_dataExtension.dataExtension-meta.json +++ b/test/mockRoot/deploy/testInstance/testBU/dataExtension/testExisting_dataExtension.dataExtension-meta.json @@ -31,6 +31,7 @@ }, { "Name": "EmailAddress", + "Name_new": "Email", "DefaultValue": "", "MaxLength": 254, "IsRequired": true, diff --git a/test/resources/9999999/dataExtension/update-expected.json b/test/resources/9999999/dataExtension/update-expected.json index 4de9f5b49..bc10677a2 100644 --- a/test/resources/9999999/dataExtension/update-expected.json +++ b/test/resources/9999999/dataExtension/update-expected.json @@ -26,7 +26,7 @@ "FieldType": "Text" }, { - "Name": "EmailAddress", + "Name": "Email", "DefaultValue": "", "MaxLength": 254, "IsRequired": true, diff --git a/test/type.dataExtension.test.js b/test/type.dataExtension.test.js index 17f30a354..15dae6984 100644 --- a/test/type.dataExtension.test.js +++ b/test/type.dataExtension.test.js @@ -97,7 +97,7 @@ describe('type: dataExtension', () => { beforeEach(() => { testUtils.mockSetup(true); }); - it('Should create & update a dataExtension', async () => { + it('Should create & update a dataExtension including a field rename', async () => { // WHEN const deployResult = await handler.deploy('testInstance/testBU', ['dataExtension']); // THEN @@ -235,7 +235,6 @@ describe('type: dataExtension', () => { ); return; }); - it('Should rename fields'); }); describe('Templating ================', () => { it('Should create a dataExtension template via retrieveAsTemplate and build it', async () => { From fbb99635a84b0a4d386fd3a38eb0dc974cf09e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 17 Aug 2023 17:49:53 +0200 Subject: [PATCH 28/70] #940: simplified logic by no longer checking for CHANGED fields but simply all fields --- lib/metadataTypes/AttributeSet.js | 6 ------ lib/metadataTypes/DataExtension.js | 12 ------------ lib/metadataTypes/DataExtensionField.js | 15 ++------------- 3 files changed, 2 insertions(+), 31 deletions(-) diff --git a/lib/metadataTypes/AttributeSet.js b/lib/metadataTypes/AttributeSet.js index ef8bd19d5..575531ee7 100644 --- a/lib/metadataTypes/AttributeSet.js +++ b/lib/metadataTypes/AttributeSet.js @@ -78,12 +78,6 @@ class AttributeSet extends MetadataType { const deKey = sharedDataExtensionMap[metadataMap[asKey].storageReferenceID.value]; const asFields = metadataMap[asKey].valueDefinitions; - if (!fixShared_fields[deKey]) { - Util.logger.debug( - ` - No changed fields found. Assuming re-deploy with --fixShared flag` - ); - return true; - } const deFields = Object.values(fixShared_fields[deKey]); return deFields.some((deField) => { const search = asFields.filter((asf) => asf.name === deField.Name); diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index 39a7e5601..6e96f463b 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -391,18 +391,6 @@ class DataExtension extends MetadataType { const sharedDataExtensionsKeys = this.deployedSharedKeys; this.deployedSharedKeys = null; - if ( - !sharedDataExtensionsKeys.filter( - (deKey) => - DataExtensionField.fixShared_fieldChange[deKey] && - Object.keys(DataExtensionField.fixShared_fieldChange[deKey])?.length - ) - ) { - Util.logger.debug( - ` - No changed fields found. Assuming re-deploy with --fixShared flag` - ); - return true; - } if (Util.OPTIONS.fixShared) { // select which BUs to run this for diff --git a/lib/metadataTypes/DataExtensionField.js b/lib/metadataTypes/DataExtensionField.js index c2755e073..d5da433e8 100644 --- a/lib/metadataTypes/DataExtensionField.js +++ b/lib/metadataTypes/DataExtensionField.js @@ -102,8 +102,6 @@ class DataExtensionField extends MetadataType { // create list of DE keys that had changes to their fields to be able to use it as a filter in the --fixShared logic this.fixShared_fields ||= {}; this.fixShared_fields[deKey] ||= {}; - this.fixShared_fieldChange ||= {}; - this.fixShared_fieldChange[deKey] ||= {}; // get row count to know which field restrictions apply let hasData = false; @@ -190,6 +188,7 @@ class DataExtensionField extends MetadataType { changeFound = true; } } + // share fields with fixShared logic this.fixShared_fields[deKey][item.Name] = JSON.parse(JSON.stringify(item)); this.fixShared_fields[deKey][item.Name].FieldType = itemOld.FieldType; @@ -198,9 +197,6 @@ class DataExtensionField extends MetadataType { Util.logger.verbose(`no change - removed field [${deKey}].[${item.Name}]`); continue; } - // track name of changed field - this.fixShared_fieldChange[deKey][item.Name] = JSON.parse(JSON.stringify(item)); - this.fixShared_fieldChange[deKey][item.Name].FieldType = itemOld.FieldType; // set the ObjectId for clear identification during update item.ObjectID = itemOld.ObjectID; @@ -231,7 +227,6 @@ class DataExtensionField extends MetadataType { delete item.ObjectID; this.fixShared_fields[deKey][item.Name] = JSON.parse(JSON.stringify(item)); - this.fixShared_fieldChange[deKey][item.Name] = JSON.parse(JSON.stringify(item)); } if (Util.isTrue(item.IsPrimaryKey) && Util.isFalse(item.IsRequired)) { // applicable: with or without data @@ -247,14 +242,12 @@ class DataExtensionField extends MetadataType { `- Invalid value for 'IsRequired' of [${deKey}].[${item.Name}]. Found '${item.IsRequired}' instead of 'true'/'false'. Removing field from deploy!` ); deployColumns.splice(i, 1); - delete this.fixShared_fieldChange[deKey][item.Name]; } if (!Util.isTrue(item.IsPrimaryKey) && !Util.isFalse(item.IsPrimaryKey)) { Util.logger.error( `- Invalid value for 'IsPrimaryKey' of [${deKey}].[${item.Name}]. Found '${item.IsPrimaryKey}' instead of 'true'/'false'. Removing field from deploy!` ); deployColumns.splice(i, 1); - delete this.fixShared_fieldChange[deKey][item.Name]; } } @@ -265,11 +258,7 @@ class DataExtensionField extends MetadataType { }` + deployColumns.map((item) => item.Name).join(', ') ) ); - // create list of DE keys that had changes to their fields to be able to use it as a filter in the --fixShared logic - if (!deployColumns.length || !Object.keys(this.fixShared_fieldChange[deKey]).length) { - // no changed fields found. remove entry from list for easier processing - delete this.fixShared_fieldChange[deKey]; - } + return existingFieldByName; } From f6aee7bcc130f23bd0ff322e5f8f6716a670f3c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 18 Aug 2023 15:52:43 +0200 Subject: [PATCH 29/70] #325: add support to retrieve, cache & upsert dataVerification activities --- docs/dist/documentation.md | 155 ++++++++++++++- lib/Deployer.js | 8 +- lib/MetadataTypeDefinitions.js | 1 + lib/MetadataTypeInfo.js | 1 + lib/metadataTypes/Automation.js | 30 ++- lib/metadataTypes/DataVerification.js | 178 ++++++++++++++++++ lib/metadataTypes/MetadataType.js | 13 +- .../definitions/Automation.definition.js | 4 +- .../DataVerification.definition.js | 82 ++++++++ types/mcdev.d.js | 14 ++ 10 files changed, 471 insertions(+), 15 deletions(-) create mode 100644 lib/metadataTypes/DataVerification.js create mode 100644 lib/metadataTypes/definitions/DataVerification.definition.js diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index af33ea2e1..00eaf1571 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -46,6 +46,9 @@ Source and target business units are also compared before the deployment to appl Only for Caching No retrieve/upsert is required as this is a configuration in the EID

+
DataVerificationMetadataType
+

DataVerification MetadataType

+
DiscoveryMetadataType

ImportFile MetadataType

@@ -293,6 +296,8 @@ helper for DataExtension.#fixShared_item
AutomationItem : object
+
DataVerificationItem : object
+
SDK : Object.<string, AutomationItem>
skipInteraction : object
@@ -2353,6 +2358,126 @@ Retrieves Metadata of Data Extract Type for caching. **Kind**: static method of [DataExtractType](#DataExtractType) **Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise of metadata + + +## DataVerification ⇐ [MetadataType](#MetadataType) +DataVerification MetadataType + +**Kind**: global class +**Extends**: [MetadataType](#MetadataType) + +* [DataVerification](#DataVerification) ⇐ [MetadataType](#MetadataType) + * [.retrieve(retrieveDir, [_], [__], key)](#DataVerification.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.retrieveForCache([_], [__], [keyArr])](#DataVerification.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.create(metadata)](#DataVerification.create) ⇒ Promise + * [.postCreateTasks(metadataEntry, apiResponse, metadataEntryWithAllFields)](#DataVerification.postCreateTasks) ⇒ void + * [.update(metadata)](#DataVerification.update) ⇒ Promise + * [.preDeployTasks(metadata)](#DataVerification.preDeployTasks) ⇒ TYPE.DataVerificationItem + * [.postRetrieveTasks(metadata)](#DataVerification.postRetrieveTasks) ⇒ TYPE.DataVerificationItem + * [.deleteByKey(key)](#DataVerification.deleteByKey) ⇒ Promise.<boolean> + + + +### DataVerification.retrieve(retrieveDir, [_], [__], key) ⇒ Promise.<TYPE.MetadataTypeMapObj> +Retrieves Metadata of Data Verification Activity. + +**Kind**: static method of [DataVerification](#DataVerification) +**Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise of metadata + +| Param | Type | Description | +| --- | --- | --- | +| retrieveDir | string | Directory where retrieved metadata directory will be saved | +| [_] | void | unused parameter | +| [__] | void | unused parameter | +| key | string | customer key of single item to retrieve | + + + +### DataVerification.retrieveForCache([_], [__], [keyArr]) ⇒ Promise.<TYPE.MetadataTypeMapObj> +Retrieves Metadata of Data Extract Activity for caching + +**Kind**: static method of [DataVerification](#DataVerification) +**Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise of metadata + +| Param | Type | Description | +| --- | --- | --- | +| [_] | void | not used | +| [__] | void | not used | +| [keyArr] | Array.<string> | customer key of single item to retrieve | + + + +### DataVerification.create(metadata) ⇒ Promise +Creates a single Data Extract + +**Kind**: static method of [DataVerification](#DataVerification) +**Returns**: Promise - Promise + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.DataVerificationItem | a single Data Extract | + + + +### DataVerification.postCreateTasks(metadataEntry, apiResponse, metadataEntryWithAllFields) ⇒ void +helper for [createREST](#MetadataType.createREST) + +**Kind**: static method of [DataVerification](#DataVerification) + +| Param | Type | Description | +| --- | --- | --- | +| metadataEntry | TYPE.MetadataTypeItem | a single metadata Entry | +| apiResponse | object | varies depending on the API call | +| metadataEntryWithAllFields | TYPE.MetadataTypeItem | like metadataEntry but before non-creatable fields were stripped | + + + +### DataVerification.update(metadata) ⇒ Promise +Updates a single Data Extract + +**Kind**: static method of [DataVerification](#DataVerification) +**Returns**: Promise - Promise + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.DataVerificationItem | a single Data Extract | + + + +### DataVerification.preDeployTasks(metadata) ⇒ TYPE.DataVerificationItem +prepares a dataVerification for deployment + +**Kind**: static method of [DataVerification](#DataVerification) +**Returns**: TYPE.DataVerificationItem - metadata object + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.DataVerificationItem | a single dataVerification activity definition | + + + +### DataVerification.postRetrieveTasks(metadata) ⇒ TYPE.DataVerificationItem +parses retrieved Metadata before saving + +**Kind**: static method of [DataVerification](#DataVerification) +**Returns**: TYPE.DataVerificationItem - Array with one metadata object and one sql string + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.DataVerificationItem | a single dataVerification activity definition | + + + +### DataVerification.deleteByKey(key) ⇒ Promise.<boolean> +Delete a metadata item from the specified business unit + +**Kind**: static method of [DataVerification](#DataVerification) +**Returns**: Promise.<boolean> - deletion success status + +| Param | Type | Description | +| --- | --- | --- | +| key | string | Identifier of item | + ## Discovery ⇐ [MetadataType](#MetadataType) @@ -3382,7 +3507,7 @@ Provides default functionality that can be overwritten by child metadata type cl * [.getFieldNamesToRetrieve([additionalFields], [isCaching])](#MetadataType.getFieldNamesToRetrieve) ⇒ Array.<string> * [.deploy(metadata, deployDir, retrieveDir)](#MetadataType.deploy) ⇒ Promise.<TYPE.MetadataTypeMap> * [.postDeployTasks(upsertResults, originalMetadata, createdUpdated)](#MetadataType.postDeployTasks) ⇒ void - * [.postCreateTasks(metadataEntry, apiResponse)](#MetadataType.postCreateTasks) ⇒ void + * [.postCreateTasks(metadataEntry, apiResponse, metadataEntryWithAllFields)](#MetadataType.postCreateTasks) ⇒ void * [.postUpdateTasks(metadataEntry, apiResponse)](#MetadataType.postUpdateTasks) ⇒ void * [.postDeployTasks_legacyApi(metadataEntry, apiResponse)](#MetadataType.postDeployTasks_legacyApi) ⇒ Promise.<void> * [.postRetrieveTasks(metadata, targetDir, [isTemplating])](#MetadataType.postRetrieveTasks) ⇒ TYPE.MetadataTypeItem @@ -3390,7 +3515,7 @@ Provides default functionality that can be overwritten by child metadata type cl * [.setFolderId(metadata)](#MetadataType.setFolderId) * [.retrieve(retrieveDir, [additionalFields], [subTypeArr], [key])](#MetadataType.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.retrieveChangelog([additionalFields], [subTypeArr])](#MetadataType.retrieveChangelog) ⇒ Promise.<TYPE.MetadataTypeMapObj> - * [.retrieveForCache([additionalFields], [subTypeArr], [key])](#MetadataType.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.retrieveForCache([additionalFields], [subTypeArr], [keyArr])](#MetadataType.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.retrieveAsTemplate(templateDir, name, templateVariables, [subType])](#MetadataType.retrieveAsTemplate) ⇒ Promise.<TYPE.MetadataTypeItemObj> * [.retrieveTemplateREST(templateDir, uri, templateVariables, name)](#MetadataType.retrieveTemplateREST) ⇒ Promise.<{metadata: TYPE.MetadataTypeItem, type: string}> * [.buildTemplate(retrieveDir, templateDir, key, templateVariables)](#MetadataType.buildTemplate) ⇒ Promise.<TYPE.MetadataTypeItemObj> @@ -3512,7 +3637,7 @@ Gets executed after deployment of metadata type -### MetadataType.postCreateTasks(metadataEntry, apiResponse) ⇒ void +### MetadataType.postCreateTasks(metadataEntry, apiResponse, metadataEntryWithAllFields) ⇒ void helper for [createREST](#MetadataType.createREST) **Kind**: static method of [MetadataType](#MetadataType) @@ -3521,6 +3646,7 @@ helper for [createREST](#MetadataType.createREST) | --- | --- | --- | | metadataEntry | TYPE.MetadataTypeItem | a single metadata Entry | | apiResponse | object | varies depending on the API call | +| metadataEntryWithAllFields | TYPE.MetadataTypeItem | like metadataEntry but before non-creatable fields were stripped | @@ -3613,7 +3739,7 @@ Gets metadata from Marketing Cloud -### MetadataType.retrieveForCache([additionalFields], [subTypeArr], [key]) ⇒ Promise.<TYPE.MetadataTypeMapObj> +### MetadataType.retrieveForCache([additionalFields], [subTypeArr], [keyArr]) ⇒ Promise.<TYPE.MetadataTypeMapObj> Gets metadata cache with limited fields and does not store value to disk **Kind**: static method of [MetadataType](#MetadataType) @@ -3623,7 +3749,7 @@ Gets metadata cache with limited fields and does not store value to disk | --- | --- | --- | | [additionalFields] | Array.<string> | Returns specified fields even if their retrieve definition is not set to true | | [subTypeArr] | Array.<string> | optionally limit to a single subtype | -| [key] | string | customer key of single item to retrieve | +| [keyArr] | Array.<string> | customer key of single item to retrieve | @@ -9035,6 +9161,25 @@ SOAP format | r__folder_Path | string | folder path | | [categoryId] | string | holds folder ID, replaced with r__folder_Path during retrieve | + + +## DataVerificationItem : object +**Kind**: global typedef +**Properties** + +| Name | Type | Description | +| --- | --- | --- | +| dataVerificationDefinitionId | string | ID / Key | +| verificationType | 'IsEqualTo' \| 'IsLessThan' \| 'IsGreaterThan' \| 'IsOutsideRange' \| 'IsInsideRange' \| 'IsNotEqualTo' \| 'IsNotLessThan' \| 'IsNotGreaterThan' \| 'IsNotOutsideRange' \| 'IsNotInsideRange' | key | +| value1 | number | used for all verificationTypes; lower value for IsOutsideRange, IsInsideRange, IsNotOutsideRange, IsNotInsideRange | +| value2 | number | only used for IsOutsideRange, IsInsideRange, IsNotOutsideRange, IsNotInsideRange; otherwise set to 0 | +| shouldStopOnFailure | boolean | flag to stop automation if verification fails | +| shouldEmailOnFailure | boolean | flag to send email if verification fails | +| notificationEmailAddress | string | email address to send notification to; empty string if shouldEmailOnFailure=false | +| notificationEmailMessage | string | email message to send; empty string if shouldEmailOnFailure=false | +| createdBy | number | user id of creator | +| r__dataExtension_CustomerKey | string | key of target data extension | + ## SDK : Object.<string, AutomationItem> diff --git a/lib/Deployer.js b/lib/Deployer.js index 5b4d283ff..51ed12328 100644 --- a/lib/Deployer.js +++ b/lib/Deployer.js @@ -279,7 +279,13 @@ class Deployer { MetadataTypeInfo[type].buObject = this.buObject; Util.logger.info(`Caching dependent Metadata: ${metadataType}`); Util.logSubtypes(subTypeArr); - const result = await MetadataTypeInfo[type].retrieveForCache(null, subTypeArr); + const result = await MetadataTypeInfo[type].retrieveForCache( + null, + subTypeArr, + MetadataTypeInfo[type].definition.retrieveRequiresKey + ? Object.keys(this.metadata[type]) + : undefined + ); cache.setMetadata(type, result.metadata); } /** @type {TYPE.MultiMetadataTypeMap} */ diff --git a/lib/MetadataTypeDefinitions.js b/lib/MetadataTypeDefinitions.js index 477774ef5..014644d34 100644 --- a/lib/MetadataTypeDefinitions.js +++ b/lib/MetadataTypeDefinitions.js @@ -15,6 +15,7 @@ const MetadataTypeDefinitions = { dataExtensionTemplate: require('./metadataTypes/definitions/DataExtensionTemplate.definition'), dataExtract: require('./metadataTypes/definitions/DataExtract.definition'), dataExtractType: require('./metadataTypes/definitions/DataExtractType.definition'), + dataVerification: require('./metadataTypes/definitions/DataVerification.definition'), discovery: require('./metadataTypes/definitions/Discovery.definition'), email: require('./metadataTypes/definitions/Email.definition'), emailSend: require('./metadataTypes/definitions/EmailSend.definition'), diff --git a/lib/MetadataTypeInfo.js b/lib/MetadataTypeInfo.js index 0622a8f84..3c991c6b0 100644 --- a/lib/MetadataTypeInfo.js +++ b/lib/MetadataTypeInfo.js @@ -15,6 +15,7 @@ const MetadataTypeInfo = { dataExtensionTemplate: require('./metadataTypes/DataExtensionTemplate'), dataExtract: require('./metadataTypes/DataExtract'), dataExtractType: require('./metadataTypes/DataExtractType'), + dataVerification: require('./metadataTypes/DataVerification'), discovery: require('./metadataTypes/Discovery'), email: require('./metadataTypes/Email'), emailSend: require('./metadataTypes/EmailSend'), diff --git a/lib/metadataTypes/Automation.js b/lib/metadataTypes/Automation.js index d68105298..ba61d319b 100644 --- a/lib/metadataTypes/Automation.js +++ b/lib/metadataTypes/Automation.js @@ -5,6 +5,7 @@ const TYPE = require('../../types/mcdev.d'); const Util = require('../util/util'); const File = require('../util/file'); const Definitions = require('../MetadataTypeDefinitions'); +const DataVerification = require('./DataVerification'); const cache = require('../util/cache'); const pLimit = require('p-limit'); @@ -90,6 +91,7 @@ class Automation extends MetadataType { // * retrieveDir can be empty when we use it in the context of postDeployTasks if (retrieveDir) { + this.retrieveDir = retrieveDir; metadataMap = await this.saveResults(metadataMap, retrieveDir, null, null); Util.logger.info( `Downloaded: ${this.definition.type} (${Object.keys(metadataMap).length})` + @@ -363,7 +365,7 @@ class Automation extends MetadataType { * @param {TYPE.AutomationItem} metadata a single automation * @returns {TYPE.AutomationItem | void} parsed item */ - static postRetrieveTasks(metadata) { + static async postRetrieveTasks(metadata) { // folder this.setFolderPath(metadata); // automations are often skipped due to lack of support. @@ -423,7 +425,13 @@ class Automation extends MetadataType { } due to missing activityObjectId: ${JSON.stringify(activity)}` ); // empty if block - } else if (!this.definition.dependencies.includes(activity.r__type)) { + continue; + } else if ( + !this.definition.dependencies.includes(activity.r__type) && + !this.definition.manuallyRetrievedDependencies.includes( + activity.r__type + ) + ) { Util.logger.debug( ` - skipping ${ metadata[this.definition.keyField] @@ -433,9 +441,25 @@ class Automation extends MetadataType { activity.r__type } is not set up as a dependency for ${this.definition.type}` ); + continue; + } + if (activity.r__type === 'dataVerification') { + // data verifications can only be retrieved 1-by-1. The get-API does not work without IDs it seems. + DataVerification.client = this.client; + DataVerification.buObject = this.buObject; + DataVerification.properties = this.properties; + const dvResult = await DataVerification.retrieve( + this.retrieveDir, + undefined, + undefined, + activity.activityObjectId + ); + if (dvResult?.metadata) { + cache.mergeMetadata('dataVerification', dvResult.metadata); + } } // / if managed by cache we can update references to support deployment - else if ( + if ( Definitions[activity.r__type]?.['idField'] && cache.getCache(this.buObject.mid)[activity.r__type] ) { diff --git a/lib/metadataTypes/DataVerification.js b/lib/metadataTypes/DataVerification.js new file mode 100644 index 000000000..aac8a0a13 --- /dev/null +++ b/lib/metadataTypes/DataVerification.js @@ -0,0 +1,178 @@ +'use strict'; + +const TYPE = require('../../types/mcdev.d'); +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); +const cache = require('../util/cache'); + +/** + * DataVerification MetadataType + * + * @augments MetadataType + */ +class DataVerification extends MetadataType { + /** + * Retrieves Metadata of Data Verification Activity. + * + * @param {string} retrieveDir Directory where retrieved metadata directory will be saved + * @param {void} [_] unused parameter + * @param {void} [__] unused parameter + * @param {string} key customer key of single item to retrieve + * @returns {Promise.} Promise of metadata + */ + static async retrieve(retrieveDir, _, __, key) { + let param = ''; + if (key?.startsWith('id:')) { + param = key.slice(3); + } else if (key) { + param = key; + } + if (param === '') { + throw new Error('DataVerification can only be retrieved if the ID is known'); + } + try { + return await super.retrieveREST( + null, + '/automation/v1/dataverifications/' + param, + null, + key + ); + } catch (ex) { + if ( + ex.message === 'Not Found' || + ex.message === 'Request failed with status code 400' + ) { + if (retrieveDir) { + Util.logger.info( + `Downloaded: ${this.definition.type} (0)${Util.getKeysString(param)}` + ); + } + // if the ID is too short, the system will throw the 400 error + return { metadata: {} }; + } else { + throw ex; + } + } + } + /** + * Retrieves Metadata of Data Extract Activity for caching + * + * @param {void} [_] not used + * @param {void} [__] not used + * @param {string[]} [keyArr] customer key of single item to retrieve + * @returns {Promise.} Promise of metadata + */ + static async retrieveForCache(_, __, keyArr) { + const resultArr = await Promise.all( + keyArr.map(async (key) => this.retrieve(null, null, null, key)) + ); + const base = resultArr[0]; + if (resultArr.length > 1) { + base.metadata = resultArr.reduce((acc, cur) => { + if (cur?.metadata) { + acc.metadata = Object.assign(acc.metadata, cur.metadata); + } + return acc; + }).metadata; + } + return base; + } + + /** + * Creates a single Data Extract + * + * @param {TYPE.DataVerificationItem} metadata a single Data Extract + * @returns {Promise} Promise + */ + static create(metadata) { + return super.createREST(metadata, '/automation/v1/dataverifications/'); + } + + /** + * helper for {@link MetadataType.createREST} + * + * @param {TYPE.MetadataTypeItem} metadataEntry a single metadata Entry + * @param {object} apiResponse varies depending on the API call + * @param {TYPE.MetadataTypeItem} metadataEntryWithAllFields like metadataEntry but before non-creatable fields were stripped + * @returns {void} + */ + static async postCreateTasks(metadataEntry, apiResponse, metadataEntryWithAllFields) { + if (!apiResponse?.[this.definition.idField]) { + return; + } + Util.logger.warn( + ` - ${this.definition.type} ${ + metadataEntryWithAllFields?.[this.definition.idField] + }: new key ${ + apiResponse?.[this.definition.idField] + } automatically assigned during creation` + ); + metadataEntry[this.definition.idField] = apiResponse?.[this.definition.idField]; + } + + /** + * Updates a single Data Extract + * + * @param {TYPE.DataVerificationItem} metadata a single Data Extract + * @returns {Promise} Promise + */ + static update(metadata) { + return super.updateREST( + metadata, + '/automation/v1/dataverifications/' + metadata.dataVerificationDefinitionId + ); + } + + /** + * prepares a dataVerification for deployment + * + * @param {TYPE.DataVerificationItem} metadata a single dataVerification activity definition + * @returns {TYPE.DataVerificationItem} metadata object + */ + static preDeployTasks(metadata) { + metadata.targetObjectId = cache.searchForField( + 'dataExtension', + metadata.r__dataExtension_CustomerKey, + 'CustomerKey', + 'ObjectID' + ); + delete metadata.r__dataExtension_CustomerKey; + return metadata; + } + /** + * parses retrieved Metadata before saving + * + * @param {TYPE.DataVerificationItem} metadata a single dataVerification activity definition + * @returns {TYPE.DataVerificationItem} Array with one metadata object and one sql string + */ + static postRetrieveTasks(metadata) { + try { + metadata.r__dataExtension_CustomerKey = cache.searchForField( + 'dataExtension', + metadata.targetObjectId, + 'ObjectID', + 'CustomerKey' + ); + delete metadata.targetObjectId; + } catch (ex) { + Util.logger.warn( + ` - ${this.definition.type} ${metadata[this.definition.keyField]}: ${ex.message}` + ); + } + return metadata; + } + /** + * Delete a metadata item from the specified business unit + * + * @param {string} key Identifier of item + * @returns {Promise.} deletion success status + */ + static deleteByKey(key) { + return super.deleteByKeyREST('/automation/v1/dataverifications/' + key, key); + } +} + +// Assign definition to static attributes +DataVerification.definition = require('../MetadataTypeDefinitions').dataVerification; + +module.exports = DataVerification; diff --git a/lib/metadataTypes/MetadataType.js b/lib/metadataTypes/MetadataType.js index 36f5f52bc..0d98601a8 100644 --- a/lib/metadataTypes/MetadataType.js +++ b/lib/metadataTypes/MetadataType.js @@ -139,9 +139,10 @@ class MetadataType { * * @param {TYPE.MetadataTypeItem} metadataEntry a single metadata Entry * @param {object} apiResponse varies depending on the API call + * @param {TYPE.MetadataTypeItem} metadataEntryWithAllFields like metadataEntry but before non-creatable fields were stripped * @returns {void} */ - static postCreateTasks(metadataEntry, apiResponse) {} + static postCreateTasks(metadataEntry, apiResponse, metadataEntryWithAllFields) {} /** * helper for {@link MetadataType.updateREST} @@ -265,11 +266,11 @@ class MetadataType { * * @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true * @param {string[]} [subTypeArr] optionally limit to a single subtype - * @param {string} [key] customer key of single item to retrieve + * @param {string[]} [keyArr] customer key of single item to retrieve * @returns {Promise.} metadata */ - static async retrieveForCache(additionalFields, subTypeArr, key) { - return this.retrieve(null, additionalFields, subTypeArr, key); + static async retrieveForCache(additionalFields, subTypeArr, keyArr) { + return this.retrieve(null, additionalFields, subTypeArr, keyArr); } /** * Gets metadata cache with limited fields and does not store value to disk @@ -808,11 +809,12 @@ class MetadataType { * @returns {Promise. | null} Promise of API response or null in case of an error */ static async createREST(metadataEntry, uri) { + const metadataClone = JSON.parse(JSON.stringify(metadataEntry)); this.removeNotCreateableFields(metadataEntry); try { // set to empty object in case API returned nothing to be able to update it in helper classes const response = (await this.client.rest.post(uri, metadataEntry)) || {}; - await this.postCreateTasks(metadataEntry, response); + await this.postCreateTasks(metadataEntry, response, metadataClone); Util.logger.info( ` - created ${this.definition.type}: ${ metadataEntry[this.definition.keyField] || @@ -1060,6 +1062,7 @@ class MetadataType { const results = this.parseResponseBody(response, singleRetrieve); // get extended metadata if applicable if (this.definition.hasExtended) { + Util.logger.debug(' - retrieving extended metadata'); const extended = await this.client.rest.getCollection( Object.keys(results).map((key) => uri + results[key][this.definition.idField]) ); diff --git a/lib/metadataTypes/definitions/Automation.definition.js b/lib/metadataTypes/definitions/Automation.definition.js index e34b258ca..f78db4e95 100644 --- a/lib/metadataTypes/definitions/Automation.definition.js +++ b/lib/metadataTypes/definitions/Automation.definition.js @@ -11,7 +11,7 @@ module.exports = { journeyEntryOld: 733, query: 300, script: 423, - verification: 1000, + dataVerification: 1000, wait: 467, push: 736, sms: 725, @@ -23,7 +23,9 @@ module.exports = { importMobileContact: 726, }, bodyIteratorField: 'items', + manuallyRetrievedDependencies: ['dataVerification'], dependencies: [ + 'dataExtension', // for dataVerification 'dataExtract', 'emailSend', 'fileTransfer', diff --git a/lib/metadataTypes/definitions/DataVerification.definition.js b/lib/metadataTypes/definitions/DataVerification.definition.js new file mode 100644 index 000000000..ecd4b8969 --- /dev/null +++ b/lib/metadataTypes/definitions/DataVerification.definition.js @@ -0,0 +1,82 @@ +module.exports = { + bodyIteratorField: 'items', + dependencies: ['dataExtension'], + hasExtended: false, + idField: 'dataVerificationDefinitionId', + keyIsFixed: true, + keyField: 'dataVerificationDefinitionId', + createdDateField: null, + createdNameField: 'createdBy', + lastmodDateField: null, + lastmodNameField: null, + nameField: 'dataVerificationDefinitionId', + restPagination: false, + retrieveRequiresKey: true, + maxKeyLength: 36, // confirmed max length + type: 'dataVerification', + typeDescription: 'Check DataExtension for a row count', + typeRetrieveByDefault: true, + typeName: 'Automation: Data Verification Activity', + fields: { + createdBy: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + dataVerificationDefinitionId: { + isCreateable: false, // auto-assigned during creation by SFMC + isUpdateable: true, + retrieving: true, + template: true, + }, + notificationEmailAddress: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + notificationEmailMessage: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + shouldEmailOnFailure: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + shouldStopOnFailure: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + targetObjectId: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + value1: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + value2: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + verificationType: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + }, +}; diff --git a/types/mcdev.d.js b/types/mcdev.d.js index b1dced361..fce52d0c3 100644 --- a/types/mcdev.d.js +++ b/types/mcdev.d.js @@ -251,6 +251,20 @@ const SDK = require('sfmc-sdk'); * @property {string} r__folder_Path folder path * @property {string} [categoryId] holds folder ID, replaced with r__folder_Path during retrieve */ +/** + * @typedef {object} DataVerificationItem + * @property {string} dataVerificationDefinitionId ID / Key + * @property {'IsEqualTo'|'IsLessThan'|'IsGreaterThan'|'IsOutsideRange'|'IsInsideRange'|'IsNotEqualTo'|'IsNotLessThan'|'IsNotGreaterThan'|'IsNotOutsideRange'|'IsNotInsideRange'} verificationType key + * @property {number} value1 used for all verificationTypes; lower value for IsOutsideRange, IsInsideRange, IsNotOutsideRange, IsNotInsideRange + * @property {number} value2 only used for IsOutsideRange, IsInsideRange, IsNotOutsideRange, IsNotInsideRange; otherwise set to 0 + * @property {boolean} shouldStopOnFailure flag to stop automation if verification fails + * @property {boolean} shouldEmailOnFailure flag to send email if verification fails + * @property {string} notificationEmailAddress email address to send notification to; empty string if shouldEmailOnFailure=false + * @property {string} notificationEmailMessage email message to send; empty string if shouldEmailOnFailure=false + * @property {number} createdBy user id of creator + * @property {string} r__dataExtension_CustomerKey key of target data extension + */ + /** * @typedef {Object.} AutomationMap * @typedef {{metadata:AutomationMap,type:string}} AutomationMapObj From 141fa350f42bf825b4437fbc2e3742356e9dcf2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Sat, 19 Aug 2023 16:03:31 +0200 Subject: [PATCH 30/70] #0: fix incorrect delete logic for mobileKeyword --- lib/metadataTypes/MobileKeyword.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/metadataTypes/MobileKeyword.js b/lib/metadataTypes/MobileKeyword.js index 12b796bc0..61bc9c409 100644 --- a/lib/metadataTypes/MobileKeyword.js +++ b/lib/metadataTypes/MobileKeyword.js @@ -501,7 +501,7 @@ class MobileKeyword extends MetadataType { */ static async deleteByKey(key) { // get id from cache - const { metadata } = await this.retrieveForCache(key); + const { metadata } = await this.retrieveForCache(undefined, undefined, key); if (!metadata[key]) { Util.logger.error(`Could not find ${this.definition.type} with key ${key}.`); return false; From 2cbe93fcb4f8a72bc1f2e234a41c5706a7cf972a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Sat, 19 Aug 2023 16:06:11 +0200 Subject: [PATCH 31/70] #0: fix incorrect delete-tests (array of key passed instead of single key) --- test/type.automation.test.js | 8 +++++--- test/type.dataExtract.test.js | 8 +++++--- test/type.fileTransfer.test.js | 8 +++++--- test/type.importFile.test.js | 8 +++++--- test/type.mobileKeyword.test.js | 8 +++++--- test/type.mobileMessage.test.js | 8 +++++--- test/type.query.test.js | 12 +++++++----- test/type.triggeredSend.test.js | 8 +++++--- 8 files changed, 42 insertions(+), 26 deletions(-) diff --git a/test/type.automation.test.js b/test/type.automation.test.js index b4643f3fe..3caf3b474 100644 --- a/test/type.automation.test.js +++ b/test/type.automation.test.js @@ -653,9 +653,11 @@ describe('type: automation', () => { describe('Delete ================', () => { it('Should delete the item', async () => { // WHEN - const result = await handler.deleteByKey('testInstance/testBU', 'automation', [ - 'testExisting_automation', - ]); + const result = await handler.deleteByKey( + 'testInstance/testBU', + 'automation', + 'testExisting_automation' + ); // THEN assert.equal(process.exitCode, false, 'delete should not have thrown an error'); diff --git a/test/type.dataExtract.test.js b/test/type.dataExtract.test.js index 6c2b3434e..0f55f5c8e 100644 --- a/test/type.dataExtract.test.js +++ b/test/type.dataExtract.test.js @@ -172,9 +172,11 @@ describe('type: dataExtract', () => { describe('Delete ================', () => { it('Should NOT delete the item', async () => { // WHEN - await handler.deleteByKey('testInstance/testBU', 'dataExtract', [ - 'testExisting_fileTranfer', - ]); + await handler.deleteByKey( + 'testInstance/testBU', + 'dataExtract', + 'testExisting_fileTranfer' + ); // THEN assert.equal( process.exitCode, diff --git a/test/type.fileTransfer.test.js b/test/type.fileTransfer.test.js index 2d33c05b7..9424c893e 100644 --- a/test/type.fileTransfer.test.js +++ b/test/type.fileTransfer.test.js @@ -170,9 +170,11 @@ describe('type: fileTransfer', () => { describe('Delete ================', () => { it('Should NOT delete the item', async () => { // WHEN - await handler.deleteByKey('testInstance/testBU', 'fileTransfer', [ - 'testExisting_fileTranfer', - ]); + await handler.deleteByKey( + 'testInstance/testBU', + 'fileTransfer', + 'testExisting_fileTranfer' + ); // THEN assert.equal( process.exitCode, diff --git a/test/type.importFile.test.js b/test/type.importFile.test.js index 76e9c3642..7b4bc1c3b 100644 --- a/test/type.importFile.test.js +++ b/test/type.importFile.test.js @@ -171,9 +171,11 @@ describe('type: importFile', () => { describe('Delete ================', () => { it('Should NOT delete the item', async () => { // WHEN - await handler.deleteByKey('testInstance/testBU', 'importFile', [ - 'testExisting_fileTranfer', - ]); + await handler.deleteByKey( + 'testInstance/testBU', + 'importFile', + 'testExisting_fileTranfer' + ); // THEN assert.equal( process.exitCode, diff --git a/test/type.mobileKeyword.test.js b/test/type.mobileKeyword.test.js index 9f15adaf0..83d49810d 100644 --- a/test/type.mobileKeyword.test.js +++ b/test/type.mobileKeyword.test.js @@ -245,9 +245,11 @@ describe('type: mobileKeyword', () => { describe('Delete ================', () => { it('Should delete the item', async () => { // WHEN - const result = await handler.deleteByKey('testInstance/testBU', 'mobileKeyword', [ - '4912312345678.TESTEXISTING_KEYWORD', - ]); + const result = await handler.deleteByKey( + 'testInstance/testBU', + 'mobileKeyword', + '4912312345678.TESTEXISTING_KEYWORD' + ); // THEN assert.equal(process.exitCode, false, 'delete should not have thrown an error'); diff --git a/test/type.mobileMessage.test.js b/test/type.mobileMessage.test.js index a77416471..4562434ee 100644 --- a/test/type.mobileMessage.test.js +++ b/test/type.mobileMessage.test.js @@ -163,9 +163,11 @@ describe('type: mobileMessage', () => { describe('Delete ================', () => { it('Should delete the item', async () => { // WHEN - const result = await handler.deleteByKey('testInstance/testBU', 'mobileMessage', [ - 'NTIzOjc4OjA', - ]); + const result = await handler.deleteByKey( + 'testInstance/testBU', + 'mobileMessage', + 'NTIzOjc4OjA' + ); // THEN assert.equal(process.exitCode, false, 'delete should not have thrown an error'); diff --git a/test/type.query.test.js b/test/type.query.test.js index c63420be7..01c3dd7c2 100644 --- a/test/type.query.test.js +++ b/test/type.query.test.js @@ -458,7 +458,7 @@ describe('type: query', () => { // check number of API calls assert.equal( testUtils.getAPIHistoryLength(), - 33, + 36, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; @@ -498,7 +498,7 @@ describe('type: query', () => { // check number of API calls assert.equal( testUtils.getAPIHistoryLength(), - 35, + 38, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; @@ -656,9 +656,11 @@ describe('type: query', () => { describe('Delete ================', () => { it('Should delete the item', async () => { // WHEN - const result = await handler.deleteByKey('testInstance/testBU', 'query', [ - 'testExisting_query', - ]); + const result = await handler.deleteByKey( + 'testInstance/testBU', + 'query', + 'testExisting_query' + ); // THEN assert.equal(process.exitCode, false, 'delete should not have thrown an error'); diff --git a/test/type.triggeredSend.test.js b/test/type.triggeredSend.test.js index ee548e28c..a4fd91b4e 100644 --- a/test/type.triggeredSend.test.js +++ b/test/type.triggeredSend.test.js @@ -130,9 +130,11 @@ describe('type: triggeredSend', () => { describe('Delete ================', () => { it('Should delete the item', async () => { // WHEN - const result = await handler.deleteByKey('testInstance/testBU', 'triggeredSend', [ - 'testExisting_triggeredSend', - ]); + const result = await handler.deleteByKey( + 'testInstance/testBU', + 'triggeredSend', + 'testExisting_triggeredSend' + ); // THEN assert.equal(process.exitCode, false, 'delete should not have thrown an error'); From c8ef4332241823c2acd558f8ca957df94f7a5644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Mon, 21 Aug 2023 16:47:55 +0200 Subject: [PATCH 32/70] #325: rename from dataVerification to verification to match name in SFMC GUI --- docs/dist/documentation.md | 252 +++++++++--------- lib/MetadataTypeDefinitions.js | 2 +- lib/MetadataTypeInfo.js | 2 +- lib/metadataTypes/Automation.js | 14 +- .../{DataVerification.js => Verification.js} | 26 +- .../definitions/Automation.definition.js | 6 +- ...finition.js => Verification.definition.js} | 2 +- types/mcdev.d.js | 2 +- 8 files changed, 153 insertions(+), 153 deletions(-) rename lib/metadataTypes/{DataVerification.js => Verification.js} (84%) rename lib/metadataTypes/definitions/{DataVerification.definition.js => Verification.definition.js} (98%) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 00eaf1571..79a3cb8a1 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -46,9 +46,6 @@ Source and target business units are also compared before the deployment to appl Only for Caching No retrieve/upsert is required as this is a configuration in the EID

-
DataVerificationMetadataType
-

DataVerification MetadataType

-
DiscoveryMetadataType

ImportFile MetadataType

@@ -129,6 +126,9 @@ Provides default functionality that can be overwritten by child metadata type cl
UserMetadataType

MetadataType

+
VerificationMetadataType
+

Verification MetadataType

+
Retriever

Retrieves metadata from a business unit and saves it to the local filesystem.

@@ -296,7 +296,7 @@ helper for DataExtension.#fixShared_item
AutomationItem : object
-
DataVerificationItem : object
+
VerificationItem : object
SDK : Object.<string, AutomationItem>
@@ -2358,126 +2358,6 @@ Retrieves Metadata of Data Extract Type for caching. **Kind**: static method of [DataExtractType](#DataExtractType) **Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise of metadata - - -## DataVerification ⇐ [MetadataType](#MetadataType) -DataVerification MetadataType - -**Kind**: global class -**Extends**: [MetadataType](#MetadataType) - -* [DataVerification](#DataVerification) ⇐ [MetadataType](#MetadataType) - * [.retrieve(retrieveDir, [_], [__], key)](#DataVerification.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> - * [.retrieveForCache([_], [__], [keyArr])](#DataVerification.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> - * [.create(metadata)](#DataVerification.create) ⇒ Promise - * [.postCreateTasks(metadataEntry, apiResponse, metadataEntryWithAllFields)](#DataVerification.postCreateTasks) ⇒ void - * [.update(metadata)](#DataVerification.update) ⇒ Promise - * [.preDeployTasks(metadata)](#DataVerification.preDeployTasks) ⇒ TYPE.DataVerificationItem - * [.postRetrieveTasks(metadata)](#DataVerification.postRetrieveTasks) ⇒ TYPE.DataVerificationItem - * [.deleteByKey(key)](#DataVerification.deleteByKey) ⇒ Promise.<boolean> - - - -### DataVerification.retrieve(retrieveDir, [_], [__], key) ⇒ Promise.<TYPE.MetadataTypeMapObj> -Retrieves Metadata of Data Verification Activity. - -**Kind**: static method of [DataVerification](#DataVerification) -**Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise of metadata - -| Param | Type | Description | -| --- | --- | --- | -| retrieveDir | string | Directory where retrieved metadata directory will be saved | -| [_] | void | unused parameter | -| [__] | void | unused parameter | -| key | string | customer key of single item to retrieve | - - - -### DataVerification.retrieveForCache([_], [__], [keyArr]) ⇒ Promise.<TYPE.MetadataTypeMapObj> -Retrieves Metadata of Data Extract Activity for caching - -**Kind**: static method of [DataVerification](#DataVerification) -**Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise of metadata - -| Param | Type | Description | -| --- | --- | --- | -| [_] | void | not used | -| [__] | void | not used | -| [keyArr] | Array.<string> | customer key of single item to retrieve | - - - -### DataVerification.create(metadata) ⇒ Promise -Creates a single Data Extract - -**Kind**: static method of [DataVerification](#DataVerification) -**Returns**: Promise - Promise - -| Param | Type | Description | -| --- | --- | --- | -| metadata | TYPE.DataVerificationItem | a single Data Extract | - - - -### DataVerification.postCreateTasks(metadataEntry, apiResponse, metadataEntryWithAllFields) ⇒ void -helper for [createREST](#MetadataType.createREST) - -**Kind**: static method of [DataVerification](#DataVerification) - -| Param | Type | Description | -| --- | --- | --- | -| metadataEntry | TYPE.MetadataTypeItem | a single metadata Entry | -| apiResponse | object | varies depending on the API call | -| metadataEntryWithAllFields | TYPE.MetadataTypeItem | like metadataEntry but before non-creatable fields were stripped | - - - -### DataVerification.update(metadata) ⇒ Promise -Updates a single Data Extract - -**Kind**: static method of [DataVerification](#DataVerification) -**Returns**: Promise - Promise - -| Param | Type | Description | -| --- | --- | --- | -| metadata | TYPE.DataVerificationItem | a single Data Extract | - - - -### DataVerification.preDeployTasks(metadata) ⇒ TYPE.DataVerificationItem -prepares a dataVerification for deployment - -**Kind**: static method of [DataVerification](#DataVerification) -**Returns**: TYPE.DataVerificationItem - metadata object - -| Param | Type | Description | -| --- | --- | --- | -| metadata | TYPE.DataVerificationItem | a single dataVerification activity definition | - - - -### DataVerification.postRetrieveTasks(metadata) ⇒ TYPE.DataVerificationItem -parses retrieved Metadata before saving - -**Kind**: static method of [DataVerification](#DataVerification) -**Returns**: TYPE.DataVerificationItem - Array with one metadata object and one sql string - -| Param | Type | Description | -| --- | --- | --- | -| metadata | TYPE.DataVerificationItem | a single dataVerification activity definition | - - - -### DataVerification.deleteByKey(key) ⇒ Promise.<boolean> -Delete a metadata item from the specified business unit - -**Kind**: static method of [DataVerification](#DataVerification) -**Returns**: Promise.<boolean> - deletion success status - -| Param | Type | Description | -| --- | --- | --- | -| key | string | Identifier of item | - ## Discovery ⇐ [MetadataType](#MetadataType) @@ -6158,6 +6038,126 @@ manages post retrieve steps | --- | --- | --- | | metadata | TYPE.MetadataTypeItem | a single item | + + +## Verification ⇐ [MetadataType](#MetadataType) +Verification MetadataType + +**Kind**: global class +**Extends**: [MetadataType](#MetadataType) + +* [Verification](#Verification) ⇐ [MetadataType](#MetadataType) + * [.retrieve(retrieveDir, [_], [__], key)](#Verification.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.retrieveForCache([_], [__], [keyArr])](#Verification.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.create(metadata)](#Verification.create) ⇒ Promise + * [.postCreateTasks(metadataEntry, apiResponse, metadataEntryWithAllFields)](#Verification.postCreateTasks) ⇒ void + * [.update(metadata)](#Verification.update) ⇒ Promise + * [.preDeployTasks(metadata)](#Verification.preDeployTasks) ⇒ TYPE.VerificationItem + * [.postRetrieveTasks(metadata)](#Verification.postRetrieveTasks) ⇒ TYPE.VerificationItem + * [.deleteByKey(key)](#Verification.deleteByKey) ⇒ Promise.<boolean> + + + +### Verification.retrieve(retrieveDir, [_], [__], key) ⇒ Promise.<TYPE.MetadataTypeMapObj> +Retrieves Metadata of Data Verification Activity. + +**Kind**: static method of [Verification](#Verification) +**Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise of metadata + +| Param | Type | Description | +| --- | --- | --- | +| retrieveDir | string | Directory where retrieved metadata directory will be saved | +| [_] | void | unused parameter | +| [__] | void | unused parameter | +| key | string | customer key of single item to retrieve | + + + +### Verification.retrieveForCache([_], [__], [keyArr]) ⇒ Promise.<TYPE.MetadataTypeMapObj> +Retrieves Metadata of Data Extract Activity for caching + +**Kind**: static method of [Verification](#Verification) +**Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise of metadata + +| Param | Type | Description | +| --- | --- | --- | +| [_] | void | not used | +| [__] | void | not used | +| [keyArr] | Array.<string> | customer key of single item to retrieve | + + + +### Verification.create(metadata) ⇒ Promise +Creates a single Data Extract + +**Kind**: static method of [Verification](#Verification) +**Returns**: Promise - Promise + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.VerificationItem | a single Data Extract | + + + +### Verification.postCreateTasks(metadataEntry, apiResponse, metadataEntryWithAllFields) ⇒ void +helper for [createREST](#MetadataType.createREST) + +**Kind**: static method of [Verification](#Verification) + +| Param | Type | Description | +| --- | --- | --- | +| metadataEntry | TYPE.MetadataTypeItem | a single metadata Entry | +| apiResponse | object | varies depending on the API call | +| metadataEntryWithAllFields | TYPE.MetadataTypeItem | like metadataEntry but before non-creatable fields were stripped | + + + +### Verification.update(metadata) ⇒ Promise +Updates a single Data Extract + +**Kind**: static method of [Verification](#Verification) +**Returns**: Promise - Promise + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.VerificationItem | a single Data Extract | + + + +### Verification.preDeployTasks(metadata) ⇒ TYPE.VerificationItem +prepares a verification for deployment + +**Kind**: static method of [Verification](#Verification) +**Returns**: TYPE.VerificationItem - metadata object + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.VerificationItem | a single verification activity definition | + + + +### Verification.postRetrieveTasks(metadata) ⇒ TYPE.VerificationItem +parses retrieved Metadata before saving + +**Kind**: static method of [Verification](#Verification) +**Returns**: TYPE.VerificationItem - Array with one metadata object and one sql string + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.VerificationItem | a single verification activity definition | + + + +### Verification.deleteByKey(key) ⇒ Promise.<boolean> +Delete a metadata item from the specified business unit + +**Kind**: static method of [Verification](#Verification) +**Returns**: Promise.<boolean> - deletion success status + +| Param | Type | Description | +| --- | --- | --- | +| key | string | Identifier of item | + ## Retriever @@ -9161,9 +9161,9 @@ SOAP format | r__folder_Path | string | folder path | | [categoryId] | string | holds folder ID, replaced with r__folder_Path during retrieve | - + -## DataVerificationItem : object +## VerificationItem : object **Kind**: global typedef **Properties** diff --git a/lib/MetadataTypeDefinitions.js b/lib/MetadataTypeDefinitions.js index 014644d34..e6c0e9331 100644 --- a/lib/MetadataTypeDefinitions.js +++ b/lib/MetadataTypeDefinitions.js @@ -15,7 +15,6 @@ const MetadataTypeDefinitions = { dataExtensionTemplate: require('./metadataTypes/definitions/DataExtensionTemplate.definition'), dataExtract: require('./metadataTypes/definitions/DataExtract.definition'), dataExtractType: require('./metadataTypes/definitions/DataExtractType.definition'), - dataVerification: require('./metadataTypes/definitions/DataVerification.definition'), discovery: require('./metadataTypes/definitions/Discovery.definition'), email: require('./metadataTypes/definitions/Email.definition'), emailSend: require('./metadataTypes/definitions/EmailSend.definition'), @@ -39,6 +38,7 @@ const MetadataTypeDefinitions = { transactionalSMS: require('./metadataTypes/definitions/TransactionalSMS.definition'), triggeredSend: require('./metadataTypes/definitions/TriggeredSend.definition'), user: require('./metadataTypes/definitions/User.definition'), + verification: require('./metadataTypes/definitions/Verification.definition'), }; module.exports = MetadataTypeDefinitions; diff --git a/lib/MetadataTypeInfo.js b/lib/MetadataTypeInfo.js index 3c991c6b0..40d7bafce 100644 --- a/lib/MetadataTypeInfo.js +++ b/lib/MetadataTypeInfo.js @@ -15,7 +15,6 @@ const MetadataTypeInfo = { dataExtensionTemplate: require('./metadataTypes/DataExtensionTemplate'), dataExtract: require('./metadataTypes/DataExtract'), dataExtractType: require('./metadataTypes/DataExtractType'), - dataVerification: require('./metadataTypes/DataVerification'), discovery: require('./metadataTypes/Discovery'), email: require('./metadataTypes/Email'), emailSend: require('./metadataTypes/EmailSend'), @@ -39,6 +38,7 @@ const MetadataTypeInfo = { transactionalSMS: require('./metadataTypes/TransactionalSMS'), triggeredSend: require('./metadataTypes/TriggeredSend'), user: require('./metadataTypes/User'), + verification: require('./metadataTypes/Verification'), }; module.exports = MetadataTypeInfo; diff --git a/lib/metadataTypes/Automation.js b/lib/metadataTypes/Automation.js index ba61d319b..21160bdba 100644 --- a/lib/metadataTypes/Automation.js +++ b/lib/metadataTypes/Automation.js @@ -5,7 +5,7 @@ const TYPE = require('../../types/mcdev.d'); const Util = require('../util/util'); const File = require('../util/file'); const Definitions = require('../MetadataTypeDefinitions'); -const DataVerification = require('./DataVerification'); +const Verification = require('./Verification'); const cache = require('../util/cache'); const pLimit = require('p-limit'); @@ -443,19 +443,19 @@ class Automation extends MetadataType { ); continue; } - if (activity.r__type === 'dataVerification') { + if (activity.r__type === 'verification') { // data verifications can only be retrieved 1-by-1. The get-API does not work without IDs it seems. - DataVerification.client = this.client; - DataVerification.buObject = this.buObject; - DataVerification.properties = this.properties; - const dvResult = await DataVerification.retrieve( + Verification.client = this.client; + Verification.buObject = this.buObject; + Verification.properties = this.properties; + const dvResult = await Verification.retrieve( this.retrieveDir, undefined, undefined, activity.activityObjectId ); if (dvResult?.metadata) { - cache.mergeMetadata('dataVerification', dvResult.metadata); + cache.mergeMetadata('verification', dvResult.metadata); } } // / if managed by cache we can update references to support deployment diff --git a/lib/metadataTypes/DataVerification.js b/lib/metadataTypes/Verification.js similarity index 84% rename from lib/metadataTypes/DataVerification.js rename to lib/metadataTypes/Verification.js index aac8a0a13..2345fc1b8 100644 --- a/lib/metadataTypes/DataVerification.js +++ b/lib/metadataTypes/Verification.js @@ -6,11 +6,11 @@ const Util = require('../util/util'); const cache = require('../util/cache'); /** - * DataVerification MetadataType + * Verification MetadataType * * @augments MetadataType */ -class DataVerification extends MetadataType { +class Verification extends MetadataType { /** * Retrieves Metadata of Data Verification Activity. * @@ -28,7 +28,7 @@ class DataVerification extends MetadataType { param = key; } if (param === '') { - throw new Error('DataVerification can only be retrieved if the ID is known'); + throw new Error('Verification can only be retrieved if the ID is known'); } try { return await super.retrieveREST( @@ -81,7 +81,7 @@ class DataVerification extends MetadataType { /** * Creates a single Data Extract * - * @param {TYPE.DataVerificationItem} metadata a single Data Extract + * @param {TYPE.VerificationItem} metadata a single Data Extract * @returns {Promise} Promise */ static create(metadata) { @@ -113,21 +113,21 @@ class DataVerification extends MetadataType { /** * Updates a single Data Extract * - * @param {TYPE.DataVerificationItem} metadata a single Data Extract + * @param {TYPE.VerificationItem} metadata a single Data Extract * @returns {Promise} Promise */ static update(metadata) { return super.updateREST( metadata, - '/automation/v1/dataverifications/' + metadata.dataVerificationDefinitionId + '/automation/v1/dataverifications/' + metadata.verificationDefinitionId ); } /** - * prepares a dataVerification for deployment + * prepares a verification for deployment * - * @param {TYPE.DataVerificationItem} metadata a single dataVerification activity definition - * @returns {TYPE.DataVerificationItem} metadata object + * @param {TYPE.VerificationItem} metadata a single verification activity definition + * @returns {TYPE.VerificationItem} metadata object */ static preDeployTasks(metadata) { metadata.targetObjectId = cache.searchForField( @@ -142,8 +142,8 @@ class DataVerification extends MetadataType { /** * parses retrieved Metadata before saving * - * @param {TYPE.DataVerificationItem} metadata a single dataVerification activity definition - * @returns {TYPE.DataVerificationItem} Array with one metadata object and one sql string + * @param {TYPE.VerificationItem} metadata a single verification activity definition + * @returns {TYPE.VerificationItem} Array with one metadata object and one sql string */ static postRetrieveTasks(metadata) { try { @@ -173,6 +173,6 @@ class DataVerification extends MetadataType { } // Assign definition to static attributes -DataVerification.definition = require('../MetadataTypeDefinitions').dataVerification; +Verification.definition = require('../MetadataTypeDefinitions').verification; -module.exports = DataVerification; +module.exports = Verification; diff --git a/lib/metadataTypes/definitions/Automation.definition.js b/lib/metadataTypes/definitions/Automation.definition.js index f78db4e95..06597cd23 100644 --- a/lib/metadataTypes/definitions/Automation.definition.js +++ b/lib/metadataTypes/definitions/Automation.definition.js @@ -11,7 +11,7 @@ module.exports = { journeyEntryOld: 733, query: 300, script: 423, - dataVerification: 1000, + verification: 1000, wait: 467, push: 736, sms: 725, @@ -23,9 +23,9 @@ module.exports = { importMobileContact: 726, }, bodyIteratorField: 'items', - manuallyRetrievedDependencies: ['dataVerification'], + manuallyRetrievedDependencies: ['verification'], dependencies: [ - 'dataExtension', // for dataVerification + 'dataExtension', // for verification 'dataExtract', 'emailSend', 'fileTransfer', diff --git a/lib/metadataTypes/definitions/DataVerification.definition.js b/lib/metadataTypes/definitions/Verification.definition.js similarity index 98% rename from lib/metadataTypes/definitions/DataVerification.definition.js rename to lib/metadataTypes/definitions/Verification.definition.js index ecd4b8969..a9eb290d5 100644 --- a/lib/metadataTypes/definitions/DataVerification.definition.js +++ b/lib/metadataTypes/definitions/Verification.definition.js @@ -13,7 +13,7 @@ module.exports = { restPagination: false, retrieveRequiresKey: true, maxKeyLength: 36, // confirmed max length - type: 'dataVerification', + type: 'verification', typeDescription: 'Check DataExtension for a row count', typeRetrieveByDefault: true, typeName: 'Automation: Data Verification Activity', diff --git a/types/mcdev.d.js b/types/mcdev.d.js index fce52d0c3..0367a38f8 100644 --- a/types/mcdev.d.js +++ b/types/mcdev.d.js @@ -252,7 +252,7 @@ const SDK = require('sfmc-sdk'); * @property {string} [categoryId] holds folder ID, replaced with r__folder_Path during retrieve */ /** - * @typedef {object} DataVerificationItem + * @typedef {object} VerificationItem * @property {string} dataVerificationDefinitionId ID / Key * @property {'IsEqualTo'|'IsLessThan'|'IsGreaterThan'|'IsOutsideRange'|'IsInsideRange'|'IsNotEqualTo'|'IsNotLessThan'|'IsNotGreaterThan'|'IsNotOutsideRange'|'IsNotInsideRange'} verificationType key * @property {number} value1 used for all verificationTypes; lower value for IsOutsideRange, IsInsideRange, IsNotOutsideRange, IsNotInsideRange From 470d7511d3585dd4e739d0589b62524c04b198ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 20:26:57 +0000 Subject: [PATCH 33/70] Bump eslint-plugin-jsdoc from 46.4.6 to 46.5.0 Bumps [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) from 46.4.6 to 46.5.0. - [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases) - [Changelog](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/.releaserc) - [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v46.4.6...v46.5.0) --- updated-dependencies: - dependency-name: eslint-plugin-jsdoc dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4e55a12d5..621e1b89d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,7 +41,7 @@ "eslint": "8.47.0", "eslint-config-prettier": "9.0.0", "eslint-config-ssjs": "1.1.11", - "eslint-plugin-jsdoc": "46.4.6", + "eslint-plugin-jsdoc": "46.5.0", "eslint-plugin-mocha": "10.1.0", "eslint-plugin-prettier": "4.2.1", "eslint-plugin-unicorn": "48.0.0", @@ -3219,9 +3219,9 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "46.4.6", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.4.6.tgz", - "integrity": "sha512-z4SWYnJfOqftZI+b3RM9AtWL1vF/sLWE/LlO9yOKDof9yN2+n3zOdOJTGX/pRE/xnPsooOLG2Rq6e4d+XW3lNw==", + "version": "46.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.5.0.tgz", + "integrity": "sha512-aulXdA4I1dyWpzyS1Nh/GNoS6PavzeucxEapnMR4JUERowWvaEk2Y4A5irpHAcdXtBBHLVe8WIhdXNjoAlGQgA==", "dev": true, "dependencies": { "@es-joy/jsdoccomment": "~0.40.1", @@ -12515,9 +12515,9 @@ "requires": {} }, "eslint-plugin-jsdoc": { - "version": "46.4.6", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.4.6.tgz", - "integrity": "sha512-z4SWYnJfOqftZI+b3RM9AtWL1vF/sLWE/LlO9yOKDof9yN2+n3zOdOJTGX/pRE/xnPsooOLG2Rq6e4d+XW3lNw==", + "version": "46.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.5.0.tgz", + "integrity": "sha512-aulXdA4I1dyWpzyS1Nh/GNoS6PavzeucxEapnMR4JUERowWvaEk2Y4A5irpHAcdXtBBHLVe8WIhdXNjoAlGQgA==", "dev": true, "requires": { "@es-joy/jsdoccomment": "~0.40.1", diff --git a/package.json b/package.json index ff158ff70..af4d4948d 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "eslint": "8.47.0", "eslint-config-prettier": "9.0.0", "eslint-config-ssjs": "1.1.11", - "eslint-plugin-jsdoc": "46.4.6", + "eslint-plugin-jsdoc": "46.5.0", "eslint-plugin-mocha": "10.1.0", "eslint-plugin-prettier": "4.2.1", "eslint-plugin-unicorn": "48.0.0", From 1a8fa4fad647894825c399de57322a2aa1e54c1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Aug 2023 08:25:58 +0000 Subject: [PATCH 34/70] Bump lint-staged from 14.0.0 to 14.0.1 Bumps [lint-staged](https://github.com/okonet/lint-staged) from 14.0.0 to 14.0.1. - [Release notes](https://github.com/okonet/lint-staged/releases) - [Commits](https://github.com/okonet/lint-staged/compare/v14.0.0...v14.0.1) --- updated-dependencies: - dependency-name: lint-staged dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 621e1b89d..a9cffb152 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,7 +48,7 @@ "fast-xml-parser": "4.2.7", "husky": "8.0.3", "jsdoc-to-markdown": "8.0.0", - "lint-staged": "14.0.0", + "lint-staged": "14.0.1", "mocha": "10.2.0", "mock-fs": "5.2.0", "npm-check": "6.0.1", @@ -5737,9 +5737,9 @@ } }, "node_modules/lint-staged": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-14.0.0.tgz", - "integrity": "sha512-0tLf0pqZYkar/wu3nTctk4rVIG+d7PanDYv4/IQR4qwdqfQkTDziLRFnqMcLuLBTuUqmcLwsHPD2EjQ18d/oaA==", + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-14.0.1.tgz", + "integrity": "sha512-Mw0cL6HXnHN1ag0mN/Dg4g6sr8uf8sn98w2Oc1ECtFto9tvRF7nkXGJRbx8gPlHyoR0pLyBr2lQHbWwmUHe1Sw==", "dev": true, "dependencies": { "chalk": "5.3.0", @@ -14292,9 +14292,9 @@ } }, "lint-staged": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-14.0.0.tgz", - "integrity": "sha512-0tLf0pqZYkar/wu3nTctk4rVIG+d7PanDYv4/IQR4qwdqfQkTDziLRFnqMcLuLBTuUqmcLwsHPD2EjQ18d/oaA==", + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-14.0.1.tgz", + "integrity": "sha512-Mw0cL6HXnHN1ag0mN/Dg4g6sr8uf8sn98w2Oc1ECtFto9tvRF7nkXGJRbx8gPlHyoR0pLyBr2lQHbWwmUHe1Sw==", "dev": true, "requires": { "chalk": "5.3.0", diff --git a/package.json b/package.json index af4d4948d..94eb5c31e 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "fast-xml-parser": "4.2.7", "husky": "8.0.3", "jsdoc-to-markdown": "8.0.0", - "lint-staged": "14.0.0", + "lint-staged": "14.0.1", "mocha": "10.2.0", "mock-fs": "5.2.0", "npm-check": "6.0.1", From 50875e1b8725be8598a6bf761548c00ea7edd7be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Aug 2023 08:28:46 +0000 Subject: [PATCH 35/70] Bump fsevents from 2.3.2 to 2.3.3 Bumps [fsevents](https://github.com/fsevents/fsevents) from 2.3.2 to 2.3.3. - [Release notes](https://github.com/fsevents/fsevents/releases) - [Commits](https://github.com/fsevents/fsevents/compare/v2.3.2...v2.3.3) --- updated-dependencies: - dependency-name: fsevents dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index a9cffb152..f035492e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "console.table": "0.10.0", "deep-equal": "2.2.2", "fs-extra": "11.1.0", + "fsevents": "*", "inquirer": "8.2.6", "json-to-table": "4.2.1", "mustache": "4.2.0", @@ -4085,9 +4086,9 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, "optional": true, "os": [ @@ -13103,9 +13104,9 @@ "dev": true }, "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "optional": true }, "function-bind": { From 27504e117d20ec3ed3d3fd6658343d56dbebc9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 22 Aug 2023 13:49:07 +0200 Subject: [PATCH 36/70] #325: enable retrieve-all for type verification via caching automations first --- lib/metadataTypes/Automation.js | 135 ++++++++---------- lib/metadataTypes/Verification.js | 106 +++++++++++--- .../definitions/Automation.definition.js | 3 +- 3 files changed, 146 insertions(+), 98 deletions(-) diff --git a/lib/metadataTypes/Automation.js b/lib/metadataTypes/Automation.js index 21160bdba..ddb6dfe3f 100644 --- a/lib/metadataTypes/Automation.js +++ b/lib/metadataTypes/Automation.js @@ -5,7 +5,6 @@ const TYPE = require('../../types/mcdev.d'); const Util = require('../util/util'); const File = require('../util/file'); const Definitions = require('../MetadataTypeDefinitions'); -const Verification = require('./Verification'); const cache = require('../util/cache'); const pLimit = require('p-limit'); @@ -26,65 +25,75 @@ class Automation extends MetadataType { * @returns {Promise.} Promise of metadata */ static async retrieve(retrieveDir, _, __, key) { - /** @type {TYPE.SoapRequestParams} */ - let requestParams = null; - if (key) { - requestParams = { - filter: { - leftOperand: 'CustomerKey', - operator: 'equals', - rightOperand: key, - }, - }; - } - const results = await this.client.soap.retrieveBulk('Program', ['ObjectID'], requestParams); - if (results.Results?.length && !key) { - // empty results will come back without "Results" defined - Util.logger.info( - Util.getGrayMsg( - ` - ${results.Results?.length} automation${ - results.Results?.length === 1 ? '' : 's' - } found. Retrieving details...` - ) + let metadataMap; + if (!key && this._cachedMetadataMap) { + metadataMap = this._cachedMetadataMap; + delete this._cachedMetadataMap; + } else { + /** @type {TYPE.SoapRequestParams} */ + let requestParams = null; + if (key) { + requestParams = { + filter: { + leftOperand: 'CustomerKey', + operator: 'equals', + rightOperand: key, + }, + }; + } + const results = await this.client.soap.retrieveBulk( + 'Program', + ['ObjectID'], + requestParams ); - } - // the API seems to handle 50 concurrent requests nicely - const rateLimit = pLimit(50); + if (results.Results?.length && !key) { + // empty results will come back without "Results" defined + Util.logger.info( + Util.getGrayMsg( + ` - ${results.Results?.length} automation${ + results.Results?.length === 1 ? '' : 's' + } found. Retrieving details...` + ) + ); + } + // the API seems to handle 50 concurrent requests nicely + const rateLimit = pLimit(50); - const details = results.Results - ? await Promise.all( - results.Results.map(async (item) => - rateLimit(async () => { - try { - return await this.client.rest.get( - '/automation/v1/automations/' + item.ObjectID - ); - } catch (ex) { + const details = results.Results + ? await Promise.all( + results.Results.map(async (item) => + rateLimit(async () => { try { - if (ex.message == 'socket hang up') { - // one more retry; it's a rare case but retrying again should solve the issue gracefully - return await this.client.rest.get( - '/automation/v1/automations/' + item.ObjectID - ); + return await this.client.rest.get( + '/automation/v1/automations/' + item.ObjectID + ); + } catch (ex) { + try { + if (ex.message == 'socket hang up') { + // one more retry; it's a rare case but retrying again should solve the issue gracefully + return await this.client.rest.get( + '/automation/v1/automations/' + item.ObjectID + ); + } + } catch { + // no extra action needed, handled below } - } catch { - // no extra action needed, handled below + // if we do get here, we should log the error and continue instead of failing to download all automations + Util.logger.error( + ` ☇ skipping Automation ${item.ObjectID}: ${ex.message} ${ex.code}` + ); + return null; } - // if we do get here, we should log the error and continue instead of failing to download all automations - Util.logger.error( - ` ☇ skipping Automation ${item.ObjectID}: ${ex.message} ${ex.code}` - ); - return null; - } - }) + }) + ) ) - ) - : []; + : []; - // * if retrieving some automations fails, a null element would remain in the details-array for each of them that needs to be filtered to prevent it from causing issues elsewhere - let metadataMap = this.parseResponseBody({ items: details.filter(Boolean) }); + // * if retrieving some automations fails, a null element would remain in the details-array for each of them that needs to be filtered to prevent it from causing issues elsewhere + metadataMap = this.parseResponseBody({ items: details.filter(Boolean) }); + } - if (Object.keys(metadataMap).length) { + if (!this._skipNotificationRetrieve && Object.keys(metadataMap).length) { // attach notification information to each automation that has any await this.#getAutomationNotificationsREST(metadataMap); } @@ -426,12 +435,7 @@ class Automation extends MetadataType { ); // empty if block continue; - } else if ( - !this.definition.dependencies.includes(activity.r__type) && - !this.definition.manuallyRetrievedDependencies.includes( - activity.r__type - ) - ) { + } else if (!this.definition.dependencies.includes(activity.r__type)) { Util.logger.debug( ` - skipping ${ metadata[this.definition.keyField] @@ -443,21 +447,6 @@ class Automation extends MetadataType { ); continue; } - if (activity.r__type === 'verification') { - // data verifications can only be retrieved 1-by-1. The get-API does not work without IDs it seems. - Verification.client = this.client; - Verification.buObject = this.buObject; - Verification.properties = this.properties; - const dvResult = await Verification.retrieve( - this.retrieveDir, - undefined, - undefined, - activity.activityObjectId - ); - if (dvResult?.metadata) { - cache.mergeMetadata('verification', dvResult.metadata); - } - } // / if managed by cache we can update references to support deployment if ( Definitions[activity.r__type]?.['idField'] && diff --git a/lib/metadataTypes/Verification.js b/lib/metadataTypes/Verification.js index 2345fc1b8..693f288bd 100644 --- a/lib/metadataTypes/Verification.js +++ b/lib/metadataTypes/Verification.js @@ -2,6 +2,7 @@ const TYPE = require('../../types/mcdev.d'); const MetadataType = require('./MetadataType'); +const Automation = require('./Automation'); const Util = require('../util/util'); const cache = require('../util/cache'); @@ -21,38 +22,97 @@ class Verification extends MetadataType { * @returns {Promise.} Promise of metadata */ static async retrieve(retrieveDir, _, __, key) { - let param = ''; + let paramArr = []; if (key?.startsWith('id:')) { - param = key.slice(3); + paramArr = [key.slice(3)]; } else if (key) { - param = key; + paramArr = [key]; } - if (param === '') { - throw new Error('Verification can only be retrieved if the ID is known'); + if (!paramArr.length) { + // there is no API endpoint to retrieve all dataVerification items, so we need to retrieve all automations and iterate over their activities + Util.logger.info(` - Caching dependent Metadata: automation`); + Automation.client = this.client; + Automation.buObject = this.buObject; + Automation.properties = this.properties; + Automation._skipNotificationRetrieve = true; + const automationsMapObj = await Automation.retrieve(); + delete Automation._skipNotificationRetrieve; + if (automationsMapObj?.metadata && Object.keys(automationsMapObj?.metadata).length) { + if (!key) { + // if we are not retrieving a single item, cache the automations for later use during retrieval of automations + Automation._cachedMetadataMap = automationsMapObj?.metadata; + } + // automations found, lets iterate over their activities to find the dataVerification items + const dataVerificationIds = []; + for (const automation of Object.values(automationsMapObj.metadata)) { + if (automation.steps) { + for (const step of automation.steps) { + for (const activity of step.activities) { + if ( + activity.objectTypeId === 1000 && + activity.activityObjectId && + activity.activityObjectId !== + '00000000-0000-0000-0000-000000000000' + ) { + dataVerificationIds.push(activity.activityObjectId); + } + } + } + } + } + if (dataVerificationIds.length) { + paramArr.push(...dataVerificationIds); + } + } } - try { - return await super.retrieveREST( - null, - '/automation/v1/dataverifications/' + param, - null, - key - ); - } catch (ex) { - if ( - ex.message === 'Not Found' || - ex.message === 'Request failed with status code 400' - ) { - if (retrieveDir) { - Util.logger.info( - `Downloaded: ${this.definition.type} (0)${Util.getKeysString(param)}` + const results = {}; + if (paramArr.length) { + if (key) { + try { + const verification = await super.retrieveREST( + null, + '/automation/v1/dataverifications/' + paramArr[0], + null, + key ); + const key = Object.values(verification?.metadata)[0]?.[ + this.definition.keyField + ]; + results[key] = verification?.metadata[key]; + } catch (ex) { + if ( + ex.message === 'Not Found' || + ex.message === 'Request failed with status code 400' + ) { + // if the ID is too short, the system will throw the 400 error + } else { + throw ex; + } } - // if the ID is too short, the system will throw the 400 error - return { metadata: {} }; } else { - throw ex; + const uri = '/automation/v1/dataverifications/'; + + const verificationArr = await this.client.rest.getCollection( + paramArr.map((id) => uri + id) + ); + for (const verification of verificationArr) { + const key = verification[this.definition.keyField]; + results[key] = verification; + } } } + if (retrieveDir) { + const savedMetadata = await this.saveResults(results, retrieveDir, null, null); + Util.logger.info( + `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` + + Util.getKeysString(key) + ); + } + + return { + metadata: results, + type: this.definition.type, + }; } /** * Retrieves Metadata of Data Extract Activity for caching diff --git a/lib/metadataTypes/definitions/Automation.definition.js b/lib/metadataTypes/definitions/Automation.definition.js index 06597cd23..656bcf659 100644 --- a/lib/metadataTypes/definitions/Automation.definition.js +++ b/lib/metadataTypes/definitions/Automation.definition.js @@ -23,9 +23,7 @@ module.exports = { importMobileContact: 726, }, bodyIteratorField: 'items', - manuallyRetrievedDependencies: ['verification'], dependencies: [ - 'dataExtension', // for verification 'dataExtract', 'emailSend', 'fileTransfer', @@ -33,6 +31,7 @@ module.exports = { 'importFile', 'query', 'script', + 'verification', ], folderType: 'automation', hasExtended: false, From 6960bc7e176fbb7ada5618eb96666f5649affca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 22 Aug 2023 15:34:57 +0200 Subject: [PATCH 37/70] #325: avoid SDK's getCollection method as it seems to have some kind of async issue --- lib/metadataTypes/Verification.js | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/lib/metadataTypes/Verification.js b/lib/metadataTypes/Verification.js index 693f288bd..8b8bd2ef3 100644 --- a/lib/metadataTypes/Verification.js +++ b/lib/metadataTypes/Verification.js @@ -5,6 +5,7 @@ const MetadataType = require('./MetadataType'); const Automation = require('./Automation'); const Util = require('../util/util'); const cache = require('../util/cache'); +const pLimit = require('p-limit'); /** * Verification MetadataType @@ -35,6 +36,7 @@ class Verification extends MetadataType { Automation.buObject = this.buObject; Automation.properties = this.properties; Automation._skipNotificationRetrieve = true; + delete Automation._cachedMetadataMap; const automationsMapObj = await Automation.retrieve(); delete Automation._skipNotificationRetrieve; if (automationsMapObj?.metadata && Object.keys(automationsMapObj?.metadata).length) { @@ -90,11 +92,29 @@ class Verification extends MetadataType { } } } else { + const rateLimit = pLimit(10); const uri = '/automation/v1/dataverifications/'; - const verificationArr = await this.client.rest.getCollection( - paramArr.map((id) => uri + id) - ); + const verificationArr = paramArr.length + ? await Promise.all( + paramArr.map(async (id) => + rateLimit(async () => { + try { + return await this.client.rest.get(uri + id); + } catch (ex) { + if ( + ex.message === 'Not Found' || + ex.message === 'Request failed with status code 400' + ) { + // if the ID is too short, the system will throw the 400 error + } else { + throw ex; + } + } + }) + ) + ) + : []; for (const verification of verificationArr) { const key = verification[this.definition.keyField]; results[key] = verification; From a40883690bbabade1c78d5fb7f16fd94cae8e157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 22 Aug 2023 16:21:45 +0200 Subject: [PATCH 38/70] #325: bundle verification retrieve logic in generic retrieveRESTcollection --- docs/dist/documentation.md | 40 ++++++++++++++++ lib/metadataTypes/MetadataType.js | 46 ++++++++++++++++++ lib/metadataTypes/Verification.js | 78 +++++++++++-------------------- 3 files changed, 113 insertions(+), 51 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 79a3cb8a1..7e9ba8905 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -3417,6 +3417,8 @@ Provides default functionality that can be overwritten by child metadata type cl * [.getSOAPErrorMsg(ex)](#MetadataType.getSOAPErrorMsg) ⇒ string * [.retrieveSOAP(retrieveDir, [requestParams], [singleRetrieve], [additionalFields])](#MetadataType.retrieveSOAP) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.retrieveREST(retrieveDir, uri, [templateVariables], [singleRetrieve])](#MetadataType.retrieveREST) ⇒ Promise.<{metadata: (TYPE.MetadataTypeMap\|TYPE.MetadataTypeItem), type: string}> + * [.retrieveRESTcollection(urlArray, [concurrentRequests])](#MetadataType.retrieveRESTcollection) ⇒ Promise.<{metadata: (TYPE.MetadataTypeMap\|TYPE.MetadataTypeItem), type: string}> + * [.handleRESTErrors(ex, id)](#MetadataType.handleRESTErrors) ⇒ null * [.executeREST(uri, key)](#MetadataType.executeREST) ⇒ Promise.<{key:string, response:string}> * [.executeSOAP([metadataEntry])](#MetadataType.executeSOAP) ⇒ Promise.<{key:string, response:object}> * [.runDocumentOnRetrieve([singleRetrieve], metadataMap)](#MetadataType.runDocumentOnRetrieve) ⇒ Promise.<void> @@ -3896,6 +3898,30 @@ Retrieves Metadata for Rest Types | [templateVariables] | TYPE.TemplateMap | variables to be replaced in the metadata | | [singleRetrieve] | string \| number | key of single item to filter by | + + +### MetadataType.retrieveRESTcollection(urlArray, [concurrentRequests]) ⇒ Promise.<{metadata: (TYPE.MetadataTypeMap\|TYPE.MetadataTypeItem), type: string}> +**Kind**: static method of [MetadataType](#MetadataType) +**Returns**: Promise.<{metadata: (TYPE.MetadataTypeMap\|TYPE.MetadataTypeItem), type: string}> - Promise of item map (single item for templated result) + +| Param | Type | Description | +| --- | --- | --- | +| urlArray | Array.<object> | {uri: string, id: string} combo of URL and ID/key of metadata | +| [concurrentRequests] | number | optionally set a different amount of concurrent requests | + + + +### MetadataType.handleRESTErrors(ex, id) ⇒ null +helper for [this.retrieveRESTcollection](this.retrieveRESTcollection) + +**Kind**: static method of [MetadataType](#MetadataType) +**Returns**: null - - + +| Param | Type | Description | +| --- | --- | --- | +| ex | Error | exception | +| id | string | id or key of item | + ### MetadataType.executeREST(uri, key) ⇒ Promise.<{key:string, response:string}> @@ -6048,6 +6074,7 @@ Verification MetadataType * [Verification](#Verification) ⇐ [MetadataType](#MetadataType) * [.retrieve(retrieveDir, [_], [__], key)](#Verification.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.handleRESTErrors(ex, id)](#Verification.handleRESTErrors) ⇒ null * [.retrieveForCache([_], [__], [keyArr])](#Verification.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.create(metadata)](#Verification.create) ⇒ Promise * [.postCreateTasks(metadataEntry, apiResponse, metadataEntryWithAllFields)](#Verification.postCreateTasks) ⇒ void @@ -6071,6 +6098,19 @@ Retrieves Metadata of Data Verification Activity. | [__] | void | unused parameter | | key | string | customer key of single item to retrieve | + + +### Verification.handleRESTErrors(ex, id) ⇒ null +helper for [this.retrieveRESTcollection](this.retrieveRESTcollection) + +**Kind**: static method of [Verification](#Verification) +**Returns**: null - - + +| Param | Type | Description | +| --- | --- | --- | +| ex | Error | exception | +| id | string | id or key of item | + ### Verification.retrieveForCache([_], [__], [keyArr]) ⇒ Promise.<TYPE.MetadataTypeMapObj> diff --git a/lib/metadataTypes/MetadataType.js b/lib/metadataTypes/MetadataType.js index 0d98601a8..12a824f61 100644 --- a/lib/metadataTypes/MetadataType.js +++ b/lib/metadataTypes/MetadataType.js @@ -1091,6 +1091,52 @@ class MetadataType { type: this.definition.type, }; } + /** + * + * @param {object[]} urlArray {uri: string, id: string} combo of URL and ID/key of metadata + * @param {number} [concurrentRequests] optionally set a different amount of concurrent requests + * @returns {Promise.<{metadata: (TYPE.MetadataTypeMap | TYPE.MetadataTypeItem), type: string}>} Promise of item map (single item for templated result) + */ + static async retrieveRESTcollection(urlArray, concurrentRequests) { + const rateLimit = pLimit(concurrentRequests || 10); + + const metadataArr = urlArray.length + ? await Promise.all( + urlArray.map(async (item) => + rateLimit(async () => { + try { + return await this.client.rest.get(item.uri); + } catch (ex) { + return this.handleRESTErrors(ex, item.id); + } + }) + ) + ) + : []; + const results = {}; + for (const item of metadataArr) { + const key = item[this.definition.keyField]; + results[key] = item; + } + return { + metadata: results, + type: this.definition.type, + }; + } + + /** + * helper for {@link this.retrieveRESTcollection} + * + * @param {Error} ex exception + * @param {string} id id or key of item + * @returns {null} - + */ + static handleRESTErrors(ex, id) { + // if the ID is too short, the system will throw the 400 error + Util.logger.debug(` ☇ skipping ${this.definition.type} ${id}: ${ex.message} ${ex.code}`); + + return null; + } /** * Used to execute a query/automation etc. * diff --git a/lib/metadataTypes/Verification.js b/lib/metadataTypes/Verification.js index 8b8bd2ef3..a628e463b 100644 --- a/lib/metadataTypes/Verification.js +++ b/lib/metadataTypes/Verification.js @@ -5,7 +5,6 @@ const MetadataType = require('./MetadataType'); const Automation = require('./Automation'); const Util = require('../util/util'); const cache = require('../util/cache'); -const pLimit = require('p-limit'); /** * Verification MetadataType @@ -69,56 +68,11 @@ class Verification extends MetadataType { } const results = {}; if (paramArr.length) { - if (key) { - try { - const verification = await super.retrieveREST( - null, - '/automation/v1/dataverifications/' + paramArr[0], - null, - key - ); - const key = Object.values(verification?.metadata)[0]?.[ - this.definition.keyField - ]; - results[key] = verification?.metadata[key]; - } catch (ex) { - if ( - ex.message === 'Not Found' || - ex.message === 'Request failed with status code 400' - ) { - // if the ID is too short, the system will throw the 400 error - } else { - throw ex; - } - } - } else { - const rateLimit = pLimit(10); - const uri = '/automation/v1/dataverifications/'; - - const verificationArr = paramArr.length - ? await Promise.all( - paramArr.map(async (id) => - rateLimit(async () => { - try { - return await this.client.rest.get(uri + id); - } catch (ex) { - if ( - ex.message === 'Not Found' || - ex.message === 'Request failed with status code 400' - ) { - // if the ID is too short, the system will throw the 400 error - } else { - throw ex; - } - } - }) - ) - ) - : []; - for (const verification of verificationArr) { - const key = verification[this.definition.keyField]; - results[key] = verification; - } + const response = await this.retrieveRESTcollection( + paramArr.map((id) => ({ id, uri: '/automation/v1/dataverifications/' + id })) + ); + if (response?.metadata) { + Object.assign(results, response.metadata); } } if (retrieveDir) { @@ -134,6 +88,28 @@ class Verification extends MetadataType { type: this.definition.type, }; } + /** + * helper for {@link this.retrieveRESTcollection} + * + * @param {Error} ex exception + * @param {string} id id or key of item + * @returns {null} - + */ + static handleRESTErrors(ex, id) { + if (ex.message === 'Not Found' || ex.message === 'Request failed with status code 400') { + // if the ID is too short, the system will throw the 400 error + Util.logger.debug( + ` ☇ skipping ${this.definition.type} ${id}: ${ex.message} ${ex.code}` + ); + } else { + // 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} ${id}: ${ex.message} ${ex.code}` + ); + } + return null; + } + /** * Retrieves Metadata of Data Extract Activity for caching * From 55a92ee9580f2396181a3323cb88ac1349714cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 22 Aug 2023 16:33:48 +0200 Subject: [PATCH 39/70] #325: upgrade Automation class to use new retrieveRESTcollection() --- docs/dist/documentation.md | 14 ++++++++ lib/metadataTypes/Automation.js | 63 ++++++++++++++++----------------- 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 7e9ba8905..9c1370795 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -1413,6 +1413,7 @@ Automation MetadataType * [Automation](#Automation) ⇐ [MetadataType](#MetadataType) * [.retrieve(retrieveDir, [_], [__], [key])](#Automation.retrieve) ⇒ Promise.<TYPE.AutomationMapObj> + * [.handleRESTErrors(ex, id)](#Automation.handleRESTErrors) ⇒ null * [.retrieveChangelog()](#Automation.retrieveChangelog) ⇒ Promise.<TYPE.AutomationMapObj> * [.retrieveForCache()](#Automation.retrieveForCache) ⇒ Promise.<TYPE.AutomationMapObj> * [.retrieveAsTemplate(templateDir, name, templateVariables)](#Automation.retrieveAsTemplate) ⇒ Promise.<TYPE.AutomationItemObj> @@ -1450,6 +1451,19 @@ Retrieves Metadata of Automation | [__] | void | unused parameter | | [key] | string | customer key of single item to retrieve | + + +### Automation.handleRESTErrors(ex, id) ⇒ null +helper for [this.retrieveRESTcollection](this.retrieveRESTcollection) + +**Kind**: static method of [Automation](#Automation) +**Returns**: null - - + +| Param | Type | Description | +| --- | --- | --- | +| ex | Error | exception | +| id | string | id or key of item | + ### Automation.retrieveChangelog() ⇒ Promise.<TYPE.AutomationMapObj> diff --git a/lib/metadataTypes/Automation.js b/lib/metadataTypes/Automation.js index ddb6dfe3f..61926367d 100644 --- a/lib/metadataTypes/Automation.js +++ b/lib/metadataTypes/Automation.js @@ -57,40 +57,16 @@ class Automation extends MetadataType { ); } // the API seems to handle 50 concurrent requests nicely - const rateLimit = pLimit(50); - - const details = results.Results - ? await Promise.all( - results.Results.map(async (item) => - rateLimit(async () => { - try { - return await this.client.rest.get( - '/automation/v1/automations/' + item.ObjectID - ); - } catch (ex) { - try { - if (ex.message == 'socket hang up') { - // one more retry; it's a rare case but retrying again should solve the issue gracefully - return await this.client.rest.get( - '/automation/v1/automations/' + item.ObjectID - ); - } - } catch { - // no extra action needed, handled below - } - // if we do get here, we should log the error and continue instead of failing to download all automations - Util.logger.error( - ` ☇ skipping Automation ${item.ObjectID}: ${ex.message} ${ex.code}` - ); - return null; - } - }) - ) + const response = results?.Results?.length + ? await this.retrieveRESTcollection( + results?.Results.map((item) => ({ + id: item.ObjectID, + uri: '/automation/v1/automations/' + item.ObjectID, + })), + 50 ) - : []; - - // * if retrieving some automations fails, a null element would remain in the details-array for each of them that needs to be filtered to prevent it from causing issues elsewhere - metadataMap = this.parseResponseBody({ items: details.filter(Boolean) }); + : null; + metadataMap = response?.metadata || {}; } if (!this._skipNotificationRetrieve && Object.keys(metadataMap).length) { @@ -112,6 +88,27 @@ class Automation extends MetadataType { return { metadata: metadataMap, type: this.definition.type }; } + /** + * helper for {@link this.retrieveRESTcollection} + * + * @param {Error} ex exception + * @param {string} id id or key of item + * @returns {null} - + */ + static async handleRESTErrors(ex, id) { + try { + if (ex.message == 'socket hang up') { + // one more retry; it's a rare case but retrying again should solve the issue gracefully + return await this.client.rest.get('/automation/v1/automations/' + id); + } + } catch { + // no extra action needed, handled below + } + // if we do get here, we should log the error and continue instead of failing to download all automations + Util.logger.error(` ☇ skipping Automation ${id}: ${ex.message} ${ex.code}`); + return null; + } + /** * helper for {@link Automation.retrieve} to get Automation Notifications * From bf8f6f4015a6242e70db139aaded6ec14a71e17f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 22 Aug 2023 21:25:43 +0200 Subject: [PATCH 40/70] #0: improve logs --- lib/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/index.js b/lib/index.js index 535fa4f08..d781897a6 100644 --- a/lib/index.js +++ b/lib/index.js @@ -312,7 +312,8 @@ class Mcdev { triggeredSend: 'triggeredSendDefinition', user: 'accountUser', }; - Util.logger.info(`:: Retrieving ${cred}/${bu}\n`); + Util.logger.info(''); + Util.logger.info(`:: Retrieving ${cred}/${bu}`); const retrieveTypesArr = []; if (selectedTypesArr) { for (const selectedType of Array.isArray(selectedTypesArr) From 4e62e7f7491fbb06443eb472b2a95287a0e70860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 23 Aug 2023 10:13:08 +0200 Subject: [PATCH 41/70] #325: revert require-key logic --- docs/dist/documentation.md | 16 ++++------------ lib/Deployer.js | 8 +------- lib/metadataTypes/MetadataType.js | 5 ++--- lib/metadataTypes/Verification.js | 19 ++----------------- .../definitions/Verification.definition.js | 1 - 5 files changed, 9 insertions(+), 40 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 9c1370795..404c2f588 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -3409,7 +3409,7 @@ Provides default functionality that can be overwritten by child metadata type cl * [.setFolderId(metadata)](#MetadataType.setFolderId) * [.retrieve(retrieveDir, [additionalFields], [subTypeArr], [key])](#MetadataType.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.retrieveChangelog([additionalFields], [subTypeArr])](#MetadataType.retrieveChangelog) ⇒ Promise.<TYPE.MetadataTypeMapObj> - * [.retrieveForCache([additionalFields], [subTypeArr], [keyArr])](#MetadataType.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.retrieveForCache([additionalFields], [subTypeArr])](#MetadataType.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.retrieveAsTemplate(templateDir, name, templateVariables, [subType])](#MetadataType.retrieveAsTemplate) ⇒ Promise.<TYPE.MetadataTypeItemObj> * [.retrieveTemplateREST(templateDir, uri, templateVariables, name)](#MetadataType.retrieveTemplateREST) ⇒ Promise.<{metadata: TYPE.MetadataTypeItem, type: string}> * [.buildTemplate(retrieveDir, templateDir, key, templateVariables)](#MetadataType.buildTemplate) ⇒ Promise.<TYPE.MetadataTypeItemObj> @@ -3635,7 +3635,7 @@ Gets metadata from Marketing Cloud -### MetadataType.retrieveForCache([additionalFields], [subTypeArr], [keyArr]) ⇒ Promise.<TYPE.MetadataTypeMapObj> +### MetadataType.retrieveForCache([additionalFields], [subTypeArr]) ⇒ Promise.<TYPE.MetadataTypeMapObj> Gets metadata cache with limited fields and does not store value to disk **Kind**: static method of [MetadataType](#MetadataType) @@ -3645,7 +3645,6 @@ Gets metadata cache with limited fields and does not store value to disk | --- | --- | --- | | [additionalFields] | Array.<string> | Returns specified fields even if their retrieve definition is not set to true | | [subTypeArr] | Array.<string> | optionally limit to a single subtype | -| [keyArr] | Array.<string> | customer key of single item to retrieve | @@ -6089,7 +6088,7 @@ Verification MetadataType * [Verification](#Verification) ⇐ [MetadataType](#MetadataType) * [.retrieve(retrieveDir, [_], [__], key)](#Verification.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.handleRESTErrors(ex, id)](#Verification.handleRESTErrors) ⇒ null - * [.retrieveForCache([_], [__], [keyArr])](#Verification.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.retrieveForCache()](#Verification.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.create(metadata)](#Verification.create) ⇒ Promise * [.postCreateTasks(metadataEntry, apiResponse, metadataEntryWithAllFields)](#Verification.postCreateTasks) ⇒ void * [.update(metadata)](#Verification.update) ⇒ Promise @@ -6127,18 +6126,11 @@ helper for [this.retrieveRESTcollection](this.retrieveRESTcollection) -### Verification.retrieveForCache([_], [__], [keyArr]) ⇒ Promise.<TYPE.MetadataTypeMapObj> +### Verification.retrieveForCache() ⇒ Promise.<TYPE.MetadataTypeMapObj> Retrieves Metadata of Data Extract Activity for caching **Kind**: static method of [Verification](#Verification) **Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise of metadata - -| Param | Type | Description | -| --- | --- | --- | -| [_] | void | not used | -| [__] | void | not used | -| [keyArr] | Array.<string> | customer key of single item to retrieve | - ### Verification.create(metadata) ⇒ Promise diff --git a/lib/Deployer.js b/lib/Deployer.js index 51ed12328..5b4d283ff 100644 --- a/lib/Deployer.js +++ b/lib/Deployer.js @@ -279,13 +279,7 @@ class Deployer { MetadataTypeInfo[type].buObject = this.buObject; Util.logger.info(`Caching dependent Metadata: ${metadataType}`); Util.logSubtypes(subTypeArr); - const result = await MetadataTypeInfo[type].retrieveForCache( - null, - subTypeArr, - MetadataTypeInfo[type].definition.retrieveRequiresKey - ? Object.keys(this.metadata[type]) - : undefined - ); + const result = await MetadataTypeInfo[type].retrieveForCache(null, subTypeArr); cache.setMetadata(type, result.metadata); } /** @type {TYPE.MultiMetadataTypeMap} */ diff --git a/lib/metadataTypes/MetadataType.js b/lib/metadataTypes/MetadataType.js index 12a824f61..9b71aac6e 100644 --- a/lib/metadataTypes/MetadataType.js +++ b/lib/metadataTypes/MetadataType.js @@ -266,11 +266,10 @@ class MetadataType { * * @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true * @param {string[]} [subTypeArr] optionally limit to a single subtype - * @param {string[]} [keyArr] customer key of single item to retrieve * @returns {Promise.} metadata */ - static async retrieveForCache(additionalFields, subTypeArr, keyArr) { - return this.retrieve(null, additionalFields, subTypeArr, keyArr); + static async retrieveForCache(additionalFields, subTypeArr) { + return this.retrieve(null, additionalFields, subTypeArr); } /** * Gets metadata cache with limited fields and does not store value to disk diff --git a/lib/metadataTypes/Verification.js b/lib/metadataTypes/Verification.js index a628e463b..c4a75069a 100644 --- a/lib/metadataTypes/Verification.js +++ b/lib/metadataTypes/Verification.js @@ -113,25 +113,10 @@ class Verification extends MetadataType { /** * Retrieves Metadata of Data Extract Activity for caching * - * @param {void} [_] not used - * @param {void} [__] not used - * @param {string[]} [keyArr] customer key of single item to retrieve * @returns {Promise.} Promise of metadata */ - static async retrieveForCache(_, __, keyArr) { - const resultArr = await Promise.all( - keyArr.map(async (key) => this.retrieve(null, null, null, key)) - ); - const base = resultArr[0]; - if (resultArr.length > 1) { - base.metadata = resultArr.reduce((acc, cur) => { - if (cur?.metadata) { - acc.metadata = Object.assign(acc.metadata, cur.metadata); - } - return acc; - }).metadata; - } - return base; + static async retrieveForCache() { + return this.retrieve(); } /** diff --git a/lib/metadataTypes/definitions/Verification.definition.js b/lib/metadataTypes/definitions/Verification.definition.js index a9eb290d5..468fc9bc4 100644 --- a/lib/metadataTypes/definitions/Verification.definition.js +++ b/lib/metadataTypes/definitions/Verification.definition.js @@ -11,7 +11,6 @@ module.exports = { lastmodNameField: null, nameField: 'dataVerificationDefinitionId', restPagination: false, - retrieveRequiresKey: true, maxKeyLength: 36, // confirmed max length type: 'verification', typeDescription: 'Check DataExtension for a row count', From 8975371720ece906b05ad29542e21211501f2b15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 23 Aug 2023 10:59:37 +0200 Subject: [PATCH 42/70] #1088: attributeSet cannot find shared data extension folders --- .../definitions/AttributeSet.definition.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/metadataTypes/definitions/AttributeSet.definition.js b/lib/metadataTypes/definitions/AttributeSet.definition.js index 22d196ba0..f29abfd0d 100644 --- a/lib/metadataTypes/definitions/AttributeSet.definition.js +++ b/lib/metadataTypes/definitions/AttributeSet.definition.js @@ -1,6 +1,15 @@ module.exports = { bodyIteratorField: 'setDefinition', - dependencies: ['folder-hidden', 'folder-dataextension', 'dataExtension'], // future may have dependency on Data Extensions + dependencies: [ + 'folder-hidden', + 'folder-dataextension', + 'folder-salesforcedataextension', + 'folder-shared_data', + 'folder-shared_dataextension', + 'folder-shared_salesforcedataextension', + 'folder-synchronizeddataextension', + 'dataExtension', + ], hasExtended: false, idField: 'definitionID', keyIsFixed: null, From 17a00e0cd48d316685225fce176a387170c403cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 23 Aug 2023 11:25:10 +0200 Subject: [PATCH 43/70] #1088: switch attributeSet test to looking into custom attributeSet --- .../attributeSet/retrieve-expected.json | 738 ++++-------------- ...extensionORContentType=hidden-response.xml | 117 +++ test/type.attributeSet.test.js | 2 +- 3 files changed, 263 insertions(+), 594 deletions(-) create mode 100644 test/resources/9999999/dataFolder/retrieve-ContentType=synchronizeddataextensionORContentType=shared_salesforcedataextensionORContentType=shared_dataextensionORContentType=shared_dataORContentType=salesforcedataextensionORContentType=dataextensionORContentType=hidden-response.xml diff --git a/test/resources/9999999/attributeSet/retrieve-expected.json b/test/resources/9999999/attributeSet/retrieve-expected.json index a81a4fe12..ebcfcb66d 100644 --- a/test/resources/9999999/attributeSet/retrieve-expected.json +++ b/test/resources/9999999/attributeSet/retrieve-expected.json @@ -1,746 +1,298 @@ { - "applicationID": "e25893f9-08f3-480f-8def-7f8ab0583611", - "applicationKey": "com.exacttarget.mobileconnect", "attributeCount": 0, - "canAddValues": false, - "canChangeValues": false, - "canModify": false, - "canRemove": false, - "createDate": "2017-01-24T06:33:00", - "createdBy": -1000, + "canAddValues": true, + "canChangeValues": true, + "canModify": true, + "canRemove": true, + "createDate": "2023-08-11T08:22:00", + "createdBy": 700301950, "dataRetentionProperties": { "isDeleteAtEndOfRetentionPeriod": false, "isResetRetentionPeriodOnImport": false, - "isRowBasedRetention": false + "isRowBasedRetention": true, + "periodLength": 6, + "periodUnitOfMeasure": 5 }, - "definitionKey": "MobileSubscriptions", + "definitionKey": "testExisting_dataExtensionShared", "isCustomObjectBacked": true, "isEvent": false, "isHidden": false, "isReadOnly": false, "isRoot": false, - "isSendable": false, - "isShared": false, - "isSystemDefined": true, - "isTestaable": false, - "localizedDescription": {}, - "name": "MobileConnect Subscriptions", + "isSendable": true, + "isShared": true, + "isSystemDefined": false, + "isTestaable": true, + "localizedDescription": { + "value": "Container for my test emails" + }, + "name": "testExisting_dataExtensionShared", "parentID": "00000000-0000-0000-0000-000000000000", - "r__folder_Path": "Data Extensions", - "relationshipCount": 0, - "setDefinitionKey": "MobileSubscriptions", + "r__dataExtension_CustomerKey": "testExisting_dataExtensionShared", + "r__folder_Path": "Shared Items/Shared Data Extensions", + "relationshipCount": 1, + "relationships": [ + { + "canModify": true, + "canRemove": true, + "isGroupToSetRelationship": true, + "isHidden": false, + "isSystemDefined": false, + "leftItem": { + "cardinality": "One", + "r__attributeGroup_definitionKey": "testExisting_attributeGroup", + "relationshipType": "AttributeGroup" + }, + "leftRelationshipIDs": [ + { + "type": "int16", + "value": "3" + } + ], + "leftRelationshipReferenceType": "CustomerData", + "relationshipAttributes": [ + { + "c__rightFullyQualifiedName": "testExisting_dataExtensionShared.ContactKey", + "leftAttributeID": "37778523-13f7-e911-a2d8-1402ec938a35", + "leftConnectingID": { + "identifierType": "FullyQualifiedName" + } + } + ], + "relationshipID": "598b787a-5238-ee11-b85a-48df37d1de8a", + "rightItem": { + "cardinality": "One", + "r__attributeSet_definitionKey": "testExisting_dataExtensionShared", + "relationshipType": "AttributeSet" + } + } + ], + "sendAttributeStorageName": "ContactKey", + "sendContactKeyStorageName": "_SubscriberKey", + "setDefinitionKey": "testExisting_dataExtensionShared", "storageLogicalType": "DataExtension", - "storageName": "_MobileSubscription", - "storageObjectIDs": ["2252de3f-31e2-e611-80cc-1402ec7222b4"], - "storageReferenceID": { - "type": "guid", - "value": "7793dc39-31e2-e611-80cc-1402ec7222b4" - }, + "storageObjectIDs": ["5b8b787a-5238-ee11-b85a-48df37d1de8a"], "valueDefinitions": [ { "baseType": "Numeric", "connectingID": { "identifierType": "FullyQualifiedName" }, - "dataSourceID": 3, "dataSourceName": {}, "dataType": "LongNumber", - "definitionID": "3252de3f-31e2-e611-80cc-1402ec7222b4", - "definitionKey": "CreatedBy", + "definitionID": "548b787a-5238-ee11-b85a-48df37d1de8a", + "definitionKey": "CustomObjectKey", "definitionName": { - "value": "Created By" + "value": "Custom Object Key" }, "description": "", - "displayOrder": 1, - "fullyQualifiedName": "MobileConnect Subscriptions.Created By", + "fullyQualifiedName": "testExisting_dataExtensionShared.Custom Object Key", "isHidden": true, - "isIdentityValue": false, - "isNullable": true, - "isPrimaryKey": false, - "isReadOnly": true, - "isSystemDefined": true, - "isUpdateable": false, - "localizedDescription": { - "value": "" - }, - "name": "Created By", - "obfuscationProperties": { - "maskType": "None", - "maskTypeID": 0, - "storageType": "Plain", - "storageTypeID": 1, - "valueDefinitionID": "3252de3f-31e2-e611-80cc-1402ec7222b4" - }, - "ordinal": 1, - "parentType": "Set", - "setDefinitionName": { - "value": "MobileConnect Subscriptions" - }, - "storageFieldReferenceID": { - "type": "guid", - "value": "a64a3ed9-b8fd-46cc-a10b-1d5fdf074ce6" - }, - "storageName": "_CreatedBy", - "valueDefinitionID": "3252de3f-31e2-e611-80cc-1402ec7222b4", - "valueDefinitionKey": "CreatedBy" - }, - { - "baseType": "Date", - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "dataSourceID": 3, - "dataSourceName": {}, - "dataType": "Date", - "defaultValue": "GETDATE()", - "definitionID": "3352de3f-31e2-e611-80cc-1402ec7222b4", - "definitionKey": "CreatedDate", - "definitionName": { - "value": "Created Date" - }, - "description": "", - "displayOrder": 2, - "fullyQualifiedName": "MobileConnect Subscriptions.Created Date", - "isHidden": false, - "isIdentityValue": false, + "isIdentityValue": true, "isNullable": false, "isPrimaryKey": false, "isReadOnly": true, "isSystemDefined": true, - "isUpdateable": false, + "isUpdateable": true, "localizedDescription": { "value": "" }, - "name": "Created Date", - "obfuscationProperties": { - "maskType": "None", - "maskTypeID": 0, - "storageType": "Plain", - "storageTypeID": 1, - "valueDefinitionID": "3352de3f-31e2-e611-80cc-1402ec7222b4" - }, - "ordinal": 2, + "name": "Custom Object Key", "parentType": "Set", "setDefinitionName": { - "value": "MobileConnect Subscriptions" - }, - "storageFieldReferenceID": { - "type": "guid", - "value": "dfc3f6fb-a250-4ef8-93ed-bb079ced468d" + "value": "testExisting_dataExtensionShared" }, - "storageName": "_CreatedDate", - "valueDefinitionID": "3352de3f-31e2-e611-80cc-1402ec7222b4", - "valueDefinitionKey": "CreatedDate" + "storageName": "_CustomObjectKey", + "valueDefinitionID": "548b787a-5238-ee11-b85a-48df37d1de8a", + "valueDefinitionKey": "CustomObjectKey" }, { "baseType": "Text", "connectingID": { "identifierType": "FullyQualifiedName" }, - "dataSourceID": 3, "dataSourceName": {}, "dataType": "Text", - "definitionID": "3f52de3f-31e2-e611-80cc-1402ec7222b4", - "definitionKey": "SubscriptionDefinitionID", - "definitionName": { - "value": "Keyword" - }, - "description": "", - "displayOrder": 3, - "fullyQualifiedName": "MobileConnect Subscriptions.Keyword", - "isHidden": false, - "isIdentityValue": false, - "isNullable": false, - "isPrimaryKey": true, - "isReadOnly": false, - "isSystemDefined": true, - "isUpdateable": false, - "length": 200, - "localizedDescription": { - "value": "" - }, - "name": "Keyword", - "obfuscationProperties": { - "maskType": "None", - "maskTypeID": 0, - "storageType": "Plain", - "storageTypeID": 1, - "valueDefinitionID": "3f52de3f-31e2-e611-80cc-1402ec7222b4" - }, - "ordinal": 3, - "parentType": "Set", - "restrictionLookupListID": 7, - "setDefinitionName": { - "value": "MobileConnect Subscriptions" - }, - "storageFieldReferenceID": { - "type": "guid", - "value": "c13ab46d-6e65-475a-990d-291bd43e5063" - }, - "storageName": "_SubscriptionDefinitionID", - "valueDefinitionID": "3f52de3f-31e2-e611-80cc-1402ec7222b4", - "valueDefinitionKey": "SubscriptionDefinitionID" - }, - { - "baseType": "Text", - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "dataSourceID": 3, - "dataSourceName": {}, - "dataType": "Phone", - "definitionID": "3452de3f-31e2-e611-80cc-1402ec7222b4", - "definitionKey": "MobileNumber", - "definitionName": { - "value": "Mobile Number" - }, - "description": "", - "displayOrder": 4, - "fullyQualifiedName": "MobileConnect Subscriptions.Mobile Number", - "isHidden": false, - "isIdentityValue": false, - "isNullable": false, - "isPrimaryKey": true, - "isReadOnly": true, - "isSystemDefined": true, - "isUpdateable": false, - "length": 15, - "localizedDescription": { - "value": "" - }, - "name": "Mobile Number", - "obfuscationProperties": { - "maskType": "None", - "maskTypeID": 0, - "storageType": "Plain", - "storageTypeID": 1, - "valueDefinitionID": "3452de3f-31e2-e611-80cc-1402ec7222b4" - }, - "ordinal": 4, - "parentType": "Set", - "setDefinitionName": { - "value": "MobileConnect Subscriptions" - }, - "storageFieldReferenceID": { - "type": "guid", - "value": "ead8a6d2-bc6c-4768-a6e3-c3cc749bfc1d" - }, - "storageName": "_MobileNumber", - "valueDefinitionID": "3452de3f-31e2-e611-80cc-1402ec7222b4", - "valueDefinitionKey": "MobileNumber" - }, - { - "baseType": "Numeric", - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "dataSourceID": 3, - "dataSourceName": {}, - "dataType": "LongNumber", - "definitionID": "53dd5f89-bca1-ed11-b852-48df37d1df5b", - "definitionKey": "MobileSubscriptionID", - "definitionName": { - "value": "Mobile Subscription ID" - }, - "description": "", - "displayOrder": 5, - "fullyQualifiedName": "MobileConnect Subscriptions.Mobile Subscription ID", - "isHidden": true, - "isIdentityValue": true, - "isNullable": false, - "isPrimaryKey": false, - "isReadOnly": true, - "isSystemDefined": true, - "isUpdateable": false, - "localizedDescription": { - "value": "" - }, - "name": "Mobile Subscription ID", - "ordinal": 5, - "parentType": "Set", - "setDefinitionName": { - "value": "MobileConnect Subscriptions" - }, - "storageName": "_MobileSubscriptionID", - "valueDefinitionID": "53dd5f89-bca1-ed11-b852-48df37d1df5b", - "valueDefinitionKey": "MobileSubscriptionID" - }, - { - "baseType": "Numeric", - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "dataSourceID": 3, - "dataSourceName": {}, - "dataType": "LongNumber", - "definitionID": "3552de3f-31e2-e611-80cc-1402ec7222b4", - "definitionKey": "ModifiedBy", - "definitionName": { - "value": "Modified By" - }, - "description": "", - "displayOrder": 6, - "fullyQualifiedName": "MobileConnect Subscriptions.Modified By", - "isHidden": true, - "isIdentityValue": false, - "isNullable": true, - "isPrimaryKey": false, - "isReadOnly": true, - "isSystemDefined": true, - "isUpdateable": false, - "localizedDescription": { - "value": "" - }, - "name": "Modified By", - "obfuscationProperties": { - "maskType": "None", - "maskTypeID": 0, - "storageType": "Plain", - "storageTypeID": 1, - "valueDefinitionID": "3552de3f-31e2-e611-80cc-1402ec7222b4" - }, - "ordinal": 6, - "parentType": "Set", - "setDefinitionName": { - "value": "MobileConnect Subscriptions" - }, - "storageFieldReferenceID": { - "type": "guid", - "value": "30d7ec39-85d1-46dc-9888-771f11ffe10d" - }, - "storageName": "_ModifiedBy", - "valueDefinitionID": "3552de3f-31e2-e611-80cc-1402ec7222b4", - "valueDefinitionKey": "ModifiedBy" - }, - { - "baseType": "Date", - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "dataSourceID": 3, - "dataSourceName": {}, - "dataType": "Date", - "defaultValue": "GETDATE()", - "definitionID": "3652de3f-31e2-e611-80cc-1402ec7222b4", - "definitionKey": "ModifiedDate", - "definitionName": { - "value": "Modified Date" - }, - "description": "", - "displayOrder": 7, - "fullyQualifiedName": "MobileConnect Subscriptions.Modified Date", - "isHidden": false, - "isIdentityValue": false, - "isNullable": false, - "isPrimaryKey": false, - "isReadOnly": true, - "isSystemDefined": true, - "isUpdateable": false, - "localizedDescription": { - "value": "" - }, - "name": "Modified Date", - "obfuscationProperties": { - "maskType": "None", - "maskTypeID": 0, - "storageType": "Plain", - "storageTypeID": 1, - "valueDefinitionID": "3652de3f-31e2-e611-80cc-1402ec7222b4" - }, - "ordinal": 7, - "parentType": "Set", - "setDefinitionName": { - "value": "MobileConnect Subscriptions" - }, - "storageFieldReferenceID": { - "type": "guid", - "value": "b68155cc-e483-4949-82f9-fb47cbd59764" - }, - "storageName": "_ModifiedDate", - "valueDefinitionID": "3652de3f-31e2-e611-80cc-1402ec7222b4", - "valueDefinitionKey": "ModifiedDate" - }, - { - "baseType": "Date", - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "dataSourceID": 3, - "dataSourceName": {}, - "dataType": "Date", - "definitionID": "3752de3f-31e2-e611-80cc-1402ec7222b4", - "definitionKey": "OptInDate", + "definitionID": "588b787a-5238-ee11-b85a-48df37d1de8a", + "definitionKey": "FirstName", "definitionName": { - "value": "Opt In Date" + "value": "FirstName" }, "description": "", - "displayOrder": 8, - "fullyQualifiedName": "MobileConnect Subscriptions.Opt In Date", + "displayOrder": 0, + "fullyQualifiedName": "testExisting_dataExtensionShared.FirstName", "isHidden": false, "isIdentityValue": false, "isNullable": true, "isPrimaryKey": false, - "isReadOnly": true, - "isSystemDefined": true, - "isUpdateable": false, - "localizedDescription": { - "value": "" - }, - "name": "Opt In Date", - "obfuscationProperties": { - "maskType": "None", - "maskTypeID": 0, - "storageType": "Plain", - "storageTypeID": 1, - "valueDefinitionID": "3752de3f-31e2-e611-80cc-1402ec7222b4" - }, - "ordinal": 8, - "parentType": "Set", - "setDefinitionName": { - "value": "MobileConnect Subscriptions" - }, - "storageFieldReferenceID": { - "type": "guid", - "value": "8da557ee-7ba7-44f4-99ba-0681cbeb1ba1" - }, - "storageName": "_OptInDate", - "valueDefinitionID": "3752de3f-31e2-e611-80cc-1402ec7222b4", - "valueDefinitionKey": "OptInDate" - }, - { - "baseType": "Numeric", - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "dataSourceID": 3, - "dataSourceName": {}, - "dataType": "Byte", - "definitionID": "3852de3f-31e2-e611-80cc-1402ec7222b4", - "definitionKey": "OptInMethodID", - "definitionName": { - "value": "Opt In Method" - }, - "description": "", - "displayOrder": 9, - "fullyQualifiedName": "MobileConnect Subscriptions.Opt In Method", - "isHidden": false, - "isIdentityValue": false, - "isNullable": true, - "isPrimaryKey": false, - "isReadOnly": true, - "isSystemDefined": true, - "isUpdateable": false, - "localizedDescription": { - "value": "" - }, - "name": "Opt In Method", - "obfuscationProperties": { - "maskType": "None", - "maskTypeID": 0, - "storageType": "Plain", - "storageTypeID": 1, - "valueDefinitionID": "3852de3f-31e2-e611-80cc-1402ec7222b4" - }, - "ordinal": 9, - "parentType": "Set", - "restrictionLookupListID": 2, - "setDefinitionName": { - "value": "MobileConnect Subscriptions" - }, - "storageFieldReferenceID": { - "type": "guid", - "value": "da2713ab-a802-4cd6-9e05-0a7444d62388" - }, - "storageName": "_OptInMethodID", - "valueDefinitionID": "3852de3f-31e2-e611-80cc-1402ec7222b4", - "valueDefinitionKey": "OptInMethodID" - }, - { - "baseType": "Numeric", - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "dataSourceID": 3, - "dataSourceName": {}, - "dataType": "Byte", - "definitionID": "3952de3f-31e2-e611-80cc-1402ec7222b4", - "definitionKey": "OptInStatusID", - "definitionName": { - "value": "Opt In Status" - }, - "description": "", - "displayOrder": 10, - "fullyQualifiedName": "MobileConnect Subscriptions.Opt In Status", - "isHidden": false, - "isIdentityValue": false, - "isNullable": false, - "isPrimaryKey": false, "isReadOnly": false, - "isSystemDefined": true, - "isUpdateable": false, - "localizedDescription": { - "value": "" - }, - "name": "Opt In Status", - "obfuscationProperties": { - "maskType": "None", - "maskTypeID": 0, - "storageType": "Plain", - "storageTypeID": 1, - "valueDefinitionID": "3952de3f-31e2-e611-80cc-1402ec7222b4" - }, - "ordinal": 10, - "parentType": "Set", - "restrictionLookupListID": 1, - "setDefinitionName": { - "value": "MobileConnect Subscriptions" - }, - "storageFieldReferenceID": { - "type": "guid", - "value": "c733b80d-2ed5-4fb6-94a3-9c996cbae558" - }, - "storageName": "_OptInStatusID", - "valueDefinitionID": "3952de3f-31e2-e611-80cc-1402ec7222b4", - "valueDefinitionKey": "OptInStatusID" - }, - { - "baseType": "Date", - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "dataSourceID": 3, - "dataSourceName": {}, - "dataType": "Date", - "definitionID": "3a52de3f-31e2-e611-80cc-1402ec7222b4", - "definitionKey": "OptOutDate", - "definitionName": { - "value": "Opt Out Date" - }, - "description": "", - "displayOrder": 11, - "fullyQualifiedName": "MobileConnect Subscriptions.Opt Out Date", - "isHidden": false, - "isIdentityValue": false, - "isNullable": true, - "isPrimaryKey": false, - "isReadOnly": true, - "isSystemDefined": true, - "isUpdateable": false, - "localizedDescription": { - "value": "" - }, - "name": "Opt Out Date", - "obfuscationProperties": { - "maskType": "None", - "maskTypeID": 0, - "storageType": "Plain", - "storageTypeID": 1, - "valueDefinitionID": "3a52de3f-31e2-e611-80cc-1402ec7222b4" - }, - "ordinal": 11, - "parentType": "Set", - "setDefinitionName": { - "value": "MobileConnect Subscriptions" - }, - "storageFieldReferenceID": { - "type": "guid", - "value": "27c3ccec-47ad-4578-9051-a41f85178049" - }, - "storageName": "_OptOutDate", - "valueDefinitionID": "3a52de3f-31e2-e611-80cc-1402ec7222b4", - "valueDefinitionKey": "OptOutDate" - }, - { - "baseType": "Numeric", - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "dataSourceID": 3, - "dataSourceName": {}, - "dataType": "Byte", - "definitionID": "3b52de3f-31e2-e611-80cc-1402ec7222b4", - "definitionKey": "OptOutMethodID", - "definitionName": { - "value": "Opt Out Method" - }, - "description": "", - "displayOrder": 12, - "fullyQualifiedName": "MobileConnect Subscriptions.Opt Out Method", - "isHidden": false, - "isIdentityValue": false, - "isNullable": true, - "isPrimaryKey": false, - "isReadOnly": true, - "isSystemDefined": true, - "isUpdateable": false, + "isSystemDefined": false, + "isUpdateable": true, + "length": 50, "localizedDescription": { "value": "" }, - "name": "Opt Out Method", + "name": "FirstName", "obfuscationProperties": { "maskType": "None", "maskTypeID": 0, "storageType": "Plain", "storageTypeID": 1, - "valueDefinitionID": "3b52de3f-31e2-e611-80cc-1402ec7222b4" + "valueDefinitionID": "588b787a-5238-ee11-b85a-48df37d1de8a" }, - "ordinal": 12, + "ordinal": 0, "parentType": "Set", - "restrictionLookupListID": 4, "setDefinitionName": { - "value": "MobileConnect Subscriptions" + "value": "testExisting_dataExtensionShared" }, "storageFieldReferenceID": { "type": "guid", - "value": "089b96fc-bfc6-402e-a481-1e18d3e18a8d" + "value": "391bfc9e-ea85-4610-a24b-d8400a36cdfc" }, - "storageName": "_OptOutMethodID", - "valueDefinitionID": "3b52de3f-31e2-e611-80cc-1402ec7222b4", - "valueDefinitionKey": "OptOutMethodID" + "storageName": "FirstName", + "valueDefinitionID": "588b787a-5238-ee11-b85a-48df37d1de8a", + "valueDefinitionKey": "FirstName" }, { - "baseType": "Numeric", + "baseType": "Text", "connectingID": { "identifierType": "FullyQualifiedName" }, - "dataSourceID": 3, "dataSourceName": {}, - "dataType": "Byte", - "definitionID": "3c52de3f-31e2-e611-80cc-1402ec7222b4", - "definitionKey": "OptOutStatusID", + "dataType": "Text", + "definitionID": "578b787a-5238-ee11-b85a-48df37d1de8a", + "definitionKey": "LastName", "definitionName": { - "value": "Opt Out Status" + "value": "LastName" }, "description": "", - "displayOrder": 13, - "fullyQualifiedName": "MobileConnect Subscriptions.Opt Out Status", + "displayOrder": 1, + "fullyQualifiedName": "testExisting_dataExtensionShared.LastName", "isHidden": false, "isIdentityValue": false, "isNullable": true, "isPrimaryKey": false, "isReadOnly": false, - "isSystemDefined": true, - "isUpdateable": false, + "isSystemDefined": false, + "isUpdateable": true, + "length": 55, "localizedDescription": { "value": "" }, - "name": "Opt Out Status", + "name": "LastName", "obfuscationProperties": { "maskType": "None", "maskTypeID": 0, "storageType": "Plain", "storageTypeID": 1, - "valueDefinitionID": "3c52de3f-31e2-e611-80cc-1402ec7222b4" + "valueDefinitionID": "578b787a-5238-ee11-b85a-48df37d1de8a" }, - "ordinal": 13, + "ordinal": 1, "parentType": "Set", - "restrictionLookupListID": 3, "setDefinitionName": { - "value": "MobileConnect Subscriptions" + "value": "testExisting_dataExtensionShared" }, "storageFieldReferenceID": { "type": "guid", - "value": "67194503-acd8-4c1c-b042-9dfb3f3ebd44" + "value": "3f80ba1f-f957-400f-88cb-a9303491026d" }, - "storageName": "_OptOutStatusID", - "valueDefinitionID": "3c52de3f-31e2-e611-80cc-1402ec7222b4", - "valueDefinitionKey": "OptOutStatusID" + "storageName": "LastName", + "valueDefinitionID": "578b787a-5238-ee11-b85a-48df37d1de8a", + "valueDefinitionKey": "LastName" }, { - "baseType": "Numeric", + "baseType": "Text", "connectingID": { "identifierType": "FullyQualifiedName" }, - "dataSourceID": 3, "dataSourceName": {}, - "dataType": "Byte", - "definitionID": "3d52de3f-31e2-e611-80cc-1402ec7222b4", - "definitionKey": "Source", + "dataType": "EmailAddress", + "definitionID": "568b787a-5238-ee11-b85a-48df37d1de8a", + "definitionKey": "EmailAddress", "definitionName": { - "value": "Source" + "value": "EmailAddress" }, "description": "", - "displayOrder": 14, - "fullyQualifiedName": "MobileConnect Subscriptions.Source", + "displayOrder": 2, + "fullyQualifiedName": "testExisting_dataExtensionShared.EmailAddress", "isHidden": false, "isIdentityValue": false, - "isNullable": true, + "isNullable": false, "isPrimaryKey": false, - "isReadOnly": true, - "isSystemDefined": true, - "isUpdateable": false, + "isReadOnly": false, + "isSystemDefined": false, + "isUpdateable": true, + "length": 254, "localizedDescription": { "value": "" }, - "name": "Source", + "name": "EmailAddress", "obfuscationProperties": { "maskType": "None", "maskTypeID": 0, "storageType": "Plain", "storageTypeID": 1, - "valueDefinitionID": "3d52de3f-31e2-e611-80cc-1402ec7222b4" + "valueDefinitionID": "568b787a-5238-ee11-b85a-48df37d1de8a" }, - "ordinal": 14, + "ordinal": 2, "parentType": "Set", - "restrictionLookupListID": 5, "setDefinitionName": { - "value": "MobileConnect Subscriptions" + "value": "testExisting_dataExtensionShared" }, "storageFieldReferenceID": { "type": "guid", - "value": "2e903257-43e8-4a7b-8ed4-757ca772ddbb" + "value": "41b9575b-da06-41ed-8551-f76868451a51" }, - "storageName": "_Source", - "valueDefinitionID": "3d52de3f-31e2-e611-80cc-1402ec7222b4", - "valueDefinitionKey": "Source" + "storageName": "EmailAddress", + "valueDefinitionID": "568b787a-5238-ee11-b85a-48df37d1de8a", + "valueDefinitionKey": "EmailAddress" }, { "baseType": "Text", "connectingID": { "identifierType": "FullyQualifiedName" }, - "dataSourceID": 3, "dataSourceName": {}, "dataType": "Text", - "definitionID": "3e52de3f-31e2-e611-80cc-1402ec7222b4", - "definitionKey": "SourceObjectID", + "definitionID": "558b787a-5238-ee11-b85a-48df37d1de8a", + "definitionKey": "ContactKey", "definitionName": { - "value": "Source Object ID" + "value": "ContactKey" }, "description": "", - "displayOrder": 15, - "fullyQualifiedName": "MobileConnect Subscriptions.Source Object ID", + "displayOrder": 4, + "fullyQualifiedName": "testExisting_dataExtensionShared.ContactKey", "isHidden": false, "isIdentityValue": false, - "isNullable": true, - "isPrimaryKey": false, - "isReadOnly": true, - "isSystemDefined": true, - "isUpdateable": false, - "length": 200, + "isNullable": false, + "isPrimaryKey": true, + "isReadOnly": false, + "isSystemDefined": false, + "isUpdateable": true, + "length": 50, "localizedDescription": { "value": "" }, - "name": "Source Object ID", + "name": "ContactKey", "obfuscationProperties": { "maskType": "None", "maskTypeID": 0, "storageType": "Plain", "storageTypeID": 1, - "valueDefinitionID": "3e52de3f-31e2-e611-80cc-1402ec7222b4" + "valueDefinitionID": "558b787a-5238-ee11-b85a-48df37d1de8a" }, - "ordinal": 15, + "ordinal": 4, "parentType": "Set", "setDefinitionName": { - "value": "MobileConnect Subscriptions" + "value": "testExisting_dataExtensionShared" }, "storageFieldReferenceID": { "type": "guid", - "value": "1d4f26f5-ec91-46de-9fc1-d819832f07e7" + "value": "49d0db37-dff0-49d9-9d82-eb29b345f238" }, - "storageName": "_SourceObjectId", - "valueDefinitionID": "3e52de3f-31e2-e611-80cc-1402ec7222b4", - "valueDefinitionKey": "SourceObjectID" + "storageName": "ContactKey", + "valueDefinitionID": "558b787a-5238-ee11-b85a-48df37d1de8a", + "valueDefinitionKey": "ContactKey" } ] } diff --git a/test/resources/9999999/dataFolder/retrieve-ContentType=synchronizeddataextensionORContentType=shared_salesforcedataextensionORContentType=shared_dataextensionORContentType=shared_dataORContentType=salesforcedataextensionORContentType=dataextensionORContentType=hidden-response.xml b/test/resources/9999999/dataFolder/retrieve-ContentType=synchronizeddataextensionORContentType=shared_salesforcedataextensionORContentType=shared_dataextensionORContentType=shared_dataORContentType=salesforcedataextensionORContentType=dataextensionORContentType=hidden-response.xml new file mode 100644 index 000000000..d51b42687 --- /dev/null +++ b/test/resources/9999999/dataFolder/retrieve-ContentType=synchronizeddataextensionORContentType=shared_salesforcedataextensionORContentType=shared_dataextensionORContentType=shared_dataORContentType=salesforcedataextensionORContentType=dataextensionORContentType=hidden-response.xml @@ -0,0 +1,117 @@ + + + + RetrieveResponse + urn:uuid:f36f3303-3b5a-4641-8109-b26447634d91 + urn:uuid:33983968-28c4-4379-bb5f-f80ae32eb988 + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + + + 2022-04-19T20:03:41Z + 2022-04-19T20:08:41Z + + + + + + OK + 02cd5ccb-8f84-4651-826f-71169eeecf05 + + + 9999999 + + + 2016-07-22T11:52:19.6 + 2016-07-22T11:52:19.6 + 2 + + dataextension_default + + + 0 + + + Data Extensions + + dataextension + true + false + true + + + + 9999999 + + + 2017-02-08T10:19:37.123 + 2017-02-08T10:19:37.123 + 386 + + + + + 2 + + dataextension_default + + Einstein + + dataextension + true + true + true + + + + 1111111 + + + 2016-07-22T11:52:19.603 + 2016-07-22T11:52:19.603 + 89356 + + shared_dataextension_default + + + 89344 + + shared_data_default + + Shared Data Extensions + + shared_dataextension + true + false + true + + + + 1111111 + + + 2016-07-22T11:52:18.73 + 2016-07-22T11:52:19.603 + 89344 + + shared_data_default + + + 0 + + + Shared Items + + shared_data + true + false + false + + + + + diff --git a/test/type.attributeSet.test.js b/test/type.attributeSet.test.js index a894b7f09..2a55fb9ce 100644 --- a/test/type.attributeSet.test.js +++ b/test/type.attributeSet.test.js @@ -38,7 +38,7 @@ describe('type: attributeSet', () => { ); assert.deepEqual( - await testUtils.getActualJson('MobileSubscriptions', 'attributeSet'), + await testUtils.getActualJson('testExisting_dataExtensionShared', 'attributeSet'), await testUtils.getExpectedJson('9999999', 'attributeSet', 'retrieve'), 'returned metadata was not equal expected' From f363b65b5142a40e8f724c3b722d7e9d4e6aaf06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 23 Aug 2023 12:03:02 +0200 Subject: [PATCH 44/70] #1088: clean up field list of attributeSet --- .../definitions/AttributeSet.definition.js | 52 +++++-- .../attributeSet/retrieve-expected.json | 136 +----------------- 2 files changed, 42 insertions(+), 146 deletions(-) diff --git a/lib/metadataTypes/definitions/AttributeSet.definition.js b/lib/metadataTypes/definitions/AttributeSet.definition.js index f29abfd0d..4c2dcbea8 100644 --- a/lib/metadataTypes/definitions/AttributeSet.definition.js +++ b/lib/metadataTypes/definitions/AttributeSet.definition.js @@ -153,14 +153,14 @@ module.exports = { template: false, }, 'definitionName.value': { - // equal to 'name'; auto-populated by preDeployTasks + // equal to 'name' isCreateable: true, isUpdateable: true, retrieving: false, template: false, }, fullyQualifiedName: { - // equal to 'name'; auto-populated by preDeployTasks + // equal to 'name' isCreateable: true, isUpdateable: true, retrieving: false, @@ -617,9 +617,10 @@ module.exports = { template: null, }, 'valueDefinitions[].baseType': { + // "Numeric", "Text", ... valueDefinitions[].dataType is more relevant isCreateable: null, isUpdateable: null, - retrieving: true, + retrieving: false, template: null, }, 'valueDefinitions[].customerDataID': { @@ -628,6 +629,12 @@ module.exports = { retrieving: true, template: null, }, + 'valueDefinitions[].connectingID': { + isCreateable: null, + isUpdateable: null, + retrieving: false, + template: null, + }, 'valueDefinitions[].dataSourceID': { isCreateable: null, isUpdateable: null, @@ -637,7 +644,7 @@ module.exports = { 'valueDefinitions[].dataSourceName': { isCreateable: null, isUpdateable: null, - retrieving: true, + retrieving: false, template: null, }, 'valueDefinitions[].dataType': { @@ -664,10 +671,11 @@ module.exports = { retrieving: true, template: null, }, - 'valueDefinitions[].definitionName.value': { + 'valueDefinitions[].definitionName': { + // equal to valueDefinitions[].name isCreateable: null, isUpdateable: null, - retrieving: true, + retrieving: false, template: null, }, 'valueDefinitions[].description': { @@ -677,15 +685,17 @@ module.exports = { template: null, }, 'valueDefinitions[].displayOrder': { + // merely a numeric counter; equal to valueDefinitions[].ordinal; not given for isHidden:true entries isCreateable: null, isUpdateable: null, - retrieving: true, + retrieving: false, template: null, }, 'valueDefinitions[].fullyQualifiedName': { + // dataExtension name + field name isCreateable: null, isUpdateable: null, - retrieving: true, + retrieving: false, template: null, }, 'valueDefinitions[].identifierType': { @@ -742,10 +752,11 @@ module.exports = { retrieving: true, template: null, }, - 'valueDefinitions[].localizedDescription.value': { + 'valueDefinitions[].localizedDescription': { + // always equal to { value: "" } isCreateable: null, isUpdateable: null, - retrieving: true, + retrieving: false, template: null, }, 'valueDefinitions[].name': { @@ -754,6 +765,13 @@ module.exports = { retrieving: true, template: null, }, + 'valueDefinitions[].obfuscationProperties': { + // might become relevant when fields are encrypted but for most cases we should simply skip it + isCreateable: null, + isUpdateable: null, + retrieving: false, + template: null, + }, 'valueDefinitions[].obfuscationProperties.maskType': { isCreateable: null, isUpdateable: null, @@ -785,9 +803,10 @@ module.exports = { template: null, }, 'valueDefinitions[].ordinal': { + // merely a numeric counter; equal to valueDefinitions[].displayOrder; not given for isHidden:true entries isCreateable: null, isUpdateable: null, - retrieving: true, + retrieving: false, template: null, }, 'valueDefinitions[].parentDefinition': { @@ -803,9 +822,10 @@ module.exports = { template: null, }, 'valueDefinitions[].parentType': { + // always "Set" isCreateable: null, isUpdateable: null, - retrieving: true, + retrieving: false, template: null, }, 'valueDefinitions[].restrictionLookupListID': { @@ -832,7 +852,7 @@ module.exports = { retrieving: false, template: null, }, - 'valueDefinitions[].setDefinitionName.value': { + 'valueDefinitions[].setDefinitionName': { isCreateable: null, isUpdateable: null, retrieving: false, @@ -857,15 +877,17 @@ module.exports = { template: null, }, 'valueDefinitions[].valueDefinitionID': { + // equal to valueDefinitions[].definitionID isCreateable: null, isUpdateable: null, - retrieving: true, + retrieving: false, template: null, }, 'valueDefinitions[].valueDefinitionKey': { + // equal to valueDefinitions[].definitionKey isCreateable: null, isUpdateable: null, - retrieving: true, + retrieving: false, template: null, }, r__folder_Path: { diff --git a/test/resources/9999999/attributeSet/retrieve-expected.json b/test/resources/9999999/attributeSet/retrieve-expected.json index ebcfcb66d..f0b87afe9 100644 --- a/test/resources/9999999/attributeSet/retrieve-expected.json +++ b/test/resources/9999999/attributeSet/retrieve-expected.json @@ -74,19 +74,10 @@ "storageObjectIDs": ["5b8b787a-5238-ee11-b85a-48df37d1de8a"], "valueDefinitions": [ { - "baseType": "Numeric", - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "dataSourceName": {}, "dataType": "LongNumber", "definitionID": "548b787a-5238-ee11-b85a-48df37d1de8a", "definitionKey": "CustomObjectKey", - "definitionName": { - "value": "Custom Object Key" - }, "description": "", - "fullyQualifiedName": "testExisting_dataExtensionShared.Custom Object Key", "isHidden": true, "isIdentityValue": true, "isNullable": false, @@ -94,33 +85,14 @@ "isReadOnly": true, "isSystemDefined": true, "isUpdateable": true, - "localizedDescription": { - "value": "" - }, "name": "Custom Object Key", - "parentType": "Set", - "setDefinitionName": { - "value": "testExisting_dataExtensionShared" - }, - "storageName": "_CustomObjectKey", - "valueDefinitionID": "548b787a-5238-ee11-b85a-48df37d1de8a", - "valueDefinitionKey": "CustomObjectKey" + "storageName": "_CustomObjectKey" }, { - "baseType": "Text", - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "dataSourceName": {}, "dataType": "Text", "definitionID": "588b787a-5238-ee11-b85a-48df37d1de8a", "definitionKey": "FirstName", - "definitionName": { - "value": "FirstName" - }, "description": "", - "displayOrder": 0, - "fullyQualifiedName": "testExisting_dataExtensionShared.FirstName", "isHidden": false, "isIdentityValue": false, "isNullable": true, @@ -129,45 +101,18 @@ "isSystemDefined": false, "isUpdateable": true, "length": 50, - "localizedDescription": { - "value": "" - }, "name": "FirstName", - "obfuscationProperties": { - "maskType": "None", - "maskTypeID": 0, - "storageType": "Plain", - "storageTypeID": 1, - "valueDefinitionID": "588b787a-5238-ee11-b85a-48df37d1de8a" - }, - "ordinal": 0, - "parentType": "Set", - "setDefinitionName": { - "value": "testExisting_dataExtensionShared" - }, "storageFieldReferenceID": { "type": "guid", "value": "391bfc9e-ea85-4610-a24b-d8400a36cdfc" }, - "storageName": "FirstName", - "valueDefinitionID": "588b787a-5238-ee11-b85a-48df37d1de8a", - "valueDefinitionKey": "FirstName" + "storageName": "FirstName" }, { - "baseType": "Text", - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "dataSourceName": {}, "dataType": "Text", "definitionID": "578b787a-5238-ee11-b85a-48df37d1de8a", "definitionKey": "LastName", - "definitionName": { - "value": "LastName" - }, "description": "", - "displayOrder": 1, - "fullyQualifiedName": "testExisting_dataExtensionShared.LastName", "isHidden": false, "isIdentityValue": false, "isNullable": true, @@ -176,45 +121,18 @@ "isSystemDefined": false, "isUpdateable": true, "length": 55, - "localizedDescription": { - "value": "" - }, "name": "LastName", - "obfuscationProperties": { - "maskType": "None", - "maskTypeID": 0, - "storageType": "Plain", - "storageTypeID": 1, - "valueDefinitionID": "578b787a-5238-ee11-b85a-48df37d1de8a" - }, - "ordinal": 1, - "parentType": "Set", - "setDefinitionName": { - "value": "testExisting_dataExtensionShared" - }, "storageFieldReferenceID": { "type": "guid", "value": "3f80ba1f-f957-400f-88cb-a9303491026d" }, - "storageName": "LastName", - "valueDefinitionID": "578b787a-5238-ee11-b85a-48df37d1de8a", - "valueDefinitionKey": "LastName" + "storageName": "LastName" }, { - "baseType": "Text", - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "dataSourceName": {}, "dataType": "EmailAddress", "definitionID": "568b787a-5238-ee11-b85a-48df37d1de8a", "definitionKey": "EmailAddress", - "definitionName": { - "value": "EmailAddress" - }, "description": "", - "displayOrder": 2, - "fullyQualifiedName": "testExisting_dataExtensionShared.EmailAddress", "isHidden": false, "isIdentityValue": false, "isNullable": false, @@ -223,45 +141,18 @@ "isSystemDefined": false, "isUpdateable": true, "length": 254, - "localizedDescription": { - "value": "" - }, "name": "EmailAddress", - "obfuscationProperties": { - "maskType": "None", - "maskTypeID": 0, - "storageType": "Plain", - "storageTypeID": 1, - "valueDefinitionID": "568b787a-5238-ee11-b85a-48df37d1de8a" - }, - "ordinal": 2, - "parentType": "Set", - "setDefinitionName": { - "value": "testExisting_dataExtensionShared" - }, "storageFieldReferenceID": { "type": "guid", "value": "41b9575b-da06-41ed-8551-f76868451a51" }, - "storageName": "EmailAddress", - "valueDefinitionID": "568b787a-5238-ee11-b85a-48df37d1de8a", - "valueDefinitionKey": "EmailAddress" + "storageName": "EmailAddress" }, { - "baseType": "Text", - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "dataSourceName": {}, "dataType": "Text", "definitionID": "558b787a-5238-ee11-b85a-48df37d1de8a", "definitionKey": "ContactKey", - "definitionName": { - "value": "ContactKey" - }, "description": "", - "displayOrder": 4, - "fullyQualifiedName": "testExisting_dataExtensionShared.ContactKey", "isHidden": false, "isIdentityValue": false, "isNullable": false, @@ -270,29 +161,12 @@ "isSystemDefined": false, "isUpdateable": true, "length": 50, - "localizedDescription": { - "value": "" - }, "name": "ContactKey", - "obfuscationProperties": { - "maskType": "None", - "maskTypeID": 0, - "storageType": "Plain", - "storageTypeID": 1, - "valueDefinitionID": "558b787a-5238-ee11-b85a-48df37d1de8a" - }, - "ordinal": 4, - "parentType": "Set", - "setDefinitionName": { - "value": "testExisting_dataExtensionShared" - }, "storageFieldReferenceID": { "type": "guid", "value": "49d0db37-dff0-49d9-9d82-eb29b345f238" }, - "storageName": "ContactKey", - "valueDefinitionID": "558b787a-5238-ee11-b85a-48df37d1de8a", - "valueDefinitionKey": "ContactKey" + "storageName": "ContactKey" } ] } From 9af8def21ca718194596b1d0b9f4293571de3b39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 23 Aug 2023 13:07:26 +0200 Subject: [PATCH 45/70] #1088: add All Contacts to attributeSet response --- .../attributeSet/retrieve-expected.json | 7 +- .../schema/setDefinitions/get-response.json | 276 +++++++++++++++++- 2 files changed, 277 insertions(+), 6 deletions(-) diff --git a/test/resources/9999999/attributeSet/retrieve-expected.json b/test/resources/9999999/attributeSet/retrieve-expected.json index f0b87afe9..c994c2dec 100644 --- a/test/resources/9999999/attributeSet/retrieve-expected.json +++ b/test/resources/9999999/attributeSet/retrieve-expected.json @@ -52,11 +52,8 @@ "leftRelationshipReferenceType": "CustomerData", "relationshipAttributes": [ { - "c__rightFullyQualifiedName": "testExisting_dataExtensionShared.ContactKey", - "leftAttributeID": "37778523-13f7-e911-a2d8-1402ec938a35", - "leftConnectingID": { - "identifierType": "FullyQualifiedName" - } + "c__leftFullyQualifiedName": "Contact.Contact Key", + "c__rightFullyQualifiedName": "testExisting_dataExtensionShared.ContactKey" } ], "relationshipID": "598b787a-5238-ee11-b85a-48df37d1de8a", diff --git a/test/resources/9999999/hub/v1/contacts/schema/setDefinitions/get-response.json b/test/resources/9999999/hub/v1/contacts/schema/setDefinitions/get-response.json index 54e6f02ab..2826a4f3b 100644 --- a/test/resources/9999999/hub/v1/contacts/schema/setDefinitions/get-response.json +++ b/test/resources/9999999/hub/v1/contacts/schema/setDefinitions/get-response.json @@ -19827,7 +19827,7 @@ "leftRelationshipReferenceType": "CustomerData", "relationshipAttributes": [ { - "leftAttributeID": "37778523-13f7-e911-a2d8-1402ec938a35", + "leftAttributeID": "9503534e-6816-e811-80d0-1402ec7222b5", "leftConnectingID": { "identifierType": "FullyQualifiedName" }, @@ -20180,6 +20180,280 @@ "setDefinitionID": "528b787a-5238-ee11-b85a-48df37d1de8a", "setDefinitionKey": "testExisting_dataExtensionShared", "name": "testExisting_dataExtensionShared" + }, + { + "definitionID": "9203534e-6816-e811-80d0-1402ec7222b5", + "definitionKey": "Contact", + "definitionName": { + "value": "Contact" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + }, + "relationships": [ + { + "canModify": false, + "canRemove": false, + "isHidden": false, + "isSystemDefined": false, + "isGroupToSetRelationship": true, + "leftItem": { + "cardinality": "One", + "connectingID": { + "identifierType": "FullyQualifiedName" + }, + "identifier": "9103534e-6816-e811-80d0-1402ec7222b5", + "relationshipType": "AttributeGroup" + }, + "leftRelationshipIDs": [ + { + "type": "int16", + "value": "2" + } + ], + "leftRelationshipReferenceType": "CustomerData", + "relationshipAttributes": [ + { + "leftAttributeID": "9403534e-6816-e811-80d0-1402ec7222b5", + "leftConnectingID": { + "identifierType": "FullyQualifiedName" + }, + "rightAttributeID": "9403534e-6816-e811-80d0-1402ec7222b5", + "rightConnectingID": { + "identifierType": "FullyQualifiedName" + } + } + ], + "relationshipID": "9a03534e-6816-e811-80d0-1402ec7222b5", + "rightItem": { + "cardinality": "One", + "connectingID": { + "identifierType": "FullyQualifiedName" + }, + "identifier": "9203534e-6816-e811-80d0-1402ec7222b5", + "relationshipType": "AttributeSet" + } + } + ], + "valueDefinitions": [ + { + "baseType": "Numeric", + "customerDataID": 9, + "dataSourceID": 1, + "dataSourceName": {}, + "dataType": "Byte", + "description": "", + "localizedDescription": { + "value": "" + }, + "definitionID": "660eea87-ed21-ee11-b861-48df37d1dc79", + "definitionKey": "Status", + "definitionName": { + "value": "Status" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + }, + "fullyQualifiedName": "Contact.Status", + "isHidden": true, + "isIdentityValue": false, + "isNullable": true, + "isPrimaryKey": false, + "isReadOnly": true, + "isSystemDefined": true, + "isUpdateable": false, + "parentDefinition": { + "definitionID": "9203534e-6816-e811-80d0-1402ec7222b5", + "definitionKey": "Contact", + "definitionName": { + "value": "Contact" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + } + }, + "parentType": "Set", + "storageName": "Status", + "valueDefinitionID": "660eea87-ed21-ee11-b861-48df37d1dc79", + "valueDefinitionKey": "Status", + "name": "Status", + "setDefinitionID": "9203534e-6816-e811-80d0-1402ec7222b5", + "setDefinitionKey": "Contact", + "setDefinitionName": { + "value": "Contact" + }, + "parentIdentifier": "9203534e-6816-e811-80d0-1402ec7222b5" + }, + { + "baseType": "Numeric", + "customerDataID": 1, + "dataSourceID": 1, + "dataSourceName": {}, + "dataType": "Number", + "description": "", + "localizedDescription": { + "value": "" + }, + "definitionID": "9303534e-6816-e811-80d0-1402ec7222b5", + "definitionKey": "BusinessUnitID", + "definitionName": { + "value": "Business Unit ID" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + }, + "fullyQualifiedName": "Contact.Business Unit ID", + "isHidden": true, + "isIdentityValue": false, + "isNullable": false, + "isPrimaryKey": true, + "isReadOnly": true, + "isSystemDefined": true, + "isUpdateable": false, + "parentDefinition": { + "definitionID": "9203534e-6816-e811-80d0-1402ec7222b5", + "definitionKey": "Contact", + "definitionName": { + "value": "Contact" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + } + }, + "parentType": "Set", + "storageName": "ClientID", + "valueDefinitionID": "9303534e-6816-e811-80d0-1402ec7222b5", + "valueDefinitionKey": "BusinessUnitID", + "name": "Business Unit ID", + "setDefinitionID": "9203534e-6816-e811-80d0-1402ec7222b5", + "setDefinitionKey": "Contact", + "setDefinitionName": { + "value": "Contact" + }, + "parentIdentifier": "9203534e-6816-e811-80d0-1402ec7222b5" + }, + { + "baseType": "Text", + "customerDataID": 3, + "dataSourceID": 1, + "dataSourceName": {}, + "dataType": "Text", + "description": "", + "localizedDescription": { + "value": "" + }, + "definitionID": "9503534e-6816-e811-80d0-1402ec7222b5", + "definitionKey": "ContactKey", + "definitionName": { + "value": "Contact Key" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + }, + "fullyQualifiedName": "Contact.Contact Key", + "isHidden": false, + "isIdentityValue": false, + "isNullable": false, + "isPrimaryKey": false, + "isReadOnly": true, + "isSystemDefined": true, + "isUpdateable": false, + "length": -1, + "parentDefinition": { + "definitionID": "9203534e-6816-e811-80d0-1402ec7222b5", + "definitionKey": "Contact", + "definitionName": { + "value": "Contact" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + } + }, + "parentType": "Set", + "storageName": "SubscriberKey", + "valueDefinitionID": "9503534e-6816-e811-80d0-1402ec7222b5", + "valueDefinitionKey": "ContactKey", + "name": "Contact Key", + "setDefinitionID": "9203534e-6816-e811-80d0-1402ec7222b5", + "setDefinitionKey": "Contact", + "setDefinitionName": { + "value": "Contact" + }, + "parentIdentifier": "9203534e-6816-e811-80d0-1402ec7222b5" + }, + { + "baseType": "Numeric", + "customerDataID": 2, + "dataSourceID": 1, + "dataSourceName": {}, + "dataType": "Number", + "description": "", + "localizedDescription": { + "value": "" + }, + "definitionID": "9403534e-6816-e811-80d0-1402ec7222b5", + "definitionKey": "ContactID", + "definitionName": { + "value": "Contact ID" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + }, + "fullyQualifiedName": "Contact.Contact ID", + "isHidden": false, + "isIdentityValue": false, + "isNullable": false, + "isPrimaryKey": true, + "isReadOnly": true, + "isSystemDefined": true, + "isUpdateable": false, + "parentDefinition": { + "definitionID": "9203534e-6816-e811-80d0-1402ec7222b5", + "definitionKey": "Contact", + "definitionName": { + "value": "Contact" + }, + "connectingID": { + "identifierType": "FullyQualifiedName" + } + }, + "parentType": "Set", + "storageName": "SubscriberID", + "valueDefinitionID": "9403534e-6816-e811-80d0-1402ec7222b5", + "valueDefinitionKey": "ContactID", + "name": "Contact ID", + "setDefinitionID": "9203534e-6816-e811-80d0-1402ec7222b5", + "setDefinitionKey": "Contact", + "setDefinitionName": { + "value": "Contact" + }, + "parentIdentifier": "9203534e-6816-e811-80d0-1402ec7222b5" + } + ], + "applicationID": "ce703ed3-e01f-4f5f-900d-76a95b363e29", + "applicationKey": "com.exacttarget.contacts", + "attributeCount": 0, + "canAddValues": false, + "canChangeValues": false, + "canModify": false, + "canRemove": false, + "createdBy": -1000, + "createDate": "2018-02-20T12:03:00", + "localizedDescription": {}, + "fullyQualifiedName": "Contact", + "isCustomObjectBacked": false, + "isEvent": false, + "isHidden": false, + "isReadOnly": true, + "isRoot": false, + "isSendable": false, + "isSystemDefined": true, + "parentID": "00000000-0000-0000-0000-000000000000", + "relationshipCount": 1, + "storageObjectIDs": ["2ba72136-9f31-4a79-ab62-4ba5d19cd759"], + "setDefinitionID": "9203534e-6816-e811-80d0-1402ec7222b5", + "setDefinitionKey": "Contact", + "name": "Contact" } ], "responseContext": { From c970fdf9ad1caee3dcb279974923d43da8951092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 23 Aug 2023 13:47:00 +0200 Subject: [PATCH 46/70] #1088: remove additional fields from retrieve to increase readability --- .../definitions/AttributeSet.definition.js | 14 +- .../attributeSet/retrieve-expected.json | 36 +-- .../schema/setDefinitions/get-response.json | 276 +----------------- 3 files changed, 18 insertions(+), 308 deletions(-) diff --git a/lib/metadataTypes/definitions/AttributeSet.definition.js b/lib/metadataTypes/definitions/AttributeSet.definition.js index 4c2dcbea8..30cc2e8e3 100644 --- a/lib/metadataTypes/definitions/AttributeSet.definition.js +++ b/lib/metadataTypes/definitions/AttributeSet.definition.js @@ -660,9 +660,10 @@ module.exports = { template: null, }, 'valueDefinitions[].definitionID': { + // likely the main ID of the value definition. No use for simple checks on git though as long as we cannot update it isCreateable: null, isUpdateable: null, - retrieving: true, + retrieving: false, template: null, }, 'valueDefinitions[].definitionKey': { @@ -858,22 +859,31 @@ module.exports = { retrieving: false, template: null, }, + 'valueDefinitions[].storageFieldReferenceID': { + isCreateable: null, + isUpdateable: null, + retrieving: false, + template: null, + }, 'valueDefinitions[].storageFieldReferenceID.type': { + // always "guid" isCreateable: null, isUpdateable: null, retrieving: true, template: null, }, 'valueDefinitions[].storageFieldReferenceID.value': { + // unknown GUID isCreateable: null, isUpdateable: null, retrieving: true, template: null, }, 'valueDefinitions[].storageName': { + // always equal valueDefinitions[].definitionKey, except for when that is CustomObjectKey - then this will be _CustomObjectKey (with an underscore) isCreateable: null, isUpdateable: null, - retrieving: true, + retrieving: false, template: null, }, 'valueDefinitions[].valueDefinitionID': { diff --git a/test/resources/9999999/attributeSet/retrieve-expected.json b/test/resources/9999999/attributeSet/retrieve-expected.json index c994c2dec..f24f33133 100644 --- a/test/resources/9999999/attributeSet/retrieve-expected.json +++ b/test/resources/9999999/attributeSet/retrieve-expected.json @@ -72,7 +72,6 @@ "valueDefinitions": [ { "dataType": "LongNumber", - "definitionID": "548b787a-5238-ee11-b85a-48df37d1de8a", "definitionKey": "CustomObjectKey", "description": "", "isHidden": true, @@ -82,12 +81,10 @@ "isReadOnly": true, "isSystemDefined": true, "isUpdateable": true, - "name": "Custom Object Key", - "storageName": "_CustomObjectKey" + "name": "Custom Object Key" }, { "dataType": "Text", - "definitionID": "588b787a-5238-ee11-b85a-48df37d1de8a", "definitionKey": "FirstName", "description": "", "isHidden": false, @@ -98,16 +95,10 @@ "isSystemDefined": false, "isUpdateable": true, "length": 50, - "name": "FirstName", - "storageFieldReferenceID": { - "type": "guid", - "value": "391bfc9e-ea85-4610-a24b-d8400a36cdfc" - }, - "storageName": "FirstName" + "name": "FirstName" }, { "dataType": "Text", - "definitionID": "578b787a-5238-ee11-b85a-48df37d1de8a", "definitionKey": "LastName", "description": "", "isHidden": false, @@ -118,16 +109,10 @@ "isSystemDefined": false, "isUpdateable": true, "length": 55, - "name": "LastName", - "storageFieldReferenceID": { - "type": "guid", - "value": "3f80ba1f-f957-400f-88cb-a9303491026d" - }, - "storageName": "LastName" + "name": "LastName" }, { "dataType": "EmailAddress", - "definitionID": "568b787a-5238-ee11-b85a-48df37d1de8a", "definitionKey": "EmailAddress", "description": "", "isHidden": false, @@ -138,16 +123,10 @@ "isSystemDefined": false, "isUpdateable": true, "length": 254, - "name": "EmailAddress", - "storageFieldReferenceID": { - "type": "guid", - "value": "41b9575b-da06-41ed-8551-f76868451a51" - }, - "storageName": "EmailAddress" + "name": "EmailAddress" }, { "dataType": "Text", - "definitionID": "558b787a-5238-ee11-b85a-48df37d1de8a", "definitionKey": "ContactKey", "description": "", "isHidden": false, @@ -158,12 +137,7 @@ "isSystemDefined": false, "isUpdateable": true, "length": 50, - "name": "ContactKey", - "storageFieldReferenceID": { - "type": "guid", - "value": "49d0db37-dff0-49d9-9d82-eb29b345f238" - }, - "storageName": "ContactKey" + "name": "ContactKey" } ] } diff --git a/test/resources/9999999/hub/v1/contacts/schema/setDefinitions/get-response.json b/test/resources/9999999/hub/v1/contacts/schema/setDefinitions/get-response.json index 2826a4f3b..aef3bea05 100644 --- a/test/resources/9999999/hub/v1/contacts/schema/setDefinitions/get-response.json +++ b/test/resources/9999999/hub/v1/contacts/schema/setDefinitions/get-response.json @@ -19827,7 +19827,7 @@ "leftRelationshipReferenceType": "CustomerData", "relationshipAttributes": [ { - "leftAttributeID": "9503534e-6816-e811-80d0-1402ec7222b5", + "leftAttributeID": "9393dc39-31e2-e611-80cc-1402ec7222b4", "leftConnectingID": { "identifierType": "FullyQualifiedName" }, @@ -20180,280 +20180,6 @@ "setDefinitionID": "528b787a-5238-ee11-b85a-48df37d1de8a", "setDefinitionKey": "testExisting_dataExtensionShared", "name": "testExisting_dataExtensionShared" - }, - { - "definitionID": "9203534e-6816-e811-80d0-1402ec7222b5", - "definitionKey": "Contact", - "definitionName": { - "value": "Contact" - }, - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "relationships": [ - { - "canModify": false, - "canRemove": false, - "isHidden": false, - "isSystemDefined": false, - "isGroupToSetRelationship": true, - "leftItem": { - "cardinality": "One", - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "identifier": "9103534e-6816-e811-80d0-1402ec7222b5", - "relationshipType": "AttributeGroup" - }, - "leftRelationshipIDs": [ - { - "type": "int16", - "value": "2" - } - ], - "leftRelationshipReferenceType": "CustomerData", - "relationshipAttributes": [ - { - "leftAttributeID": "9403534e-6816-e811-80d0-1402ec7222b5", - "leftConnectingID": { - "identifierType": "FullyQualifiedName" - }, - "rightAttributeID": "9403534e-6816-e811-80d0-1402ec7222b5", - "rightConnectingID": { - "identifierType": "FullyQualifiedName" - } - } - ], - "relationshipID": "9a03534e-6816-e811-80d0-1402ec7222b5", - "rightItem": { - "cardinality": "One", - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "identifier": "9203534e-6816-e811-80d0-1402ec7222b5", - "relationshipType": "AttributeSet" - } - } - ], - "valueDefinitions": [ - { - "baseType": "Numeric", - "customerDataID": 9, - "dataSourceID": 1, - "dataSourceName": {}, - "dataType": "Byte", - "description": "", - "localizedDescription": { - "value": "" - }, - "definitionID": "660eea87-ed21-ee11-b861-48df37d1dc79", - "definitionKey": "Status", - "definitionName": { - "value": "Status" - }, - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "fullyQualifiedName": "Contact.Status", - "isHidden": true, - "isIdentityValue": false, - "isNullable": true, - "isPrimaryKey": false, - "isReadOnly": true, - "isSystemDefined": true, - "isUpdateable": false, - "parentDefinition": { - "definitionID": "9203534e-6816-e811-80d0-1402ec7222b5", - "definitionKey": "Contact", - "definitionName": { - "value": "Contact" - }, - "connectingID": { - "identifierType": "FullyQualifiedName" - } - }, - "parentType": "Set", - "storageName": "Status", - "valueDefinitionID": "660eea87-ed21-ee11-b861-48df37d1dc79", - "valueDefinitionKey": "Status", - "name": "Status", - "setDefinitionID": "9203534e-6816-e811-80d0-1402ec7222b5", - "setDefinitionKey": "Contact", - "setDefinitionName": { - "value": "Contact" - }, - "parentIdentifier": "9203534e-6816-e811-80d0-1402ec7222b5" - }, - { - "baseType": "Numeric", - "customerDataID": 1, - "dataSourceID": 1, - "dataSourceName": {}, - "dataType": "Number", - "description": "", - "localizedDescription": { - "value": "" - }, - "definitionID": "9303534e-6816-e811-80d0-1402ec7222b5", - "definitionKey": "BusinessUnitID", - "definitionName": { - "value": "Business Unit ID" - }, - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "fullyQualifiedName": "Contact.Business Unit ID", - "isHidden": true, - "isIdentityValue": false, - "isNullable": false, - "isPrimaryKey": true, - "isReadOnly": true, - "isSystemDefined": true, - "isUpdateable": false, - "parentDefinition": { - "definitionID": "9203534e-6816-e811-80d0-1402ec7222b5", - "definitionKey": "Contact", - "definitionName": { - "value": "Contact" - }, - "connectingID": { - "identifierType": "FullyQualifiedName" - } - }, - "parentType": "Set", - "storageName": "ClientID", - "valueDefinitionID": "9303534e-6816-e811-80d0-1402ec7222b5", - "valueDefinitionKey": "BusinessUnitID", - "name": "Business Unit ID", - "setDefinitionID": "9203534e-6816-e811-80d0-1402ec7222b5", - "setDefinitionKey": "Contact", - "setDefinitionName": { - "value": "Contact" - }, - "parentIdentifier": "9203534e-6816-e811-80d0-1402ec7222b5" - }, - { - "baseType": "Text", - "customerDataID": 3, - "dataSourceID": 1, - "dataSourceName": {}, - "dataType": "Text", - "description": "", - "localizedDescription": { - "value": "" - }, - "definitionID": "9503534e-6816-e811-80d0-1402ec7222b5", - "definitionKey": "ContactKey", - "definitionName": { - "value": "Contact Key" - }, - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "fullyQualifiedName": "Contact.Contact Key", - "isHidden": false, - "isIdentityValue": false, - "isNullable": false, - "isPrimaryKey": false, - "isReadOnly": true, - "isSystemDefined": true, - "isUpdateable": false, - "length": -1, - "parentDefinition": { - "definitionID": "9203534e-6816-e811-80d0-1402ec7222b5", - "definitionKey": "Contact", - "definitionName": { - "value": "Contact" - }, - "connectingID": { - "identifierType": "FullyQualifiedName" - } - }, - "parentType": "Set", - "storageName": "SubscriberKey", - "valueDefinitionID": "9503534e-6816-e811-80d0-1402ec7222b5", - "valueDefinitionKey": "ContactKey", - "name": "Contact Key", - "setDefinitionID": "9203534e-6816-e811-80d0-1402ec7222b5", - "setDefinitionKey": "Contact", - "setDefinitionName": { - "value": "Contact" - }, - "parentIdentifier": "9203534e-6816-e811-80d0-1402ec7222b5" - }, - { - "baseType": "Numeric", - "customerDataID": 2, - "dataSourceID": 1, - "dataSourceName": {}, - "dataType": "Number", - "description": "", - "localizedDescription": { - "value": "" - }, - "definitionID": "9403534e-6816-e811-80d0-1402ec7222b5", - "definitionKey": "ContactID", - "definitionName": { - "value": "Contact ID" - }, - "connectingID": { - "identifierType": "FullyQualifiedName" - }, - "fullyQualifiedName": "Contact.Contact ID", - "isHidden": false, - "isIdentityValue": false, - "isNullable": false, - "isPrimaryKey": true, - "isReadOnly": true, - "isSystemDefined": true, - "isUpdateable": false, - "parentDefinition": { - "definitionID": "9203534e-6816-e811-80d0-1402ec7222b5", - "definitionKey": "Contact", - "definitionName": { - "value": "Contact" - }, - "connectingID": { - "identifierType": "FullyQualifiedName" - } - }, - "parentType": "Set", - "storageName": "SubscriberID", - "valueDefinitionID": "9403534e-6816-e811-80d0-1402ec7222b5", - "valueDefinitionKey": "ContactID", - "name": "Contact ID", - "setDefinitionID": "9203534e-6816-e811-80d0-1402ec7222b5", - "setDefinitionKey": "Contact", - "setDefinitionName": { - "value": "Contact" - }, - "parentIdentifier": "9203534e-6816-e811-80d0-1402ec7222b5" - } - ], - "applicationID": "ce703ed3-e01f-4f5f-900d-76a95b363e29", - "applicationKey": "com.exacttarget.contacts", - "attributeCount": 0, - "canAddValues": false, - "canChangeValues": false, - "canModify": false, - "canRemove": false, - "createdBy": -1000, - "createDate": "2018-02-20T12:03:00", - "localizedDescription": {}, - "fullyQualifiedName": "Contact", - "isCustomObjectBacked": false, - "isEvent": false, - "isHidden": false, - "isReadOnly": true, - "isRoot": false, - "isSendable": false, - "isSystemDefined": true, - "parentID": "00000000-0000-0000-0000-000000000000", - "relationshipCount": 1, - "storageObjectIDs": ["2ba72136-9f31-4a79-ab62-4ba5d19cd759"], - "setDefinitionID": "9203534e-6816-e811-80d0-1402ec7222b5", - "setDefinitionKey": "Contact", - "name": "Contact" } ], "responseContext": { From bef0105af07326a47f4f7a72799062c69eeb4bce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 23 Aug 2023 14:08:28 +0200 Subject: [PATCH 47/70] #1090: make attributeGroup and attributeSet be retrieved by default --- lib/metadataTypes/definitions/AttributeGroup.definition.js | 4 ++-- lib/metadataTypes/definitions/AttributeSet.definition.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/metadataTypes/definitions/AttributeGroup.definition.js b/lib/metadataTypes/definitions/AttributeGroup.definition.js index 2dc24a507..0138541b5 100644 --- a/lib/metadataTypes/definitions/AttributeGroup.definition.js +++ b/lib/metadataTypes/definitions/AttributeGroup.definition.js @@ -8,8 +8,8 @@ module.exports = { nameField: 'definitionName.value', restPagination: false, // Hub API does not support pagination and returns everything instead type: 'attributeGroup', - typeDescription: 'BETA: Groupings of Set Definitions (Data Extensions) in Data Designer.', - typeRetrieveByDefault: false, + typeDescription: 'Groupings of Attribute Sets (Data Extensions) in Data Designer.', + typeRetrieveByDefault: true, typeName: 'Data Designer Attribute Groups', fields: { applicationID: { diff --git a/lib/metadataTypes/definitions/AttributeSet.definition.js b/lib/metadataTypes/definitions/AttributeSet.definition.js index 30cc2e8e3..b771421c5 100644 --- a/lib/metadataTypes/definitions/AttributeSet.definition.js +++ b/lib/metadataTypes/definitions/AttributeSet.definition.js @@ -22,9 +22,9 @@ module.exports = { lastmodNameField: null, restPagination: false, // Hub API does not support pagination and returns everything instead type: 'attributeSet', - typeDescription: 'BETA: Data Extensions linked to Attribute Groups in Data Designer.', - typeRetrieveByDefault: false, - typeName: 'Data Designer Set Definitions', + typeDescription: 'Data Extensions linked together in Attribute Groups in Data Designer.', + typeRetrieveByDefault: true, + typeName: 'Data Designer Attribute Sets', fields: { applicationID: { isCreateable: null, From 87dffd61e8dada3f770cb481e0133e4e1e1062ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 24 Aug 2023 14:10:39 +0200 Subject: [PATCH 48/70] #325: added tests for type verification --- docs/dist/documentation.md | 13 +- lib/metadataTypes/MetadataType.js | 14 +- lib/metadataTypes/Verification.js | 6 +- .../definitions/Verification.definition.js | 9 +- ...a488-20eb-4ba0-b0b9.verification-meta.json | 11 ++ ...a488-20eb-4ba0-b0b9.verification-meta.json | 11 ++ .../get-response.json | 7 + .../v1/dataverifications/post-response.json | 12 ++ .../delete-response.json | 0 .../get-response.json | 12 ++ .../patch-response.json | 12 ++ .../9999999/verification/build-expected.json | 11 ++ .../9999999/verification/get-expected.json | 11 ++ .../9999999/verification/patch-expected.json | 11 ++ .../9999999/verification/post-expected.json | 11 ++ .../verification/template-expected.json | 11 ++ test/type.verification.test.js | 171 ++++++++++++++++++ 17 files changed, 322 insertions(+), 11 deletions(-) create mode 100644 test/mockRoot/deploy/testInstance/testBU/verification/testExisting_39f6a488-20eb-4ba0-b0b9.verification-meta.json create mode 100644 test/mockRoot/deploy/testInstance/testBU/verification/testNew_39f6a488-20eb-4ba0-b0b9.verification-meta.json create mode 100644 test/resources/9999999/automation/v1/dataverifications/post-response.json create mode 100644 test/resources/9999999/automation/v1/dataverifications/testExisting_39f6a488-20eb-4ba0-b0b9/delete-response.json create mode 100644 test/resources/9999999/automation/v1/dataverifications/testExisting_39f6a488-20eb-4ba0-b0b9/get-response.json create mode 100644 test/resources/9999999/automation/v1/dataverifications/testExisting_39f6a488-20eb-4ba0-b0b9/patch-response.json create mode 100644 test/resources/9999999/verification/build-expected.json create mode 100644 test/resources/9999999/verification/get-expected.json create mode 100644 test/resources/9999999/verification/patch-expected.json create mode 100644 test/resources/9999999/verification/post-expected.json create mode 100644 test/resources/9999999/verification/template-expected.json create mode 100644 test/type.verification.test.js diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 404c2f588..1bffd85d8 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -3431,7 +3431,7 @@ Provides default functionality that can be overwritten by child metadata type cl * [.getSOAPErrorMsg(ex)](#MetadataType.getSOAPErrorMsg) ⇒ string * [.retrieveSOAP(retrieveDir, [requestParams], [singleRetrieve], [additionalFields])](#MetadataType.retrieveSOAP) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.retrieveREST(retrieveDir, uri, [templateVariables], [singleRetrieve])](#MetadataType.retrieveREST) ⇒ Promise.<{metadata: (TYPE.MetadataTypeMap\|TYPE.MetadataTypeItem), type: string}> - * [.retrieveRESTcollection(urlArray, [concurrentRequests])](#MetadataType.retrieveRESTcollection) ⇒ Promise.<{metadata: (TYPE.MetadataTypeMap\|TYPE.MetadataTypeItem), type: string}> + * [.retrieveRESTcollection(urlArray, [concurrentRequests], [logAmountOfUrls])](#MetadataType.retrieveRESTcollection) ⇒ Promise.<{metadata: (TYPE.MetadataTypeMap\|TYPE.MetadataTypeItem), type: string}> * [.handleRESTErrors(ex, id)](#MetadataType.handleRESTErrors) ⇒ null * [.executeREST(uri, key)](#MetadataType.executeREST) ⇒ Promise.<{key:string, response:string}> * [.executeSOAP([metadataEntry])](#MetadataType.executeSOAP) ⇒ Promise.<{key:string, response:object}> @@ -3913,14 +3913,15 @@ Retrieves Metadata for Rest Types -### MetadataType.retrieveRESTcollection(urlArray, [concurrentRequests]) ⇒ Promise.<{metadata: (TYPE.MetadataTypeMap\|TYPE.MetadataTypeItem), type: string}> +### MetadataType.retrieveRESTcollection(urlArray, [concurrentRequests], [logAmountOfUrls]) ⇒ Promise.<{metadata: (TYPE.MetadataTypeMap\|TYPE.MetadataTypeItem), type: string}> **Kind**: static method of [MetadataType](#MetadataType) **Returns**: Promise.<{metadata: (TYPE.MetadataTypeMap\|TYPE.MetadataTypeItem), type: string}> - Promise of item map (single item for templated result) -| Param | Type | Description | -| --- | --- | --- | -| urlArray | Array.<object> | {uri: string, id: string} combo of URL and ID/key of metadata | -| [concurrentRequests] | number | optionally set a different amount of concurrent requests | +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| urlArray | Array.<object> | | {uri: string, id: string} combo of URL and ID/key of metadata | +| [concurrentRequests] | number | 10 | optionally set a different amount of concurrent requests | +| [logAmountOfUrls] | boolean | true | if true, prints an info message about to-be loaded amount of metadata | diff --git a/lib/metadataTypes/MetadataType.js b/lib/metadataTypes/MetadataType.js index 9b71aac6e..5db7f9a1a 100644 --- a/lib/metadataTypes/MetadataType.js +++ b/lib/metadataTypes/MetadataType.js @@ -1094,10 +1094,20 @@ class MetadataType { * * @param {object[]} urlArray {uri: string, id: string} combo of URL and ID/key of metadata * @param {number} [concurrentRequests] optionally set a different amount of concurrent requests + * @param {boolean} [logAmountOfUrls] if true, prints an info message about to-be loaded amount of metadata * @returns {Promise.<{metadata: (TYPE.MetadataTypeMap | TYPE.MetadataTypeItem), type: string}>} Promise of item map (single item for templated result) */ - static async retrieveRESTcollection(urlArray, concurrentRequests) { - const rateLimit = pLimit(concurrentRequests || 10); + static async retrieveRESTcollection(urlArray, concurrentRequests = 10, logAmountOfUrls = true) { + if (logAmountOfUrls) { + Util.logger.info( + Util.getGrayMsg( + ` - ${urlArray?.length} ${this.definition.type}${ + urlArray?.length === 1 ? '' : 's' + } found. Retrieving details...` + ) + ); + } + const rateLimit = pLimit(concurrentRequests); const metadataArr = urlArray.length ? await Promise.all( diff --git a/lib/metadataTypes/Verification.js b/lib/metadataTypes/Verification.js index c4a75069a..891b9382f 100644 --- a/lib/metadataTypes/Verification.js +++ b/lib/metadataTypes/Verification.js @@ -69,7 +69,9 @@ class Verification extends MetadataType { const results = {}; if (paramArr.length) { const response = await this.retrieveRESTcollection( - paramArr.map((id) => ({ id, uri: '/automation/v1/dataverifications/' + id })) + paramArr.map((id) => ({ id, uri: '/automation/v1/dataverifications/' + id })), + undefined, + !key ); if (response?.metadata) { Object.assign(results, response.metadata); @@ -160,7 +162,7 @@ class Verification extends MetadataType { static update(metadata) { return super.updateREST( metadata, - '/automation/v1/dataverifications/' + metadata.verificationDefinitionId + '/automation/v1/dataverifications/' + metadata.dataVerificationDefinitionId ); } diff --git a/lib/metadataTypes/definitions/Verification.definition.js b/lib/metadataTypes/definitions/Verification.definition.js index 468fc9bc4..25e63ac5d 100644 --- a/lib/metadataTypes/definitions/Verification.definition.js +++ b/lib/metadataTypes/definitions/Verification.definition.js @@ -18,9 +18,10 @@ module.exports = { typeName: 'Automation: Data Verification Activity', fields: { createdBy: { + // User ID isCreateable: false, isUpdateable: false, - retrieving: true, + retrieving: false, template: false, }, dataVerificationDefinitionId: { @@ -77,5 +78,11 @@ module.exports = { retrieving: true, template: true, }, + r__dataExtension_CustomerKey: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: true, + }, }, }; diff --git a/test/mockRoot/deploy/testInstance/testBU/verification/testExisting_39f6a488-20eb-4ba0-b0b9.verification-meta.json b/test/mockRoot/deploy/testInstance/testBU/verification/testExisting_39f6a488-20eb-4ba0-b0b9.verification-meta.json new file mode 100644 index 000000000..dc8d975d2 --- /dev/null +++ b/test/mockRoot/deploy/testInstance/testBU/verification/testExisting_39f6a488-20eb-4ba0-b0b9.verification-meta.json @@ -0,0 +1,11 @@ +{ + "dataVerificationDefinitionId": "testExisting_39f6a488-20eb-4ba0-b0b9", + "notificationEmailAddress": "test@accenture.com", + "notificationEmailMessage": "", + "r__dataExtension_CustomerKey": "testExisting_dataExtension", + "shouldEmailOnFailure": true, + "shouldStopOnFailure": true, + "value1": 1, + "value2": 0, + "verificationType": "IsEqualTo" +} diff --git a/test/mockRoot/deploy/testInstance/testBU/verification/testNew_39f6a488-20eb-4ba0-b0b9.verification-meta.json b/test/mockRoot/deploy/testInstance/testBU/verification/testNew_39f6a488-20eb-4ba0-b0b9.verification-meta.json new file mode 100644 index 000000000..ee5c6ed27 --- /dev/null +++ b/test/mockRoot/deploy/testInstance/testBU/verification/testNew_39f6a488-20eb-4ba0-b0b9.verification-meta.json @@ -0,0 +1,11 @@ +{ + "dataVerificationDefinitionId": "testNew_39f6a488-20eb-4ba0-b0b9", + "notificationEmailAddress": "", + "notificationEmailMessage": "", + "r__dataExtension_CustomerKey": "testExisting_dataExtension", + "shouldEmailOnFailure": false, + "shouldStopOnFailure": false, + "value1": 2, + "value2": 0, + "verificationType": "IsEqualTo" +} diff --git a/test/resources/9999999/automation/v1/automations/08afb0e2-b00a-4c88-ad2e-1f7f8788c560/get-response.json b/test/resources/9999999/automation/v1/automations/08afb0e2-b00a-4c88-ad2e-1f7f8788c560/get-response.json index 5d86c87b2..3c982a924 100644 --- a/test/resources/9999999/automation/v1/automations/08afb0e2-b00a-4c88-ad2e-1f7f8788c560/get-response.json +++ b/test/resources/9999999/automation/v1/automations/08afb0e2-b00a-4c88-ad2e-1f7f8788c560/get-response.json @@ -78,6 +78,13 @@ "activityObjectId": "39f6a488-20eb-4ba0-b0b9-023725b574e4", "objectTypeId": 423, "displayOrder": 6 + }, + { + "id": "f3774dc2-a271-4a44-8cbe-f630a6d6545e", + "name": "testExisting_dataExtension", + "activityObjectId": "testExisting_39f6a488-20eb-4ba0-b0b9", + "objectTypeId": 1000, + "displayOrder": 7 } ] } diff --git a/test/resources/9999999/automation/v1/dataverifications/post-response.json b/test/resources/9999999/automation/v1/dataverifications/post-response.json new file mode 100644 index 000000000..96d88a20e --- /dev/null +++ b/test/resources/9999999/automation/v1/dataverifications/post-response.json @@ -0,0 +1,12 @@ +{ + "dataVerificationDefinitionId": "testNew_RANDOM_NEW_GUID", + "targetObjectId": "21711373-72c1-ec11-b83b-48df37d1deb7", + "verificationType": "IsEqualTo", + "value1": 2, + "value2": 0, + "shouldStopOnFailure": false, + "shouldEmailOnFailure": false, + "notificationEmailAddress": "", + "notificationEmailMessage": "", + "createdBy": 700301950 +} diff --git a/test/resources/9999999/automation/v1/dataverifications/testExisting_39f6a488-20eb-4ba0-b0b9/delete-response.json b/test/resources/9999999/automation/v1/dataverifications/testExisting_39f6a488-20eb-4ba0-b0b9/delete-response.json new file mode 100644 index 000000000..e69de29bb diff --git a/test/resources/9999999/automation/v1/dataverifications/testExisting_39f6a488-20eb-4ba0-b0b9/get-response.json b/test/resources/9999999/automation/v1/dataverifications/testExisting_39f6a488-20eb-4ba0-b0b9/get-response.json new file mode 100644 index 000000000..6b6d4101e --- /dev/null +++ b/test/resources/9999999/automation/v1/dataverifications/testExisting_39f6a488-20eb-4ba0-b0b9/get-response.json @@ -0,0 +1,12 @@ +{ + "dataVerificationDefinitionId": "testExisting_39f6a488-20eb-4ba0-b0b9", + "targetObjectId": "21711373-72c1-ec11-b83b-48df37d1deb7", + "verificationType": "IsEqualTo", + "value1": 1, + "value2": 0, + "shouldStopOnFailure": true, + "shouldEmailOnFailure": false, + "notificationEmailAddress": "", + "notificationEmailMessage": "", + "createdBy": 700301950 +} diff --git a/test/resources/9999999/automation/v1/dataverifications/testExisting_39f6a488-20eb-4ba0-b0b9/patch-response.json b/test/resources/9999999/automation/v1/dataverifications/testExisting_39f6a488-20eb-4ba0-b0b9/patch-response.json new file mode 100644 index 000000000..5d7c8d336 --- /dev/null +++ b/test/resources/9999999/automation/v1/dataverifications/testExisting_39f6a488-20eb-4ba0-b0b9/patch-response.json @@ -0,0 +1,12 @@ +{ + "dataVerificationDefinitionId": "testExisting_39f6a488-20eb-4ba0-b0b9", + "targetObjectId": "21711373-72c1-ec11-b83b-48df37d1deb7", + "verificationType": "IsEqualTo", + "value1": 1, + "value2": 0, + "shouldStopOnFailure": true, + "shouldEmailOnFailure": true, + "notificationEmailAddress": "test@accenture.com", + "notificationEmailMessage": "", + "createdBy": 700301950 +} diff --git a/test/resources/9999999/verification/build-expected.json b/test/resources/9999999/verification/build-expected.json new file mode 100644 index 000000000..f717cc626 --- /dev/null +++ b/test/resources/9999999/verification/build-expected.json @@ -0,0 +1,11 @@ +{ + "dataVerificationDefinitionId": "testTemplated_39f6a488-20eb-4ba0-b0b9", + "notificationEmailAddress": "", + "notificationEmailMessage": "", + "r__dataExtension_CustomerKey": "testTemplated_dataExtension", + "shouldEmailOnFailure": false, + "shouldStopOnFailure": true, + "value1": 1, + "value2": 0, + "verificationType": "IsEqualTo" +} diff --git a/test/resources/9999999/verification/get-expected.json b/test/resources/9999999/verification/get-expected.json new file mode 100644 index 000000000..1f9c1825b --- /dev/null +++ b/test/resources/9999999/verification/get-expected.json @@ -0,0 +1,11 @@ +{ + "dataVerificationDefinitionId": "testExisting_39f6a488-20eb-4ba0-b0b9", + "notificationEmailAddress": "", + "notificationEmailMessage": "", + "r__dataExtension_CustomerKey": "testExisting_dataExtension", + "shouldEmailOnFailure": false, + "shouldStopOnFailure": true, + "value1": 1, + "value2": 0, + "verificationType": "IsEqualTo" +} diff --git a/test/resources/9999999/verification/patch-expected.json b/test/resources/9999999/verification/patch-expected.json new file mode 100644 index 000000000..dc8d975d2 --- /dev/null +++ b/test/resources/9999999/verification/patch-expected.json @@ -0,0 +1,11 @@ +{ + "dataVerificationDefinitionId": "testExisting_39f6a488-20eb-4ba0-b0b9", + "notificationEmailAddress": "test@accenture.com", + "notificationEmailMessage": "", + "r__dataExtension_CustomerKey": "testExisting_dataExtension", + "shouldEmailOnFailure": true, + "shouldStopOnFailure": true, + "value1": 1, + "value2": 0, + "verificationType": "IsEqualTo" +} diff --git a/test/resources/9999999/verification/post-expected.json b/test/resources/9999999/verification/post-expected.json new file mode 100644 index 000000000..b06a77b76 --- /dev/null +++ b/test/resources/9999999/verification/post-expected.json @@ -0,0 +1,11 @@ +{ + "dataVerificationDefinitionId": "testNew_RANDOM_NEW_GUID", + "r__dataExtension_CustomerKey": "testExisting_dataExtension", + "verificationType": "IsEqualTo", + "value1": 2, + "value2": 0, + "shouldStopOnFailure": false, + "shouldEmailOnFailure": false, + "notificationEmailAddress": "", + "notificationEmailMessage": "" +} diff --git a/test/resources/9999999/verification/template-expected.json b/test/resources/9999999/verification/template-expected.json new file mode 100644 index 000000000..04296b7ea --- /dev/null +++ b/test/resources/9999999/verification/template-expected.json @@ -0,0 +1,11 @@ +{ + "dataVerificationDefinitionId": "{{{prefix}}}39f6a488-20eb-4ba0-b0b9", + "notificationEmailAddress": "", + "notificationEmailMessage": "", + "r__dataExtension_CustomerKey": "{{{prefix}}}dataExtension", + "shouldEmailOnFailure": false, + "shouldStopOnFailure": true, + "value1": 1, + "value2": 0, + "verificationType": "IsEqualTo" +} diff --git a/test/type.verification.test.js b/test/type.verification.test.js new file mode 100644 index 000000000..993f26095 --- /dev/null +++ b/test/type.verification.test.js @@ -0,0 +1,171 @@ +const chai = require('chai'); +const chaiFiles = require('chai-files'); +const assert = chai.assert; +chai.use(chaiFiles); +const cache = require('../lib/util/cache'); +const testUtils = require('./utils'); +const handler = require('../lib/index'); + +describe('type: verification', () => { + beforeEach(() => { + testUtils.mockSetup(); + }); + afterEach(() => { + testUtils.mockReset(); + }); + + describe('Retrieve ================', () => { + it('Should retrieve a verification', async () => { + // WHEN + const retrieved = await handler.retrieve('testInstance/testBU', ['verification']); + // THEN + assert.equal(process.exitCode, false, 'retrieve should not have thrown an error'); + // get results from cache + const result = cache.getCache(); + assert.equal( + result.verification ? Object.keys(result.verification).length : 0, + 1, + 'only one verification expected' + ); + assert.equal( + retrieved['testInstance/testBU']?.verification + ? Object.keys(retrieved['testInstance/testBU']?.verification).length + : 0, + 1, + 'one verifications to be retrieved' + ); + + assert.deepEqual( + await testUtils.getActualJson( + 'testExisting_39f6a488-20eb-4ba0-b0b9', + 'verification' + ), + await testUtils.getExpectedJson('9999999', 'verification', 'get'), + 'returned JSON was not equal expected' + ); + assert.equal( + testUtils.getAPIHistoryLength(), + 9, + 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' + ); + return; + }); + }); + describe('Deploy ================', () => { + beforeEach(() => { + testUtils.mockSetup(true); + }); + it('Should create & upsert a verification', async () => { + // WHEN + + const deployed = await handler.deploy('testInstance/testBU', ['verification']); + // THEN + assert.equal(process.exitCode, false, 'deploy should not have thrown an error'); + // get results from cache + const result = cache.getCache(); + assert.equal( + result.verification ? Object.keys(result.verification).length : 0, + 2, + 'two verifications expected' + ); + assert.equal( + deployed['testInstance/testBU']?.verification + ? Object.keys(deployed['testInstance/testBU']?.verification).length + : 0, + 2, + 'two verifications to be deployed' + ); + // confirm created item + assert.deepEqual( + await testUtils.getActualJson('testNew_RANDOM_NEW_GUID', 'verification'), + await testUtils.getExpectedJson('9999999', 'verification', 'post'), + 'returned new-JSON was not equal expected for insert verification' + ); + // confirm updated item + assert.deepEqual( + await testUtils.getActualJson( + 'testExisting_39f6a488-20eb-4ba0-b0b9', + 'verification' + ), + await testUtils.getExpectedJson('9999999', 'verification', 'patch'), + 'returned existing-JSON was not equal expected for update verification' + ); + // check number of API calls + assert.equal( + testUtils.getAPIHistoryLength(), + 11, + 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' + ); + return; + }); + }); + describe('Templating ================', () => { + it('Should create a verification template via buildTemplate and build it', async () => { + // download first before we test buildTemplate + await handler.retrieve('testInstance/testBU', ['verification']); + // buildTemplate + const result = await handler.buildTemplate( + 'testInstance/testBU', + 'verification', + ['testExisting_39f6a488-20eb-4ba0-b0b9'], + 'testSourceMarket' + ); + assert.equal(process.exitCode, false, 'buildTemplate should not have thrown an error'); + assert.equal( + result.verification ? Object.keys(result.verification).length : 0, + 1, + 'only one verification expected' + ); + assert.deepEqual( + await testUtils.getActualTemplateJson( + 'testExisting_39f6a488-20eb-4ba0-b0b9', + 'verification' + ), + await testUtils.getExpectedJson('9999999', 'verification', 'template'), + 'returned template JSON was not equal expected' + ); + // buildDefinition + await handler.buildDefinition( + 'testInstance/testBU', + 'verification', + 'testExisting_39f6a488-20eb-4ba0-b0b9', + 'testTargetMarket' + ); + assert.equal( + process.exitCode, + false, + 'buildDefinition should not have thrown an error' + ); + assert.deepEqual( + await testUtils.getActualDeployJson( + 'testTemplated_39f6a488-20eb-4ba0-b0b9', + 'verification' + ), + await testUtils.getExpectedJson('9999999', 'verification', 'build'), + 'returned deployment JSON was not equal expected' + ); + assert.equal( + testUtils.getAPIHistoryLength(), + 9, + 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' + ); + return; + }); + }); + describe('Delete ================', () => { + it('Should delete the item', async () => { + // WHEN + const isDeleted = await handler.deleteByKey('testInstance/testBU', 'verification', [ + 'testExisting_39f6a488-20eb-4ba0-b0b9', + ]); + // THEN + assert.equal( + process.exitCode, + 0, + 'deleteByKey should have thrown an error due to lack of support' + ); + assert.equal(isDeleted, true, 'deleteByKey should have returned true for success'); + return; + }); + }); +}); From 0d437e94c5346764996ed9d1cbc455140d1b26c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 24 Aug 2023 14:24:29 +0200 Subject: [PATCH 49/70] #325: fix retrieve test for automation --- test/resources/9999999/automation/retrieve-expected.json | 4 ++++ .../automation/retrieve-testExisting_automation-expected.md | 1 + test/type.automation.test.js | 3 +-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/test/resources/9999999/automation/retrieve-expected.json b/test/resources/9999999/automation/retrieve-expected.json index f4771678a..a598c8aff 100644 --- a/test/resources/9999999/automation/retrieve-expected.json +++ b/test/resources/9999999/automation/retrieve-expected.json @@ -36,6 +36,10 @@ { "name": "testExisting_script", "r__type": "script" + }, + { + "name": "testExisting_39f6a488-20eb-4ba0-b0b9", + "r__type": "verification" } ], "name": "" diff --git a/test/resources/9999999/automation/retrieve-testExisting_automation-expected.md b/test/resources/9999999/automation/retrieve-testExisting_automation-expected.md index 6c22b1c53..161e31040 100644 --- a/test/resources/9999999/automation/retrieve-testExisting_automation-expected.md +++ b/test/resources/9999999/automation/retrieve-testExisting_automation-expected.md @@ -28,3 +28,4 @@ | _1.4: importFile_
testExisting_importFile | | _1.5: query_
testExisting_query | | _1.6: script_
testExisting_script | +| _1.7: verification_
testExisting_39f6a488-20eb-4ba0-b0b9 | diff --git a/test/type.automation.test.js b/test/type.automation.test.js index 3caf3b474..921b39429 100644 --- a/test/type.automation.test.js +++ b/test/type.automation.test.js @@ -47,10 +47,9 @@ describe('type: automation', () => { ) ) ); - assert.equal( testUtils.getAPIHistoryLength(), - 17, + 18, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; From 6cee703640654d279ddb922d413c0a44fa292a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 24 Aug 2023 14:26:17 +0200 Subject: [PATCH 50/70] #325: move log msg into generic method --- lib/metadataTypes/Automation.js | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/lib/metadataTypes/Automation.js b/lib/metadataTypes/Automation.js index 61926367d..f0ee48014 100644 --- a/lib/metadataTypes/Automation.js +++ b/lib/metadataTypes/Automation.js @@ -46,16 +46,6 @@ class Automation extends MetadataType { ['ObjectID'], requestParams ); - if (results.Results?.length && !key) { - // empty results will come back without "Results" defined - Util.logger.info( - Util.getGrayMsg( - ` - ${results.Results?.length} automation${ - results.Results?.length === 1 ? '' : 's' - } found. Retrieving details...` - ) - ); - } // the API seems to handle 50 concurrent requests nicely const response = results?.Results?.length ? await this.retrieveRESTcollection( @@ -63,7 +53,8 @@ class Automation extends MetadataType { id: item.ObjectID, uri: '/automation/v1/automations/' + item.ObjectID, })), - 50 + 50, + !key ) : null; metadataMap = response?.metadata || {}; From 4ea27792a0f4b8a304ab0cd3245cd5ba5137b5f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 24 Aug 2023 14:28:36 +0200 Subject: [PATCH 51/70] #325: improve test api logs --- test/utils.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/utils.js b/test/utils.js index 3e7a5a070..e54c41c2c 100644 --- a/test/utils.js +++ b/test/utils.js @@ -208,7 +208,13 @@ exports.getAPIHistory = () => apimock.history; function getAPIHistoryDebug() { const historyArr = Object.values(apimock.history) .flat() - .map((item) => ({ url: item.url, data: item.data })); + .map((item) => { + const log = { method: item.method, url: item.url }; + if (item.data) { + log.body = item.data; + } + return log; + }); return historyArr; } exports.getAPIHistoryDebug = getAPIHistoryDebug; From 1099ed4fb0a03c1dcad8a8419111b374f3dddff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 24 Aug 2023 16:11:10 +0200 Subject: [PATCH 52/70] #325: fix caching logic for automations together with verifications --- lib/metadataTypes/Automation.js | 40 ++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/lib/metadataTypes/Automation.js b/lib/metadataTypes/Automation.js index f0ee48014..992ca8ed4 100644 --- a/lib/metadataTypes/Automation.js +++ b/lib/metadataTypes/Automation.js @@ -26,7 +26,11 @@ class Automation extends MetadataType { */ static async retrieve(retrieveDir, _, __, key) { let metadataMap; - if (!key && this._cachedMetadataMap) { + if (key && this._cachedMetadataMap?.[key]) { + metadataMap = {}; + metadataMap[key] = this._cachedMetadataMap[key]; + delete this._cachedMetadataMap; + } else if (!key && this._cachedMetadataMap) { metadataMap = this._cachedMetadataMap; delete this._cachedMetadataMap; } else { @@ -229,12 +233,18 @@ class Automation extends MetadataType { * @returns {Promise.} Promise of metadata */ static async retrieveForCache() { - // get automations for cache - const results = await this.client.soap.retrieveBulk('Program', [ - 'ObjectID', - 'CustomerKey', - 'Name', - ]); + let results = {}; + if (this._cachedMetadataMap) { + results.Results = Object.values(this._cachedMetadataMap); + delete this._cachedMetadataMap; + } else { + // get automations for cache + results = await this.client.soap.retrieveBulk('Program', [ + 'ObjectID', + 'CustomerKey', + 'Name', + ]); + } const resultsConverted = {}; if (Array.isArray(results?.Results)) { // get encodedAutomationID to retrieve notification information @@ -251,16 +261,16 @@ class Automation extends MetadataType { // merge encodedAutomationID into results for (const m of results.Results) { - resultsConverted[m.CustomerKey] = { - id: m.ObjectID, - key: m.CustomerKey, - name: m.Name, - programId: automationsLegacy.metadata[m.CustomerKey]?.id, - status: automationsLegacy.metadata[m.CustomerKey]?.status, + const key = m.CustomerKey || m.key; + resultsConverted[key] = { + id: m.ObjectID || m.id, + key: key, + name: m.Name || m.name, + programId: automationsLegacy.metadata[key]?.id, + status: automationsLegacy.metadata[key]?.status, }; } } - return { metadata: resultsConverted, type: this.definition.type }; } @@ -362,7 +372,7 @@ class Automation extends MetadataType { * @param {TYPE.AutomationItem} metadata a single automation * @returns {TYPE.AutomationItem | void} parsed item */ - static async postRetrieveTasks(metadata) { + static postRetrieveTasks(metadata) { // folder this.setFolderPath(metadata); // automations are often skipped due to lack of support. From 733b20161ebc2d5177e436c5ec2c30e1d20b993d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 24 Aug 2023 16:14:20 +0200 Subject: [PATCH 53/70] #325: fix automation tests after adding type verification --- .../9999999/automation/build-expected.json | 4 +++ .../9999999/automation/template-expected.json | 4 +++ .../automation/definition/get-response.json | 2 +- test/type.automation.test.js | 25 ++++++++++--------- test/type.query.test.js | 4 +-- 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/test/resources/9999999/automation/build-expected.json b/test/resources/9999999/automation/build-expected.json index 87301aa55..d0e111d1e 100644 --- a/test/resources/9999999/automation/build-expected.json +++ b/test/resources/9999999/automation/build-expected.json @@ -36,6 +36,10 @@ { "name": "testTemplated_script", "r__type": "script" + }, + { + "name": "testTemplated_39f6a488-20eb-4ba0-b0b9", + "r__type": "verification" } ], "name": "" diff --git a/test/resources/9999999/automation/template-expected.json b/test/resources/9999999/automation/template-expected.json index 32a6bcbc5..9e3db99fc 100644 --- a/test/resources/9999999/automation/template-expected.json +++ b/test/resources/9999999/automation/template-expected.json @@ -36,6 +36,10 @@ { "name": "{{{prefix}}}script", "r__type": "script" + }, + { + "name": "{{{prefix}}}39f6a488-20eb-4ba0-b0b9", + "r__type": "verification" } ], "name": "" diff --git a/test/resources/9999999/legacy/v1/beta/bulk/automations/automation/definition/get-response.json b/test/resources/9999999/legacy/v1/beta/bulk/automations/automation/definition/get-response.json index bbf38898d..124f8fb90 100644 --- a/test/resources/9999999/legacy/v1/beta/bulk/automations/automation/definition/get-response.json +++ b/test/resources/9999999/legacy/v1/beta/bulk/automations/automation/definition/get-response.json @@ -10,7 +10,7 @@ "name": "testExisting_automation", "description": "bla bla", "clientId": 9999999, - "status": "Building", + "status": "Scheduled", "createdBy": { "id": "NzE3MzUzNDA1OjQ6MA", "name": "Tom Tester", diff --git a/test/type.automation.test.js b/test/type.automation.test.js index 921b39429..365c6c323 100644 --- a/test/type.automation.test.js +++ b/test/type.automation.test.js @@ -119,7 +119,7 @@ describe('type: automation', () => { assert.equal( testUtils.getAPIHistoryLength(), - 16, + 21, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; @@ -188,7 +188,7 @@ describe('type: automation', () => { assert.equal( testUtils.getAPIHistoryLength(), - 24, + 29, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; @@ -256,7 +256,7 @@ describe('type: automation', () => { assert.equal( testUtils.getAPIHistoryLength(), - 20, + 25, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; @@ -288,10 +288,11 @@ describe('type: automation', () => { 1, 'one automation expected' ); + testUtils.logAPIHistoryDebug(); // check number of API calls assert.equal( testUtils.getAPIHistoryLength(), - 14, + 18, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; @@ -335,7 +336,7 @@ describe('type: automation', () => { // check number of API calls assert.equal( testUtils.getAPIHistoryLength(), - 29, + 40, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; @@ -379,7 +380,7 @@ describe('type: automation', () => { // check number of API calls assert.equal( testUtils.getAPIHistoryLength(), - 31, + 42, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; @@ -423,7 +424,7 @@ describe('type: automation', () => { // check number of API calls assert.equal( testUtils.getAPIHistoryLength(), - 32, + 43, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; @@ -463,7 +464,7 @@ describe('type: automation', () => { // check number of API calls assert.equal( testUtils.getAPIHistoryLength(), - 26, + 35, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; @@ -504,7 +505,7 @@ describe('type: automation', () => { // check number of API calls assert.equal( testUtils.getAPIHistoryLength(), - 30, + 41, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; @@ -545,7 +546,7 @@ describe('type: automation', () => { // check number of API calls assert.equal( testUtils.getAPIHistoryLength(), - 32, + 43, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; @@ -596,7 +597,7 @@ describe('type: automation', () => { ); assert.equal( testUtils.getAPIHistoryLength(), - 14, + 20, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; @@ -643,7 +644,7 @@ describe('type: automation', () => { ); assert.equal( testUtils.getAPIHistoryLength(), - 17, + 18, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; diff --git a/test/type.query.test.js b/test/type.query.test.js index 01c3dd7c2..73ae86d21 100644 --- a/test/type.query.test.js +++ b/test/type.query.test.js @@ -458,7 +458,7 @@ describe('type: query', () => { // check number of API calls assert.equal( testUtils.getAPIHistoryLength(), - 36, + 34, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; @@ -498,7 +498,7 @@ describe('type: query', () => { // check number of API calls assert.equal( testUtils.getAPIHistoryLength(), - 38, + 36, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; From 5e0f00ece322c0e368f7729081d69fc58a1d1e6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 25 Aug 2023 10:08:15 +0200 Subject: [PATCH 54/70] #325: consolidate / fix tests for deleteByKey across types --- test/type.automation.test.js | 4 ++-- test/type.dataExtension.test.js | 8 ++++---- test/type.dataExtract.test.js | 7 ++++++- test/type.fileTransfer.test.js | 7 ++++++- test/type.importFile.test.js | 7 ++++++- test/type.mobileKeyword.test.js | 4 ++-- test/type.mobileMessage.test.js | 4 ++-- test/type.query.test.js | 4 ++-- test/type.triggeredSend.test.js | 4 ++-- test/type.verification.test.js | 8 +++++--- 10 files changed, 37 insertions(+), 20 deletions(-) diff --git a/test/type.automation.test.js b/test/type.automation.test.js index 365c6c323..4a1acb0b7 100644 --- a/test/type.automation.test.js +++ b/test/type.automation.test.js @@ -653,7 +653,7 @@ describe('type: automation', () => { describe('Delete ================', () => { it('Should delete the item', async () => { // WHEN - const result = await handler.deleteByKey( + const isDeleted = await handler.deleteByKey( 'testInstance/testBU', 'automation', 'testExisting_automation' @@ -661,7 +661,7 @@ describe('type: automation', () => { // THEN assert.equal(process.exitCode, false, 'delete should not have thrown an error'); - assert.equal(result, true, 'should have deleted the item'); + assert.equal(isDeleted, true, 'should have deleted the item'); return; }); }); diff --git a/test/type.dataExtension.test.js b/test/type.dataExtension.test.js index 15dae6984..b77ef9cbf 100644 --- a/test/type.dataExtension.test.js +++ b/test/type.dataExtension.test.js @@ -343,7 +343,7 @@ describe('type: dataExtension', () => { describe('Delete ================', () => { it('Should delete the dataExtension', async () => { // WHEN - const result = await handler.deleteByKey( + const isDeleted = await handler.deleteByKey( 'testInstance/testBU', 'dataExtension', 'testExisting_dataExtension' @@ -351,12 +351,12 @@ describe('type: dataExtension', () => { // THEN assert.equal(process.exitCode, false, 'delete should not have thrown an error'); - assert.equal(result, true, 'should have deleted the item'); + assert.equal(isDeleted, true, 'should have deleted the item'); return; }); it('Should delete the dataExtensionField', async () => { // WHEN - const result = await handler.deleteByKey( + const isDeleted = await handler.deleteByKey( 'testInstance/testBU', 'dataExtensionField', 'testExisting_dataExtension.LastName' @@ -364,7 +364,7 @@ describe('type: dataExtension', () => { // THEN assert.equal(process.exitCode, false, 'delete should not have thrown an error'); - assert.equal(result, true, 'should have deleted the item'); + assert.equal(isDeleted, true, 'should have deleted the item'); return; }); }); diff --git a/test/type.dataExtract.test.js b/test/type.dataExtract.test.js index 0f55f5c8e..d0434c9a8 100644 --- a/test/type.dataExtract.test.js +++ b/test/type.dataExtract.test.js @@ -172,7 +172,7 @@ describe('type: dataExtract', () => { describe('Delete ================', () => { it('Should NOT delete the item', async () => { // WHEN - await handler.deleteByKey( + const isDeleted = await handler.deleteByKey( 'testInstance/testBU', 'dataExtract', 'testExisting_fileTranfer' @@ -183,6 +183,11 @@ describe('type: dataExtract', () => { 1, 'deleteByKey should have thrown an error due to lack of support' ); + assert.equal( + isDeleted, + false, + 'deleteByKey should have returned false due to lack of support' + ); return; }); }); diff --git a/test/type.fileTransfer.test.js b/test/type.fileTransfer.test.js index 9424c893e..660866f96 100644 --- a/test/type.fileTransfer.test.js +++ b/test/type.fileTransfer.test.js @@ -170,7 +170,7 @@ describe('type: fileTransfer', () => { describe('Delete ================', () => { it('Should NOT delete the item', async () => { // WHEN - await handler.deleteByKey( + const isDeleted = await handler.deleteByKey( 'testInstance/testBU', 'fileTransfer', 'testExisting_fileTranfer' @@ -181,6 +181,11 @@ describe('type: fileTransfer', () => { 1, 'deleteByKey should have thrown an error due to lack of support' ); + assert.equal( + isDeleted, + false, + 'deleteByKey should have returned false due to lack of support' + ); return; }); }); diff --git a/test/type.importFile.test.js b/test/type.importFile.test.js index 7b4bc1c3b..cf8c283dd 100644 --- a/test/type.importFile.test.js +++ b/test/type.importFile.test.js @@ -171,7 +171,7 @@ describe('type: importFile', () => { describe('Delete ================', () => { it('Should NOT delete the item', async () => { // WHEN - await handler.deleteByKey( + const isDeleted = await handler.deleteByKey( 'testInstance/testBU', 'importFile', 'testExisting_fileTranfer' @@ -182,6 +182,11 @@ describe('type: importFile', () => { 1, 'deleteByKey should have thrown an error due to lack of support' ); + assert.equal( + isDeleted, + false, + 'deleteByKey should have returned false due to lack of support' + ); return; }); }); diff --git a/test/type.mobileKeyword.test.js b/test/type.mobileKeyword.test.js index 83d49810d..84f1c8fa8 100644 --- a/test/type.mobileKeyword.test.js +++ b/test/type.mobileKeyword.test.js @@ -245,7 +245,7 @@ describe('type: mobileKeyword', () => { describe('Delete ================', () => { it('Should delete the item', async () => { // WHEN - const result = await handler.deleteByKey( + const isDeleted = await handler.deleteByKey( 'testInstance/testBU', 'mobileKeyword', '4912312345678.TESTEXISTING_KEYWORD' @@ -253,7 +253,7 @@ describe('type: mobileKeyword', () => { // THEN assert.equal(process.exitCode, false, 'delete should not have thrown an error'); - assert.equal(result, true, 'should have deleted the item'); + assert.equal(isDeleted, true, 'should have deleted the item'); return; }); }); diff --git a/test/type.mobileMessage.test.js b/test/type.mobileMessage.test.js index 4562434ee..d80413213 100644 --- a/test/type.mobileMessage.test.js +++ b/test/type.mobileMessage.test.js @@ -163,7 +163,7 @@ describe('type: mobileMessage', () => { describe('Delete ================', () => { it('Should delete the item', async () => { // WHEN - const result = await handler.deleteByKey( + const isDeleted = await handler.deleteByKey( 'testInstance/testBU', 'mobileMessage', 'NTIzOjc4OjA' @@ -171,7 +171,7 @@ describe('type: mobileMessage', () => { // THEN assert.equal(process.exitCode, false, 'delete should not have thrown an error'); - assert.equal(result, true, 'should have deleted the item'); + assert.equal(isDeleted, true, 'should have deleted the item'); return; }); }); diff --git a/test/type.query.test.js b/test/type.query.test.js index 73ae86d21..f2f24f53b 100644 --- a/test/type.query.test.js +++ b/test/type.query.test.js @@ -656,7 +656,7 @@ describe('type: query', () => { describe('Delete ================', () => { it('Should delete the item', async () => { // WHEN - const result = await handler.deleteByKey( + const isDeleted = await handler.deleteByKey( 'testInstance/testBU', 'query', 'testExisting_query' @@ -664,7 +664,7 @@ describe('type: query', () => { // THEN assert.equal(process.exitCode, false, 'delete should not have thrown an error'); - assert.equal(result, true, 'should have deleted the item'); + assert.equal(isDeleted, true, 'should have deleted the item'); return; }); }); diff --git a/test/type.triggeredSend.test.js b/test/type.triggeredSend.test.js index a4fd91b4e..a923f1348 100644 --- a/test/type.triggeredSend.test.js +++ b/test/type.triggeredSend.test.js @@ -130,7 +130,7 @@ describe('type: triggeredSend', () => { describe('Delete ================', () => { it('Should delete the item', async () => { // WHEN - const result = await handler.deleteByKey( + const isDeleted = await handler.deleteByKey( 'testInstance/testBU', 'triggeredSend', 'testExisting_triggeredSend' @@ -138,7 +138,7 @@ describe('type: triggeredSend', () => { // THEN assert.equal(process.exitCode, false, 'delete should not have thrown an error'); - assert.equal(result, true, 'should have deleted the item'); + assert.equal(isDeleted, true, 'should have deleted the item'); return; }); }); diff --git a/test/type.verification.test.js b/test/type.verification.test.js index 993f26095..4514f3f04 100644 --- a/test/type.verification.test.js +++ b/test/type.verification.test.js @@ -155,9 +155,11 @@ describe('type: verification', () => { describe('Delete ================', () => { it('Should delete the item', async () => { // WHEN - const isDeleted = await handler.deleteByKey('testInstance/testBU', 'verification', [ - 'testExisting_39f6a488-20eb-4ba0-b0b9', - ]); + const isDeleted = await handler.deleteByKey( + 'testInstance/testBU', + 'verification', + 'testExisting_39f6a488-20eb-4ba0-b0b9' + ); // THEN assert.equal( process.exitCode, From 78c0908ea3833dbe9d625ae34f1c5e3aae8c2949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 25 Aug 2023 10:40:09 +0200 Subject: [PATCH 55/70] #847: add test for delete journey --- lib/metadataTypes/Journey.js | 9 +++- .../delete-response.json | 1 + test/type.journey.test.js | 49 ++++++++++++++----- 3 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 test/resources/9999999/interaction/v1/interactions/233d4413-922c-4568-85a5-e5cc77efc3be/delete-response.json diff --git a/lib/metadataTypes/Journey.js b/lib/metadataTypes/Journey.js index 1719dfe47..b2ea341b2 100644 --- a/lib/metadataTypes/Journey.js +++ b/lib/metadataTypes/Journey.js @@ -191,14 +191,21 @@ class Journey extends MetadataType { ); const results = this.parseResponseBody(response, key); singleKey = results[key].id; + if (version && version > results[key].version) { + Util.logger.error( + `The chosen version (${version}) is higher than the latest known version (${results[key].version}). Please choose a lower version.` + ); + return false; + } Util.logger.debug(`Deleting interaction ${key} via its ID ${singleKey}`); } if (!/^\d+$/.test(version)) { - throw new TypeError( + Util.logger.error( 'Version is required for deleting interactions to avoid accidental deletion of the wrong item. Please append it at the end of the key or id, separated by forward-slash. Example for deleting version 4: ' + key + '/4' ); + return false; } Util.logger.warn( `Deleting Journeys via this command breaks following retrieve-by-key/id requests until you've deployed/created a new draft version! You can get still get the latest available version of your journey by retrieving all interactions on this BU.` diff --git a/test/resources/9999999/interaction/v1/interactions/233d4413-922c-4568-85a5-e5cc77efc3be/delete-response.json b/test/resources/9999999/interaction/v1/interactions/233d4413-922c-4568-85a5-e5cc77efc3be/delete-response.json new file mode 100644 index 000000000..db5917a02 --- /dev/null +++ b/test/resources/9999999/interaction/v1/interactions/233d4413-922c-4568-85a5-e5cc77efc3be/delete-response.json @@ -0,0 +1 @@ +233d4413-922c-4568-85a5-e5cc77efc3be \ No newline at end of file diff --git a/test/type.journey.test.js b/test/type.journey.test.js index 6003f511f..60a3a3fa5 100644 --- a/test/type.journey.test.js +++ b/test/type.journey.test.js @@ -141,17 +141,44 @@ describe('type: journey', () => { }); }); describe('Delete ================', () => { - // TODO: add this test - it('Should delete the item'); // , async () => { - // // WHEN - // const result = await handler.deleteByKey('testInstance/testBU', 'mobileKeyword', [ - // 'testExisting_keyword', - // ]); - // // THEN - // assert.equal(process.exitCode, false, 'delete should not have thrown an error'); + it('Should NOT delete the item due to missing version', async () => { + // WHEN + const isDeleted = await handler.deleteByKey( + 'testInstance/testBU', + 'journey', + 'testExisting_interaction' + ); + // THEN + assert.equal(process.exitCode, true, 'delete should have thrown an error'); - // assert.equal(result, true, 'should have deleted the item'); - // return; - // }); + assert.equal(isDeleted, false, 'should not have deleted the item'); + return; + }); + it('Should NOT delete the item due to unknown version', async () => { + // WHEN + const isDeleted = await handler.deleteByKey( + 'testInstance/testBU', + 'journey', + 'testExisting_interaction/2' + ); + // THEN + assert.equal(process.exitCode, true, 'delete should have thrown an error'); + + assert.equal(isDeleted, false, 'should not have deleted the item'); + return; + }); + it('Should delete the item with version', async () => { + // WHEN + const isDeleted = await handler.deleteByKey( + 'testInstance/testBU', + 'journey', + 'testExisting_interaction/1' + ); + // THEN + assert.equal(process.exitCode, false, 'delete should not have thrown an error'); + + assert.equal(isDeleted, true, 'should have deleted the item'); + return; + }); }); }); From fe53e9c6cea5e58deb33e14f375654024009bef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 25 Aug 2023 14:16:23 +0200 Subject: [PATCH 56/70] #926: fix transactionalEmail create and delete logic to deal with the connected journey; rename fields to match r__ pattern --- docs/dist/documentation.md | 42 ++++++- lib/metadataTypes/TransactionalEmail.js | 111 +++++++++++++++--- lib/metadataTypes/TransactionalMessage.js | 5 +- .../TransactionalEmail.definition.js | 20 +++- ...isting_temail.transactionalEmail-meta.json | 7 +- ...estNew_temail.transactionalEmail-meta.json | 7 +- .../v1/email/definitions/post-response.json | 2 +- .../testExisting_temail/delete-response.json | 6 + .../transactionalEmail/build-expected.json | 10 +- .../transactionalEmail/get-expected.json | 10 +- .../transactionalEmail/patch-expected.json | 10 +- .../transactionalEmail/post-expected.json | 10 +- .../transactionalEmail/template-expected.json | 10 +- test/type.transactionalEmail.test.js | 23 ++-- 14 files changed, 193 insertions(+), 80 deletions(-) create mode 100644 test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/delete-response.json diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 1bffd85d8..4b89a1cf6 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -5454,7 +5454,10 @@ TransactionalEmail MetadataType * [TransactionalEmail](#TransactionalEmail) ⇐ [TransactionalMessage](#TransactionalMessage) * [.update(metadata)](#TransactionalEmail.update) ⇒ Promise * [.preDeployTasks(metadata)](#TransactionalEmail.preDeployTasks) ⇒ TYPE.MetadataTypeItem + * [.postCreateTasks(_, apiResponse)](#TransactionalEmail.postCreateTasks) ⇒ void + * [.postDeployTasks()](#TransactionalEmail.postDeployTasks) ⇒ void * [.postRetrieveTasks(metadata)](#TransactionalEmail.postRetrieveTasks) ⇒ TYPE.MetadataTypeItem + * [.deleteByKey(key)](#TransactionalEmail.deleteByKey) ⇒ Promise.<boolean> @@ -5480,6 +5483,24 @@ prepares for deployment | --- | --- | --- | | metadata | TYPE.MetadataTypeItem | a single item | + + +### TransactionalEmail.postCreateTasks(_, apiResponse) ⇒ void +helper for [TransactionalEmail.createREST](TransactionalEmail.createREST) + +**Kind**: static method of [TransactionalEmail](#TransactionalEmail) + +| Param | Type | Description | +| --- | --- | --- | +| _ | TYPE.MetadataTypeItem | not used | +| apiResponse | object | varies depending on the API call | + + + +### TransactionalEmail.postDeployTasks() ⇒ void +Gets executed after deployment of metadata type + +**Kind**: static method of [TransactionalEmail](#TransactionalEmail) ### TransactionalEmail.postRetrieveTasks(metadata) ⇒ TYPE.MetadataTypeItem @@ -5492,6 +5513,18 @@ manages post retrieve steps | --- | --- | --- | | metadata | TYPE.MetadataTypeItem | a single item | + + +### TransactionalEmail.deleteByKey(key) ⇒ Promise.<boolean> +Delete a metadata item from the specified business unit + +**Kind**: static method of [TransactionalEmail](#TransactionalEmail) +**Returns**: Promise.<boolean> - deletion success status + +| Param | Type | Description | +| --- | --- | --- | +| key | string | Identifier of item | + ## TransactionalMessage ⇐ [MetadataType](#MetadataType) @@ -5502,7 +5535,7 @@ TransactionalMessage MetadataType * [TransactionalMessage](#TransactionalMessage) ⇐ [MetadataType](#MetadataType) * [.retrieve(retrieveDir, [_], [__], [key])](#TransactionalMessage.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> - * [.retrieveForCache()](#TransactionalMessage.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.retrieveForCache([key])](#TransactionalMessage.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.update(metadata)](#TransactionalMessage.update) ⇒ Promise * [.create(metadata)](#TransactionalMessage.create) ⇒ Promise * [.deleteByKey(key)](#TransactionalMessage.deleteByKey) ⇒ Promise.<boolean> @@ -5524,11 +5557,16 @@ Retrieves Metadata -### TransactionalMessage.retrieveForCache() ⇒ Promise.<TYPE.MetadataTypeMapObj> +### TransactionalMessage.retrieveForCache([key]) ⇒ Promise.<TYPE.MetadataTypeMapObj> Retrieves event definition metadata for caching **Kind**: static method of [TransactionalMessage](#TransactionalMessage) **Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise of metadata + +| Param | Type | Description | +| --- | --- | --- | +| [key] | string | customer key of single item to cache | + ### TransactionalMessage.update(metadata) ⇒ Promise diff --git a/lib/metadataTypes/TransactionalEmail.js b/lib/metadataTypes/TransactionalEmail.js index 560bfdc61..c17b91410 100644 --- a/lib/metadataTypes/TransactionalEmail.js +++ b/lib/metadataTypes/TransactionalEmail.js @@ -40,21 +40,24 @@ class TransactionalEmail extends TransactionalMessage { */ static async preDeployTasks(metadata) { // asset - if (metadata.content?.customerKey) { + if (metadata.r__asset_customerKey) { // we merely want to be able to show an error if it does not exist - cache.searchForField( - 'asset', - metadata.content.customerKey, - 'customerKey', - 'customerKey' - ); + metadata.content = { + customerKey: cache.searchForField( + 'asset', + metadata.r__asset_customerKey, + 'customerKey', + 'customerKey' + ), + }; + delete metadata.r__asset_customerKey; } // subscriptions: dataExtension - if (metadata.subscriptions?.dataExtension) { + if (metadata.subscriptions?.r__dataExtension_CustomerKey) { // we merely want to be able to show an error if it does not exist - cache.searchForField( + metadata.subscriptions.dataExtension = cache.searchForField( 'dataExtension', - metadata.subscriptions.dataExtension, + metadata.subscriptions.r__dataExtension_CustomerKey, 'CustomerKey', 'CustomerKey' ); @@ -69,15 +72,49 @@ class TransactionalEmail extends TransactionalMessage { } // journey - if (metadata.journey?.interactionKey) { - // ! update & create enpoints dont accept journey.interactionKey. They only allow to create a new journey - metadata.options ||= {}; - metadata.options.createJourney = true; // only send this during create or else we might end up with an unexpected outcome - delete metadata.journey.interactionKey; - } + // ! update & create enpoints dont accept journey.interactionKey. They only allow to create a new journey + metadata.options ||= {}; + metadata.options.createJourney = true; // only send this during create or else we might end up with an unexpected outcome return metadata; } + /** + * helper for {@link TransactionalEmail.createREST} + * + * @param {TYPE.MetadataTypeItem} _ not used + * @param {object} apiResponse varies depending on the API call + * @returns {void} + */ + static async postCreateTasks(_, apiResponse) { + if (apiResponse.journey?.interactionKey) { + Util.logger.warn( + ` - created journey: ${apiResponse.journey.interactionKey} (auto-created when ${this.definition.type} ${apiResponse.definitionKey} was created)` + ); + // when we create new transactionalEmails, we should also download the new journey that was created with it + this._createdJourneyKeys ||= []; + this._createdJourneyKeys.push(apiResponse.journey?.interactionKey); + + // do what postRetrieveTasks won't be able to do without spending lots of time on caching + apiResponse.r__journey_key = apiResponse.journey.interactionKey; + delete apiResponse.journey; + } + } + /** + * Gets executed after deployment of metadata type + * + * @returns {void} + */ + static postDeployTasks() { + if (this._createdJourneyKeys?.length) { + Util.logger.warn( + `Please download related journeys via: mcdev r ${this.buObject.credential}/${ + this.buObject.businessUnit + } journey "${this._createdJourneyKeys.join(',')}"` + ); + } + delete this._createdJourneyKeys; + } + /** * manages post retrieve steps * @@ -87,6 +124,7 @@ class TransactionalEmail extends TransactionalMessage { static postRetrieveTasks(metadata) { // asset if (metadata.content?.customerKey) { + metadata.r__asset_customerKey = metadata.content.customerKey; try { // we merely want to be able to show an error if it does not exist cache.searchForField( @@ -102,17 +140,20 @@ class TransactionalEmail extends TransactionalMessage { }): ${ex.message}.` ); } + delete metadata.content; } + // subscriptions: dataExtension if (metadata.subscriptions?.dataExtension) { try { // we merely want to be able to show a warning if it does not exist - cache.searchForField( + metadata.subscriptions.r__dataExtension_CustomerKey = cache.searchForField( 'dataExtension', metadata.subscriptions.dataExtension, 'CustomerKey', 'CustomerKey' ); + delete metadata.subscriptions.dataExtension; } catch (ex) { Util.logger.warn( ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${ @@ -140,9 +181,15 @@ class TransactionalEmail extends TransactionalMessage { } // journey if (metadata.journey?.interactionKey) { + metadata.r__journey_key = metadata.journey.interactionKey; try { // we merely want to be able to show a warning if it does not exist - cache.searchForField('journey', metadata.journey.interactionKey, 'key', 'key'); + metadata.r__journey_key = cache.searchForField( + 'journey', + metadata.journey.interactionKey, + 'key', + 'key' + ); } catch (ex) { Util.logger.warn( ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${ @@ -150,10 +197,38 @@ class TransactionalEmail extends TransactionalMessage { }): ${ex.message}.` ); } + delete metadata.journey; } return metadata; } + /** + * Delete a metadata item from the specified business unit + * + * @param {string} key Identifier of item + * @returns {Promise.} deletion success status + */ + static async deleteByKey(key) { + const metadataMapObj = await this.retrieveForCache(key); + const journeyKey = metadataMapObj?.metadata?.[key]?.journey?.interactionKey; + + const isDeleted = await super.deleteByKeyREST( + '/messaging/v1/' + this.subType + '/definitions/' + key, + key, + false + ); + if (isDeleted && journeyKey) { + const Journey = require('./Journey'); + Util.logger.info( + ` - deleted ${Journey.definition.type}: ${journeyKey} (SFMC auto-deletes the related journey of ${this.definition.type} ${key})` + ); + Journey.buObject = this.buObject; + Journey.properties = this.properties; + Journey.client = this.client; + Journey.postDeleteTasks(journeyKey); + } + return isDeleted; + } } // Assign definition to static attributes diff --git a/lib/metadataTypes/TransactionalMessage.js b/lib/metadataTypes/TransactionalMessage.js index 58587d7db..212967687 100644 --- a/lib/metadataTypes/TransactionalMessage.js +++ b/lib/metadataTypes/TransactionalMessage.js @@ -67,13 +67,14 @@ class TransactionalMessage extends MetadataType { /** * Retrieves event definition metadata for caching * + * @param {string} [key] customer key of single item to cache * @returns {Promise.} Promise of metadata */ - static retrieveForCache() { + static retrieveForCache(key) { // the call to /messaging/v1/email/definitions/ does not return definitionId // definitionId is required for resolving dependencies on interactions. // we should therefore use the already defined retrieve method - return this.retrieve(); + return this.retrieve(undefined, undefined, undefined, key); } /** * Updates a single item diff --git a/lib/metadataTypes/definitions/TransactionalEmail.definition.js b/lib/metadataTypes/definitions/TransactionalEmail.definition.js index 1901433f0..be417d221 100644 --- a/lib/metadataTypes/definitions/TransactionalEmail.definition.js +++ b/lib/metadataTypes/definitions/TransactionalEmail.definition.js @@ -83,6 +83,12 @@ module.exports = { retrieving: true, template: true, }, + 'subscriptions.r__dataExtension_CustomerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: true, + }, 'subscriptions.list': { isCreateable: true, isUpdateable: true, @@ -127,7 +133,7 @@ module.exports = { }, 'options.createJourney': { isCreateable: true, - isUpdateable: true, + isUpdateable: false, retrieving: false, template: false, }, @@ -143,5 +149,17 @@ module.exports = { retrieving: true, template: true, }, + r__asset_customerKey: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: true, + }, + r__journey_key: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: true, + }, }, }; diff --git a/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testExisting_temail.transactionalEmail-meta.json b/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testExisting_temail.transactionalEmail-meta.json index d402194ac..777a3cc15 100644 --- a/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testExisting_temail.transactionalEmail-meta.json +++ b/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testExisting_temail.transactionalEmail-meta.json @@ -6,9 +6,6 @@ "status": "Active", "createdDate": "2020-09-10T03:29:00", "modifiedDate": "2020-09-10T03:29:00", - "content": { - "customerKey": "testExisting_asset_message" - }, "subscriptions": { "dataExtension": "testExisting_dataExtension", "autoAddSubscriber": true, @@ -17,5 +14,7 @@ }, "options": { "trackLinks": true - } + }, + "r__journey_key": "testExisting_interaction", + "r__asset_customerKey": "testExisting_asset_message" } diff --git a/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testNew_temail.transactionalEmail-meta.json b/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testNew_temail.transactionalEmail-meta.json index 64b780e9b..197e9f902 100644 --- a/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testNew_temail.transactionalEmail-meta.json +++ b/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testNew_temail.transactionalEmail-meta.json @@ -6,9 +6,6 @@ "status": "Active", "createdDate": "2020-09-10T03:29:00", "modifiedDate": "2020-09-10T03:29:00", - "content": { - "customerKey": "testExisting_asset_message" - }, "subscriptions": { "dataExtension": "testExisting_dataExtension", "autoAddSubscriber": true, @@ -18,7 +15,5 @@ "options": { "trackLinks": true }, - "journey": { - "interactionKey": "testExisting_interaction" - } + "r__asset_customerKey": "testExisting_asset_message" } diff --git a/test/resources/9999999/messaging/v1/email/definitions/post-response.json b/test/resources/9999999/messaging/v1/email/definitions/post-response.json index 0278ba775..080bde8b5 100644 --- a/test/resources/9999999/messaging/v1/email/definitions/post-response.json +++ b/test/resources/9999999/messaging/v1/email/definitions/post-response.json @@ -15,5 +15,5 @@ "updateSubscriber": true }, "options": { "trackLinks": true }, - "journey": { "interactionKey": "testExisting_interaction" } + "journey": { "interactionKey": "testNew_RANDOM_interaction" } } diff --git a/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/delete-response.json b/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/delete-response.json new file mode 100644 index 000000000..37a29e379 --- /dev/null +++ b/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/delete-response.json @@ -0,0 +1,6 @@ +{ + "requestId": "b6ad9f29-1ea4-486d-b826-e3dda072bbcd", + "message": "Success", + "deletedDefinitionKey": "1b24da84-7e55-47cb-a190-585a3b49da20", + "journey": {} +} diff --git a/test/resources/9999999/transactionalEmail/build-expected.json b/test/resources/9999999/transactionalEmail/build-expected.json index f9417a65c..bbc27cc4a 100644 --- a/test/resources/9999999/transactionalEmail/build-expected.json +++ b/test/resources/9999999/transactionalEmail/build-expected.json @@ -4,11 +4,8 @@ "description": "foobar", "classification": "Default Transactional", "status": "Active", - "content": { - "customerKey": "testTemplated_asset_message" - }, "subscriptions": { - "dataExtension": "testTemplated_dataExtension", + "r__dataExtension_CustomerKey": "testTemplated_dataExtension", "autoAddSubscriber": true, "updateSubscriber": true, "r__list_PathName": "my subscribers/All Subscribers" @@ -16,7 +13,6 @@ "options": { "trackLinks": true }, - "journey": { - "interactionKey": "testTemplated_interaction" - } + "r__asset_customerKey": "testTemplated_asset_message", + "r__journey_key": "testTemplated_interaction" } diff --git a/test/resources/9999999/transactionalEmail/get-expected.json b/test/resources/9999999/transactionalEmail/get-expected.json index 2bb3340b2..4cfc61fa2 100644 --- a/test/resources/9999999/transactionalEmail/get-expected.json +++ b/test/resources/9999999/transactionalEmail/get-expected.json @@ -6,11 +6,8 @@ "status": "Active", "createdDate": "2020-09-10T03:29:00", "modifiedDate": "2020-09-10T03:29:00", - "content": { - "customerKey": "testExisting_asset_message" - }, "subscriptions": { - "dataExtension": "testExisting_dataExtension", + "r__dataExtension_CustomerKey": "testExisting_dataExtension", "autoAddSubscriber": true, "updateSubscriber": true, "r__list_PathName": "my subscribers/All Subscribers" @@ -18,7 +15,6 @@ "options": { "trackLinks": true }, - "journey": { - "interactionKey": "testExisting_interaction" - } + "r__asset_customerKey": "testExisting_asset_message", + "r__journey_key": "testExisting_interaction" } diff --git a/test/resources/9999999/transactionalEmail/patch-expected.json b/test/resources/9999999/transactionalEmail/patch-expected.json index 8028dfeb8..18fb2d317 100644 --- a/test/resources/9999999/transactionalEmail/patch-expected.json +++ b/test/resources/9999999/transactionalEmail/patch-expected.json @@ -6,11 +6,8 @@ "status": "Active", "createdDate": "2020-09-10T03:29:00", "modifiedDate": "2020-09-10T03:29:00", - "content": { - "customerKey": "testExisting_asset_message" - }, "subscriptions": { - "dataExtension": "testExisting_dataExtension", + "r__dataExtension_CustomerKey": "testExisting_dataExtension", "autoAddSubscriber": true, "updateSubscriber": true, "r__list_PathName": "my subscribers/All Subscribers" @@ -18,7 +15,6 @@ "options": { "trackLinks": true }, - "journey": { - "interactionKey": "testExisting_interaction" - } + "r__asset_customerKey": "testExisting_asset_message", + "r__journey_key": "testExisting_interaction" } diff --git a/test/resources/9999999/transactionalEmail/post-expected.json b/test/resources/9999999/transactionalEmail/post-expected.json index e806ee1e3..e6070aa31 100644 --- a/test/resources/9999999/transactionalEmail/post-expected.json +++ b/test/resources/9999999/transactionalEmail/post-expected.json @@ -6,11 +6,8 @@ "status": "Active", "createdDate": "2022-12-06T06:08:00", "modifiedDate": "2022-12-06T06:08:00", - "content": { - "customerKey": "testExisting_asset_message" - }, "subscriptions": { - "dataExtension": "testExisting_dataExtension", + "r__dataExtension_CustomerKey": "testExisting_dataExtension", "autoAddSubscriber": true, "updateSubscriber": true, "r__list_PathName": "my subscribers/All Subscribers" @@ -18,7 +15,6 @@ "options": { "trackLinks": true }, - "journey": { - "interactionKey": "testExisting_interaction" - } + "r__asset_customerKey": "testExisting_asset_message", + "r__journey_key": "testNew_RANDOM_interaction" } diff --git a/test/resources/9999999/transactionalEmail/template-expected.json b/test/resources/9999999/transactionalEmail/template-expected.json index 26e75265a..b32da26a4 100644 --- a/test/resources/9999999/transactionalEmail/template-expected.json +++ b/test/resources/9999999/transactionalEmail/template-expected.json @@ -4,11 +4,8 @@ "description": "{{{description}}}", "classification": "Default Transactional", "status": "Active", - "content": { - "customerKey": "{{{prefix}}}asset_message" - }, "subscriptions": { - "dataExtension": "{{{prefix}}}dataExtension", + "r__dataExtension_CustomerKey": "{{{prefix}}}dataExtension", "autoAddSubscriber": true, "updateSubscriber": true, "r__list_PathName": "my subscribers/All Subscribers" @@ -16,7 +13,6 @@ "options": { "trackLinks": true }, - "journey": { - "interactionKey": "{{{prefix}}}interaction" - } + "r__asset_customerKey": "{{{prefix}}}asset_message", + "r__journey_key": "{{{prefix}}}interaction" } diff --git a/test/type.transactionalEmail.test.js b/test/type.transactionalEmail.test.js index 6a22f9d5d..996095a36 100644 --- a/test/type.transactionalEmail.test.js +++ b/test/type.transactionalEmail.test.js @@ -142,17 +142,18 @@ describe('type: transactionalEmail', () => { }); }); describe('Delete ================', () => { - // TODO: add this test - it('Should delete the item'); // , async () => { - // // WHEN - // const result = await handler.deleteByKey('testInstance/testBU', 'mobileKeyword', [ - // 'testExisting_keyword', - // ]); - // // THEN - // assert.equal(process.exitCode, false, 'delete should not have thrown an error'); + it('Should delete the item', async () => { + // WHEN + const isDeleted = await handler.deleteByKey( + 'testInstance/testBU', + 'transactionalEmail', + 'testExisting_temail' + ); + // THEN + assert.equal(process.exitCode, false, 'delete should not have thrown an error'); - // assert.equal(result, true, 'should have deleted the item'); - // return; - // }); + assert.equal(isDeleted, true, 'should have deleted the item'); + return; + }); }); }); From 7c74e14dc266bb7d721e2fe6a5b5b4c634bfa5a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 25 Aug 2023 14:19:49 +0200 Subject: [PATCH 57/70] #0: refactoring --- test/type.transactionalPush.test.js | 6 ++---- test/type.transactionalSMS.test.js | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/test/type.transactionalPush.test.js b/test/type.transactionalPush.test.js index 4dcfb8a4f..16f8222ea 100644 --- a/test/type.transactionalPush.test.js +++ b/test/type.transactionalPush.test.js @@ -146,13 +146,11 @@ describe('type: transactionalPush', () => { // TODO: add this test it('Should delete the item'); // , async () => { // // WHEN - // const result = await handler.deleteByKey('testInstance/testBU', 'mobileKeyword', [ - // 'testExisting_keyword', - // ]); + // const isDeleted = await handler.deleteByKey('testInstance/testBU', 'mobileKeyword', 'testExisting_keyword'); // // THEN // assert.equal(process.exitCode, false, 'delete should not have thrown an error'); - // assert.equal(result, true, 'should have deleted the item'); + // assert.equal(isDeleted, true, 'should have deleted the item'); // return; // }); }); diff --git a/test/type.transactionalSMS.test.js b/test/type.transactionalSMS.test.js index ad083caf5..8e9d44dde 100644 --- a/test/type.transactionalSMS.test.js +++ b/test/type.transactionalSMS.test.js @@ -176,13 +176,11 @@ describe('type: transactionalSMS', () => { // TODO: add this test it('Should delete the item'); // , async () => { // // WHEN - // const result = await handler.deleteByKey('testInstance/testBU', 'mobileKeyword', [ - // 'testExisting_keyword', - // ]); + // const isDeleted = await handler.deleteByKey('testInstance/testBU', 'mobileKeyword', 'testExisting_keyword'); // // THEN // assert.equal(process.exitCode, false, 'delete should not have thrown an error'); - // assert.equal(result, true, 'should have deleted the item'); + // assert.equal(isDeleted, true, 'should have deleted the item'); // return; // }); }); From 34a44879ab6b0beb23e571e62196d3dd01b5e7a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 25 Aug 2023 14:49:40 +0200 Subject: [PATCH 58/70] #325: ensure r__dataExtension_CustomerKey is set even if related dataExtension cannot be found --- lib/metadataTypes/TransactionalEmail.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/metadataTypes/TransactionalEmail.js b/lib/metadataTypes/TransactionalEmail.js index c17b91410..9592f1921 100644 --- a/lib/metadataTypes/TransactionalEmail.js +++ b/lib/metadataTypes/TransactionalEmail.js @@ -145,15 +145,16 @@ class TransactionalEmail extends TransactionalMessage { // subscriptions: dataExtension if (metadata.subscriptions?.dataExtension) { + metadata.subscriptions.r__dataExtension_CustomerKey = + metadata.subscriptions.dataExtension; try { // we merely want to be able to show a warning if it does not exist - metadata.subscriptions.r__dataExtension_CustomerKey = cache.searchForField( + cache.searchForField( 'dataExtension', metadata.subscriptions.dataExtension, 'CustomerKey', 'CustomerKey' ); - delete metadata.subscriptions.dataExtension; } catch (ex) { Util.logger.warn( ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${ @@ -161,6 +162,7 @@ class TransactionalEmail extends TransactionalMessage { }): ${ex.message}.` ); } + delete metadata.subscriptions.dataExtension; } // subscriptions: list if (metadata.subscriptions?.list) { From 37237b4c18c80af6c72641689f05d086c863c2af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 25 Aug 2023 15:50:34 +0200 Subject: [PATCH 59/70] #325: ensure generated verification keys get used during joint deployment of automation and verification --- lib/metadataTypes/Automation.js | 18 +++++++++++++++++ lib/metadataTypes/Verification.js | 9 +++++++++ .../testNew_automation.automation-meta.json | 4 ++++ .../9999999/automation/create-expected.json | 4 ++++ .../create-testNew_automation-expected.md | 1 + .../v1/automations/post-response.json | 7 +++++++ test/type.automation.test.js | 20 +++++++++++-------- 7 files changed, 55 insertions(+), 8 deletions(-) diff --git a/lib/metadataTypes/Automation.js b/lib/metadataTypes/Automation.js index 992ca8ed4..a1b05b884 100644 --- a/lib/metadataTypes/Automation.js +++ b/lib/metadataTypes/Automation.js @@ -814,6 +814,7 @@ class Automation extends MetadataType { delete metadata.schedule; delete metadata.type; let i = 0; + const buName = this.buObject.credential + '/' + this.buObject.businessUnit; if (metadata.steps) { for (const step of metadata.steps) { let displayOrder = 0; @@ -823,6 +824,23 @@ class Automation extends MetadataType { activity.name && this.definition.dependencies.includes(activity.r__type) ) { + if ( + activity.r__type === 'verification' && + this.createdKeyMap?.[buName]?.verification?.[activity.name] + ) { + Util.logger.info( + Util.getGrayMsg( + ` - updated verification activity name from ${ + activity.name + } to ${ + this.createdKeyMap[buName].verification[activity.name] + }` + ) + ); + // map structure: cred/bu --> type --> old key --> new key + activity.name = + this.createdKeyMap[buName].verification[activity.name]; + } // automations can have empty placeholder for activities with only their type defined activity.activityObjectId = cache.searchForField( activity.r__type, diff --git a/lib/metadataTypes/Verification.js b/lib/metadataTypes/Verification.js index 891b9382f..93ce29fd0 100644 --- a/lib/metadataTypes/Verification.js +++ b/lib/metadataTypes/Verification.js @@ -151,6 +151,15 @@ class Verification extends MetadataType { } automatically assigned during creation` ); metadataEntry[this.definition.idField] = apiResponse?.[this.definition.idField]; + + // map structure: cred/bu --> type --> old key --> new key + const buName = this.buObject.credential + '/' + this.buObject.businessUnit; + Automation.createdKeyMap ||= {}; + Automation.createdKeyMap[buName] ||= {}; + Automation.createdKeyMap[buName][this.definition.type] ||= {}; + Automation.createdKeyMap[buName][this.definition.type][ + metadataEntryWithAllFields[this.definition.idField] + ] = metadataEntry[this.definition.idField]; } /** diff --git a/test/mockRoot/deploy/testInstance/testBU/automation/testNew_automation.automation-meta.json b/test/mockRoot/deploy/testInstance/testBU/automation/testNew_automation.automation-meta.json index 715b69f27..e27ad3c97 100644 --- a/test/mockRoot/deploy/testInstance/testBU/automation/testNew_automation.automation-meta.json +++ b/test/mockRoot/deploy/testInstance/testBU/automation/testNew_automation.automation-meta.json @@ -36,6 +36,10 @@ { "name": "testExisting_script", "r__type": "script" + }, + { + "name": "testNew_39f6a488-20eb-4ba0-b0b9", + "r__type": "verification" } ], "name": "" diff --git a/test/resources/9999999/automation/create-expected.json b/test/resources/9999999/automation/create-expected.json index 92ea826e1..794e0dff7 100644 --- a/test/resources/9999999/automation/create-expected.json +++ b/test/resources/9999999/automation/create-expected.json @@ -37,6 +37,10 @@ { "name": "testExisting_script", "r__type": "script" + }, + { + "name": "testNew_RANDOM_NEW_GUID", + "r__type": "verification" } ], "name": "" diff --git a/test/resources/9999999/automation/create-testNew_automation-expected.md b/test/resources/9999999/automation/create-testNew_automation-expected.md index ce52ff454..d9a7fe86c 100644 --- a/test/resources/9999999/automation/create-testNew_automation-expected.md +++ b/test/resources/9999999/automation/create-testNew_automation-expected.md @@ -26,3 +26,4 @@ | _1.4: importFile_
testExisting_importFile | | _1.5: query_
testExisting_query | | _1.6: script_
testExisting_script | +| _1.7: verification_
testNew_RANDOM_NEW_GUID | diff --git a/test/resources/9999999/automation/v1/automations/post-response.json b/test/resources/9999999/automation/v1/automations/post-response.json index ba11ebf1e..bacde7613 100644 --- a/test/resources/9999999/automation/v1/automations/post-response.json +++ b/test/resources/9999999/automation/v1/automations/post-response.json @@ -77,6 +77,13 @@ "activityObjectId": "39f6a488-20eb-4ba0-b0b9-023725b574e4", "objectTypeId": 423, "displayOrder": 6 + }, + { + "id": "f3774dc2-a271-4a44-8cbe-f630a6d6545e", + "name": "testExisting_dataExtension", + "activityObjectId": "testNew_RANDOM_NEW_GUID", + "objectTypeId": 1000, + "displayOrder": 7 } ], "annotation": "", diff --git a/test/type.automation.test.js b/test/type.automation.test.js index 4a1acb0b7..b85ac4473 100644 --- a/test/type.automation.test.js +++ b/test/type.automation.test.js @@ -61,7 +61,11 @@ describe('type: automation', () => { }); it('Should create & update a automation', async () => { // WHEN - const deployResult = await handler.deploy('testInstance/testBU', ['automation']); + const deployResult = await handler.deploy( + 'testInstance/testBU', + ['automation', 'verification'], + ['testExisting_automation', 'testNew_automation', 'testNew_39f6a488-20eb-4ba0-b0b9'] + ); // THEN assert.equal(process.exitCode, false, 'deploy should not have thrown an error'); @@ -119,7 +123,7 @@ describe('type: automation', () => { assert.equal( testUtils.getAPIHistoryLength(), - 21, + 25, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; @@ -129,8 +133,8 @@ describe('type: automation', () => { handler.setOptions({ schedule: true }); const deployed = await handler.deploy( 'testInstance/testBU', - ['automation'], - ['testExisting_automation', 'testNew_automation'] + ['automation', 'verification'], + ['testExisting_automation', 'testNew_automation', 'testNew_39f6a488-20eb-4ba0-b0b9'] ); // THEN assert.equal( @@ -188,7 +192,7 @@ describe('type: automation', () => { assert.equal( testUtils.getAPIHistoryLength(), - 29, + 33, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; @@ -198,8 +202,8 @@ describe('type: automation', () => { handler.setOptions({ execute: true }); const deployed = await handler.deploy( 'testInstance/testBU', - ['automation'], - ['testExisting_automation', 'testNew_automation'] + ['automation', 'verification'], + ['testExisting_automation', 'testNew_automation', 'testNew_39f6a488-20eb-4ba0-b0b9'] ); // THEN assert.equal( @@ -256,7 +260,7 @@ describe('type: automation', () => { assert.equal( testUtils.getAPIHistoryLength(), - 25, + 29, 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests' ); return; From 5ac58b38bafc6fd0db496e1d349a5bfb0c41c0c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Mon, 28 Aug 2023 09:49:41 +0200 Subject: [PATCH 60/70] #325: type verification renamed --- lib/metadataTypes/definitions/Verification.definition.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/metadataTypes/definitions/Verification.definition.js b/lib/metadataTypes/definitions/Verification.definition.js index 25e63ac5d..cbcad30c9 100644 --- a/lib/metadataTypes/definitions/Verification.definition.js +++ b/lib/metadataTypes/definitions/Verification.definition.js @@ -15,7 +15,7 @@ module.exports = { type: 'verification', typeDescription: 'Check DataExtension for a row count', typeRetrieveByDefault: true, - typeName: 'Automation: Data Verification Activity', + typeName: 'Automation: Verification Activity', fields: { createdBy: { // User ID From 0faad87c87c77c4981bd2359fe16829a919bfc48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Mon, 28 Aug 2023 15:53:16 +0200 Subject: [PATCH 61/70] #1088: ensure multi-BU retrieves do not interact badly with each other --- lib/metadataTypes/AttributeSet.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/metadataTypes/AttributeSet.js b/lib/metadataTypes/AttributeSet.js index 575531ee7..0a782c18c 100644 --- a/lib/metadataTypes/AttributeSet.js +++ b/lib/metadataTypes/AttributeSet.js @@ -345,8 +345,11 @@ class AttributeSet extends MetadataType { * @returns {object[]} all system value definitions */ static _getSystemValueDefinitions() { - if (!this.systemValueDefinitions) { - this.systemValueDefinitions = Object.values(cache.getCache()['attributeSet']) + this.systemValueDefinitions ||= {}; + if (!this.systemValueDefinitions[this.buObject.mid]) { + this.systemValueDefinitions[this.buObject.mid] = Object.values( + cache.getCache()['attributeSet'] + ) .flatMap((item) => { if (item.isSystemDefined) { return item.valueDefinitions; @@ -354,7 +357,7 @@ class AttributeSet extends MetadataType { }) .filter(Boolean); } - return this.systemValueDefinitions; + return this.systemValueDefinitions[this.buObject.mid]; } } From 7551affaafd7d31fbd8a356211cd529dfff91478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Mon, 28 Aug 2023 15:55:44 +0200 Subject: [PATCH 62/70] #0: reduce chatter in log files --- lib/metadataTypes/DataExtension.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index 6e96f463b..07dfe3637 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -835,9 +835,9 @@ class DataExtension extends MetadataType { this.buObject.eid ); if (!folderTypesFromParent.includes(folderContentType)) { - Util.logger.verbose( - `removing ${metadataEntry} because r__folder_ContentType '${folderContentType}' identifies this DE as not being shared` - ); + // Util.logger.verbose( + // `removing ${metadataEntry} because r__folder_ContentType '${folderContentType}' identifies this DE as not being shared` + // ); delete metadataParentBu[metadataEntry]; } } catch (ex) { From 4422bb61017200b3473a446aaaa21c3e0b80303d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 20:54:21 +0000 Subject: [PATCH 63/70] Bump eslint from 8.47.0 to 8.48.0 Bumps [eslint](https://github.com/eslint/eslint) from 8.47.0 to 8.48.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.47.0...v8.48.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 31 +++++++++++++++---------------- package.json | 2 +- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index f035492e1..5f99b6ef7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,6 @@ "console.table": "0.10.0", "deep-equal": "2.2.2", "fs-extra": "11.1.0", - "fsevents": "*", "inquirer": "8.2.6", "json-to-table": "4.2.1", "mustache": "4.2.0", @@ -39,7 +38,7 @@ "axios-mock-adapter": "1.21.5", "chai": "4.3.7", "chai-files": "1.4.0", - "eslint": "8.47.0", + "eslint": "8.48.0", "eslint-config-prettier": "9.0.0", "eslint-config-ssjs": "1.1.11", "eslint-plugin-jsdoc": "46.5.0", @@ -600,9 +599,9 @@ "dev": true }, "node_modules/@eslint/js": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", - "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3139,15 +3138,15 @@ } }, "node_modules/eslint": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", - "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "^8.47.0", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -10504,9 +10503,9 @@ } }, "@eslint/js": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", - "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", "dev": true }, "@humanwhocodes/config-array": { @@ -12437,15 +12436,15 @@ "dev": true }, "eslint": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", - "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "^8.47.0", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", diff --git a/package.json b/package.json index 94eb5c31e..124288652 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "axios-mock-adapter": "1.21.5", "chai": "4.3.7", "chai-files": "1.4.0", - "eslint": "8.47.0", + "eslint": "8.48.0", "eslint-config-prettier": "9.0.0", "eslint-config-ssjs": "1.1.11", "eslint-plugin-jsdoc": "46.5.0", From b2620a32453d883776c47e2f388337dd8fa22d04 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Aug 2023 07:43:31 +0000 Subject: [PATCH 64/70] Bump chai from 4.3.7 to 4.3.8 Bumps [chai](https://github.com/chaijs/chai) from 4.3.7 to 4.3.8. - [Release notes](https://github.com/chaijs/chai/releases) - [Changelog](https://github.com/chaijs/chai/blob/4.x.x/History.md) - [Commits](https://github.com/chaijs/chai/compare/v4.3.7...v4.3.8) --- updated-dependencies: - dependency-name: chai dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5f99b6ef7..86e352906 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,7 @@ "devDependencies": { "assert": "2.0.0", "axios-mock-adapter": "1.21.5", - "chai": "4.3.7", + "chai": "4.3.8", "chai-files": "1.4.0", "eslint": "8.48.0", "eslint-config-prettier": "9.0.0", @@ -1896,9 +1896,9 @@ } }, "node_modules/chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.8.tgz", + "integrity": "sha512-vX4YvVVtxlfSZ2VecZgFUTU5qPCYsobVI2O9FmwEXBhDigYGQA6jRXCycIs1yJnnWbZ6/+a2zNIF5DfVCcJBFQ==", "dev": true, "dependencies": { "assertion-error": "^1.1.0", @@ -11465,9 +11465,9 @@ } }, "chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.8.tgz", + "integrity": "sha512-vX4YvVVtxlfSZ2VecZgFUTU5qPCYsobVI2O9FmwEXBhDigYGQA6jRXCycIs1yJnnWbZ6/+a2zNIF5DfVCcJBFQ==", "dev": true, "requires": { "assertion-error": "^1.1.0", diff --git a/package.json b/package.json index 124288652..804530c47 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "devDependencies": { "assert": "2.0.0", "axios-mock-adapter": "1.21.5", - "chai": "4.3.7", + "chai": "4.3.8", "chai-files": "1.4.0", "eslint": "8.48.0", "eslint-config-prettier": "9.0.0", From 15bcd9d8e991282deb820e7cf1daff2aee0441fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 31 Aug 2023 16:12:56 +0200 Subject: [PATCH 65/70] #1097: adapted tests to include fields of type Decimal and Number as a baseline --- .../9999999/dataExtension/build-expected.json | 16 ++++++++ .../dataExtension/retrieve-expected.json | 16 ++++++++ .../dataExtension/retrieve-expected.md | 4 +- .../dataExtension/template-expected.json | 16 ++++++++ .../dataExtension/update-expected.json | 16 ++++++++ ...ey=testExisting_dataExtension-response.xml | 37 ++++++++++++++++++- .../dataExtensionField/retrieve-response.xml | 37 ++++++++++++++++++- 7 files changed, 139 insertions(+), 3 deletions(-) diff --git a/test/resources/9999999/dataExtension/build-expected.json b/test/resources/9999999/dataExtension/build-expected.json index f31d2e876..21d43fdbf 100644 --- a/test/resources/9999999/dataExtension/build-expected.json +++ b/test/resources/9999999/dataExtension/build-expected.json @@ -44,6 +44,22 @@ "IsRequired": true, "IsPrimaryKey": true, "FieldType": "Text" + }, + { + "Name": "decimalField", + "DefaultValue": "", + "MaxLength": 6, + "Scale": 3, + "IsPrimaryKey": false, + "IsRequired": true, + "FieldType": "Decimal" + }, + { + "Name": "numberField", + "DefaultValue": "", + "IsPrimaryKey": false, + "IsRequired": true, + "FieldType": "Number" } ], "r__folder_ContentType": "dataextension", diff --git a/test/resources/9999999/dataExtension/retrieve-expected.json b/test/resources/9999999/dataExtension/retrieve-expected.json index bf80cd122..c8c945cc3 100644 --- a/test/resources/9999999/dataExtension/retrieve-expected.json +++ b/test/resources/9999999/dataExtension/retrieve-expected.json @@ -44,6 +44,22 @@ "IsRequired": true, "IsPrimaryKey": true, "FieldType": "Text" + }, + { + "Name": "decimalField", + "DefaultValue": "", + "MaxLength": 6, + "Scale": 3, + "IsPrimaryKey": false, + "IsRequired": true, + "FieldType": "Decimal" + }, + { + "Name": "numberField", + "DefaultValue": "", + "IsPrimaryKey": false, + "IsRequired": true, + "FieldType": "Number" } ], "r__folder_ContentType": "dataextension", diff --git a/test/resources/9999999/dataExtension/retrieve-expected.md b/test/resources/9999999/dataExtension/retrieve-expected.md index fda321a2d..51cffe4b6 100644 --- a/test/resources/9999999/dataExtension/retrieve-expected.md +++ b/test/resources/9999999/dataExtension/retrieve-expected.md @@ -4,7 +4,7 @@ **Folder:** Data Extensions/ -**Fields in table:** 4 +**Fields in table:** 6 **Sendable:** Yes (`ContactKey` to `Subscriber Key`) @@ -16,3 +16,5 @@ | LastName | Text | 50 | - | + | | | EmailAddress | EmailAddress | 254 | - | - | | | ContactKey | Text | 50 | + | - | | +| decimalField | Decimal | 6 | - | - | | +| numberField | Number | | - | - | | diff --git a/test/resources/9999999/dataExtension/template-expected.json b/test/resources/9999999/dataExtension/template-expected.json index 5c3cd96b7..c2513cfa0 100644 --- a/test/resources/9999999/dataExtension/template-expected.json +++ b/test/resources/9999999/dataExtension/template-expected.json @@ -44,6 +44,22 @@ "IsRequired": true, "IsPrimaryKey": true, "FieldType": "Text" + }, + { + "Name": "decimalField", + "DefaultValue": "", + "MaxLength": 6, + "Scale": 3, + "IsPrimaryKey": false, + "IsRequired": true, + "FieldType": "Decimal" + }, + { + "Name": "numberField", + "DefaultValue": "", + "IsPrimaryKey": false, + "IsRequired": true, + "FieldType": "Number" } ], "r__folder_ContentType": "dataextension", diff --git a/test/resources/9999999/dataExtension/update-expected.json b/test/resources/9999999/dataExtension/update-expected.json index bc10677a2..7106168c8 100644 --- a/test/resources/9999999/dataExtension/update-expected.json +++ b/test/resources/9999999/dataExtension/update-expected.json @@ -41,6 +41,22 @@ "IsPrimaryKey": true, "FieldType": "Text" }, + { + "Name": "decimalField", + "DefaultValue": "", + "MaxLength": 6, + "Scale": 3, + "IsPrimaryKey": false, + "IsRequired": true, + "FieldType": "Decimal" + }, + { + "Name": "numberField", + "DefaultValue": "", + "IsPrimaryKey": false, + "IsRequired": true, + "FieldType": "Number" + }, { "Name": "testField", "DefaultValue": "", diff --git a/test/resources/9999999/dataExtensionField/retrieve-DataExtension.CustomerKey=testExisting_dataExtension-response.xml b/test/resources/9999999/dataExtensionField/retrieve-DataExtension.CustomerKey=testExisting_dataExtension-response.xml index 3ffc1b3eb..c927c2258 100644 --- a/test/resources/9999999/dataExtensionField/retrieve-DataExtension.CustomerKey=testExisting_dataExtension-response.xml +++ b/test/resources/9999999/dataExtensionField/retrieve-DataExtension.CustomerKey=testExisting_dataExtension-response.xml @@ -93,6 +93,41 @@ testExisting_dataExtension + + + 7b7ef009-4b85-455b-9bdf-b7bef93791d7 + [testExisting_dataExtension].[numberField] + numberField + 0 + + true + 5 + false + Number + + + + testExisting_dataExtension + + + + + c5d553cc-2c2a-464d-953d-6901be040f20 + [testExisting_dataExtension].[decimalField] + decimalField + 3 + + 6 + true + 4 + false + Decimal + + + + testExisting_dataExtension + + - \ No newline at end of file + diff --git a/test/resources/9999999/dataExtensionField/retrieve-response.xml b/test/resources/9999999/dataExtensionField/retrieve-response.xml index 3ffc1b3eb..c927c2258 100644 --- a/test/resources/9999999/dataExtensionField/retrieve-response.xml +++ b/test/resources/9999999/dataExtensionField/retrieve-response.xml @@ -93,6 +93,41 @@ testExisting_dataExtension + + + 7b7ef009-4b85-455b-9bdf-b7bef93791d7 + [testExisting_dataExtension].[numberField] + numberField + 0 + + true + 5 + false + Number + + + + testExisting_dataExtension + + + + + c5d553cc-2c2a-464d-953d-6901be040f20 + [testExisting_dataExtension].[decimalField] + decimalField + 3 + + 6 + true + 4 + false + Decimal + + + + testExisting_dataExtension + + - \ No newline at end of file + From a64a66a4f9a05468d81e4f7d679ff432bec45d81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 31 Aug 2023 16:22:30 +0200 Subject: [PATCH 66/70] #1097: fixed decimal length documentation in dataExtension markdowns --- lib/metadataTypes/DataExtension.js | 6 +++++- test/resources/9999999/dataExtension/retrieve-expected.md | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index 07dfe3637..86fe9e342 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -1215,7 +1215,11 @@ class DataExtension extends MetadataType { for (const element of fieldsJson) { const newJsonElement = {}; for (const field of fieldsToKeep) { - newJsonElement[field] = element[field]; + if (field === 'MaxLength' && element.FieldType === 'Decimal') { + newJsonElement.MaxLength = `${element.MaxLength},${element.Scale}`; + } else { + newJsonElement[field] = element[field]; + } } newJson.push(newJsonElement); } diff --git a/test/resources/9999999/dataExtension/retrieve-expected.md b/test/resources/9999999/dataExtension/retrieve-expected.md index 51cffe4b6..4f2221484 100644 --- a/test/resources/9999999/dataExtension/retrieve-expected.md +++ b/test/resources/9999999/dataExtension/retrieve-expected.md @@ -16,5 +16,5 @@ | LastName | Text | 50 | - | + | | | EmailAddress | EmailAddress | 254 | - | - | | | ContactKey | Text | 50 | + | - | | -| decimalField | Decimal | 6 | - | - | | +| decimalField | Decimal | 6,3 | - | - | | | numberField | Number | | - | - | | From ed60e34fa3b4ecf0c6e9426d9d935d67828230ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 31 Aug 2023 16:49:00 +0200 Subject: [PATCH 67/70] #0: manually applied hotfix for outdated sfmc-sdk dependency fast-xml-parser --- package-lock.json | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index 86e352906..683469afe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3628,7 +3628,6 @@ "version": "4.2.7", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.7.tgz", "integrity": "sha512-J8r6BriSLO1uj2miOk1NW0YVm8AGOOu3Si2HQp/cSmo6EA4m3fcwu2WKjJ4RK9wMLBtg69y1kS8baDiQBR41Ig==", - "dev": true, "funding": [ { "type": "paypal", @@ -8835,7 +8834,7 @@ "integrity": "sha512-0GdDnNa6jqoLGSHf3ByoQNAzlzdGxjSHeH55zFaY9tM4YPtSRgTrsBkZCh5xOBLwr4u7u2X9lbJpxbTWyULhpA==", "dependencies": { "axios": "^1.3.5", - "fast-xml-parser": "4.2.0", + "fast-xml-parser": "4.2.7", "p-limit": "3.1.0" }, "engines": { @@ -8843,20 +8842,27 @@ "npm": ">=6.14.4" } }, - "node_modules/sfmc-sdk/node_modules/fast-xml-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.0.tgz", - "integrity": "sha512-+zVQv4aVTO+o8oRUyRL7PjgeVo1J6oP8Cw2+a8UTZQcj5V0yUK5T63gTN0ldgiHDPghUjKc4OpT6SwMTwnOQug==", + "node_modules/sfmc-sdk/fast-xml-parser": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.7.tgz", + "integrity": "sha512-J8r6BriSLO1uj2miOk1NW0YVm8AGOOu3Si2HQp/cSmo6EA4m3fcwu2WKjJ4RK9wMLBtg69y1kS8baDiQBR41Ig==", + "extraneous": true, "dependencies": { "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" }, - "funding": { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - } + "funding": [ + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + }, + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ] }, "node_modules/shebang-command": { "version": "2.0.0", @@ -12780,7 +12786,6 @@ "version": "4.2.7", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.7.tgz", "integrity": "sha512-J8r6BriSLO1uj2miOk1NW0YVm8AGOOu3Si2HQp/cSmo6EA4m3fcwu2WKjJ4RK9wMLBtg69y1kS8baDiQBR41Ig==", - "dev": true, "requires": { "strnum": "^1.0.5" } @@ -16595,18 +16600,8 @@ "integrity": "sha512-0GdDnNa6jqoLGSHf3ByoQNAzlzdGxjSHeH55zFaY9tM4YPtSRgTrsBkZCh5xOBLwr4u7u2X9lbJpxbTWyULhpA==", "requires": { "axios": "^1.3.5", - "fast-xml-parser": "4.2.0", + "fast-xml-parser": "4.2.7", "p-limit": "3.1.0" - }, - "dependencies": { - "fast-xml-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.0.tgz", - "integrity": "sha512-+zVQv4aVTO+o8oRUyRL7PjgeVo1J6oP8Cw2+a8UTZQcj5V0yUK5T63gTN0ldgiHDPghUjKc4OpT6SwMTwnOQug==", - "requires": { - "strnum": "^1.0.5" - } - } } }, "shebang-command": { From 4f125f7a7daa36d73dc8c9914308470ac30fd52c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 31 Aug 2023 16:50:49 +0200 Subject: [PATCH 68/70] #0: semver dependency hotfix --- package-lock.json | 100 +++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 51 deletions(-) diff --git a/package-lock.json b/package-lock.json index 683469afe..1ff0c6351 100644 --- a/package-lock.json +++ b/package-lock.json @@ -154,9 +154,9 @@ } }, "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -195,9 +195,9 @@ } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -4089,9 +4089,7 @@ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, "optional": true, - "os": [ - "darwin" - ], + "os": ["darwin"], "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } @@ -5369,9 +5367,9 @@ } }, "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -6334,9 +6332,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -6886,9 +6884,9 @@ } }, "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -7169,9 +7167,9 @@ } }, "node_modules/npm-run-all/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -7671,9 +7669,9 @@ } }, "node_modules/package-json/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -8806,9 +8804,9 @@ } }, "node_modules/semver-diff/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -10163,9 +10161,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -10194,9 +10192,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -13995,9 +13993,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -14729,9 +14727,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -15149,9 +15147,9 @@ }, "dependencies": { "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true } } @@ -15368,9 +15366,9 @@ "dev": true }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, "shebang-command": { @@ -15745,9 +15743,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -16573,9 +16571,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, From 96120fe56f4b06006b4eec869df326cf1e1e0037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 31 Aug 2023 16:57:21 +0200 Subject: [PATCH 69/70] 5.3.0 --- package-lock.json | 8 +++++--- package.json | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1ff0c6351..f288104a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mcdev", - "version": "5.2.0", + "version": "5.3.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "mcdev", - "version": "5.2.0", + "version": "5.3.0", "license": "MIT", "dependencies": { "beauty-amp-core": "0.3.7", @@ -4089,7 +4089,9 @@ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, "optional": true, - "os": ["darwin"], + "os": [ + "darwin" + ], "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } diff --git a/package.json b/package.json index 804530c47..a8d1faa23 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mcdev", - "version": "5.2.0", + "version": "5.3.0", "description": "Accenture Salesforce Marketing Cloud DevTools", "author": "Accenture: joern.berkefeld, douglas.midgley, robert.zimmermann, maciej.barnas", "license": "MIT", From e4e4f47e45fcf553cff8f1b24d2118a75183b37d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 31 Aug 2023 17:02:10 +0200 Subject: [PATCH 70/70] #0: release 5.3 prep --- .github/ISSUE_TEMPLATE/bug.yml | 1 + test/mockRoot/.mcdevrc.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index a5c9d99d7..461f21e53 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: + - 5.3.0 - 5.2.0 - 5.1.0 - 5.0.2 diff --git a/test/mockRoot/.mcdevrc.json b/test/mockRoot/.mcdevrc.json index bc3be43a4..30c384a28 100644 --- a/test/mockRoot/.mcdevrc.json +++ b/test/mockRoot/.mcdevrc.json @@ -78,5 +78,5 @@ "triggeredSend" ] }, - "version": "5.2.0" + "version": "5.3.0" }