diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md
index 4b89a1cf6..0df8883e8 100644
--- a/docs/dist/documentation.md
+++ b/docs/dist/documentation.md
@@ -67,6 +67,12 @@ as this is a configuration in the EID
Filter ⇐ MetadataType
Filter MetadataType
+FilterDefinition ⇐ MetadataType
+FilterDefinition MetadataType
+
+FilterDefinitionHidden ⇐ FilterDefinitionHidden
+FilterDefinitionHidden MetadataType
+
Folder ⇐ MetadataType
Folder MetadataType
@@ -303,6 +309,16 @@ helper for DataExtension.#fixShared_item
skipInteraction : object
signals what to insert automatically for things usually asked via wizard
+FilterMap : object
+
+FilterDefinitionSOAPItemMap : object
+
+AutomationFilterDefinitionItem : object
+/automation/v1/filterdefinitions/ (not used)
+
+FilterDefinitionMap : object
+/email/v1/filters/filterdefinition/
+
AuthObject : object
SoapFilter : object
@@ -2868,16 +2884,22 @@ Filter MetadataType
**Kind**: global class
**Extends**: [MetadataType
](#MetadataType)
+
+* [Filter](#Filter) ⇐ [MetadataType
](#MetadataType)
+ * [.retrieve(retrieveDir, [_], [__], [key])](#Filter.retrieve) ⇒ Promise.<{metadata: TYPE.FilterMap, type: string}>
+ * [.postRetrieveTasks(metadata)](#Filter.postRetrieveTasks) ⇒ TYPE.FilterItem
+ * [.preDeployTasks(metadata)](#Filter.preDeployTasks) ⇒ Promise.<TYPE.FilterItem>
+
-### Filter.retrieve(retrieveDir, [_], [__], [key]) ⇒ Promise.<TYPE.MetadataTypeMapObj>
+### Filter.retrieve(retrieveDir, [_], [__], [key]) ⇒ Promise.<{metadata: TYPE.FilterMap, type: string}>
Retrieves Metadata of Filter.
Endpoint /automation/v1/filters/ returns all Filters,
but only with some of the fields. So it is needed to loop over
Filters with the endpoint /automation/v1/filters/{id}
**Kind**: static method of [Filter
](#Filter)
-**Returns**: Promise.<TYPE.MetadataTypeMapObj>
- Promise
+**Returns**: Promise.<{metadata: TYPE.FilterMap, type: string}>
- Promise of items
| Param | Type | Description |
| --- | --- | --- |
@@ -2886,6 +2908,204 @@ Filters with the endpoint /automation/v1/filters/{id}
| [__] | void
| unused parameter |
| [key] | string
| customer key of single item to retrieve |
+
+
+### Filter.postRetrieveTasks(metadata) ⇒ TYPE.FilterItem
+parses retrieved Metadata before saving
+
+**Kind**: static method of [Filter
](#Filter)
+**Returns**: TYPE.FilterItem
- parsed metadata definition
+
+| Param | Type | Description |
+| --- | --- | --- |
+| metadata | TYPE.FilterItem
| a single record |
+
+
+
+### Filter.preDeployTasks(metadata) ⇒ Promise.<TYPE.FilterItem>
+prepares a record for deployment
+
+**Kind**: static method of [Filter
](#Filter)
+**Returns**: Promise.<TYPE.FilterItem>
- Promise of updated single record
+
+| Param | Type | Description |
+| --- | --- | --- |
+| metadata | TYPE.FilterItem
| a single record |
+
+
+
+## FilterDefinition ⇐ [MetadataType
](#MetadataType)
+FilterDefinition MetadataType
+
+**Kind**: global class
+**Extends**: [MetadataType
](#MetadataType)
+
+* [FilterDefinition](#FilterDefinition) ⇐ [MetadataType
](#MetadataType)
+ * [.retrieve(retrieveDir, [_], [__], [key])](#FilterDefinition.retrieve) ⇒ Promise.<{metadata: TYPE.FilterDefinitionMap, type: string}>
+ * [.getFilterFolderIds([hidden])](#FilterDefinition.getFilterFolderIds) ⇒ Array.<number>
+ * [.getMeasureFolderIds()](#FilterDefinition.getMeasureFolderIds) ⇒ Array.<number>
+ * [.cacheDeFields(metadataTypeMapObj)](#FilterDefinition.cacheDeFields)
+ * [.cacheContactAttributes(metadataTypeMapObj)](#FilterDefinition.cacheContactAttributes)
+ * [.cacheMeasures(metadataTypeMapObj)](#FilterDefinition.cacheMeasures)
+ * [.retrieveForCache()](#FilterDefinition.retrieveForCache) ⇒ Promise.<{metadata: TYPE.FilterDefinitionMap, type: string}>
+ * [.postRetrieveTasks(metadata)](#FilterDefinition.postRetrieveTasks) ⇒ TYPE.FilterDefinitionItem
+ * [.resolveFieldIds(metadata, [fieldCache], [filter])](#FilterDefinition.resolveFieldIds) ⇒ void
+ * [.resolveAttributeIds(metadata, [filter])](#FilterDefinition.resolveAttributeIds) ⇒ void
+ * [.preDeployTasks(metadata)](#FilterDefinition.preDeployTasks) ⇒ Promise.<TYPE.FilterDefinitionItem>
+ * [.create(metadata)](#FilterDefinition.create) ⇒ Promise.<TYPE.FilterDefinitionItem>
+ * [.update(metadata)](#FilterDefinition.update) ⇒ Promise.<TYPE.FilterDefinitionItem>
+
+
+
+### FilterDefinition.retrieve(retrieveDir, [_], [__], [key]) ⇒ Promise.<{metadata: TYPE.FilterDefinitionMap, type: string}>
+Retrieves all records and saves it to disk
+
+**Kind**: static method of [FilterDefinition
](#FilterDefinition)
+**Returns**: Promise.<{metadata: TYPE.FilterDefinitionMap, type: string}>
- Promise of items
+
+| 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 |
+
+
+
+### FilterDefinition.getFilterFolderIds([hidden]) ⇒ Array.<number>
+helper for [retrieve](#FilterDefinition.retrieve)
+
+**Kind**: static method of [FilterDefinition
](#FilterDefinition)
+**Returns**: Array.<number>
- Array of folder IDs
+
+| Param | Type | Default | Description |
+| --- | --- | --- | --- |
+| [hidden] | boolean
| false
| used to filter out hidden or non-hidden filterDefinitions |
+
+
+
+### FilterDefinition.getMeasureFolderIds() ⇒ Array.<number>
+helper for [retrieve](#FilterDefinition.retrieve)
+
+**Kind**: static method of [FilterDefinition
](#FilterDefinition)
+**Returns**: Array.<number>
- Array of folder IDs
+
+
+### FilterDefinition.cacheDeFields(metadataTypeMapObj)
+**Kind**: static method of [FilterDefinition
](#FilterDefinition)
+
+| Param | Type | Description |
+| --- | --- | --- |
+| metadataTypeMapObj | TYPE.MultiMetadataTypeMap
| - |
+
+
+
+### FilterDefinition.cacheContactAttributes(metadataTypeMapObj)
+**Kind**: static method of [FilterDefinition
](#FilterDefinition)
+
+| Param | Type | Description |
+| --- | --- | --- |
+| metadataTypeMapObj | TYPE.MultiMetadataTypeMap
| - |
+
+
+
+### FilterDefinition.cacheMeasures(metadataTypeMapObj)
+**Kind**: static method of [FilterDefinition
](#FilterDefinition)
+
+| Param | Type | Description |
+| --- | --- | --- |
+| metadataTypeMapObj | TYPE.MultiMetadataTypeMap
| - |
+
+
+
+### FilterDefinition.retrieveForCache() ⇒ Promise.<{metadata: TYPE.FilterDefinitionMap, type: string}>
+Retrieves all records for caching
+
+**Kind**: static method of [FilterDefinition
](#FilterDefinition)
+**Returns**: Promise.<{metadata: TYPE.FilterDefinitionMap, type: string}>
- Promise of items
+
+
+### FilterDefinition.postRetrieveTasks(metadata) ⇒ TYPE.FilterDefinitionItem
+parses retrieved Metadata before saving
+
+**Kind**: static method of [FilterDefinition
](#FilterDefinition)
+**Returns**: TYPE.FilterDefinitionItem
- parsed metadata definition
+
+| Param | Type | Description |
+| --- | --- | --- |
+| metadata | TYPE.FilterDefinitionItem
| a single record |
+
+
+
+### FilterDefinition.resolveFieldIds(metadata, [fieldCache], [filter]) ⇒ void
+**Kind**: static method of [FilterDefinition
](#FilterDefinition)
+
+| Param | Type | Description |
+| --- | --- | --- |
+| metadata | TYPE.FilterDefinitionItem
| - |
+| [fieldCache] | Array.<object>
| - |
+| [filter] | object
| - |
+
+
+
+### FilterDefinition.resolveAttributeIds(metadata, [filter]) ⇒ void
+**Kind**: static method of [FilterDefinition
](#FilterDefinition)
+
+| Param | Type | Description |
+| --- | --- | --- |
+| metadata | TYPE.FilterDefinitionItem
| - |
+| [filter] | object
| - |
+
+
+
+### FilterDefinition.preDeployTasks(metadata) ⇒ Promise.<TYPE.FilterDefinitionItem>
+prepares a item for deployment
+
+**Kind**: static method of [FilterDefinition
](#FilterDefinition)
+**Returns**: Promise.<TYPE.FilterDefinitionItem>
- Promise of updated single item
+
+| Param | Type | Description |
+| --- | --- | --- |
+| metadata | TYPE.FilterDefinitionItem
| a single record |
+
+
+
+### FilterDefinition.create(metadata) ⇒ Promise.<TYPE.FilterDefinitionItem>
+Creates a single item
+
+**Kind**: static method of [FilterDefinition
](#FilterDefinition)
+**Returns**: Promise.<TYPE.FilterDefinitionItem>
- Promise
+
+| Param | Type | Description |
+| --- | --- | --- |
+| metadata | TYPE.FilterDefinitionItem
| a single item |
+
+
+
+### FilterDefinition.update(metadata) ⇒ Promise.<TYPE.FilterDefinitionItem>
+Updates a single item
+
+**Kind**: static method of [FilterDefinition
](#FilterDefinition)
+**Returns**: Promise.<TYPE.FilterDefinitionItem>
- Promise
+
+| Param | Type | Description |
+| --- | --- | --- |
+| metadata | TYPE.FilterDefinitionItem
| a single item |
+
+
+
+## FilterDefinitionHidden ⇐ [FilterDefinitionHidden
](#FilterDefinitionHidden)
+FilterDefinitionHidden MetadataType
+
+**Kind**: global class
+**Extends**: [FilterDefinitionHidden
](#FilterDefinitionHidden)
+
+
+### FilterDefinitionHidden.getFilterFolderIds() ⇒ Array.<number>
+helper for [retrieve](#FilterDefinition.retrieve)
+
+**Kind**: static method of [FilterDefinitionHidden
](#FilterDefinitionHidden)
+**Returns**: Array.<number>
- Array of folder IDs
## Folder ⇐ [MetadataType
](#MetadataType)
@@ -9304,6 +9524,124 @@ signals what to insert automatically for things usually asked via wizard
| credentialName | string
| how you would like the credential to be named |
| gitRemoteUrl | string
| URL of Git remote server |
+
+
+## FilterMap : object
+**Kind**: global typedef
+**Properties**
+
+| Name | Type | Description |
+| --- | --- | --- |
+| categoryId | number
| folder id |
+| [createdDate] | string
| - |
+| customerKey | string
| key |
+| destinationObjectId | string
| DE/List ID |
+| destinationTypeId | 1
\| 2
\| 3
\| 4
| 1:SubscriberList, 2:DataExtension, 3:GroupWizard, 4:BehavioralData |
+| filterActivityId | string
| ? |
+| filterDefinitionId | string
| ObjectID of filterDefinition |
+| modifiedDate | string
| - |
+| name | string
| name |
+| sourceObjectId | string
| DE/List ID |
+| sourceTypeId | 1
\| 2
\| 3
\| 4
| 1:SubscriberList, 2:DataExtension, 3:GroupWizard, 4:BehavioralData |
+| statusId | number
| ? |
+
+
+
+## FilterDefinitionSOAPItemMap : object
+**Kind**: global typedef
+**Properties**
+
+| Name | Type | Description |
+| --- | --- | --- |
+| ObjectID | string
| id |
+| CustomerKey | string
| key |
+| [DataFilter] | object
| most relevant part that defines the filter |
+| DataFilter.LeftOperand | object
| - |
+| DataFilter.LeftOperand.Property | string
| - |
+| DataFilter.LeftOperand.SimpleOperator | string
| - |
+| DataFilter.LeftOperand.Value | string
| - |
+| DataFilter.LogicalOperator | string
| - |
+| [DataFilter.RightOperand] | object
| - |
+| DataFilter.RightOperand.Property | string
| - |
+| DataFilter.RightOperand.SimpleOperator | string
| - |
+| DataFilter.RightOperand.Value | string
| - |
+| Name | string
| name |
+| Description | string
| - |
+| [ObjectState] | string
| returned from SOAP API; used to return error messages |
+
+
+
+## AutomationFilterDefinitionItem : object
+/automation/v1/filterdefinitions/ (not used)
+
+**Kind**: global typedef
+**Properties**
+
+| Name | Type | Description |
+| --- | --- | --- |
+| id | string
| object id |
+| key | string
| external key |
+| createdDate | string
| - |
+| createdBy | number
| user id |
+| createdName | string
| - |
+| [description] | string
| (omitted by API if empty) |
+| modifiedDate | string
| - |
+| modifiedBy | number
| user id |
+| modifiedName | string
| - |
+| name | string
| name |
+| categoryId | string
| folder id |
+| filterDefinitionXml | string
| from REST API defines the filter in XML form |
+| derivedFromType | 1
\| 2
| 1:list/profile attributes/measures, 2: dataExtension |
+| isSendable | boolean
| ? |
+| [soap__DataFilter] | object
| copied from SOAP API, defines the filter in readable form |
+| soap__DataFilter.LeftOperand | object
| - |
+| soap__DataFilter.LeftOperand.Property | string
| - |
+| soap__DataFilter.LeftOperand.SimpleOperator | string
| - |
+| soap__DataFilter.LeftOperand.Value | string
| - |
+| soap__DataFilter.LogicalOperator | string
| - |
+| [soap__DataFilter.RightOperand] | object
| - |
+| soap__DataFilter.RightOperand.Property | string
| - |
+| soap__DataFilter.RightOperand.SimpleOperator | string
| - |
+| soap__DataFilter.RightOperand.Value | string
| - |
+
+
+
+## FilterDefinitionMap : object
+/email/v1/filters/filterdefinition/
+
+**Kind**: global typedef
+**Properties**
+
+| Name | Type | Description |
+| --- | --- | --- |
+| id | string
| object id |
+| key | string
| external key |
+| createdDate | string
| date |
+| createdBy | number
| user id |
+| createdName | string
| name |
+| [description] | string
| (omitted by API if empty) |
+| lastUpdated | string
| date |
+| lastUpdatedBy | number
| user id |
+| lastUpdatedName | string
| name |
+| name | string
| name |
+| categoryId | string
| folder id |
+| filterDefinitionXml | string
| from REST API defines the filter in XML form |
+| derivedFromType | 1
\| 2
| 1:list/profile attributes/measures, 2: dataExtension |
+| derivedFromObjectId | string
| Id of DataExtension - present if derivedFromType=2 |
+| derivedFromObjectTypeName | 'DataExtension'
\| 'SubscriberAttributes'
| - |
+| [derivedFromObjectName] | string
| name of DataExtension |
+| isSendable | boolean
| ? |
+| [soap__DataFilter] | object
| copied from SOAP API, defines the filter in readable form |
+| soap__DataFilter.LeftOperand | object
| - |
+| soap__DataFilter.LeftOperand.Property | string
| - |
+| soap__DataFilter.LeftOperand.SimpleOperator | string
| - |
+| soap__DataFilter.LeftOperand.Value | string
| - |
+| soap__DataFilter.LogicalOperator | string
| - |
+| [soap__DataFilter.RightOperand] | object
| - |
+| soap__DataFilter.RightOperand.Property | string
| - |
+| soap__DataFilter.RightOperand.SimpleOperator | string
| - |
+| soap__DataFilter.RightOperand.Value | string
| - |
+
## AuthObject : object
diff --git a/lib/MetadataTypeDefinitions.js b/lib/MetadataTypeDefinitions.js
index e6c0e9331..68da505fa 100644
--- a/lib/MetadataTypeDefinitions.js
+++ b/lib/MetadataTypeDefinitions.js
@@ -22,6 +22,8 @@ const MetadataTypeDefinitions = {
fileLocation: require('./metadataTypes/definitions/FileLocation.definition'),
fileTransfer: require('./metadataTypes/definitions/FileTransfer.definition'),
filter: require('./metadataTypes/definitions/Filter.definition'),
+ filterDefinition: require('./metadataTypes/definitions/FilterDefinition.definition'),
+ filterDefinitionHidden: require('./metadataTypes/definitions/FilterDefinitionHidden.definition'),
folder: require('./metadataTypes/definitions/Folder.definition'),
importFile: require('./metadataTypes/definitions/ImportFile.definition'),
journey: require('./metadataTypes/definitions/Journey.definition'),
diff --git a/lib/MetadataTypeInfo.js b/lib/MetadataTypeInfo.js
index 40d7bafce..6f4112bc4 100644
--- a/lib/MetadataTypeInfo.js
+++ b/lib/MetadataTypeInfo.js
@@ -22,6 +22,8 @@ const MetadataTypeInfo = {
fileLocation: require('./metadataTypes/FileLocation'),
fileTransfer: require('./metadataTypes/FileTransfer'),
filter: require('./metadataTypes/Filter'),
+ filterDefinition: require('./metadataTypes/FilterDefinition'),
+ filterDefinitionHidden: require('./metadataTypes/FilterDefinitionHidden'),
folder: require('./metadataTypes/Folder'),
importFile: require('./metadataTypes/ImportFile'),
journey: require('./metadataTypes/Journey'),
diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js
index 07dfe3637..43c8be922 100644
--- a/lib/metadataTypes/DataExtension.js
+++ b/lib/metadataTypes/DataExtension.js
@@ -1341,7 +1341,7 @@ class DataExtension extends MetadataType {
* @returns {Promise.<{metadata: TYPE.DataExtensionMap, type: string}>} Promise
*/
static async retrieveForCache() {
- return this.retrieve(null, ['ObjectID', 'CustomerKey', 'Name'], this.buObject, null, null);
+ return this.retrieve(null, ['ObjectID', 'CustomerKey', 'Name']);
}
/**
* Retrieves dataExtension metadata in template format.
diff --git a/lib/metadataTypes/Filter.js b/lib/metadataTypes/Filter.js
index dafa8691f..214a6c3c6 100644
--- a/lib/metadataTypes/Filter.js
+++ b/lib/metadataTypes/Filter.js
@@ -2,6 +2,15 @@
const TYPE = require('../../types/mcdev.d');
const MetadataType = require('./MetadataType');
+const Util = require('../util/util');
+const cache = require('../util/cache');
+
+const dataTypes = {
+ 1: 'List',
+ 2: 'DataExtension',
+ 3: 'Group Wizard',
+ 4: 'Behavioral Data',
+};
/**
* Filter MetadataType
@@ -19,11 +28,179 @@ class Filter extends MetadataType {
* @param {void} [_] unused parameter
* @param {void} [__] unused parameter
* @param {string} [key] customer key of single item to retrieve
- * @returns {Promise.} Promise
+ * @returns {Promise.<{metadata: TYPE.FilterMap, type: string}>} Promise of items
*/
static async retrieve(retrieveDir, _, __, key) {
return super.retrieveREST(retrieveDir, '/automation/v1/filters/', null, key);
}
+ /**
+ * parses retrieved Metadata before saving
+ *
+ * @param {TYPE.FilterItem} metadata a single record
+ * @returns {TYPE.FilterItem} parsed metadata definition
+ */
+ static postRetrieveTasks(metadata) {
+ // folder
+ this.setFolderPath(metadata);
+
+ try {
+ // filterDefinition
+ metadata.r__filterDefinition_CustomerKey = cache.searchForField(
+ 'filterDefinition',
+ metadata.filterDefinitionId,
+ 'id',
+ 'key'
+ );
+ delete metadata.filterDefinitionId;
+ } catch {
+ try {
+ // filterDefinition
+ metadata.r__filterDefinition_CustomerKey = cache.searchForField(
+ 'filterDefinitionHidden',
+ metadata.filterDefinitionId,
+ 'id',
+ 'key'
+ );
+ delete metadata.filterDefinitionId;
+ } catch {
+ // ignore
+ }
+ }
+ try {
+ // source
+ if (metadata.sourceTypeId === 1) {
+ // list
+ } else if (metadata.sourceTypeId === 2) {
+ // dataExtension
+ metadata.r__source_dataExtension_CustomerKey = cache.searchForField(
+ 'dataExtension',
+ metadata.sourceObjectId,
+ 'ObjectID',
+ 'CustomerKey'
+ );
+ delete metadata.sourceObjectId;
+ delete metadata.sourceTypeId;
+ } else {
+ Util.logger.warn(
+ ` - Filter '${metadata.name}' (${
+ metadata.customerKey
+ }): Unsupported source type ${metadata.sourceTypeId}=${
+ dataTypes[metadata.sourceTypeId]
+ }`
+ );
+ }
+ } catch (ex) {
+ Util.logger.warn(
+ ` - filter '${metadata.name}' (${metadata.customerKey}): Destination not found (${ex.message})`
+ );
+ }
+ try {
+ // target
+ if (metadata.destinationTypeId === 1) {
+ // list
+ } else if (metadata.destinationTypeId === 2) {
+ // dataExtension
+ metadata.r__destination_dataExtension_CustomerKey = cache.searchForField(
+ 'dataExtension',
+ metadata.destinationObjectId,
+ 'ObjectID',
+ 'CustomerKey'
+ );
+ delete metadata.destinationObjectId;
+ delete metadata.destinationTypeId;
+ } else {
+ Util.logger.warn(
+ ` - filter '${metadata.name}' (${
+ metadata.customerKey
+ }): Unsupported destination type ${metadata.destinationTypeId}=${
+ dataTypes[metadata.destinationTypeId]
+ }`
+ );
+ }
+ } catch (ex) {
+ Util.logger.warn(
+ ` - filter '${metadata.name}' (${metadata.customerKey}): Source not found (${ex.message})`
+ );
+ }
+ return metadata;
+ }
+ /**
+ * prepares a record for deployment
+ *
+ * @param {TYPE.FilterItem} metadata a single record
+ * @returns {Promise.} Promise of updated single record
+ */
+ static async preDeployTasks(metadata) {
+ // folder
+ if (metadata.r__folder_Path) {
+ metadata.categoryId = cache.searchForField(
+ 'folder',
+ metadata.r__folder_Path,
+ 'Path',
+ 'ID'
+ );
+ delete metadata.r__folder_Path;
+ }
+
+ // filterDefinition
+ if (metadata.r__filterDefinition_CustomerKey) {
+ metadata.filterDefinitionId = cache.searchForField(
+ 'filterDefinition',
+ metadata.r__filterDefinition_CustomerKey,
+ 'CustomerKey',
+ 'ObjectID'
+ );
+ delete metadata.r__filterDefinition_CustomerKey;
+ }
+
+ // source
+ if (metadata.sourceTypeId === 1) {
+ // list
+ } else if (metadata.r__source_dataExtension_CustomerKey) {
+ // dataExtension
+ metadata.sourceObjectId = cache.searchForField(
+ 'dataExtension',
+ metadata.r__source_dataExtension_CustomerKey,
+ 'CustomerKey',
+ 'ObjectID'
+ );
+ metadata.sourceTypeId = 2;
+ delete metadata.r__source_dataExtension_CustomerKey;
+ } else {
+ // assume the type id is still in the metadata
+ throw new Error(
+ ` - Filter '${metadata.name}' (${metadata.customerKey}): Unsupported source type ${
+ metadata.sourceTypeId
+ }=${dataTypes[metadata.sourceTypeId]}`
+ );
+ }
+
+ // target
+ if (metadata.destinationTypeId === 1) {
+ // list
+ } else if (metadata.r__destination_dataExtension_CustomerKey) {
+ // dataExtension
+ metadata.destinationObjectId = cache.searchForField(
+ 'dataExtension',
+ metadata.r__destination_dataExtension_CustomerKey,
+ 'CustomerKey',
+ 'ObjectID'
+ );
+ metadata.destinationTypeId = 2;
+ delete metadata.r__destination_dataExtension_CustomerKey;
+ } else {
+ // assume the type id is still in the metadata
+ throw new Error(
+ ` - Filter '${metadata.name}' (${
+ metadata.customerKey
+ }): Unsupported destination type ${metadata.destinationTypeId}=${
+ dataTypes[metadata.destinationTypeId]
+ }`
+ );
+ }
+
+ return metadata;
+ }
}
// Assign definition to static attributes
diff --git a/lib/metadataTypes/FilterDefinition.js b/lib/metadataTypes/FilterDefinition.js
new file mode 100644
index 000000000..6c480fed2
--- /dev/null
+++ b/lib/metadataTypes/FilterDefinition.js
@@ -0,0 +1,523 @@
+'use strict';
+
+const TYPE = require('../../types/mcdev.d');
+const MetadataType = require('./MetadataType');
+const DataExtensionField = require('./DataExtensionField');
+const Folder = require('./Folder');
+const Util = require('../util/util');
+const cache = require('../util/cache');
+const { XMLBuilder, XMLParser } = require('fast-xml-parser');
+
+/**
+ * FilterDefinition MetadataType
+ *
+ * @augments MetadataType
+ */
+class FilterDefinition extends MetadataType {
+ static cache = {}; // type internal cache for various things
+ /**
+ * Retrieves all records and saves it to disk
+ *
+ * @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.<{metadata: TYPE.FilterDefinitionMap, type: string}>} Promise of items
+ */
+ static async retrieve(retrieveDir, _, __, key) {
+ const filterFolders = await this.getFilterFolderIds();
+
+ const metadataTypeMapObj = { metadata: {}, type: this.definition.type };
+ for (const folderId of filterFolders) {
+ const metadataMapFolder = await super.retrieveREST(
+ null,
+ 'email/v1/filters/filterdefinition/category/' +
+ folderId +
+ '?derivedFromType=1,2,3,4&',
+ null,
+ key
+ );
+ if (Object.keys(metadataMapFolder.metadata).length) {
+ metadataTypeMapObj.metadata = {
+ ...metadataTypeMapObj.metadata,
+ ...metadataMapFolder.metadata,
+ };
+ if (key) {
+ // if key was found we can stop checking other folders
+ break;
+ }
+ }
+ }
+ // console.log('metadataMap', metadataMap);
+
+ for (const item of Object.values(metadataTypeMapObj.metadata)) {
+ // description is not returned when emptyg
+ item.description ||= '';
+ }
+ if (retrieveDir) {
+ // custom dataExtensionField caching
+ await this.cacheDeFields(metadataTypeMapObj);
+ await this.cacheContactAttributes(metadataTypeMapObj);
+ await this.cacheMeasures(metadataTypeMapObj);
+
+ const savedMetadata = await this.saveResults(metadataTypeMapObj.metadata, retrieveDir);
+ Util.logger.info(
+ `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` +
+ Util.getKeysString(key)
+ );
+ }
+
+ return metadataTypeMapObj;
+ }
+ /**
+ * helper for {@link FilterDefinition.retrieve}
+ *
+ * @param {boolean} [hidden] used to filter out hidden or non-hidden filterDefinitions
+ * @returns {number[]} Array of folder IDs
+ */
+ static async getFilterFolderIds(hidden = false) {
+ const fromCache =
+ this.cache.folderFilter || cache.getCache().folder
+ ? Object.values(this.cache.folderFilter || cache.getCache().folder)
+ .filter((item) => item.ContentType === 'filterdefinition')
+ .filter(
+ (item) =>
+ (!hidden && item.Path.startsWith('Data Filters')) ||
+ (hidden && !item.Path.startsWith('Data Filters'))
+ ) // only retrieve from Data Filters folder
+ .map((item) => item.ID)
+ : [];
+ if (fromCache.length) {
+ return fromCache;
+ }
+
+ const subTypeArr = ['hidden', 'filterdefinition'];
+ Util.logger.info(` - Caching dependent Metadata: folder`);
+ Util.logSubtypes(subTypeArr);
+
+ Folder.client = this.client;
+ Folder.buObject = this.buObject;
+ Folder.properties = this.properties;
+ this.cache.folderFilter = (await Folder.retrieveForCache(null, subTypeArr)).metadata;
+ return this.getFilterFolderIds(hidden);
+ }
+ /**
+ * helper for {@link FilterDefinition.retrieve}
+ *
+ * @returns {number[]} Array of folder IDs
+ */
+ static async getMeasureFolderIds() {
+ const fromCache =
+ this.cache.folderMeasure?.[this.buObject.mid] || cache.getCache().folder
+ ? Object.values(
+ this.cache.folderMeasure?.[this.buObject.mid] || cache.getCache().folder
+ )
+ .filter((item) => item.ContentType === 'measure')
+ .map((item) => item.ID)
+ : [];
+ if (fromCache.length) {
+ return fromCache;
+ }
+
+ const subTypeArr = ['measure'];
+ Util.logger.info(` - Caching dependent Metadata: folder`);
+ Util.logSubtypes(subTypeArr);
+
+ Folder.client = this.client;
+ Folder.buObject = this.buObject;
+ Folder.properties = this.properties;
+ this.cache.folderMeasure ||= {};
+ this.cache.folderMeasure[this.buObject.mid] = (
+ await Folder.retrieveForCache(null, subTypeArr)
+ ).metadata;
+ return this.getMeasureFolderIds();
+ }
+
+ /**
+ *
+ * @param {TYPE.MultiMetadataTypeMap} metadataTypeMapObj -
+ */
+ static async cacheDeFields(metadataTypeMapObj) {
+ const deKeys = Object.values(metadataTypeMapObj.metadata)
+ .filter((item) => item.derivedFromObjectTypeName === 'DataExtension')
+ .filter((item) => item.derivedFromObjectId)
+ .map((item) => {
+ try {
+ const deKey = cache.searchForField(
+ 'dataExtension',
+ item.derivedFromObjectId,
+ 'ObjectID',
+ 'CustomerKey'
+ );
+ if (deKey) {
+ this.deIdKeyMap ||= {};
+ this.deIdKeyMap[item.derivedFromObjectId] = deKey;
+ return deKey;
+ }
+ } catch {
+ return null;
+ }
+ })
+ .filter(Boolean);
+ if (deKeys.length) {
+ Util.logger.info(' - Caching dependent Metadata: dataExtensionField');
+ // only proceed with the download if we have dataExtension keys
+ const fieldOptions = {};
+ for (const deKey of deKeys) {
+ fieldOptions.filter = fieldOptions.filter
+ ? {
+ leftOperand: {
+ leftOperand: 'DataExtension.CustomerKey',
+ operator: 'equals',
+ rightOperand: deKey,
+ },
+ operator: 'OR',
+ rightOperand: fieldOptions.filter,
+ }
+ : {
+ leftOperand: 'DataExtension.CustomerKey',
+ operator: 'equals',
+ rightOperand: deKey,
+ };
+ }
+ DataExtensionField.buObject = this.buObject;
+ DataExtensionField.client = this.client;
+ DataExtensionField.properties = this.properties;
+ this.dataExtensionFieldCache = (
+ await DataExtensionField.retrieveForCache(fieldOptions, ['Name', 'ObjectID'])
+ ).metadata;
+ }
+ }
+ /**
+ *
+ * @param {TYPE.MultiMetadataTypeMap} metadataTypeMapObj -
+ */
+ static async cacheContactAttributes(metadataTypeMapObj) {
+ if (this.cache.contactAttributes?.[this.buObject.mid]) {
+ return;
+ }
+ const subscriberFilters = Object.values(metadataTypeMapObj.metadata)
+ .filter((item) => item.derivedFromObjectTypeName === 'SubscriberAttributes')
+ .filter((item) => item.derivedFromObjectId);
+ if (subscriberFilters.length) {
+ Util.logger.info(' - Caching dependent Metadata: contactAttributes');
+ const response = await this.client.rest.get('/email/v1/Contacts/Attributes/');
+ const keyFieldBackup = this.definition.keyField;
+ this.definition.keyField = 'id';
+ this.cache.contactAttributes ||= {};
+ this.cache.contactAttributes[this.buObject.mid] = this.parseResponseBody(response);
+ this.definition.keyField = keyFieldBackup;
+ }
+ }
+ /**
+ *
+ * @param {TYPE.MultiMetadataTypeMap} metadataTypeMapObj -
+ */
+ static async cacheMeasures(metadataTypeMapObj) {
+ if (this.cache.measures?.[this.buObject.mid]) {
+ return;
+ }
+ const subscriberFilters = Object.values(metadataTypeMapObj.metadata)
+ .filter((item) => item.derivedFromObjectTypeName === 'SubscriberAttributes')
+ .filter((item) => item.derivedFromObjectId);
+ const measureFolders = await this.getMeasureFolderIds();
+ if (subscriberFilters.length) {
+ Util.logger.info(' - Caching dependent Metadata: measure');
+ const response = { items: [] };
+ for (const folderId of measureFolders) {
+ const metadataMapFolder = await this.client.rest.getBulk(
+ 'email/v1/Measures/category/' + folderId + '/',
+ 250 // 250 is what the GUI is using
+ );
+ if (Object.keys(metadataMapFolder.items).length) {
+ response.items.push(...metadataMapFolder.items);
+ }
+ }
+
+ const keyFieldBackup = this.definition.keyField;
+ this.definition.keyField = 'measureID';
+ this.cache.measures ||= {};
+ this.cache.measures[this.buObject.mid] = this.parseResponseBody(response);
+ this.definition.keyField = keyFieldBackup;
+ }
+ }
+
+ /**
+ * Retrieves all records for caching
+ *
+ * @returns {Promise.<{metadata: TYPE.FilterDefinitionMap, type: string}>} Promise of items
+ */
+ static async retrieveForCache() {
+ return this.retrieve(null);
+ }
+
+ /**
+ * parses retrieved Metadata before saving
+ *
+ * @param {TYPE.FilterDefinitionItem} metadata a single record
+ * @returns {TYPE.FilterDefinitionItem} parsed metadata definition
+ */
+ static async postRetrieveTasks(metadata) {
+ if (metadata.derivedFromType > 4) {
+ // GUI only shows types 1,2,3,4; lets mimic that here.
+ // type 6 seems to be journey related. Maybe we need to change that again in the future
+ return;
+ }
+ // folder
+ this.setFolderPath(metadata);
+
+ // parse XML filter for further processing in JSON format
+ const xmlToJson = new XMLParser({ ignoreAttributes: false });
+ metadata.c__filterDefinition = xmlToJson.parse(
+ metadata.filterDefinitionXml
+ )?.FilterDefinition;
+ delete metadata.filterDefinitionXml;
+
+ switch (metadata.derivedFromType) {
+ case 1: {
+ if (metadata.c__filterDefinition['@_Source'] === 'SubscriberAttribute') {
+ if (
+ metadata.derivedFromObjectId &&
+ metadata.derivedFromObjectId !== '00000000-0000-0000-0000-000000000000'
+ ) {
+ // Lists
+ try {
+ metadata.r__source_list_PathName = cache.getListPathName(
+ metadata.derivedFromObjectId,
+ 'ObjectID'
+ );
+ } catch {
+ Util.logger.warn(
+ ` - skipping ${this.definition.type} ${metadata.key}: list ${metadata.derivedFromObjectId} not found on current or Parent BU`
+ );
+ // return;
+ }
+ } else {
+ // SubscriberAttributes
+ // - nothing to do
+ }
+ }
+
+ break;
+ }
+ case 2: {
+ // DataExtension + XXX?
+ if (
+ metadata.c__filterDefinition['@_Source'] === 'Meta' ||
+ metadata.derivedFromObjectId === '00000000-0000-0000-0000-000000000000'
+ ) {
+ // TODO - weird so far not understood case of Source=Meta
+ // sample:
+ } else if (metadata.c__filterDefinition['@_Source'] === 'DataExtension') {
+ // DataExtension
+ try {
+ metadata.r__source_dataExtension_CustomerKey =
+ this.deIdKeyMap?.[metadata.derivedFromObjectId] ||
+ cache.searchForField(
+ 'dataExtension',
+ metadata.derivedFromObjectId,
+ 'ObjectID',
+ 'CustomerKey'
+ );
+ } catch {
+ Util.logger.debug(
+ ` - skipping ${this.definition.type} ${metadata.key}: dataExtension ${metadata.derivedFromObjectId} not found on BU`
+ );
+ return;
+ }
+ }
+ break;
+ }
+ case 3: {
+ // TODO
+ break;
+ }
+ case 4: {
+ // TODO
+ break;
+ }
+ case 5: {
+ // TODO
+ break;
+ }
+ case 6: {
+ // TODO
+ break;
+ }
+ }
+
+ // map Condition ID to fields ID
+ switch (metadata.derivedFromType) {
+ case 1: {
+ // SubscriberAttributes
+ this.resolveAttributeIds(metadata);
+ delete metadata.derivedFromObjectId;
+ delete metadata.derivedFromType;
+ delete metadata.c__filterDefinition['@_Source'];
+ break;
+ }
+ case 2: {
+ if (metadata.c__filterDefinition['@_Source'] === 'Meta') {
+ // TODO - weird so far not understood case of Source=Meta
+ // sample:
+ } else if (metadata.c__filterDefinition['@_Source'] === 'DataExtension') {
+ // DataExtension
+ this.resolveFieldIds(metadata);
+ delete metadata.derivedFromObjectId;
+ delete metadata.derivedFromType;
+ delete metadata.c__filterDefinition['@_Source'];
+ delete metadata.c__filterDefinition['@_SourceID'];
+ }
+ break;
+ }
+ case 3: {
+ // TODO
+ break;
+ }
+ case 4: {
+ // TODO
+ break;
+ }
+ case 5: {
+ // TODO
+ break;
+ }
+ case 6: {
+ // TODO
+ break;
+ }
+ }
+ return metadata;
+ }
+
+ /**
+ *
+ * @param {TYPE.FilterDefinitionItem} metadata -
+ * @param {object[]} [fieldCache] -
+ * @param {object} [filter] -
+ * @returns {void}
+ */
+ static resolveFieldIds(metadata, fieldCache, filter) {
+ if (!filter) {
+ return this.resolveFieldIds(
+ metadata,
+ Object.values(this.dataExtensionFieldCache),
+ metadata.c__filterDefinition?.ConditionSet
+ );
+ }
+ const conditionsArr = Array.isArray(filter.Condition)
+ ? filter.Condition
+ : [filter.Condition];
+ for (const condition of conditionsArr) {
+ condition.r__dataExtensionField = fieldCache.find(
+ (field) => field.ObjectID === condition['@_ID']
+ )?.Name;
+ delete condition['@_ID'];
+ if (['IsEmpty', 'IsNotEmpty'].includes(condition['@_Operator'])) {
+ delete condition.Value;
+ }
+ }
+ if (filter.ConditionSet) {
+ this.resolveFieldIds(metadata, fieldCache, filter.ConditionSet);
+ }
+ }
+ /**
+ *
+ * @param {TYPE.FilterDefinitionItem} metadata -
+ * @param {object} [filter] -
+ * @returns {void}
+ */
+ static resolveAttributeIds(metadata, filter) {
+ if (!filter) {
+ return this.resolveAttributeIds(metadata, metadata.c__filterDefinition?.ConditionSet);
+ }
+ const contactAttributes = this.cache.contactAttributes[this.buObject.mid];
+ const measures = this.cache.measures[this.buObject.mid];
+ const conditionsArr = Array.isArray(filter.Condition)
+ ? filter.Condition
+ : [filter.Condition];
+ for (const condition of conditionsArr) {
+ condition['@_ID'] += '';
+ if (condition['@_SourceType'] === 'Measure' && measures[condition['@_ID']]) {
+ condition.r__measure = measures[condition['@_ID']]?.name;
+ delete condition['@_ID'];
+ } else if (
+ condition['@_SourceType'] !== 'Measure' &&
+ contactAttributes[condition['@_ID']]
+ ) {
+ condition.r__contactAttribute = contactAttributes[condition['@_ID']]?.name;
+ delete condition['@_ID'];
+ }
+ if (['IsEmpty', 'IsNotEmpty'].includes(condition['@_Operator'])) {
+ delete condition.Value;
+ }
+ }
+ if (filter.ConditionSet) {
+ this.resolveAttributeIds(metadata, filter.ConditionSet);
+ }
+ }
+
+ /**
+ * prepares a item for deployment
+ *
+ * @param {TYPE.FilterDefinitionItem} metadata a single record
+ * @returns {Promise.} Promise of updated single item
+ */
+ static async preDeployTasks(metadata) {
+ // folder
+ super.setFolderId(metadata);
+
+ if (metadata.derivedFromObjectTypeName === 'SubscriberAttributes') {
+ // SubscriberAttributes
+ metadata.derivedFromType = 1;
+ metadata.derivedFromObjectId = '00000000-0000-0000-0000-000000000000';
+ } else {
+ // DataExtension
+ metadata.derivedFromType = 2;
+
+ if (metadata.r__dataExtension_CustomerKey) {
+ metadata.derivedFromObjectId = cache.searchForField(
+ 'dataExtension',
+ metadata.r__dataExtension_CustomerKey,
+ 'CustomerKey',
+ 'ObjectID'
+ );
+ delete metadata.r__dataExtension_CustomerKey;
+ }
+ }
+
+ const jsonToXml = new XMLBuilder({ ignoreAttributes: false });
+ metadata.filterDefinitionXml = jsonToXml.build(metadata.c__filterDefinition);
+ delete metadata.c__filterDefinition;
+ delete metadata.c__soap_DataFilter;
+
+ return metadata;
+ }
+ /**
+ * Creates a single item
+ *
+ * @param {TYPE.FilterDefinitionItem} metadata a single item
+ * @returns {Promise.} Promise
+ */
+ static create(metadata) {
+ // TODO test the create
+ return super.createREST(metadata, '/email/v1/filters/filterdefinition/');
+ }
+ /**
+ * Updates a single item
+ *
+ * @param {TYPE.FilterDefinitionItem} metadata a single item
+ * @returns {Promise.} Promise
+ */
+ static update(metadata) {
+ // TODO test the update
+ return super.updateREST(
+ metadata,
+ '/email/v1/filters/filterdefinition/' + metadata[this.definition.idField]
+ );
+ }
+}
+// Assign definition to static attributes
+FilterDefinition.definition = require('../MetadataTypeDefinitions').filterDefinition;
+
+module.exports = FilterDefinition;
diff --git a/lib/metadataTypes/FilterDefinitionHidden.js b/lib/metadataTypes/FilterDefinitionHidden.js
new file mode 100644
index 000000000..634fb9a44
--- /dev/null
+++ b/lib/metadataTypes/FilterDefinitionHidden.js
@@ -0,0 +1,24 @@
+'use strict';
+
+// const TYPE = require('../../types/mcdev.d');
+const FilterDefinition = require('./FilterDefinition');
+
+/**
+ * FilterDefinitionHidden MetadataType
+ *
+ * @augments FilterDefinitionHidden
+ */
+class FilterDefinitionHidden extends FilterDefinition {
+ /**
+ * helper for {@link FilterDefinition.retrieve}
+ *
+ * @returns {number[]} Array of folder IDs
+ */
+ static async getFilterFolderIds() {
+ return super.getFilterFolderIds(true);
+ }
+}
+// Assign definition to static attributes
+FilterDefinitionHidden.definition = require('../MetadataTypeDefinitions').filterDefinitionHidden;
+
+module.exports = FilterDefinitionHidden;
diff --git a/lib/metadataTypes/Verification.js b/lib/metadataTypes/Verification.js
index 93ce29fd0..6e967521b 100644
--- a/lib/metadataTypes/Verification.js
+++ b/lib/metadataTypes/Verification.js
@@ -189,6 +189,10 @@ class Verification extends MetadataType {
'ObjectID'
);
delete metadata.r__dataExtension_CustomerKey;
+ metadata.notificationEmailAddress = Array.isArray(metadata.notificationEmailAddress)
+ ? metadata.notificationEmailAddress.map((item) => item.trim()).join(',')
+ : Array.isArray(metadata.notificationEmailAddress);
+
return metadata;
}
/**
@@ -211,6 +215,9 @@ class Verification extends MetadataType {
` - ${this.definition.type} ${metadata[this.definition.keyField]}: ${ex.message}`
);
}
+ metadata.notificationEmailAddress = metadata.notificationEmailAddress
+ .split(',')
+ .map((item) => item.trim());
return metadata;
}
/**
diff --git a/lib/metadataTypes/definitions/Automation.definition.js b/lib/metadataTypes/definitions/Automation.definition.js
index 656bcf659..d6054bb9f 100644
--- a/lib/metadataTypes/definitions/Automation.definition.js
+++ b/lib/metadataTypes/definitions/Automation.definition.js
@@ -27,6 +27,7 @@ module.exports = {
'dataExtract',
'emailSend',
'fileTransfer',
+ 'filter',
'folder-automations',
'importFile',
'query',
diff --git a/lib/metadataTypes/definitions/Filter.definition.js b/lib/metadataTypes/definitions/Filter.definition.js
index ef6499104..5fbea7be4 100644
--- a/lib/metadataTypes/definitions/Filter.definition.js
+++ b/lib/metadataTypes/definitions/Filter.definition.js
@@ -1,11 +1,20 @@
module.exports = {
bodyIteratorField: 'items',
- dependencies: [],
+ dependencies: [
+ 'filterDefinition',
+ 'filterDefinitionHidden',
+ 'list',
+ 'dataExtension',
+ 'folder-filteractivity',
+ 'folder-hidden',
+ ],
hasExtended: false,
- idField: 'id',
+ idField: 'filterActivityId',
keyIsFixed: null,
keyField: 'customerKey',
nameField: 'name',
+ folderType: 'filteractivity',
+ folderIdField: 'categoryId',
createdDateField: 'createdDate',
createdNameField: null,
lastmodDateField: 'modifiedDate',
@@ -14,56 +23,58 @@ module.exports = {
maxKeyLength: 36, // confirmed max length
type: 'filter',
typeDescription:
- 'BETA: Part of how filtered Data Extensions are created. Depends on type "FilterDefinitions".',
- typeRetrieveByDefault: false,
+ 'Used in automations to filter lists and DEs. Depends on type "FilterDefinitions".',
+ typeRetrieveByDefault: true,
typeName: 'Automation: Filter Activity',
fields: {
+ // https://developer.salesforce.com/docs/atlas.en-us.noversion.mc-apis.meta/mc-apis/filteractivity.htm
categoryId: {
- isCreateable: false,
- isUpdateable: false,
+ isCreateable: true,
+ isUpdateable: true,
retrieving: true,
- template: false,
+ template: true,
},
createdDate: {
isCreateable: false,
isUpdateable: false,
- retrieving: true,
+ retrieving: false,
template: false,
},
customerKey: {
- isCreateable: null,
- isUpdateable: false,
+ isCreateable: true,
+ isUpdateable: true,
retrieving: true,
- template: false,
+ template: true,
},
description: {
- isCreateable: false,
- isUpdateable: false,
+ isCreateable: true,
+ isUpdateable: true,
retrieving: true,
- template: false,
+ template: true,
},
destinationObjectId: {
- isCreateable: false,
- isUpdateable: false,
+ isCreateable: true,
+ isUpdateable: true,
retrieving: true,
- template: false,
+ template: true,
},
destinationTypeId: {
- isCreateable: false,
- isUpdateable: false,
+ isCreateable: true,
+ isUpdateable: true,
retrieving: true,
- template: false,
+ template: true,
},
filterActivityId: {
- isCreateable: null,
- isUpdateable: null,
- retrieving: true,
+ isCreateable: false,
+ isUpdateable: true,
+ retrieving: false,
+ template: false,
},
filterDefinitionId: {
- isCreateable: false,
- isUpdateable: false,
+ isCreateable: true,
+ isUpdateable: true,
retrieving: true,
- template: false,
+ template: true,
},
modifiedDate: {
isCreateable: false,
@@ -72,28 +83,28 @@ module.exports = {
template: false,
},
name: {
- isCreateable: null,
- isUpdateable: false,
+ isCreateable: true,
+ isUpdateable: true,
retrieving: true,
- template: false,
+ template: true,
},
sourceObjectId: {
- isCreateable: false,
- isUpdateable: false,
+ isCreateable: true,
+ isUpdateable: true,
retrieving: true,
- template: false,
+ template: true,
},
sourceTypeId: {
- isCreateable: false,
- isUpdateable: false,
+ isCreateable: true,
+ isUpdateable: true,
retrieving: true,
- template: false,
+ template: true,
},
statusId: {
- isCreateable: false,
- isUpdateable: false,
+ isCreateable: true,
+ isUpdateable: true,
retrieving: true,
- template: false,
+ template: true,
},
},
};
diff --git a/lib/metadataTypes/definitions/FilterDefinition.definition.js b/lib/metadataTypes/definitions/FilterDefinition.definition.js
new file mode 100644
index 000000000..b8299c046
--- /dev/null
+++ b/lib/metadataTypes/definitions/FilterDefinition.definition.js
@@ -0,0 +1,155 @@
+module.exports = {
+ bodyIteratorField: 'items',
+ dependencies: ['folder-filterdefinition', 'folder-hidden', 'dataExtension'],
+ filter: {},
+ hasExtended: false,
+ idField: 'id',
+ keyField: 'key',
+ nameField: 'name',
+ folderType: 'filterdefinition',
+ folderIdField: 'categoryId',
+ createdDateField: 'createdDate',
+ createdNameField: 'createdBy',
+ lastmodDateField: 'lastUpdated',
+ lastmodNameField: 'lastUpdatedBy',
+ restPagination: true,
+ restPageSize: 100,
+ type: 'filterDefinition',
+ typeDescription: 'Defines an audience based on specified rules. Used by Filter Activities.',
+ typeRetrieveByDefault: true,
+ typeName: 'Filter Definition',
+ fields: {
+ // the GUI seems to ONLY send fields during update that are actually changed. It has yet to be tested if that also works with sending other fiedls as well
+ id: {
+ isCreateable: false,
+ isUpdateable: false, // included in URL
+ retrieving: false,
+ template: false,
+ },
+ key: {
+ isCreateable: true,
+ isUpdateable: true,
+ retrieving: true,
+ template: true,
+ },
+ createdDate: {
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: false,
+ template: false,
+ },
+ createdBy: {
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: false,
+ template: false,
+ },
+ createdByName: {
+ // actual name of user indicated by id in createdBy
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: false,
+ template: false,
+ },
+ lastUpdated: {
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: false,
+ template: false,
+ },
+ lastUpdatedBy: {
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: false,
+ template: false,
+ },
+ lastUpdatedByName: {
+ // actual name of user indicated by id in lastUpdatedBy
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: false,
+ template: false,
+ },
+ name: {
+ isCreateable: true,
+ isUpdateable: true,
+ retrieving: true,
+ template: true,
+ },
+ categoryId: {
+ // returned by GET / CREATE / UPDATE; used in CREATE payload
+ isCreateable: true,
+ isUpdateable: true,
+ retrieving: true,
+ template: true,
+ },
+ description: {
+ isCreateable: true,
+ isUpdateable: true,
+ retrieving: true,
+ template: true,
+ },
+ filterDefinitionXml: {
+ isCreateable: true,
+ isUpdateable: true,
+ retrieving: true,
+ template: true,
+ },
+ // DerivedFromType: {
+ // // this upper-cased spelling is used by GUI when creating a dataExtension based filterDefintion
+ // isCreateable: true,
+ // isUpdateable: false, // cannot be updated
+ // retrieving: false,
+ // template: false,
+ // },
+ derivedFromType: {
+ // 1: SubscriberAttributes, 2: DataExtension, 6: EntryCriteria;
+ isCreateable: true,
+ isUpdateable: false, // cannot be updated
+ retrieving: true,
+ template: true,
+ },
+ derivedFromObjectId: {
+ // dataExtension ID or '00000000-0000-0000-0000-000000000000' for lists
+ isCreateable: true,
+ isUpdateable: false, // cannot be updated
+ retrieving: true,
+ template: true,
+ },
+ derivedFromObjectTypeName: {
+ // "SubscriberAttributes" | "DataExtension" | "EntryCriteria" ...; only returned by GET API
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: true,
+ template: true,
+ },
+ derivedFromObjectName: {
+ // dataExtension name; field only returned by GET-API
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: true,
+ template: true,
+ },
+ isSendable: {
+ isCreateable: false, // automatically set during create
+ isUpdateable: false,
+ retrieving: true,
+ template: true,
+ },
+ r__dataExtension_CustomerKey: {
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: true,
+ template: true,
+ },
+ c__filterDefinition: {
+ skipValidation: true,
+ },
+ r__folder_Path: {
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: true,
+ template: true,
+ },
+ },
+};
diff --git a/lib/metadataTypes/definitions/FilterDefinitionHidden.definition.js b/lib/metadataTypes/definitions/FilterDefinitionHidden.definition.js
new file mode 100644
index 000000000..60b8a7c70
--- /dev/null
+++ b/lib/metadataTypes/definitions/FilterDefinitionHidden.definition.js
@@ -0,0 +1,156 @@
+module.exports = {
+ bodyIteratorField: 'items',
+ dependencies: ['folder-filterdefinition', 'folder-hidden', 'dataExtension', 'list'],
+ filter: {},
+ hasExtended: false,
+ idField: 'id',
+ keyField: 'key',
+ nameField: 'name',
+ folderType: 'filterdefinition',
+ folderIdField: 'categoryId',
+ createdDateField: 'createdDate',
+ createdNameField: 'createdBy',
+ lastmodDateField: 'lastUpdated',
+ lastmodNameField: 'lastUpdatedBy',
+ restPagination: true,
+ restPageSize: 100,
+ type: 'filterDefinitionHidden',
+ typeDescription:
+ 'Defines an audience based on specified rules. Used by filtered DEs and filtered Lists.',
+ typeRetrieveByDefault: false,
+ typeName: 'Filter Definition for filtered Lists && Data Extensions',
+ fields: {
+ // the GUI seems to ONLY send fields during update that are actually changed. It has yet to be tested if that also works with sending other fiedls as well
+ id: {
+ isCreateable: false,
+ isUpdateable: false, // included in URL
+ retrieving: false,
+ template: false,
+ },
+ key: {
+ isCreateable: true,
+ isUpdateable: true,
+ retrieving: true,
+ template: true,
+ },
+ createdDate: {
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: false,
+ template: false,
+ },
+ createdBy: {
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: false,
+ template: false,
+ },
+ createdByName: {
+ // actual name of user indicated by id in createdBy
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: false,
+ template: false,
+ },
+ lastUpdated: {
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: false,
+ template: false,
+ },
+ lastUpdatedBy: {
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: false,
+ template: false,
+ },
+ lastUpdatedByName: {
+ // actual name of user indicated by id in lastUpdatedBy
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: false,
+ template: false,
+ },
+ name: {
+ isCreateable: true,
+ isUpdateable: true,
+ retrieving: true,
+ template: true,
+ },
+ categoryId: {
+ // returned by GET / CREATE / UPDATE; used in CREATE payload
+ isCreateable: true,
+ isUpdateable: true,
+ retrieving: true,
+ template: true,
+ },
+ description: {
+ isCreateable: true,
+ isUpdateable: true,
+ retrieving: true,
+ template: true,
+ },
+ filterDefinitionXml: {
+ isCreateable: true,
+ isUpdateable: true,
+ retrieving: true,
+ template: true,
+ },
+ // DerivedFromType: {
+ // // this upper-cased spelling is used by GUI when creating a dataExtension based filterDefintion
+ // isCreateable: true,
+ // isUpdateable: false, // cannot be updated
+ // retrieving: false,
+ // template: false,
+ // },
+ derivedFromType: {
+ // 1: SubscriberAttributes, 2: DataExtension, 6: EntryCriteria;
+ isCreateable: true,
+ isUpdateable: false, // cannot be updated
+ retrieving: true,
+ template: true,
+ },
+ derivedFromObjectId: {
+ // dataExtension ID or '00000000-0000-0000-0000-000000000000' for lists
+ isCreateable: true,
+ isUpdateable: false, // cannot be updated
+ retrieving: true,
+ template: true,
+ },
+ derivedFromObjectTypeName: {
+ // "SubscriberAttributes" | "DataExtension" | "EntryCriteria" ...; only returned by GET API
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: true,
+ template: true,
+ },
+ derivedFromObjectName: {
+ // dataExtension name; field only returned by GET-API
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: true,
+ template: true,
+ },
+ isSendable: {
+ isCreateable: false, // automatically set during create
+ isUpdateable: false,
+ retrieving: true,
+ template: true,
+ },
+ r__dataExtension_CustomerKey: {
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: true,
+ template: true,
+ },
+ c__filterDefinition: {
+ skipValidation: true,
+ },
+ r__folder_Path: {
+ isCreateable: false,
+ isUpdateable: false,
+ retrieving: true,
+ template: true,
+ },
+ },
+};
diff --git a/package-lock.json b/package-lock.json
index 86e352906..011f186c6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,6 +15,7 @@
"conf": "10.2.0",
"console.table": "0.10.0",
"deep-equal": "2.2.2",
+ "fast-xml-parser": "4.2.7",
"fs-extra": "11.1.0",
"inquirer": "8.2.6",
"json-to-table": "4.2.1",
@@ -45,7 +46,6 @@
"eslint-plugin-mocha": "10.1.0",
"eslint-plugin-prettier": "4.2.1",
"eslint-plugin-unicorn": "48.0.0",
- "fast-xml-parser": "4.2.7",
"husky": "8.0.3",
"jsdoc-to-markdown": "8.0.0",
"lint-staged": "14.0.1",
@@ -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"
@@ -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",
@@ -5370,9 +5369,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"
@@ -6335,9 +6334,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"
}
@@ -6887,9 +6886,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"
@@ -7170,9 +7169,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"
@@ -7672,9 +7671,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"
}
@@ -8807,9 +8806,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"
}
@@ -10157,9 +10156,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
}
}
@@ -10188,9 +10187,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
}
}
@@ -12780,7 +12779,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"
}
@@ -13990,9 +13988,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
}
}
@@ -14724,9 +14722,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=="
}
}
},
@@ -15144,9 +15142,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
}
}
@@ -15363,9 +15361,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": {
@@ -15740,9 +15738,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=="
}
}
},
@@ -16568,9 +16566,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=="
}
}
},
diff --git a/package.json b/package.json
index 804530c47..6414c2c38 100644
--- a/package.json
+++ b/package.json
@@ -64,6 +64,7 @@
"conf": "10.2.0",
"console.table": "0.10.0",
"deep-equal": "2.2.2",
+ "fast-xml-parser": "4.2.7",
"fs-extra": "11.1.0",
"inquirer": "8.2.6",
"json-to-table": "4.2.1",
@@ -91,7 +92,6 @@
"eslint-plugin-mocha": "10.1.0",
"eslint-plugin-prettier": "4.2.1",
"eslint-plugin-unicorn": "48.0.0",
- "fast-xml-parser": "4.2.7",
"husky": "8.0.3",
"jsdoc-to-markdown": "8.0.0",
"lint-staged": "14.0.1",
@@ -106,8 +106,6 @@
"fsevents": "*"
},
"lint-staged": {
- "*.{js,jsx,ts,tsx}": [
- "eslint --fix"
- ]
+ "*.{js,jsx,ts,tsx}": ["eslint --fix"]
}
}
diff --git a/test/resources/9999999/verification/build-expected.json b/test/resources/9999999/verification/build-expected.json
index f717cc626..98d32e01c 100644
--- a/test/resources/9999999/verification/build-expected.json
+++ b/test/resources/9999999/verification/build-expected.json
@@ -1,6 +1,6 @@
{
"dataVerificationDefinitionId": "testTemplated_39f6a488-20eb-4ba0-b0b9",
- "notificationEmailAddress": "",
+ "notificationEmailAddress": [""],
"notificationEmailMessage": "",
"r__dataExtension_CustomerKey": "testTemplated_dataExtension",
"shouldEmailOnFailure": false,
diff --git a/test/resources/9999999/verification/get-expected.json b/test/resources/9999999/verification/get-expected.json
index 1f9c1825b..1be889a98 100644
--- a/test/resources/9999999/verification/get-expected.json
+++ b/test/resources/9999999/verification/get-expected.json
@@ -1,6 +1,6 @@
{
"dataVerificationDefinitionId": "testExisting_39f6a488-20eb-4ba0-b0b9",
- "notificationEmailAddress": "",
+ "notificationEmailAddress": [""],
"notificationEmailMessage": "",
"r__dataExtension_CustomerKey": "testExisting_dataExtension",
"shouldEmailOnFailure": false,
diff --git a/test/resources/9999999/verification/patch-expected.json b/test/resources/9999999/verification/patch-expected.json
index dc8d975d2..dde339242 100644
--- a/test/resources/9999999/verification/patch-expected.json
+++ b/test/resources/9999999/verification/patch-expected.json
@@ -1,6 +1,6 @@
{
"dataVerificationDefinitionId": "testExisting_39f6a488-20eb-4ba0-b0b9",
- "notificationEmailAddress": "test@accenture.com",
+ "notificationEmailAddress": ["test@accenture.com"],
"notificationEmailMessage": "",
"r__dataExtension_CustomerKey": "testExisting_dataExtension",
"shouldEmailOnFailure": true,
diff --git a/test/resources/9999999/verification/post-expected.json b/test/resources/9999999/verification/post-expected.json
index b06a77b76..41795da1f 100644
--- a/test/resources/9999999/verification/post-expected.json
+++ b/test/resources/9999999/verification/post-expected.json
@@ -6,6 +6,6 @@
"value2": 0,
"shouldStopOnFailure": false,
"shouldEmailOnFailure": false,
- "notificationEmailAddress": "",
+ "notificationEmailAddress": [""],
"notificationEmailMessage": ""
}
diff --git a/test/resources/9999999/verification/template-expected.json b/test/resources/9999999/verification/template-expected.json
index 04296b7ea..f75056e69 100644
--- a/test/resources/9999999/verification/template-expected.json
+++ b/test/resources/9999999/verification/template-expected.json
@@ -1,6 +1,6 @@
{
"dataVerificationDefinitionId": "{{{prefix}}}39f6a488-20eb-4ba0-b0b9",
- "notificationEmailAddress": "",
+ "notificationEmailAddress": [""],
"notificationEmailMessage": "",
"r__dataExtension_CustomerKey": "{{{prefix}}}dataExtension",
"shouldEmailOnFailure": false,
diff --git a/types/mcdev.d.js b/types/mcdev.d.js
index 0367a38f8..b167a342c 100644
--- a/types/mcdev.d.js
+++ b/types/mcdev.d.js
@@ -296,6 +296,105 @@ const SDK = require('sfmc-sdk');
* @property {string} gitRemoteUrl URL of Git remote server
*/
+/**
+ * @typedef {object} FilterItem
+ * @property {number} categoryId folder id
+ * @property {string} [createdDate] -
+ * @property {string} customerKey key
+ * @property {string} destinationObjectId DE/List ID
+ * @property {1|2|3|4} destinationTypeId 1:SubscriberList, 2:DataExtension, 3:GroupWizard, 4:BehavioralData
+ * @property {string} filterActivityId ?
+ * @property {string} filterDefinitionId ObjectID of filterDefinition
+ * @property {string} modifiedDate -
+ * @property {string} name name
+ * @property {string} sourceObjectId DE/List ID
+ * @property {1|2|3|4} sourceTypeId 1:SubscriberList, 2:DataExtension, 3:GroupWizard, 4:BehavioralData
+ * @property {number} statusId ?
+ * @typedef {Object.} FilterMap
+ */
+
+/**
+ * @typedef {object} FilterDefinitionSOAPItem
+ * @property {string} ObjectID id
+ * @property {string} CustomerKey key
+ * @property {object} [DataFilter] most relevant part that defines the filter
+ * @property {object} DataFilter.LeftOperand -
+ * @property {string} DataFilter.LeftOperand.Property -
+ * @property {string} DataFilter.LeftOperand.SimpleOperator -
+ * @property {string} DataFilter.LeftOperand.Value -
+ * @property {string} DataFilter.LogicalOperator -
+ * @property {object} [DataFilter.RightOperand] -
+ * @property {string} DataFilter.RightOperand.Property -
+ * @property {string} DataFilter.RightOperand.SimpleOperator -
+ * @property {string} DataFilter.RightOperand.Value -
+ * @property {string} Name name
+ * @property {string} Description -
+ * @property {string} [ObjectState] returned from SOAP API; used to return error messages
+ * @typedef {Object.} FilterDefinitionSOAPItemMap
+ */
+/**
+ * /automation/v1/filterdefinitions/ (not used)
+ *
+ * @typedef {object} AutomationFilterDefinitionItem
+ * @property {string} id object id
+ * @property {string} key external key
+ * @property {string} createdDate -
+ * @property {number} createdBy user id
+ * @property {string} createdName -
+ * @property {string} [description] (omitted by API if empty)
+ * @property {string} modifiedDate -
+ * @property {number} modifiedBy user id
+ * @property {string} modifiedName -
+ * @property {string} name name
+ * @property {string} categoryId folder id
+ * @property {string} filterDefinitionXml from REST API defines the filter in XML form
+ * @property {1|2} derivedFromType 1:list/profile attributes/measures, 2: dataExtension
+ * @property {boolean} isSendable ?
+ * @property {object} [soap__DataFilter] copied from SOAP API, defines the filter in readable form
+ * @property {object} soap__DataFilter.LeftOperand -
+ * @property {string} soap__DataFilter.LeftOperand.Property -
+ * @property {string} soap__DataFilter.LeftOperand.SimpleOperator -
+ * @property {string} soap__DataFilter.LeftOperand.Value -
+ * @property {string} soap__DataFilter.LogicalOperator -
+ * @property {object} [soap__DataFilter.RightOperand] -
+ * @property {string} soap__DataFilter.RightOperand.Property -
+ * @property {string} soap__DataFilter.RightOperand.SimpleOperator -
+ * @property {string} soap__DataFilter.RightOperand.Value -
+ */
+/**
+ * /email/v1/filters/filterdefinition/
+ *
+ * @typedef {object} FilterDefinitionItem
+ * @property {string} id object id
+ * @property {string} key external key
+ * @property {string} createdDate date
+ * @property {number} createdBy user id
+ * @property {string} createdName name
+ * @property {string} [description] (omitted by API if empty)
+ * @property {string} lastUpdated date
+ * @property {number} lastUpdatedBy user id
+ * @property {string} lastUpdatedName name
+ * @property {string} name name
+ * @property {string} categoryId folder id
+ * @property {string} filterDefinitionXml from REST API defines the filter in XML form
+ * @property {1|2} derivedFromType 1:list/profile attributes/measures, 2: dataExtension
+ * @property {string} derivedFromObjectId Id of DataExtension - present if derivedFromType=2
+ * @property {'DataExtension'|'SubscriberAttributes'} derivedFromObjectTypeName -
+ * @property {string} [derivedFromObjectName] name of DataExtension
+ * @property {boolean} isSendable ?
+ * @property {object} [soap__DataFilter] copied from SOAP API, defines the filter in readable form
+ * @property {object} soap__DataFilter.LeftOperand -
+ * @property {string} soap__DataFilter.LeftOperand.Property -
+ * @property {string} soap__DataFilter.LeftOperand.SimpleOperator -
+ * @property {string} soap__DataFilter.LeftOperand.Value -
+ * @property {string} soap__DataFilter.LogicalOperator -
+ * @property {object} [soap__DataFilter.RightOperand] -
+ * @property {string} soap__DataFilter.RightOperand.Property -
+ * @property {string} soap__DataFilter.RightOperand.SimpleOperator -
+ * @property {string} soap__DataFilter.RightOperand.Value -
+ * @typedef {Object.} FilterDefinitionMap
+ */
+
/**
* @typedef {object} AuthObject
* @property {string} client_id client_id client_id for sfmc-sdk auth