From 7890a7b519fb338a68df1857f3b793bcde74e791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 22 Nov 2022 15:36:48 +0100 Subject: [PATCH 001/103] #556: adding add/update/get support for Transactional SMS --- docs/dist/documentation.md | 147 +++++++++++++ lib/MetadataTypeDefinitions.js | 1 + lib/MetadataTypeInfo.js | 1 + lib/metadataTypes/TransactionalSMS.js | 194 ++++++++++++++++++ .../TransactionalSMS.definition.js | 103 ++++++++++ 5 files changed, 446 insertions(+) create mode 100644 lib/metadataTypes/TransactionalSMS.js create mode 100644 lib/metadataTypes/definitions/TransactionalSMS.definition.js diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index b3a83618a..1f695fa2a 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -101,6 +101,9 @@ Provides default functionality that can be overwritten by child metadata type cl
SetDefinitionMetadataType

SetDefinition MetadataType

+
TransactionalSMSMetadataType
+

TransactionalSMS MetadataType

+
TriggeredSendDefinitionMetadataType

MessageSendActivity MetadataType

@@ -4338,6 +4341,150 @@ Retrieves Metadata of schema set definitions for caching. **Kind**: static method of [SetDefinition](#SetDefinition) **Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise + + +## TransactionalSMS ⇐ [MetadataType](#MetadataType) +TransactionalSMS MetadataType + +**Kind**: global class +**Extends**: [MetadataType](#MetadataType) + +* [TransactionalSMS](#TransactionalSMS) ⇐ [MetadataType](#MetadataType) + * [.retrieve(retrieveDir, [_], [__], [___], [key])](#TransactionalSMS.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.retrieveForCache()](#TransactionalSMS.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.update(metadata)](#TransactionalSMS.update) ⇒ Promise + * [.create(metadata)](#TransactionalSMS.create) ⇒ Promise + * [.preDeployTasks(metadata, dir)](#TransactionalSMS.preDeployTasks) ⇒ TYPE.MetadataTypeItem + * [._mergeCode(metadata, deployDir, [templateName])](#TransactionalSMS._mergeCode) ⇒ Promise.<string> + * [.postRetrieveTasks(metadata)](#TransactionalSMS.postRetrieveTasks) ⇒ TYPE.CodeExtractItem + * [.parseMetadata(metadata)](#TransactionalSMS.parseMetadata) ⇒ TYPE.CodeExtractItem + * [.prepExtractedCode(metadataScript)](#TransactionalSMS.prepExtractedCode) ⇒ Object + * [.getFilesToCommit(keyArr)](#TransactionalSMS.getFilesToCommit) ⇒ Array.<string> + + + +### TransactionalSMS.retrieve(retrieveDir, [_], [__], [___], [key]) ⇒ Promise.<TYPE.MetadataTypeMapObj> +Retrieves Metadata of Mobile Keywords +Endpoint /legacy/v1/beta/mobile/code/ return all Mobile Codes with all details. + +**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**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 | +| [___] | void | unused parameter | +| [key] | string | customer key of single item to retrieve | + + + +### TransactionalSMS.retrieveForCache() ⇒ Promise.<TYPE.MetadataTypeMapObj> +Retrieves event definition metadata for caching + +**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise of metadata + + +### TransactionalSMS.update(metadata) ⇒ Promise +Updates a single item + +**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**Returns**: Promise - Promise + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.MetadataTypeItem | a single item | + + + +### TransactionalSMS.create(metadata) ⇒ Promise +Creates a single item + +**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**Returns**: Promise - Promise + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.MetadataTypeItem | a single item | + + + +### TransactionalSMS.preDeployTasks(metadata, dir) ⇒ TYPE.MetadataTypeItem +prepares for deployment + +**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**Returns**: TYPE.MetadataTypeItem - Promise + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.MetadataTypeItem | a single item | +| dir | string | directory of deploy files | + + + +### TransactionalSMS.\_mergeCode(metadata, deployDir, [templateName]) ⇒ Promise.<string> +helper for [preDeployTasks](preDeployTasks) that loads extracted code content back into JSON + +**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**Returns**: Promise.<string> - content for metadata.script + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.MetadataTypeItem | a single definition | +| deployDir | string | directory of deploy files | +| [templateName] | string | name of the template used to built defintion (prior applying templating) | + + + +### TransactionalSMS.postRetrieveTasks(metadata) ⇒ TYPE.CodeExtractItem +manages post retrieve steps + +**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**Returns**: TYPE.CodeExtractItem - Array with one metadata object and one ssjs string + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.MetadataTypeItem | a single item | + + + +### TransactionalSMS.parseMetadata(metadata) ⇒ TYPE.CodeExtractItem +Splits the metadata into two parts and parses in a standard manner + +**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**Returns**: TYPE.CodeExtractItem - a single item with code parts extracted + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.ScripMetadataTypeItemtItem | a single item | + + + +### TransactionalSMS.prepExtractedCode(metadataScript) ⇒ Object +helper for [parseMetadata](parseMetadata) and [_buildForNested](_buildForNested) + +**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**Returns**: Object - returns found extension and file content + +| Param | Type | Description | +| --- | --- | --- | +| metadataScript | string | the code of the file | + + + +### TransactionalSMS.getFilesToCommit(keyArr) ⇒ Array.<string> +should return only the json for all but asset, query and script that are saved as multiple files +additionally, the documentation for dataExtension and automation should be returned + +**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**Returns**: Array.<string> - list of all files that need to be committed in a flat array ['path/file1.ext', 'path/file2.ext'] + +| Param | Type | Description | +| --- | --- | --- | +| keyArr | Array.<string> | customerkey of the metadata | + ## TriggeredSendDefinition ⇐ [MetadataType](#MetadataType) diff --git a/lib/MetadataTypeDefinitions.js b/lib/MetadataTypeDefinitions.js index 4e38d59e2..5e8a59144 100644 --- a/lib/MetadataTypeDefinitions.js +++ b/lib/MetadataTypeDefinitions.js @@ -32,6 +32,7 @@ const MetadataTypeDefinitions = { role: require('./metadataTypes/definitions/Role.definition'), script: require('./metadataTypes/definitions/Script.definition'), setDefinition: require('./metadataTypes/definitions/SetDefinition.definition'), + transactionalSMS: require('./metadataTypes/definitions/TransactionalSMS.definition'), triggeredSendDefinition: require('./metadataTypes/definitions/TriggeredSendDefinition.definition'), }; diff --git a/lib/MetadataTypeInfo.js b/lib/MetadataTypeInfo.js index 3291a41c6..c125295c8 100644 --- a/lib/MetadataTypeInfo.js +++ b/lib/MetadataTypeInfo.js @@ -32,6 +32,7 @@ const MetadataTypeInfo = { role: require('./metadataTypes/Role'), script: require('./metadataTypes/Script'), setDefinition: require('./metadataTypes/SetDefinition'), + transactionalSMS: require('./metadataTypes/TransactionalSMS'), triggeredSendDefinition: require('./metadataTypes/TriggeredSendDefinition'), }; diff --git a/lib/metadataTypes/TransactionalSMS.js b/lib/metadataTypes/TransactionalSMS.js new file mode 100644 index 000000000..f050e854f --- /dev/null +++ b/lib/metadataTypes/TransactionalSMS.js @@ -0,0 +1,194 @@ +'use strict'; + +const TYPE = require('../../types/mcdev.d'); +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); +const File = require('../util/file'); + +/** + * TransactionalSMS MetadataType + * + * @augments MetadataType + */ +class TransactionalSMS extends MetadataType { + /** + * Retrieves Metadata of Mobile Keywords + * Endpoint /legacy/v1/beta/mobile/code/ return all Mobile Codes with all details. + * + * @param {string} retrieveDir Directory where retrieved metadata directory will be saved + * @param {void} [_] unused parameter + * @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 keyList; + const baseUri = '/messaging/v1/sms/definitions/'; + if (!key) { + // Retrieve all + this.client.rest.getBulk(baseUri + (key || '')); + const response = this.definition.restPagination + ? await this.client.rest.getBulk(baseUri) + : await this.client.rest.get(baseUri); + keyList = Object.keys(this.parseResponseBody(response)); + } else { + // Retrieve single + keyList = [key]; + } + + // get all sms with additional details not given by the list endpoint + const details = keyList + ? await Promise.all(keyList.map((key) => this.client.rest.get(baseUri + (key || '')))) + : []; + + const parsed = this.parseResponseBody({ definitions: details }); + + // * retrieveDir is mandatory in this method as it is not used for caching (there is a seperate method for that) + const savedMetadata = await this.saveResults(parsed, retrieveDir, null, null); + Util.logger.info( + `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` + ); + + return { metadata: savedMetadata, type: this.definition.type }; + } + + /** + * Retrieves event definition metadata for caching + * + * @returns {Promise.} Promise of metadata + */ + static retrieveForCache() { + return super.retrieveREST(null, '/messaging/v1/sms/definitions/'); + } + /** + * Updates a single item + * + * @param {TYPE.MetadataTypeItem} metadata a single item + * @returns {Promise} Promise + */ + static update(metadata) { + return super.updateREST(metadata, '/messaging/v1/sms/definitions'); + } + + /** + * Creates a single item + * + * @param {TYPE.MetadataTypeItem} metadata a single item + * @returns {Promise} Promise + */ + static create(metadata) { + return super.createREST(metadata, '/messaging/v1/sms/definitions'); + } + /** + * prepares for deployment + * + * @param {TYPE.MetadataTypeItem} metadata a single item + * @param {string} dir directory of deploy files + * @returns {TYPE.MetadataTypeItem} Promise + */ + static async preDeployTasks(metadata, dir) { + // code + metadata.content = { + message: await this._mergeCode(metadata, dir), + }; + return metadata; + } + /** + * helper for {@link preDeployTasks} that loads extracted code content back into JSON + * + * @param {TYPE.MetadataTypeItem} metadata a single definition + * @param {string} deployDir directory of deploy files + * @param {string} [templateName] name of the template used to built defintion (prior applying templating) + * @returns {Promise.} content for metadata.script + */ + static async _mergeCode(metadata, deployDir, templateName) { + templateName = templateName || metadata[this.definition.keyField]; + let code; + const codePath = File.normalizePath([ + deployDir, + this.definition.type, + templateName + '.' + this.definition.type + '-meta', + ]); + + if (await File.pathExists(codePath + '.amp')) { + code = await File.readFilteredFilename( + [deployDir, this.definition.type], + templateName + '.' + this.definition.type + '-meta', + 'amp' + ); + } else { + throw new Error(`Could not find ${codePath}.amp`); + } + return code; + } + /** + * manages post retrieve steps + * + * @param {TYPE.MetadataTypeItem} metadata a single item + * @returns {TYPE.CodeExtractItem} Array with one metadata object and one ssjs string + */ + static postRetrieveTasks(metadata) { + return this.parseMetadata(metadata); + } + + /** + * Splits the metadata into two parts and parses in a standard manner + * + * @param {TYPE.ScripMetadataTypeItemtItem} metadata a single item + * @returns {TYPE.CodeExtractItem} a single item with code parts extracted + */ + static parseMetadata(metadata) { + // extract message body + const codeArr = []; + // keep between tags + const { fileExt, code } = this.prepExtractedCode(metadata.content.message); + delete metadata.content; + codeArr.push({ + subFolder: null, + fileName: metadata[this.definition.keyField], + fileExt: fileExt, + content: code, + }); + + return { json: metadata, codeArr: codeArr, subFolder: null }; + } + /** + * helper for {@link parseMetadata} and {@link _buildForNested} + * + * @param {string} metadataScript the code of the file + * @returns {{fileExt:string,code:string}} returns found extension and file content + */ + static prepExtractedCode(metadataScript) { + const code = metadataScript; + const fileExt = 'amp'; + + return { fileExt, code }; + } + /** + * should return only the json for all but asset, query and script that are saved as multiple files + * additionally, the documentation for dataExtension and automation should be returned + * + * @param {string[]} keyArr customerkey of the metadata + * @returns {string[]} list of all files that need to be committed in a flat array ['path/file1.ext', 'path/file2.ext'] + */ + static getFilesToCommit(keyArr) { + const path = File.normalizePath([ + this.properties.directories.retrieve, + this.buObject.credential, + this.buObject.businessUnit, + this.definition.type, + ]); + + const fileList = keyArr.flatMap((key) => [ + File.normalizePath([path, `${key}.${this.definition.type}-meta.json`]), + File.normalizePath([path, `${key}.${this.definition.type}-meta.amp`]), + ]); + return fileList; + } +} + +// Assign definition to static attributes +TransactionalSMS.definition = require('../MetadataTypeDefinitions').transactionalSMS; + +module.exports = TransactionalSMS; diff --git a/lib/metadataTypes/definitions/TransactionalSMS.definition.js b/lib/metadataTypes/definitions/TransactionalSMS.definition.js new file mode 100644 index 000000000..38d8f6987 --- /dev/null +++ b/lib/metadataTypes/definitions/TransactionalSMS.definition.js @@ -0,0 +1,103 @@ +module.exports = { + bodyIteratorField: 'definitions', + dependencies: [], + hasExtended: false, + idField: 'definitionId', + keyField: 'definitionKey', + nameField: 'name', + createdDateField: 'createdDate', + createdNameField: null, + lastmodDateField: 'modifiedDate', + lastmodNameField: null, + restPagination: false, + type: 'transactionalSMS', + typeDescription: 'Lets you send SMS messages via API events', + typeRetrieveByDefault: true, + typeName: 'Transactional SMS', + fields: { + name: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + definitionKey: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + description: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + requestId: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + definitionId: { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: false, + }, + status: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: false, + }, + createdDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + modifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'content.message': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'subscriptions.shortCode': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'subscriptions.countryCode': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'subscriptions.autoAddSubscriber': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'subscriptions.updateSubscriber': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'subscriptions.keyword': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + }, +}; From 9b8c866ec0aeda87e18494e994c61b0ca3c20d86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 22 Nov 2022 15:54:16 +0100 Subject: [PATCH 002/103] #556: add auto-formatting for transactional sms body's AMPscript --- boilerplate/files/.vscode/extensions.json | 1 + boilerplate/files/.vscode/settings.json | 3 +++ lib/metadataTypes/TransactionalSMS.js | 22 +++++++++++++++++++++- package-lock.json | 11 +++++++++++ package.json | 1 + 5 files changed, 37 insertions(+), 1 deletion(-) diff --git a/boilerplate/files/.vscode/extensions.json b/boilerplate/files/.vscode/extensions.json index d847fffc5..e301d3e45 100644 --- a/boilerplate/files/.vscode/extensions.json +++ b/boilerplate/files/.vscode/extensions.json @@ -15,6 +15,7 @@ "editorconfig.editorconfig", "esbenp.prettier-vscode", "sergey-agadzhanov.ampscript", + "FiB.beautyAmp", // Markdown / Readme.md "joernberkefeld.markdown-preview-bitbucket-innersource" diff --git a/boilerplate/files/.vscode/settings.json b/boilerplate/files/.vscode/settings.json index 1ed32c224..a1b0d83d1 100644 --- a/boilerplate/files/.vscode/settings.json +++ b/boilerplate/files/.vscode/settings.json @@ -59,5 +59,8 @@ }, "[markdown]": { "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[AMPscript]": { + "editor.defaultFormatter": "FiB.beautyAmp" } } diff --git a/lib/metadataTypes/TransactionalSMS.js b/lib/metadataTypes/TransactionalSMS.js index f050e854f..e6bb6f7e3 100644 --- a/lib/metadataTypes/TransactionalSMS.js +++ b/lib/metadataTypes/TransactionalSMS.js @@ -4,6 +4,7 @@ const TYPE = require('../../types/mcdev.d'); const MetadataType = require('./MetadataType'); const Util = require('../util/util'); const File = require('../util/file'); +const beautifier = require('beauty-amp-core'); /** * TransactionalSMS MetadataType @@ -160,7 +161,26 @@ class TransactionalSMS extends MetadataType { * @returns {{fileExt:string,code:string}} returns found extension and file content */ static prepExtractedCode(metadataScript) { - const code = metadataScript; + // immutable at the moment: + const ampscript = { + capitalizeAndOrNot: true, + capitalizeIfFor: true, + capitalizeSet: true, + capitalizeVar: true, + maxParametersPerLine: 4, + }; + // immutable at the moment: + const editor = { + insertSpaces: true, + tabSize: 4, + }; + // logs trough console only for the moment. + const logs = { + loggerOn: false, // <= disable logging + }; + + beautifier.setup(ampscript, editor, logs); + const code = beautifier.beautify(metadataScript); const fileExt = 'amp'; return { fileExt, code }; diff --git a/package-lock.json b/package-lock.json index c64643ac8..b50936ed7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "4.1.12", "license": "MIT", "dependencies": { + "beauty-amp-core": "0.3.7", "cli-progress": "3.11.2", "command-exists": "1.2.9", "conf": "10.2.0", @@ -1103,6 +1104,11 @@ } ] }, + "node_modules/beauty-amp-core": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/beauty-amp-core/-/beauty-amp-core-0.3.7.tgz", + "integrity": "sha512-/a3jRMN0IZ4SaV8PoVLqprk0sSEXvTOOVs4jdSQhgNBJYRkHau0PhUwju6PzfNvQpleZXWdechrPQAVAxIKnHA==" + }, "node_modules/big-integer": { "version": "1.6.51", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", @@ -9525,6 +9531,11 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, + "beauty-amp-core": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/beauty-amp-core/-/beauty-amp-core-0.3.7.tgz", + "integrity": "sha512-/a3jRMN0IZ4SaV8PoVLqprk0sSEXvTOOVs4jdSQhgNBJYRkHau0PhUwju6PzfNvQpleZXWdechrPQAVAxIKnHA==" + }, "big-integer": { "version": "1.6.51", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", diff --git a/package.json b/package.json index 2cc94c2e7..b3eb03be5 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "test": "mocha" }, "dependencies": { + "beauty-amp-core": "0.3.7", "cli-progress": "3.11.2", "command-exists": "1.2.9", "conf": "10.2.0", From c006ed7713550edb1e1013a718f87fe66fbbfa28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 22 Nov 2022 15:59:29 +0100 Subject: [PATCH 003/103] #556: fix retrieve-never-ending --- lib/metadataTypes/TransactionalSMS.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/metadataTypes/TransactionalSMS.js b/lib/metadataTypes/TransactionalSMS.js index e6bb6f7e3..d5206ddd5 100644 --- a/lib/metadataTypes/TransactionalSMS.js +++ b/lib/metadataTypes/TransactionalSMS.js @@ -28,7 +28,6 @@ class TransactionalSMS extends MetadataType { const baseUri = '/messaging/v1/sms/definitions/'; if (!key) { // Retrieve all - this.client.rest.getBulk(baseUri + (key || '')); const response = this.definition.restPagination ? await this.client.rest.getBulk(baseUri) : await this.client.rest.get(baseUri); From 2690f24edca52463e7841c99cfd36a2368ac6f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 22 Nov 2022 16:12:49 +0100 Subject: [PATCH 004/103] #556: add delete support for TransactionalSMS --- docs/dist/documentation.md | 43 +++++++++++++++++++++++++++ lib/metadataTypes/MetadataType.js | 33 ++++++++++++++++++++ lib/metadataTypes/TransactionalSMS.js | 30 +++++++++++++++++++ 3 files changed, 106 insertions(+) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 1f695fa2a..ba4eef611 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -3030,6 +3030,7 @@ Provides default functionality that can be overwritten by child metadata type cl * [.deleteByKey(buObject, customerKey)](#MetadataType.deleteByKey) ⇒ boolean * [.postDeleteTasks(buObject, customerKey)](#MetadataType.postDeleteTasks) ⇒ void * [.deleteByKeySOAP(buObject, customerKey, [handleOutside])](#MetadataType.deleteByKeySOAP) ⇒ boolean + * [.deleteByKeyREST(buObject, url, key, [handleOutside])](#MetadataType.deleteByKeyREST) ⇒ boolean * [.readBUMetadataForType(readDir, [listBadKeys], [buMetadata])](#MetadataType.readBUMetadataForType) ⇒ object * [.getFilesToCommit(keyArr)](#MetadataType.getFilesToCommit) ⇒ Promise.<Array.<string>> @@ -3654,6 +3655,21 @@ Delete a data extension from the specified business unit | customerKey | string | Identifier of metadata | | [handleOutside] | boolean | if the API reponse is irregular this allows you to handle it outside of this generic method | + + +### MetadataType.deleteByKeyREST(buObject, url, key, [handleOutside]) ⇒ boolean +Delete a data extension from the specified business unit + +**Kind**: static method of [MetadataType](#MetadataType) +**Returns**: boolean - deletion success flag + +| Param | Type | Description | +| --- | --- | --- | +| buObject | TYPE.BuObject | references credentials | +| url | string | endpoint | +| key | string | Identifier of metadata | +| [handleOutside] | boolean | if the API reponse is irregular this allows you to handle it outside of this generic method | + ### MetadataType.readBUMetadataForType(readDir, [listBadKeys], [buMetadata]) ⇒ object @@ -4354,6 +4370,8 @@ TransactionalSMS MetadataType * [.retrieveForCache()](#TransactionalSMS.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.update(metadata)](#TransactionalSMS.update) ⇒ Promise * [.create(metadata)](#TransactionalSMS.create) ⇒ Promise + * [.deleteByKey(buObject, key)](#TransactionalSMS.deleteByKey) ⇒ Promise.<boolean> + * [.postDeleteTasks(buObject, customerKey)](#TransactionalSMS.postDeleteTasks) ⇒ void * [.preDeployTasks(metadata, dir)](#TransactionalSMS.preDeployTasks) ⇒ TYPE.MetadataTypeItem * [._mergeCode(metadata, deployDir, [templateName])](#TransactionalSMS._mergeCode) ⇒ Promise.<string> * [.postRetrieveTasks(metadata)](#TransactionalSMS.postRetrieveTasks) ⇒ TYPE.CodeExtractItem @@ -4409,6 +4427,31 @@ Creates a single item | --- | --- | --- | | metadata | TYPE.MetadataTypeItem | a single item | + + +### TransactionalSMS.deleteByKey(buObject, key) ⇒ Promise.<boolean> +Delete a metadata item from the specified business unit + +**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**Returns**: Promise.<boolean> - deletion success status + +| Param | Type | Description | +| --- | --- | --- | +| buObject | TYPE.BuObject | references credentials | +| key | string | Identifier of item | + + + +### TransactionalSMS.postDeleteTasks(buObject, customerKey) ⇒ void +clean up after deleting a metadata item + +**Kind**: static method of [TransactionalSMS](#TransactionalSMS) + +| Param | Type | Description | +| --- | --- | --- | +| buObject | TYPE.BuObject | references credentials | +| customerKey | string | Identifier of metadata item | + ### TransactionalSMS.preDeployTasks(metadata, dir) ⇒ TYPE.MetadataTypeItem diff --git a/lib/metadataTypes/MetadataType.js b/lib/metadataTypes/MetadataType.js index 3c7a429e1..d55ccac1e 100644 --- a/lib/metadataTypes/MetadataType.js +++ b/lib/metadataTypes/MetadataType.js @@ -1594,6 +1594,39 @@ class MetadataType { return false; } } + /** + * Delete a data extension from the specified business unit + * + * @param {TYPE.BuObject} buObject references credentials + * @param {string} url endpoint + * @param {string} key Identifier of metadata + * @param {boolean} [handleOutside] if the API reponse is irregular this allows you to handle it outside of this generic method + * @returns {boolean} deletion success flag + */ + static async deleteByKeyREST(buObject, url, key, handleOutside) { + const keyObj = {}; + keyObj[this.definition.keyField] = key; + try { + this.client.rest.delete(url); + if (!handleOutside) { + Util.logger.info(`- deleted ${this.definition.type}: ${key}`); + } + this.postDeleteTasks(buObject, key); + + return true; + } catch (ex) { + if (!handleOutside) { + const errorMsg = ex?.results?.length + ? `${ex.results[0].StatusMessage} (Code ${ex.results[0].ErrorCode})` + : ex.message; + Util.logger.error(`- error deleting ${this.definition.type} '${key}': ${errorMsg}`); + } else { + throw ex; + } + + return false; + } + } /** * Returns metadata of a business unit that is saved locally * diff --git a/lib/metadataTypes/TransactionalSMS.js b/lib/metadataTypes/TransactionalSMS.js index d5206ddd5..a6dbb1188 100644 --- a/lib/metadataTypes/TransactionalSMS.js +++ b/lib/metadataTypes/TransactionalSMS.js @@ -80,6 +80,36 @@ class TransactionalSMS extends MetadataType { static create(metadata) { return super.createREST(metadata, '/messaging/v1/sms/definitions'); } + /** + * Delete a metadata item from the specified business unit + * + * @param {TYPE.BuObject} buObject references credentials + * @param {string} key Identifier of item + * @returns {Promise.} deletion success status + */ + static deleteByKey(buObject, key) { + return super.deleteByKeyREST(buObject, '/messaging/v1/sms/definitions/' + key, key, false); + } + /** + * clean up after deleting a metadata item + * + * @param {TYPE.BuObject} buObject references credentials + * @param {string} customerKey Identifier of metadata item + * @returns {void} + */ + static async postDeleteTasks(buObject, customerKey) { + // delete local copy: retrieve/cred/bu/type/...json + const fileName = File.normalizePath([ + this.properties.directories.retrieve, + buObject.credential, + buObject.businessUnit, + this.definition.type, + `${customerKey}.${this.definition.type}-meta.`, + ]); + await File.remove(fileName + 'json'); + await File.remove(fileName + 'amp'); + } + /** * prepares for deployment * From 994df577620b56083e1c521e93b84c07340a7208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 22 Nov 2022 20:25:04 +0100 Subject: [PATCH 005/103] #556: refactoring --- lib/metadataTypes/Query.js | 4 ++-- lib/metadataTypes/Script.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/metadataTypes/Query.js b/lib/metadataTypes/Query.js index fa61e0674..d5b99cd47 100644 --- a/lib/metadataTypes/Query.js +++ b/lib/metadataTypes/Query.js @@ -120,7 +120,7 @@ class Query extends MetadataType { static async preDeployTasks(metadata, deployDir) { metadata.queryText = await File.readFilteredFilename( deployDir + '/' + this.definition.type, - metadata.key + '.' + this.definition.type + '-meta', + metadata[this.definition.keyField] + '.' + this.definition.type + '-meta', 'sql' ); metadata.targetKey = cache.searchForField( @@ -292,7 +292,7 @@ class Query extends MetadataType { const codeArr = [ { subFolder: null, - fileName: metadata.key, + fileName: metadata[this.definition.keyField], fileExt: 'sql', content: metadata.queryText, }, diff --git a/lib/metadataTypes/Script.js b/lib/metadataTypes/Script.js index 809f72b77..c3839e16c 100644 --- a/lib/metadataTypes/Script.js +++ b/lib/metadataTypes/Script.js @@ -93,7 +93,7 @@ class Script extends MetadataType { * @returns {Promise.} content for metadata.script */ static async _mergeCode(metadata, deployDir, templateName) { - templateName = templateName || metadata.key; + templateName = templateName || metadata[this.definition.keyField]; let code; const codePath = File.normalizePath([ deployDir, @@ -275,7 +275,7 @@ class Script extends MetadataType { delete metadata.script; codeArr.push({ subFolder: null, - fileName: metadata.key, + fileName: metadata[this.definition.keyField], fileExt: fileExt, content: code, }); From 728bd1fd92a964f2dceecd6ae0bd784129fee548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 22 Nov 2022 20:45:40 +0100 Subject: [PATCH 006/103] #556: add transactionalSMS to docs --- README.md | 10 ++++++---- .../definitions/TransactionalSMS.definition.js | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f5833b1eb..69e8a956d 100644 --- a/README.md +++ b/README.md @@ -399,16 +399,17 @@ The following metadata types are currently supported: | Data Extension Template | `dataExtensionTemplate` | Yes | - | - | - | OOTB Database table schemas used for special cases like Transactional Journeys. | | Data Extract Type | `dataExtractType` | Yes | - | - | - | Types of Data Extracts enabled for a specific business unit. This normally should not be stored. | | E-Mail (Classic) | `email` | Yes | - | - | - | **DEPRECATED**: Old way of saving E-Mails; please migrate these to new E-Mail (`Asset: message`). | -| E-Mail Send Definition | `emailSendDefinition` | Yes | Yes | yes (`bt`) | Yes | Mainly used in Automations as "Send Email Activity". | -| Folder | `folder` | Yes | Yes | yes (`bt`) | - | Used to structure all kinds of other metadata. | +| E-Mail Send Definition | `emailSendDefinition` | Yes | Yes | Yes (`bt`) | Yes | Mainly used in Automations as "Send Email Activity". | +| Folder | `folder` | Yes | Yes | Yes (`bt`) | - | Used to structure all kinds of other metadata. | | FTPLocation | `ftpLocation` | Yes | - | - | Yes | A File Location which can be used for export or import of files to/from Marketing Cloud. | | Journey | `interaction` | Yes | in backlog | in backlog | - | Journey from Builder (internally called "Interaction"). | | Journey: Entry Event Definition | `eventDefinition` | Yes | Yes | Yes | - | Used in Journeys (Interactions) to define Entry Events. | | List | `list` | Yes | in backlog | - | Yes | Old way of storing data. Still used for central Email Subscriber DB. | | Mobile Connect Code | `mobileCode` | Yes | No | No | - | Mobile Connect Shore or Long Codes used for sending. First 50 per BU are retrieved | | Mobile Connect Keyword | `mobileKeyword` | Yes | Yes | Yes | - | Mobile Connect keywords configured within the Business UNit. First 50 per BU are retrieved | -| Role | `role` | Yes | Yes | yes (`bt`) | Yes | User Roles define groups that are used to grant users access to SFMC systems. | -| Triggered Send | `triggeredSendDefinition` | Yes | Yes | - | Yes | **DEPRECATED**: Sends emails via API or DataExtension Event. | +| Role | `role` | Yes | Yes | Yes (`bt`) | Yes | User Roles define groups that are used to grant users access to SFMC systems. | +| Transactional SMS | `transactionalSMS` | Yes | Yes | Yes | Yes | Lets you send immediate SMS messages via API events | +| Triggered Send | `triggeredSendDefinition` | Yes | Yes | Yes (`bt`) | Yes | **DEPRECATED**: Sends emails via API or DataExtension Event. | | User | `accountUser` | Yes | in backlog | - | - | Users and Installed Packages including their assigned Roles, BUs and personal permissions | ## 6. Command Overview @@ -773,6 +774,7 @@ Currently supported types: | Data Extension Field | `dataExtensionField` | | Email Send Definition | `Email Send Definition` | | List | `list` | +| Transactional SMS | `transactionalSMS` | | Triggered Send | `triggeredSendDefinition` | _Example:_ diff --git a/lib/metadataTypes/definitions/TransactionalSMS.definition.js b/lib/metadataTypes/definitions/TransactionalSMS.definition.js index 38d8f6987..b03e86467 100644 --- a/lib/metadataTypes/definitions/TransactionalSMS.definition.js +++ b/lib/metadataTypes/definitions/TransactionalSMS.definition.js @@ -11,7 +11,7 @@ module.exports = { lastmodNameField: null, restPagination: false, type: 'transactionalSMS', - typeDescription: 'Lets you send SMS messages via API events', + typeDescription: 'Lets you send immediate SMS messages via API events', typeRetrieveByDefault: true, typeName: 'Transactional SMS', fields: { From a55512b4df196e8af57a45272effc61e7be46d6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 22 Nov 2022 21:14:12 +0100 Subject: [PATCH 007/103] #557: add support for transactionalEmail add/update/retrieve/delete --- README.md | 2 + docs/dist/documentation.md | 79 +++++++++++ lib/MetadataTypeDefinitions.js | 1 + lib/MetadataTypeInfo.js | 1 + lib/metadataTypes/TransactionalEmail.js | 101 +++++++++++++ .../TransactionalEmail.definition.js | 133 ++++++++++++++++++ 6 files changed, 317 insertions(+) create mode 100644 lib/metadataTypes/TransactionalEmail.js create mode 100644 lib/metadataTypes/definitions/TransactionalEmail.definition.js diff --git a/README.md b/README.md index 69e8a956d..2ae589fe9 100644 --- a/README.md +++ b/README.md @@ -408,6 +408,7 @@ The following metadata types are currently supported: | Mobile Connect Code | `mobileCode` | Yes | No | No | - | Mobile Connect Shore or Long Codes used for sending. First 50 per BU are retrieved | | Mobile Connect Keyword | `mobileKeyword` | Yes | Yes | Yes | - | Mobile Connect keywords configured within the Business UNit. First 50 per BU are retrieved | | Role | `role` | Yes | Yes | Yes (`bt`) | Yes | User Roles define groups that are used to grant users access to SFMC systems. | +| Transactional Email | `transactionalEmail` | Yes | Yes | Yes | Yes | Lets you send immediate Email messages via API events | | Transactional SMS | `transactionalSMS` | Yes | Yes | Yes | Yes | Lets you send immediate SMS messages via API events | | Triggered Send | `triggeredSendDefinition` | Yes | Yes | Yes (`bt`) | Yes | **DEPRECATED**: Sends emails via API or DataExtension Event. | | User | `accountUser` | Yes | in backlog | - | - | Users and Installed Packages including their assigned Roles, BUs and personal permissions | @@ -774,6 +775,7 @@ Currently supported types: | Data Extension Field | `dataExtensionField` | | Email Send Definition | `Email Send Definition` | | List | `list` | +| Transactional Email | `transactionalEmail` | | Transactional SMS | `transactionalSMS` | | Triggered Send | `triggeredSendDefinition` | diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index ba4eef611..e0fa50da8 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -101,6 +101,9 @@ Provides default functionality that can be overwritten by child metadata type cl
SetDefinitionMetadataType

SetDefinition MetadataType

+
TransactionalEmailMetadataType
+

TransactionalEmail MetadataType

+
TransactionalSMSMetadataType

TransactionalSMS MetadataType

@@ -4357,6 +4360,82 @@ Retrieves Metadata of schema set definitions for caching. **Kind**: static method of [SetDefinition](#SetDefinition) **Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise + + +## TransactionalEmail ⇐ [MetadataType](#MetadataType) +TransactionalEmail MetadataType + +**Kind**: global class +**Extends**: [MetadataType](#MetadataType) + +* [TransactionalEmail](#TransactionalEmail) ⇐ [MetadataType](#MetadataType) + * [.retrieve(retrieveDir, [_], [__], [___], [key])](#TransactionalEmail.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.retrieveForCache()](#TransactionalEmail.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.update(metadata)](#TransactionalEmail.update) ⇒ Promise + * [.create(metadata)](#TransactionalEmail.create) ⇒ Promise + * [.deleteByKey(buObject, key)](#TransactionalEmail.deleteByKey) ⇒ Promise.<boolean> + + + +### TransactionalEmail.retrieve(retrieveDir, [_], [__], [___], [key]) ⇒ Promise.<TYPE.MetadataTypeMapObj> +Retrieves Metadata of Mobile Keywords +Endpoint /legacy/v1/beta/mobile/code/ return all Mobile Codes with all details. + +**Kind**: static method of [TransactionalEmail](#TransactionalEmail) +**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 | +| [___] | void | unused parameter | +| [key] | string | customer key of single item to retrieve | + + + +### TransactionalEmail.retrieveForCache() ⇒ Promise.<TYPE.MetadataTypeMapObj> +Retrieves event definition metadata for caching + +**Kind**: static method of [TransactionalEmail](#TransactionalEmail) +**Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise of metadata + + +### TransactionalEmail.update(metadata) ⇒ Promise +Updates a single item + +**Kind**: static method of [TransactionalEmail](#TransactionalEmail) +**Returns**: Promise - Promise + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.MetadataTypeItem | a single item | + + + +### TransactionalEmail.create(metadata) ⇒ Promise +Creates a single item + +**Kind**: static method of [TransactionalEmail](#TransactionalEmail) +**Returns**: Promise - Promise + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.MetadataTypeItem | a single item | + + + +### TransactionalEmail.deleteByKey(buObject, 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 | +| --- | --- | --- | +| buObject | TYPE.BuObject | references credentials | +| key | string | Identifier of item | + ## TransactionalSMS ⇐ [MetadataType](#MetadataType) diff --git a/lib/MetadataTypeDefinitions.js b/lib/MetadataTypeDefinitions.js index 5e8a59144..bf274dc74 100644 --- a/lib/MetadataTypeDefinitions.js +++ b/lib/MetadataTypeDefinitions.js @@ -32,6 +32,7 @@ const MetadataTypeDefinitions = { role: require('./metadataTypes/definitions/Role.definition'), script: require('./metadataTypes/definitions/Script.definition'), setDefinition: require('./metadataTypes/definitions/SetDefinition.definition'), + transactionalEmail: require('./metadataTypes/definitions/TransactionalEmail.definition'), transactionalSMS: require('./metadataTypes/definitions/TransactionalSMS.definition'), triggeredSendDefinition: require('./metadataTypes/definitions/TriggeredSendDefinition.definition'), }; diff --git a/lib/MetadataTypeInfo.js b/lib/MetadataTypeInfo.js index c125295c8..065f9b4bd 100644 --- a/lib/MetadataTypeInfo.js +++ b/lib/MetadataTypeInfo.js @@ -32,6 +32,7 @@ const MetadataTypeInfo = { role: require('./metadataTypes/Role'), script: require('./metadataTypes/Script'), setDefinition: require('./metadataTypes/SetDefinition'), + transactionalEmail: require('./metadataTypes/TransactionalEmail'), transactionalSMS: require('./metadataTypes/TransactionalSMS'), triggeredSendDefinition: require('./metadataTypes/TriggeredSendDefinition'), }; diff --git a/lib/metadataTypes/TransactionalEmail.js b/lib/metadataTypes/TransactionalEmail.js new file mode 100644 index 000000000..1646a1b9a --- /dev/null +++ b/lib/metadataTypes/TransactionalEmail.js @@ -0,0 +1,101 @@ +'use strict'; + +const TYPE = require('../../types/mcdev.d'); +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); + +/** + * TransactionalEmail MetadataType + * + * @augments MetadataType + */ +class TransactionalEmail extends MetadataType { + /** + * Retrieves Metadata of Mobile Keywords + * Endpoint /legacy/v1/beta/mobile/code/ return all Mobile Codes with all details. + * + * @param {string} retrieveDir Directory where retrieved metadata directory will be saved + * @param {void} [_] unused parameter + * @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 keyList; + const baseUri = '/messaging/v1/email/definitions/'; + if (!key) { + // Retrieve all + const response = this.definition.restPagination + ? await this.client.rest.getBulk(baseUri) + : await this.client.rest.get(baseUri); + keyList = Object.keys(this.parseResponseBody(response)); + } else { + // Retrieve single + keyList = [key]; + } + + // get all sms with additional details not given by the list endpoint + const details = keyList + ? await Promise.all(keyList.map((key) => this.client.rest.get(baseUri + (key || '')))) + : []; + + const parsed = this.parseResponseBody({ definitions: details }); + + // * retrieveDir is mandatory in this method as it is not used for caching (there is a seperate method for that) + const savedMetadata = await this.saveResults(parsed, retrieveDir, null, null); + Util.logger.info( + `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` + ); + + return { metadata: savedMetadata, type: this.definition.type }; + } + + /** + * Retrieves event definition metadata for caching + * + * @returns {Promise.} Promise of metadata + */ + static retrieveForCache() { + return super.retrieveREST(null, '/messaging/v1/email/definitions/'); + } + /** + * Updates a single item + * + * @param {TYPE.MetadataTypeItem} metadata a single item + * @returns {Promise} Promise + */ + static update(metadata) { + return super.updateREST(metadata, '/messaging/v1/email/definitions'); + } + + /** + * Creates a single item + * + * @param {TYPE.MetadataTypeItem} metadata a single item + * @returns {Promise} Promise + */ + static create(metadata) { + return super.createREST(metadata, '/messaging/v1/email/definitions'); + } + /** + * Delete a metadata item from the specified business unit + * + * @param {TYPE.BuObject} buObject references credentials + * @param {string} key Identifier of item + * @returns {Promise.} deletion success status + */ + static deleteByKey(buObject, key) { + return super.deleteByKeyREST( + buObject, + '/messaging/v1/email/definitions/' + key, + key, + false + ); + } +} + +// Assign definition to static attributes +TransactionalEmail.definition = require('../MetadataTypeDefinitions').transactionalEmail; + +module.exports = TransactionalEmail; diff --git a/lib/metadataTypes/definitions/TransactionalEmail.definition.js b/lib/metadataTypes/definitions/TransactionalEmail.definition.js new file mode 100644 index 000000000..8ee2799dd --- /dev/null +++ b/lib/metadataTypes/definitions/TransactionalEmail.definition.js @@ -0,0 +1,133 @@ +module.exports = { + bodyIteratorField: 'definitions', + dependencies: [], + hasExtended: false, + idField: 'definitionId', + keyField: 'definitionKey', + nameField: 'name', + createdDateField: 'createdDate', + createdNameField: null, + lastmodDateField: 'modifiedDate', + lastmodNameField: null, + restPagination: false, + type: 'transactionalEmail', + typeDescription: 'Lets you send immediate Email messages via API events', + typeRetrieveByDefault: true, + typeName: 'Transactional Email', + fields: { + name: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + definitionKey: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + description: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + requestId: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + definitionId: { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: false, + }, + status: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: false, + }, + createdDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + modifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + classification: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'content.customerKey': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'subscriptions.dataExtension': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'subscriptions.list': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'subscriptions.autoAddSubscriber': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'subscriptions.updateSubscriber': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'options.trackLinks': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'options.cc': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'options.bcc': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + journey: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'journey.interactionKey': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + }, +}; From f67a42d913417303cb28e830cbb3c8083f41cb1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 22 Nov 2022 21:32:27 +0100 Subject: [PATCH 008/103] #560: add support for transactionalPush add/update/retrieve/delete --- README.md | 2 + docs/dist/documentation.md | 79 +++++++++++++++ lib/MetadataTypeDefinitions.js | 1 + lib/MetadataTypeInfo.js | 1 + lib/metadataTypes/TransactionalPush.js | 96 ++++++++++++++++++ .../TransactionalPush.definition.js | 97 +++++++++++++++++++ 6 files changed, 276 insertions(+) create mode 100644 lib/metadataTypes/TransactionalPush.js create mode 100644 lib/metadataTypes/definitions/TransactionalPush.definition.js diff --git a/README.md b/README.md index 2ae589fe9..af680c436 100644 --- a/README.md +++ b/README.md @@ -409,6 +409,7 @@ The following metadata types are currently supported: | Mobile Connect Keyword | `mobileKeyword` | Yes | Yes | Yes | - | Mobile Connect keywords configured within the Business UNit. First 50 per BU are retrieved | | Role | `role` | Yes | Yes | Yes (`bt`) | Yes | User Roles define groups that are used to grant users access to SFMC systems. | | Transactional Email | `transactionalEmail` | Yes | Yes | Yes | Yes | Lets you send immediate Email messages via API events | +| Transactional Push | `transactionalPush` | Yes | Yes | Yes | Yes | Lets you send immediate Push messages via API events | | Transactional SMS | `transactionalSMS` | Yes | Yes | Yes | Yes | Lets you send immediate SMS messages via API events | | Triggered Send | `triggeredSendDefinition` | Yes | Yes | Yes (`bt`) | Yes | **DEPRECATED**: Sends emails via API or DataExtension Event. | | User | `accountUser` | Yes | in backlog | - | - | Users and Installed Packages including their assigned Roles, BUs and personal permissions | @@ -776,6 +777,7 @@ Currently supported types: | Email Send Definition | `Email Send Definition` | | List | `list` | | Transactional Email | `transactionalEmail` | +| Transactional Push | `transactionalPush` | | Transactional SMS | `transactionalSMS` | | Triggered Send | `triggeredSendDefinition` | diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index e0fa50da8..abe9d5856 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -104,6 +104,9 @@ Provides default functionality that can be overwritten by child metadata type cl
TransactionalEmailMetadataType

TransactionalEmail MetadataType

+
TransactionalPushMetadataType
+

TransactionalPush MetadataType

+
TransactionalSMSMetadataType

TransactionalSMS MetadataType

@@ -4436,6 +4439,82 @@ Delete a metadata item from the specified business unit | buObject | TYPE.BuObject | references credentials | | key | string | Identifier of item | + + +## TransactionalPush ⇐ [MetadataType](#MetadataType) +TransactionalPush MetadataType + +**Kind**: global class +**Extends**: [MetadataType](#MetadataType) + +* [TransactionalPush](#TransactionalPush) ⇐ [MetadataType](#MetadataType) + * [.retrieve(retrieveDir, [_], [__], [___], [key])](#TransactionalPush.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.retrieveForCache()](#TransactionalPush.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.update(metadata)](#TransactionalPush.update) ⇒ Promise + * [.create(metadata)](#TransactionalPush.create) ⇒ Promise + * [.deleteByKey(buObject, key)](#TransactionalPush.deleteByKey) ⇒ Promise.<boolean> + + + +### TransactionalPush.retrieve(retrieveDir, [_], [__], [___], [key]) ⇒ Promise.<TYPE.MetadataTypeMapObj> +Retrieves Metadata of Mobile Keywords +Endpoint /legacy/v1/beta/mobile/code/ return all Mobile Codes with all details. + +**Kind**: static method of [TransactionalPush](#TransactionalPush) +**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 | +| [___] | void | unused parameter | +| [key] | string | customer key of single item to retrieve | + + + +### TransactionalPush.retrieveForCache() ⇒ Promise.<TYPE.MetadataTypeMapObj> +Retrieves event definition metadata for caching + +**Kind**: static method of [TransactionalPush](#TransactionalPush) +**Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise of metadata + + +### TransactionalPush.update(metadata) ⇒ Promise +Updates a single item + +**Kind**: static method of [TransactionalPush](#TransactionalPush) +**Returns**: Promise - Promise + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.MetadataTypeItem | a single item | + + + +### TransactionalPush.create(metadata) ⇒ Promise +Creates a single item + +**Kind**: static method of [TransactionalPush](#TransactionalPush) +**Returns**: Promise - Promise + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.MetadataTypeItem | a single item | + + + +### TransactionalPush.deleteByKey(buObject, key) ⇒ Promise.<boolean> +Delete a metadata item from the specified business unit + +**Kind**: static method of [TransactionalPush](#TransactionalPush) +**Returns**: Promise.<boolean> - deletion success status + +| Param | Type | Description | +| --- | --- | --- | +| buObject | TYPE.BuObject | references credentials | +| key | string | Identifier of item | + ## TransactionalSMS ⇐ [MetadataType](#MetadataType) diff --git a/lib/MetadataTypeDefinitions.js b/lib/MetadataTypeDefinitions.js index bf274dc74..148da00fe 100644 --- a/lib/MetadataTypeDefinitions.js +++ b/lib/MetadataTypeDefinitions.js @@ -33,6 +33,7 @@ const MetadataTypeDefinitions = { script: require('./metadataTypes/definitions/Script.definition'), setDefinition: require('./metadataTypes/definitions/SetDefinition.definition'), transactionalEmail: require('./metadataTypes/definitions/TransactionalEmail.definition'), + transactionalPush: require('./metadataTypes/definitions/TransactionalPush.definition'), transactionalSMS: require('./metadataTypes/definitions/TransactionalSMS.definition'), triggeredSendDefinition: require('./metadataTypes/definitions/TriggeredSendDefinition.definition'), }; diff --git a/lib/MetadataTypeInfo.js b/lib/MetadataTypeInfo.js index 065f9b4bd..053636fdf 100644 --- a/lib/MetadataTypeInfo.js +++ b/lib/MetadataTypeInfo.js @@ -33,6 +33,7 @@ const MetadataTypeInfo = { script: require('./metadataTypes/Script'), setDefinition: require('./metadataTypes/SetDefinition'), transactionalEmail: require('./metadataTypes/TransactionalEmail'), + transactionalPush: require('./metadataTypes/TransactionalPush'), transactionalSMS: require('./metadataTypes/TransactionalSMS'), triggeredSendDefinition: require('./metadataTypes/TriggeredSendDefinition'), }; diff --git a/lib/metadataTypes/TransactionalPush.js b/lib/metadataTypes/TransactionalPush.js new file mode 100644 index 000000000..b65102236 --- /dev/null +++ b/lib/metadataTypes/TransactionalPush.js @@ -0,0 +1,96 @@ +'use strict'; + +const TYPE = require('../../types/mcdev.d'); +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); + +/** + * TransactionalPush MetadataType + * + * @augments MetadataType + */ +class TransactionalPush extends MetadataType { + /** + * Retrieves Metadata of Mobile Keywords + * Endpoint /legacy/v1/beta/mobile/code/ return all Mobile Codes with all details. + * + * @param {string} retrieveDir Directory where retrieved metadata directory will be saved + * @param {void} [_] unused parameter + * @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 keyList; + const baseUri = '/messaging/v1/push/definitions/'; + if (!key) { + // Retrieve all + const response = this.definition.restPagination + ? await this.client.rest.getBulk(baseUri) + : await this.client.rest.get(baseUri); + keyList = Object.keys(this.parseResponseBody(response)); + } else { + // Retrieve single + keyList = [key]; + } + + // get all sms with additional details not given by the list endpoint + const details = keyList + ? await Promise.all(keyList.map((key) => this.client.rest.get(baseUri + (key || '')))) + : []; + + const parsed = this.parseResponseBody({ definitions: details }); + + // * retrieveDir is mandatory in this method as it is not used for caching (there is a seperate method for that) + const savedMetadata = await this.saveResults(parsed, retrieveDir, null, null); + Util.logger.info( + `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` + ); + + return { metadata: savedMetadata, type: this.definition.type }; + } + + /** + * Retrieves event definition metadata for caching + * + * @returns {Promise.} Promise of metadata + */ + static retrieveForCache() { + return super.retrieveREST(null, '/messaging/v1/push/definitions/'); + } + /** + * Updates a single item + * + * @param {TYPE.MetadataTypeItem} metadata a single item + * @returns {Promise} Promise + */ + static update(metadata) { + return super.updateREST(metadata, '/messaging/v1/push/definitions'); + } + + /** + * Creates a single item + * + * @param {TYPE.MetadataTypeItem} metadata a single item + * @returns {Promise} Promise + */ + static create(metadata) { + return super.createREST(metadata, '/messaging/v1/push/definitions'); + } + /** + * Delete a metadata item from the specified business unit + * + * @param {TYPE.BuObject} buObject references credentials + * @param {string} key Identifier of item + * @returns {Promise.} deletion success status + */ + static deleteByKey(buObject, key) { + return super.deleteByKeyREST(buObject, '/messaging/v1/push/definitions/' + key, key, false); + } +} + +// Assign definition to static attributes +TransactionalPush.definition = require('../MetadataTypeDefinitions').transactionalPush; + +module.exports = TransactionalPush; diff --git a/lib/metadataTypes/definitions/TransactionalPush.definition.js b/lib/metadataTypes/definitions/TransactionalPush.definition.js new file mode 100644 index 000000000..7e5a26315 --- /dev/null +++ b/lib/metadataTypes/definitions/TransactionalPush.definition.js @@ -0,0 +1,97 @@ +module.exports = { + bodyIteratorField: 'definitions', + dependencies: [], + hasExtended: false, + idField: 'definitionId', + keyField: 'definitionKey', + nameField: 'name', + createdDateField: 'createdDate', + createdNameField: null, + lastmodDateField: 'modifiedDate', + lastmodNameField: null, + restPagination: false, + type: 'transactionalPush', + typeDescription: 'Lets you send immediate Push messages via API events', + typeRetrieveByDefault: true, + typeName: 'Transactional Push', + fields: { + name: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + definitionKey: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + description: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + requestId: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + definitionId: { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: false, + }, + status: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: false, + }, + createdDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + modifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'content.customerKey': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'options.badge': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'options.sound': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'options.customKeys': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + applicationId: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + }, +}; From 1d95ea8411f94e9f9267fcab1d729cdcbdf0f7a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 23 Nov 2022 11:39:15 +0100 Subject: [PATCH 009/103] #556: check dependencies during retrieve & deploy --- docs/dist/documentation.md | 2 +- lib/metadataTypes/TransactionalSMS.js | 55 ++++++++++++++++++- .../TransactionalSMS.definition.js | 2 +- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index ba4eef611..531699d1d 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -4501,7 +4501,7 @@ Splits the metadata into two parts and parses in a standard manner | Param | Type | Description | | --- | --- | --- | -| metadata | TYPE.ScripMetadataTypeItemtItem | a single item | +| metadata | TYPE.MetadataTypeItem | a single item | diff --git a/lib/metadataTypes/TransactionalSMS.js b/lib/metadataTypes/TransactionalSMS.js index a6dbb1188..7de436b2a 100644 --- a/lib/metadataTypes/TransactionalSMS.js +++ b/lib/metadataTypes/TransactionalSMS.js @@ -5,6 +5,7 @@ const MetadataType = require('./MetadataType'); const Util = require('../util/util'); const File = require('../util/file'); const beautifier = require('beauty-amp-core'); +const cache = require('../util/cache'); /** * TransactionalSMS MetadataType @@ -122,6 +123,22 @@ class TransactionalSMS extends MetadataType { metadata.content = { message: await this._mergeCode(metadata, dir), }; + + // subscriptions: mobileCode + if (metadata.subscriptions?.shortCode) { + // we merely want to be able to show an error if it does not exist + cache.searchForField('mobileCode', metadata.subscriptions.shortCode, 'code', 'code'); + } + // subscriptions: mobileKeyword + if (metadata.subscriptions?.keyword) { + // we merely want to be able to show an error if it does not exist + cache.searchForField( + 'mobileKeyword', + metadata.subscriptions.keyword, + 'keyword', + 'keyword' + ); + } return metadata; } /** @@ -165,7 +182,7 @@ class TransactionalSMS extends MetadataType { /** * Splits the metadata into two parts and parses in a standard manner * - * @param {TYPE.ScripMetadataTypeItemtItem} metadata a single item + * @param {TYPE.MetadataTypeItem} metadata a single item * @returns {TYPE.CodeExtractItem} a single item with code parts extracted */ static parseMetadata(metadata) { @@ -180,6 +197,42 @@ class TransactionalSMS extends MetadataType { fileExt: fileExt, content: code, }); + // subscriptions: mobileCode + if (metadata.subscriptions?.shortCode) { + try { + // we merely want to be able to show a warning if it does not exist + cache.searchForField( + 'mobileCode', + metadata.subscriptions.shortCode, + 'code', + 'code' + ); + } catch { + Util.logger.warn( + ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${ + metadata[this.definition.keyField] + }): Could not find mobileCode ${metadata.subscriptions.shortCode}.` + ); + } + } + // subscriptions: mobileKeyword + if (metadata.subscriptions?.keyword) { + try { + // we merely want to be able to show a warning if it does not exist + cache.searchForField( + 'mobileKeyword', + metadata.subscriptions.keyword, + 'keyword', + 'keyword' + ); + } catch { + Util.logger.warn( + ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${ + metadata[this.definition.keyField] + }): Could not find mobileKeyword ${metadata.subscriptions.keyword}.` + ); + } + } return { json: metadata, codeArr: codeArr, subFolder: null }; } diff --git a/lib/metadataTypes/definitions/TransactionalSMS.definition.js b/lib/metadataTypes/definitions/TransactionalSMS.definition.js index b03e86467..6f56a3155 100644 --- a/lib/metadataTypes/definitions/TransactionalSMS.definition.js +++ b/lib/metadataTypes/definitions/TransactionalSMS.definition.js @@ -1,6 +1,6 @@ module.exports = { bodyIteratorField: 'definitions', - dependencies: [], + dependencies: ['mobileCode', 'mobileKeyword'], hasExtended: false, idField: 'definitionId', keyField: 'definitionKey', From 2b25ff1fe4d7549ec60935f23f1bcba36488af4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 23 Nov 2022 11:40:56 +0100 Subject: [PATCH 010/103] #556: handle key-not-found during retrieve and show key after download-count --- lib/metadataTypes/TransactionalSMS.js | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/metadataTypes/TransactionalSMS.js b/lib/metadataTypes/TransactionalSMS.js index 7de436b2a..1c409c024 100644 --- a/lib/metadataTypes/TransactionalSMS.js +++ b/lib/metadataTypes/TransactionalSMS.js @@ -39,16 +39,29 @@ class TransactionalSMS extends MetadataType { } // get all sms with additional details not given by the list endpoint - const details = keyList - ? await Promise.all(keyList.map((key) => this.client.rest.get(baseUri + (key || '')))) - : []; - + const details = ( + await Promise.all( + keyList.map(async (key) => { + try { + return await this.client.rest.get(baseUri + (key || '')); + } catch { + return null; + } + }) + ) + ).filter(Boolean); const parsed = this.parseResponseBody({ definitions: details }); // * retrieveDir is mandatory in this method as it is not used for caching (there is a seperate method for that) const savedMetadata = await this.saveResults(parsed, retrieveDir, null, null); + // defined colors for optionally printing the keys we filtered by + const color = { + reset: '\x1B[0m', + dim: '\x1B[2m', + }; Util.logger.info( - `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` + `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` + + (key !== null ? ` ${color.dim}(Key: ${key})${color.reset}` : '') ); return { metadata: savedMetadata, type: this.definition.type }; From 563728bae6a1a9dbeb83dd2cc2c23c0ba4b28b99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 23 Nov 2022 14:52:20 +0100 Subject: [PATCH 011/103] #557: check dependent metadata for transactionalEmail --- docs/dist/documentation.md | 39 ++++-- lib/metadataTypes/TransactionalEmail.js | 123 ++++++++++++++++++ .../TransactionalEmail.definition.js | 2 +- 3 files changed, 150 insertions(+), 14 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 5d59add6f..c728353e6 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -4374,6 +4374,8 @@ TransactionalEmail MetadataType * [.update(metadata)](#TransactionalEmail.update) ⇒ Promise * [.create(metadata)](#TransactionalEmail.create) ⇒ Promise * [.deleteByKey(buObject, key)](#TransactionalEmail.deleteByKey) ⇒ Promise.<boolean> + * [.preDeployTasks(metadata)](#TransactionalEmail.preDeployTasks) ⇒ TYPE.MetadataTypeItem + * [.postRetrieveTasks(metadata)](#TransactionalEmail.postRetrieveTasks) ⇒ TYPE.MetadataTypeItem @@ -4436,6 +4438,30 @@ Delete a metadata item from the specified business unit | buObject | TYPE.BuObject | references credentials | | key | string | Identifier of item | + + +### TransactionalEmail.preDeployTasks(metadata) ⇒ TYPE.MetadataTypeItem +prepares for deployment + +**Kind**: static method of [TransactionalEmail](#TransactionalEmail) +**Returns**: TYPE.MetadataTypeItem - Promise + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.MetadataTypeItem | a single item | + + + +### TransactionalEmail.postRetrieveTasks(metadata) ⇒ TYPE.MetadataTypeItem +manages post retrieve steps + +**Kind**: static method of [TransactionalEmail](#TransactionalEmail) +**Returns**: TYPE.MetadataTypeItem - a single item + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.MetadataTypeItem | a single item | + ## TransactionalSMS ⇐ [MetadataType](#MetadataType) @@ -4454,7 +4480,6 @@ TransactionalSMS MetadataType * [.preDeployTasks(metadata, dir)](#TransactionalSMS.preDeployTasks) ⇒ TYPE.MetadataTypeItem * [._mergeCode(metadata, deployDir, [templateName])](#TransactionalSMS._mergeCode) ⇒ Promise.<string> * [.postRetrieveTasks(metadata)](#TransactionalSMS.postRetrieveTasks) ⇒ TYPE.CodeExtractItem - * [.parseMetadata(metadata)](#TransactionalSMS.parseMetadata) ⇒ TYPE.CodeExtractItem * [.prepExtractedCode(metadataScript)](#TransactionalSMS.prepExtractedCode) ⇒ Object * [.getFilesToCommit(keyArr)](#TransactionalSMS.getFilesToCommit) ⇒ Array.<string> @@ -4570,18 +4595,6 @@ manages post retrieve steps | --- | --- | --- | | metadata | TYPE.MetadataTypeItem | a single item | - - -### TransactionalSMS.parseMetadata(metadata) ⇒ TYPE.CodeExtractItem -Splits the metadata into two parts and parses in a standard manner - -**Kind**: static method of [TransactionalSMS](#TransactionalSMS) -**Returns**: TYPE.CodeExtractItem - a single item with code parts extracted - -| Param | Type | Description | -| --- | --- | --- | -| metadata | TYPE.MetadataTypeItem | a single item | - ### TransactionalSMS.prepExtractedCode(metadataScript) ⇒ Object diff --git a/lib/metadataTypes/TransactionalEmail.js b/lib/metadataTypes/TransactionalEmail.js index 1646a1b9a..12af45447 100644 --- a/lib/metadataTypes/TransactionalEmail.js +++ b/lib/metadataTypes/TransactionalEmail.js @@ -3,6 +3,7 @@ const TYPE = require('../../types/mcdev.d'); const MetadataType = require('./MetadataType'); const Util = require('../util/util'); +const cache = require('../util/cache'); /** * TransactionalEmail MetadataType @@ -93,6 +94,128 @@ class TransactionalEmail extends MetadataType { false ); } + /** + * prepares for deployment + * + * @param {TYPE.MetadataTypeItem} metadata a single item + * @returns {TYPE.MetadataTypeItem} Promise + */ + static async preDeployTasks(metadata) { + // asset + if (metadata.content?.customerKey) { + // we merely want to be able to show an error if it does not exist + cache.searchForField( + 'asset', + metadata.content.customerKey, + 'customerKey', + 'customerKey' + ); + } + // subscriptions: dataExtension + if (metadata.subscriptions?.dataExtension) { + // we merely want to be able to show an error if it does not exist + cache.searchForField( + 'dataExtension', + metadata.subscriptions.dataExtension, + 'CustomerKey', + 'CustomerKey' + ); + } + // subscriptions: list + if (metadata.subscriptions?.r__list_PathName) { + metadata.subscriptions.list = cache.getListObjectId( + metadata.r__list_PathName, + 'CustomerKey' + ); + delete metadata.subscriptions.r__list_PathName; + } else if (metadata.subscriptions?.list) { + throw new Error( + `r__list_PathName not defined but instead found List.ID. Please try re-retrieving this from your BU.` + ); + } + + // journey + if (metadata.journey?.interactionKey) { + cache.searchForField('interaction', metadata.journey.interactionKey, 'key', 'key'); + } + + return metadata; + } + /** + * manages post retrieve steps + * + * @param {TYPE.MetadataTypeItem} metadata a single item + * @returns {TYPE.MetadataTypeItem} a single item + */ + static postRetrieveTasks(metadata) { + // asset + if (metadata.content?.customerKey) { + try { + // we merely want to be able to show an error if it does not exist + cache.searchForField( + 'asset', + metadata.content.customerKey, + 'customerKey', + 'customerKey' + ); + } catch (ex) { + Util.logger.warn( + ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${ + metadata[this.definition.keyField] + }): ${ex.message}.` + ); + } + } + // subscriptions: dataExtension + if (metadata.subscriptions?.dataExtension) { + try { + // we merely want to be able to show a warning if it does not exist + cache.searchForField( + 'dataExtension', + metadata.subscriptions.dataExtension, + 'CustomerKey', + 'CustomerKey' + ); + } catch (ex) { + Util.logger.warn( + ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${ + metadata[this.definition.keyField] + }): ${ex.message}.` + ); + } + } + // subscriptions: list + if (metadata.subscriptions?.list) { + try { + metadata.r__list_PathName = cache.getListPathName( + metadata.subscriptions.list, + 'CustomerKey' + ); + delete metadata.subscriptions.list; + } catch (ex) { + Util.logger.warn( + ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${ + metadata[this.definition.keyField] + }): ${ex.message}.` + ); + } + } + // journey + if (metadata.journey?.interactionKey) { + try { + // we merely want to be able to show a warning if it does not exist + cache.searchForField('interaction', metadata.journey.interactionKey, 'key', 'key'); + } catch (ex) { + Util.logger.warn( + ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${ + metadata[this.definition.keyField] + }): ${ex.message}.` + ); + } + } + + return metadata; + } } // Assign definition to static attributes diff --git a/lib/metadataTypes/definitions/TransactionalEmail.definition.js b/lib/metadataTypes/definitions/TransactionalEmail.definition.js index 8ee2799dd..bb0b56386 100644 --- a/lib/metadataTypes/definitions/TransactionalEmail.definition.js +++ b/lib/metadataTypes/definitions/TransactionalEmail.definition.js @@ -1,6 +1,6 @@ module.exports = { bodyIteratorField: 'definitions', - dependencies: [], + dependencies: ['asset', 'list', 'interaction'], hasExtended: false, idField: 'definitionId', keyField: 'definitionKey', From 3a2cfcc27f56f1fdbbab5393f623f08149864812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 23 Nov 2022 14:54:04 +0100 Subject: [PATCH 012/103] #557: handle key-not-found during retrieve and show key after download-count for transactionalEmail --- lib/metadataTypes/TransactionalEmail.js | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/metadataTypes/TransactionalEmail.js b/lib/metadataTypes/TransactionalEmail.js index 12af45447..72bb125f3 100644 --- a/lib/metadataTypes/TransactionalEmail.js +++ b/lib/metadataTypes/TransactionalEmail.js @@ -37,16 +37,29 @@ class TransactionalEmail extends MetadataType { } // get all sms with additional details not given by the list endpoint - const details = keyList - ? await Promise.all(keyList.map((key) => this.client.rest.get(baseUri + (key || '')))) - : []; - + const details = ( + await Promise.all( + keyList.map(async (key) => { + try { + return await this.client.rest.get(baseUri + (key || '')); + } catch { + return null; + } + }) + ) + ).filter(Boolean); const parsed = this.parseResponseBody({ definitions: details }); // * retrieveDir is mandatory in this method as it is not used for caching (there is a seperate method for that) const savedMetadata = await this.saveResults(parsed, retrieveDir, null, null); + // defined colors for optionally printing the keys we filtered by + const color = { + reset: '\x1B[0m', + dim: '\x1B[2m', + }; Util.logger.info( - `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` + `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` + + (key !== null ? ` ${color.dim}(Key: ${key})${color.reset}` : '') ); return { metadata: savedMetadata, type: this.definition.type }; From ecb3ddbb3a360633b3f89961347a34557ff88ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 23 Nov 2022 14:56:20 +0100 Subject: [PATCH 013/103] #556: refactoring --- docs/dist/documentation.md | 13 ------------- lib/metadataTypes/TransactionalSMS.js | 10 ---------- 2 files changed, 23 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 531699d1d..147ac47db 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -4375,7 +4375,6 @@ TransactionalSMS MetadataType * [.preDeployTasks(metadata, dir)](#TransactionalSMS.preDeployTasks) ⇒ TYPE.MetadataTypeItem * [._mergeCode(metadata, deployDir, [templateName])](#TransactionalSMS._mergeCode) ⇒ Promise.<string> * [.postRetrieveTasks(metadata)](#TransactionalSMS.postRetrieveTasks) ⇒ TYPE.CodeExtractItem - * [.parseMetadata(metadata)](#TransactionalSMS.parseMetadata) ⇒ TYPE.CodeExtractItem * [.prepExtractedCode(metadataScript)](#TransactionalSMS.prepExtractedCode) ⇒ Object * [.getFilesToCommit(keyArr)](#TransactionalSMS.getFilesToCommit) ⇒ Array.<string> @@ -4491,18 +4490,6 @@ manages post retrieve steps | --- | --- | --- | | metadata | TYPE.MetadataTypeItem | a single item | - - -### TransactionalSMS.parseMetadata(metadata) ⇒ TYPE.CodeExtractItem -Splits the metadata into two parts and parses in a standard manner - -**Kind**: static method of [TransactionalSMS](#TransactionalSMS) -**Returns**: TYPE.CodeExtractItem - a single item with code parts extracted - -| Param | Type | Description | -| --- | --- | --- | -| metadata | TYPE.MetadataTypeItem | a single item | - ### TransactionalSMS.prepExtractedCode(metadataScript) ⇒ Object diff --git a/lib/metadataTypes/TransactionalSMS.js b/lib/metadataTypes/TransactionalSMS.js index 1c409c024..fbc2d79a1 100644 --- a/lib/metadataTypes/TransactionalSMS.js +++ b/lib/metadataTypes/TransactionalSMS.js @@ -189,16 +189,6 @@ class TransactionalSMS extends MetadataType { * @returns {TYPE.CodeExtractItem} Array with one metadata object and one ssjs string */ static postRetrieveTasks(metadata) { - return this.parseMetadata(metadata); - } - - /** - * Splits the metadata into two parts and parses in a standard manner - * - * @param {TYPE.MetadataTypeItem} metadata a single item - * @returns {TYPE.CodeExtractItem} a single item with code parts extracted - */ - static parseMetadata(metadata) { // extract message body const codeArr = []; // keep between tags From 7d522a09acab646ff03f66b042e4b46f49f9fbe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 23 Nov 2022 15:10:10 +0100 Subject: [PATCH 014/103] #556: check SMS message for HTML which would be printed as-is on phones --- docs/dist/documentation.md | 13 +++++++++++++ lib/metadataTypes/TransactionalSMS.js | 27 +++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 147ac47db..ac42e2b67 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -4376,6 +4376,7 @@ TransactionalSMS MetadataType * [._mergeCode(metadata, deployDir, [templateName])](#TransactionalSMS._mergeCode) ⇒ Promise.<string> * [.postRetrieveTasks(metadata)](#TransactionalSMS.postRetrieveTasks) ⇒ TYPE.CodeExtractItem * [.prepExtractedCode(metadataScript)](#TransactionalSMS.prepExtractedCode) ⇒ Object + * [._isHTML(code)](#TransactionalSMS._isHTML) ⇒ boolean * [.getFilesToCommit(keyArr)](#TransactionalSMS.getFilesToCommit) ⇒ Array.<string> @@ -4502,6 +4503,18 @@ helper for [parseMetadata](parseMetadata) and [_buildForNested](_buildForNested) | --- | --- | --- | | metadataScript | string | the code of the file | + + +### TransactionalSMS.\_isHTML(code) ⇒ boolean +very simplified test for HTML code in our SMS + +**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**Returns**: boolean - true if HTML is found + +| Param | Type | Description | +| --- | --- | --- | +| code | string | sms source code | + ### TransactionalSMS.getFilesToCommit(keyArr) ⇒ Array.<string> diff --git a/lib/metadataTypes/TransactionalSMS.js b/lib/metadataTypes/TransactionalSMS.js index fbc2d79a1..31a3faf5f 100644 --- a/lib/metadataTypes/TransactionalSMS.js +++ b/lib/metadataTypes/TransactionalSMS.js @@ -137,6 +137,15 @@ class TransactionalSMS extends MetadataType { message: await this._mergeCode(metadata, dir), }; + if (this._isHTML(metadata.content?.message)) { + // keep this as a non-blocking warning because the test not 100% accurate + Util.logger.warn( + ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${ + metadata[this.definition.keyField] + }): HTML detected` + ); + } + // subscriptions: mobileCode if (metadata.subscriptions?.shortCode) { // we merely want to be able to show an error if it does not exist @@ -200,6 +209,15 @@ class TransactionalSMS extends MetadataType { fileExt: fileExt, content: code, }); + + if (this._isHTML(code)) { + Util.logger.warn( + ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${ + metadata[this.definition.keyField] + }): HTML detected` + ); + } + // subscriptions: mobileCode if (metadata.subscriptions?.shortCode) { try { @@ -270,6 +288,15 @@ class TransactionalSMS extends MetadataType { return { fileExt, code }; } + /** + * very simplified test for HTML code in our SMS + * + * @param {string} code sms source code + * @returns {boolean} true if HTML is found + */ + static _isHTML(code) { + return /(<([^>]+)>)/gi.test(code); + } /** * should return only the json for all but asset, query and script that are saved as multiple files * additionally, the documentation for dataExtension and automation should be returned From f36af755424c41d76d9f659b8d4cea150234a17c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 24 Nov 2022 16:52:31 +0100 Subject: [PATCH 015/103] #563: merge parseMetadata() into postRetrieveTasks() --- docs/dist/documentation.md | 15 +-------------- lib/metadataTypes/Asset.js | 21 ++++++--------------- 2 files changed, 7 insertions(+), 29 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index b3a83618a..698056e3f 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -830,7 +830,6 @@ FileTransfer MetadataType * [.buildTemplateForNested(templateDir, targetDir, metadata, templateVariables, templateName)](#Asset.buildTemplateForNested) ⇒ Promise.<void> * [._buildForNested(templateDir, targetDir, metadata, templateVariables, templateName, mode)](#Asset._buildForNested) ⇒ Promise.<void> * [.setFolderPath(metadata)](#Asset.setFolderPath) - * [.parseMetadata(metadata)](#Asset.parseMetadata) ⇒ TYPE.CodeExtractItem * [._mergeCode(metadata, deployDir, subType, [templateName], [fileListOnly])](#Asset._mergeCode) ⇒ Promise.<Array.<TYPE.CodeExtract>> * [._mergeCode_slots(prefix, metadataSlots, readDirArr, subtypeExtension, subDirArr, fileList, customerKey, [templateName], [fileListOnly])](#Asset._mergeCode_slots) ⇒ Promise.<void> * [._extractCode(metadata)](#Asset._extractCode) ⇒ TYPE.CodeExtractItem @@ -1077,18 +1076,6 @@ generic script that retrieves the folder path from cache and updates the given m | --- | --- | --- | | metadata | TYPE.MetadataTypeItem | a single script activity definition | - - -### Asset.parseMetadata(metadata) ⇒ TYPE.CodeExtractItem -parses retrieved Metadata before saving - -**Kind**: static method of [Asset](#Asset) -**Returns**: TYPE.CodeExtractItem - parsed metadata definition - -| Param | Type | Description | -| --- | --- | --- | -| metadata | TYPE.AssetItem | a single asset definition | - ### Asset.\_mergeCode(metadata, deployDir, subType, [templateName], [fileListOnly]) ⇒ Promise.<Array.<TYPE.CodeExtract>> @@ -1128,7 +1115,7 @@ helper for [preDeployTasks](preDeployTasks) that loads extracted code content ba ### Asset.\_extractCode(metadata) ⇒ TYPE.CodeExtractItem -helper for [parseMetadata](parseMetadata) that finds code content in JSON and extracts it +helper for [postRetrieveTasks](postRetrieveTasks) that finds code content in JSON and extracts it to allow saving that separately and formatted **Kind**: static method of [Asset](#Asset) diff --git a/lib/metadataTypes/Asset.js b/lib/metadataTypes/Asset.js index a19a27169..6f0a794af 100644 --- a/lib/metadataTypes/Asset.js +++ b/lib/metadataTypes/Asset.js @@ -486,7 +486,11 @@ class Asset extends MetadataType { * @returns {TYPE.CodeExtractItem} metadata */ static postRetrieveTasks(metadata) { - return this.parseMetadata(metadata); + // folder + this.setFolderPath(metadata); + // extract HTML for selected subtypes and convert payload for easier processing in MetadataType.saveResults() + metadata = this._extractCode(metadata); + return metadata; } /** @@ -778,19 +782,6 @@ class Asset extends MetadataType { } } - /** - * parses retrieved Metadata before saving - * - * @param {TYPE.AssetItem} metadata a single asset definition - * @returns {TYPE.CodeExtractItem} parsed metadata definition - */ - static parseMetadata(metadata) { - // folder - this.setFolderPath(metadata); - // extract HTML for selected subtypes and convert payload for easier processing in MetadataType.saveResults() - metadata = this._extractCode(metadata); - return metadata; - } /** * helper for {@link preDeployTasks} that loads extracted code content back into JSON * @@ -1104,7 +1095,7 @@ class Asset extends MetadataType { } } /** - * helper for {@link parseMetadata} that finds code content in JSON and extracts it + * helper for {@link postRetrieveTasks} that finds code content in JSON and extracts it * to allow saving that separately and formatted * * @param {TYPE.AssetItem} metadata a single asset definition From 69bbcd57134a273926546809158401929b852152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 24 Nov 2022 17:17:48 +0100 Subject: [PATCH 016/103] #563: trim asset keys for file/folder names --- lib/metadataTypes/Asset.js | 57 +++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/lib/metadataTypes/Asset.js b/lib/metadataTypes/Asset.js index 6f0a794af..06ba04792 100644 --- a/lib/metadataTypes/Asset.js +++ b/lib/metadataTypes/Asset.js @@ -270,6 +270,15 @@ class Asset extends MetadataType { // only when we save results do we need the complete metadata or files. caching can skip these if (retrieveDir && items.length > 0) { + for (const item of items) { + if (item.customerKey.trim() !== item.customerKey) { + Util.logger.warn( + ` - ${this.definition.type} ${item[this.definition.nameField]} (${ + item[this.definition.keyField] + }) has leading or trailing spaces in customerKey. Please remove them in SFMC.` + ); + } + } // we have to wait on execution or it potentially causes memory reference issues when changing between BUs await this.requestAndSaveExtended(items, subType, retrieveDir, templateVariables); Util.logger.debug(`Downloaded asset-${subType}: ${items.length}`); @@ -797,13 +806,15 @@ class Asset extends MetadataType { const fileList = []; let subDirArr; let readDirArr; + // unfortunately, asset's key can contain spaces at beginning/end which can break the file system when folders are created with it + const customerKey = metadata.customerKey.trim(); switch (metadata.assetType.name) { case 'templatebasedemail': // message case 'htmlemail': { // message // this complex type always creates its own subdir per asset subDirArr = [this.definition.type, subType]; - readDirArr = [deployDir, ...subDirArr, templateName || metadata.customerKey]; + readDirArr = [deployDir, ...subDirArr, templateName || customerKey]; // metadata.views.html.content (mandatory) // the main content can be empty (=not set up yet) hence check if we did extract sth or else readFile() will print error msgs @@ -824,7 +835,7 @@ class Asset extends MetadataType { if (templateName) { // to use this method in templating, store a copy of the info in fileList fileList.push({ - subFolder: [...subDirArr, metadata.customerKey], + subFolder: [...subDirArr, customerKey], fileName: 'index' + subtypeExtension, fileExt: 'html', content: metadata.views.html.content, @@ -841,7 +852,7 @@ class Asset extends MetadataType { subtypeExtension, subDirArr, fileList, - metadata.customerKey, + customerKey, templateName, fileListOnly ); @@ -857,9 +868,7 @@ class Asset extends MetadataType { await File.pathExists( File.normalizePath([ ...readDirArr, - `${ - templateName || metadata.customerKey // TODO check why this could be templateName - }${subtypeExtension}.html`, + `${templateName || customerKey}${subtypeExtension}.html`, ]) ) ) { @@ -867,7 +876,7 @@ class Asset extends MetadataType { if (!fileListOnly) { metadata.views.text.content = await File.readFilteredFilename( readDirArr, - (templateName || metadata.customerKey) + subtypeExtension, + (templateName || customerKey) + subtypeExtension, 'html' ); } @@ -875,7 +884,7 @@ class Asset extends MetadataType { // to use this method in templating, store a copy of the info in fileList fileList.push({ subFolder: subDirArr, - fileName: metadata.customerKey + subtypeExtension, + fileName: customerKey + subtypeExtension, fileExt: 'html', content: metadata.views.text.content, }); @@ -887,7 +896,7 @@ class Asset extends MetadataType { // asset // this complex type always creates its own subdir per asset subDirArr = [this.definition.type, subType]; - readDirArr = [deployDir, ...subDirArr, templateName || metadata.customerKey]; + readDirArr = [deployDir, ...subDirArr, templateName || customerKey]; // metadata.views.html.slots.<>.blocks.<>.content (optional) (pre & post 20222) if (metadata?.views?.html?.slots) { @@ -898,7 +907,7 @@ class Asset extends MetadataType { subtypeExtension, subDirArr, fileList, - metadata.customerKey, + customerKey, templateName, fileListOnly ); @@ -925,7 +934,7 @@ class Asset extends MetadataType { if (templateName) { // to use this method in templating, store a copy of the info in fileList fileList.push({ - subFolder: [...subDirArr, metadata.customerKey], + subFolder: [...subDirArr, customerKey], fileName: 'views.html.content' + subtypeExtension, fileExt: 'html', content: metadata.views.html.content, @@ -951,7 +960,7 @@ class Asset extends MetadataType { if (templateName) { // to use this method in templating, store a copy of the info in fileList fileList.push({ - subFolder: [...subDirArr, metadata.customerKey], + subFolder: [...subDirArr, customerKey], fileName: 'content' + subtypeExtension, fileExt: 'html', content: metadata.views.html.content, @@ -984,7 +993,7 @@ class Asset extends MetadataType { await File.pathExists( File.normalizePath([ ...readDirArr, - `${templateName || metadata.customerKey}${subtypeExtension}.${ext}`, + `${templateName || customerKey}${subtypeExtension}.${ext}`, ]) ) ) { @@ -992,7 +1001,7 @@ class Asset extends MetadataType { if (!fileListOnly) { metadata.content = await File.readFilteredFilename( readDirArr, - (templateName || metadata.customerKey) + subtypeExtension, + (templateName || customerKey) + subtypeExtension, ext ); } @@ -1000,7 +1009,7 @@ class Asset extends MetadataType { // to use this method in templating, store a copy of the info in fileList fileList.push({ subFolder: subDirArr, - fileName: (templateName || metadata.customerKey) + subtypeExtension, + fileName: (templateName || customerKey) + subtypeExtension, fileExt: ext, content: metadata.content, }); @@ -1104,6 +1113,8 @@ class Asset extends MetadataType { static _extractCode(metadata) { const codeArr = []; let subType; + // unfortunately, asset's key can contain spaces at beginning/end which can break the file system when folders are created with it + const customerKey = metadata.customerKey.trim(); switch (metadata.assetType.name) { case 'templatebasedemail': // message case 'htmlemail': { @@ -1124,7 +1135,11 @@ class Asset extends MetadataType { this._extractCode_slots('views.html.slots', metadata.views.html.slots, codeArr); } - return { json: metadata, codeArr: codeArr, subFolder: [metadata.customerKey] }; + return { + json: metadata, + codeArr: codeArr, + subFolder: [customerKey], + }; } case 'textonlyemail': { // message @@ -1132,7 +1147,7 @@ class Asset extends MetadataType { if (metadata.views?.text?.content?.length) { codeArr.push({ subFolder: null, - fileName: metadata.customerKey, + fileName: customerKey, fileExt: 'html', content: metadata.views.text.content, }); @@ -1170,7 +1185,11 @@ class Asset extends MetadataType { }); delete metadata.content; } - return { json: metadata, codeArr: codeArr, subFolder: [metadata.customerKey] }; + return { + json: metadata, + codeArr: codeArr, + subFolder: [customerKey], + }; } case 'buttonblock': // block - Button Block case 'freeformblock': // block @@ -1193,7 +1212,7 @@ class Asset extends MetadataType { if (metadata?.content?.length) { codeArr.push({ subFolder: null, - fileName: metadata.customerKey, + fileName: customerKey, fileExt: fileExt, content: metadata.content, }); From 04e956862311f7b71564302003f9bf27ec5c8063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 24 Nov 2022 17:48:46 +0100 Subject: [PATCH 017/103] #557: reduced asset dep to asset-message; added dataExtension dep --- lib/metadataTypes/definitions/TransactionalEmail.definition.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/metadataTypes/definitions/TransactionalEmail.definition.js b/lib/metadataTypes/definitions/TransactionalEmail.definition.js index bb0b56386..90d82b750 100644 --- a/lib/metadataTypes/definitions/TransactionalEmail.definition.js +++ b/lib/metadataTypes/definitions/TransactionalEmail.definition.js @@ -1,6 +1,6 @@ module.exports = { bodyIteratorField: 'definitions', - dependencies: ['asset', 'list', 'interaction'], + dependencies: ['asset-message', 'dataExtension', 'list', 'interaction'], hasExtended: false, idField: 'definitionId', keyField: 'definitionKey', From a35ed43eb94f0ecc945017b64bb176fe69ba0005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 24 Nov 2022 19:33:56 +0100 Subject: [PATCH 018/103] #557: show how many "deleted" transactionalEmails were filtered --- lib/metadataTypes/TransactionalEmail.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/metadataTypes/TransactionalEmail.js b/lib/metadataTypes/TransactionalEmail.js index 72bb125f3..2a8db145d 100644 --- a/lib/metadataTypes/TransactionalEmail.js +++ b/lib/metadataTypes/TransactionalEmail.js @@ -30,7 +30,13 @@ class TransactionalEmail extends MetadataType { const response = this.definition.restPagination ? await this.client.rest.getBulk(baseUri) : await this.client.rest.get(baseUri); - keyList = Object.keys(this.parseResponseBody(response)); + const parsed = this.parseResponseBody(response); + keyList = Object.keys(parsed).filter((item) => parsed[item].status !== 'Deleted'); + Util.logger.info( + ` - Filtered ${this.definition.type} with status 'deleted': ${ + Object.keys(parsed).length - keyList.length + } (downloaded but not saved to disk)` + ); } else { // Retrieve single keyList = [key]; From 174e45aac72e88250d0ae6a6d5af2ba16515a690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 24 Nov 2022 20:19:23 +0100 Subject: [PATCH 019/103] #556: switch to ecmaVersion 2022 to allow static class properties --- .eslintrc.json | 2 +- docs/dist/documentation.md | 75 ++++++++++++++++++++++---------------- 2 files changed, 45 insertions(+), 32 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 9ed3ff94f..bc3ea5f3e 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -17,7 +17,7 @@ "SharedArrayBuffer": "readonly" }, "parserOptions": { - "ecmaVersion": 2020, + "ecmaVersion": 2022, "sourceType": "module" }, "root": true, diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index ac42e2b67..1d766dc1b 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -101,6 +101,9 @@ Provides default functionality that can be overwritten by child metadata type cl
SetDefinitionMetadataType

SetDefinition MetadataType

+
TransactionalMessageMetadataType
+

TransactionalMessage MetadataType

+
TransactionalSMSMetadataType

TransactionalSMS MetadataType

@@ -4357,35 +4360,28 @@ Retrieves Metadata of schema set definitions for caching. **Kind**: static method of [SetDefinition](#SetDefinition) **Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise - + -## TransactionalSMS ⇐ [MetadataType](#MetadataType) -TransactionalSMS MetadataType +## TransactionalMessage ⇐ [MetadataType](#MetadataType) +TransactionalMessage MetadataType **Kind**: global class **Extends**: [MetadataType](#MetadataType) -* [TransactionalSMS](#TransactionalSMS) ⇐ [MetadataType](#MetadataType) - * [.retrieve(retrieveDir, [_], [__], [___], [key])](#TransactionalSMS.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> - * [.retrieveForCache()](#TransactionalSMS.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> - * [.update(metadata)](#TransactionalSMS.update) ⇒ Promise - * [.create(metadata)](#TransactionalSMS.create) ⇒ Promise - * [.deleteByKey(buObject, key)](#TransactionalSMS.deleteByKey) ⇒ Promise.<boolean> - * [.postDeleteTasks(buObject, customerKey)](#TransactionalSMS.postDeleteTasks) ⇒ void - * [.preDeployTasks(metadata, dir)](#TransactionalSMS.preDeployTasks) ⇒ TYPE.MetadataTypeItem - * [._mergeCode(metadata, deployDir, [templateName])](#TransactionalSMS._mergeCode) ⇒ Promise.<string> - * [.postRetrieveTasks(metadata)](#TransactionalSMS.postRetrieveTasks) ⇒ TYPE.CodeExtractItem - * [.prepExtractedCode(metadataScript)](#TransactionalSMS.prepExtractedCode) ⇒ Object - * [._isHTML(code)](#TransactionalSMS._isHTML) ⇒ boolean - * [.getFilesToCommit(keyArr)](#TransactionalSMS.getFilesToCommit) ⇒ Array.<string> +* [TransactionalMessage](#TransactionalMessage) ⇐ [MetadataType](#MetadataType) + * [.retrieve(retrieveDir, [_], [__], [___], [key])](#TransactionalMessage.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.retrieveForCache()](#TransactionalMessage.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.update(metadata)](#TransactionalMessage.update) ⇒ Promise + * [.create(metadata)](#TransactionalMessage.create) ⇒ Promise + * [.deleteByKey(buObject, key)](#TransactionalMessage.deleteByKey) ⇒ Promise.<boolean> - + -### TransactionalSMS.retrieve(retrieveDir, [_], [__], [___], [key]) ⇒ Promise.<TYPE.MetadataTypeMapObj> +### TransactionalMessage.retrieve(retrieveDir, [_], [__], [___], [key]) ⇒ Promise.<TYPE.MetadataTypeMapObj> Retrieves Metadata of Mobile Keywords Endpoint /legacy/v1/beta/mobile/code/ return all Mobile Codes with all details. -**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**Kind**: static method of [TransactionalMessage](#TransactionalMessage) **Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise of metadata | Param | Type | Description | @@ -4396,43 +4392,43 @@ Endpoint /legacy/v1/beta/mobile/code/ return all Mobile Codes with all details. | [___] | void | unused parameter | | [key] | string | customer key of single item to retrieve | - + -### TransactionalSMS.retrieveForCache() ⇒ Promise.<TYPE.MetadataTypeMapObj> +### TransactionalMessage.retrieveForCache() ⇒ Promise.<TYPE.MetadataTypeMapObj> Retrieves event definition metadata for caching -**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**Kind**: static method of [TransactionalMessage](#TransactionalMessage) **Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise of metadata - + -### TransactionalSMS.update(metadata) ⇒ Promise +### TransactionalMessage.update(metadata) ⇒ Promise Updates a single item -**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**Kind**: static method of [TransactionalMessage](#TransactionalMessage) **Returns**: Promise - Promise | Param | Type | Description | | --- | --- | --- | | metadata | TYPE.MetadataTypeItem | a single item | - + -### TransactionalSMS.create(metadata) ⇒ Promise +### TransactionalMessage.create(metadata) ⇒ Promise Creates a single item -**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**Kind**: static method of [TransactionalMessage](#TransactionalMessage) **Returns**: Promise - Promise | Param | Type | Description | | --- | --- | --- | | metadata | TYPE.MetadataTypeItem | a single item | - + -### TransactionalSMS.deleteByKey(buObject, key) ⇒ Promise.<boolean> +### TransactionalMessage.deleteByKey(buObject, key) ⇒ Promise.<boolean> Delete a metadata item from the specified business unit -**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**Kind**: static method of [TransactionalMessage](#TransactionalMessage) **Returns**: Promise.<boolean> - deletion success status | Param | Type | Description | @@ -4440,6 +4436,23 @@ Delete a metadata item from the specified business unit | buObject | TYPE.BuObject | references credentials | | key | string | Identifier of item | + + +## TransactionalSMS ⇐ [MetadataType](#MetadataType) +TransactionalSMS MetadataType + +**Kind**: global class +**Extends**: [MetadataType](#MetadataType) + +* [TransactionalSMS](#TransactionalSMS) ⇐ [MetadataType](#MetadataType) + * [.postDeleteTasks(buObject, customerKey)](#TransactionalSMS.postDeleteTasks) ⇒ void + * [.preDeployTasks(metadata, dir)](#TransactionalSMS.preDeployTasks) ⇒ TYPE.MetadataTypeItem + * [._mergeCode(metadata, deployDir, [templateName])](#TransactionalSMS._mergeCode) ⇒ Promise.<string> + * [.postRetrieveTasks(metadata)](#TransactionalSMS.postRetrieveTasks) ⇒ TYPE.CodeExtractItem + * [.prepExtractedCode(metadataScript)](#TransactionalSMS.prepExtractedCode) ⇒ Object + * [._isHTML(code)](#TransactionalSMS._isHTML) ⇒ boolean + * [.getFilesToCommit(keyArr)](#TransactionalSMS.getFilesToCommit) ⇒ Array.<string> + ### TransactionalSMS.postDeleteTasks(buObject, customerKey) ⇒ void From b4d653c6b2707fcd095a97fb890936a3926abeeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 24 Nov 2022 20:21:31 +0100 Subject: [PATCH 020/103] #556: extract common code into TransactionalMessage class --- lib/metadataTypes/TransactionalMessage.js | 121 ++++++++++++++++++++++ lib/metadataTypes/TransactionalSMS.js | 96 +---------------- 2 files changed, 124 insertions(+), 93 deletions(-) create mode 100644 lib/metadataTypes/TransactionalMessage.js diff --git a/lib/metadataTypes/TransactionalMessage.js b/lib/metadataTypes/TransactionalMessage.js new file mode 100644 index 000000000..5cbd1f19a --- /dev/null +++ b/lib/metadataTypes/TransactionalMessage.js @@ -0,0 +1,121 @@ +'use strict'; + +const TYPE = require('../../types/mcdev.d'); +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); + +/** + * TransactionalMessage MetadataType + * + * @augments MetadataType + */ +class TransactionalMessage extends MetadataType { + static subType = ''; + /** + * Retrieves Metadata of Mobile Keywords + * Endpoint /legacy/v1/beta/mobile/code/ return all Mobile Codes with all details. + * + * @param {string} retrieveDir Directory where retrieved metadata directory will be saved + * @param {void} [_] unused parameter + * @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 keyList; + const baseUri = '/messaging/v1/' + this.subType + '/definitions/'; + if (!key) { + // Retrieve all + const response = this.definition.restPagination + ? await this.client.rest.getBulk(baseUri) + : await this.client.rest.get(baseUri); + const parsed = this.parseResponseBody(response); + keyList = Object.keys(parsed).filter((item) => parsed[item].status !== 'Deleted'); + Util.logger.info( + ` - Filtered ${this.definition.type} with status 'deleted': ${ + Object.keys(parsed).length - keyList.length + } (downloaded but not saved to disk)` + ); + } else { + // Retrieve single + keyList = [key]; + } + + // get all sms with additional details not given by the list endpoint + const details = ( + await Promise.all( + keyList.map(async (key) => { + try { + return await this.client.rest.get(baseUri + (key || '')); + } catch { + return null; + } + }) + ) + ).filter(Boolean); + const parsed = this.parseResponseBody({ definitions: details }); + + // * retrieveDir is mandatory in this method as it is not used for caching (there is a seperate method for that) + const savedMetadata = await this.saveResults(parsed, retrieveDir, null, null); + // defined colors for optionally printing the keys we filtered by + const color = { + reset: '\x1B[0m', + dim: '\x1B[2m', + }; + Util.logger.info( + `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` + + (key !== null ? ` ${color.dim}(Key: ${key})${color.reset}` : '') + ); + + return { metadata: savedMetadata, type: this.definition.type }; + } + + /** + * Retrieves event definition metadata for caching + * + * @returns {Promise.} Promise of metadata + */ + static retrieveForCache() { + return super.retrieveREST(null, '/messaging/v1/' + this.subType + '/definitions/'); + } + /** + * Updates a single item + * + * @param {TYPE.MetadataTypeItem} metadata a single item + * @returns {Promise} Promise + */ + static update(metadata) { + return super.updateREST(metadata, '/messaging/v1/' + this.subType + '/definitions'); + } + + /** + * Creates a single item + * + * @param {TYPE.MetadataTypeItem} metadata a single item + * @returns {Promise} Promise + */ + static create(metadata) { + return super.createREST(metadata, '/messaging/v1/' + this.subType + '/definitions'); + } + /** + * Delete a metadata item from the specified business unit + * + * @param {TYPE.BuObject} buObject references credentials + * @param {string} key Identifier of item + * @returns {Promise.} deletion success status + */ + static deleteByKey(buObject, key) { + return super.deleteByKeyREST( + buObject, + '/messaging/v1/' + this.subType + '/definitions/' + key, + key, + false + ); + } +} + +// Assign definition to static attributes +TransactionalMessage.definition = {}; + +module.exports = TransactionalMessage; diff --git a/lib/metadataTypes/TransactionalSMS.js b/lib/metadataTypes/TransactionalSMS.js index 31a3faf5f..84847e280 100644 --- a/lib/metadataTypes/TransactionalSMS.js +++ b/lib/metadataTypes/TransactionalSMS.js @@ -1,7 +1,7 @@ 'use strict'; const TYPE = require('../../types/mcdev.d'); -const MetadataType = require('./MetadataType'); +const TransactionalMessage = require('./TransactionalMessage'); const Util = require('../util/util'); const File = require('../util/file'); const beautifier = require('beauty-amp-core'); @@ -12,98 +12,8 @@ const cache = require('../util/cache'); * * @augments MetadataType */ -class TransactionalSMS extends MetadataType { - /** - * Retrieves Metadata of Mobile Keywords - * Endpoint /legacy/v1/beta/mobile/code/ return all Mobile Codes with all details. - * - * @param {string} retrieveDir Directory where retrieved metadata directory will be saved - * @param {void} [_] unused parameter - * @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 keyList; - const baseUri = '/messaging/v1/sms/definitions/'; - if (!key) { - // Retrieve all - const response = this.definition.restPagination - ? await this.client.rest.getBulk(baseUri) - : await this.client.rest.get(baseUri); - keyList = Object.keys(this.parseResponseBody(response)); - } else { - // Retrieve single - keyList = [key]; - } - - // get all sms with additional details not given by the list endpoint - const details = ( - await Promise.all( - keyList.map(async (key) => { - try { - return await this.client.rest.get(baseUri + (key || '')); - } catch { - return null; - } - }) - ) - ).filter(Boolean); - const parsed = this.parseResponseBody({ definitions: details }); - - // * retrieveDir is mandatory in this method as it is not used for caching (there is a seperate method for that) - const savedMetadata = await this.saveResults(parsed, retrieveDir, null, null); - // defined colors for optionally printing the keys we filtered by - const color = { - reset: '\x1B[0m', - dim: '\x1B[2m', - }; - Util.logger.info( - `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` + - (key !== null ? ` ${color.dim}(Key: ${key})${color.reset}` : '') - ); - - return { metadata: savedMetadata, type: this.definition.type }; - } - - /** - * Retrieves event definition metadata for caching - * - * @returns {Promise.} Promise of metadata - */ - static retrieveForCache() { - return super.retrieveREST(null, '/messaging/v1/sms/definitions/'); - } - /** - * Updates a single item - * - * @param {TYPE.MetadataTypeItem} metadata a single item - * @returns {Promise} Promise - */ - static update(metadata) { - return super.updateREST(metadata, '/messaging/v1/sms/definitions'); - } - - /** - * Creates a single item - * - * @param {TYPE.MetadataTypeItem} metadata a single item - * @returns {Promise} Promise - */ - static create(metadata) { - return super.createREST(metadata, '/messaging/v1/sms/definitions'); - } - /** - * Delete a metadata item from the specified business unit - * - * @param {TYPE.BuObject} buObject references credentials - * @param {string} key Identifier of item - * @returns {Promise.} deletion success status - */ - static deleteByKey(buObject, key) { - return super.deleteByKeyREST(buObject, '/messaging/v1/sms/definitions/' + key, key, false); - } +class TransactionalSMS extends TransactionalMessage { + static subType = 'sms'; /** * clean up after deleting a metadata item * From d8f0b01fae8bc47a83459a364e5b04f89ee2159f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 24 Nov 2022 20:34:29 +0100 Subject: [PATCH 021/103] #557: refactoring TransactionalEmail to use TransactionalMessage as a baseline --- docs/dist/documentation.md | 82 ++---------------- lib/metadataTypes/TransactionalEmail.js | 108 +----------------------- lib/metadataTypes/TransactionalSMS.js | 2 +- 3 files changed, 13 insertions(+), 179 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 9ee52feb6..1bb9b03f9 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -101,13 +101,13 @@ Provides default functionality that can be overwritten by child metadata type cl
SetDefinitionMetadataType

SetDefinition MetadataType

-
TransactionalEmailMetadataType
+
TransactionalEmailTransactionalMessage

TransactionalEmail MetadataType

TransactionalMessageMetadataType

TransactionalMessage MetadataType

-
TransactionalSMSMetadataType
+
TransactionalSMSTransactionalMessage

TransactionalSMS MetadataType

TriggeredSendDefinitionMetadataType
@@ -4365,82 +4365,16 @@ Retrieves Metadata of schema set definitions for caching. **Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise -## TransactionalEmail ⇐ [MetadataType](#MetadataType) +## TransactionalEmail ⇐ [TransactionalMessage](#TransactionalMessage) TransactionalEmail MetadataType **Kind**: global class -**Extends**: [MetadataType](#MetadataType) +**Extends**: [TransactionalMessage](#TransactionalMessage) -* [TransactionalEmail](#TransactionalEmail) ⇐ [MetadataType](#MetadataType) - * [.retrieve(retrieveDir, [_], [__], [___], [key])](#TransactionalEmail.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> - * [.retrieveForCache()](#TransactionalEmail.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> - * [.update(metadata)](#TransactionalEmail.update) ⇒ Promise - * [.create(metadata)](#TransactionalEmail.create) ⇒ Promise - * [.deleteByKey(buObject, key)](#TransactionalEmail.deleteByKey) ⇒ Promise.<boolean> +* [TransactionalEmail](#TransactionalEmail) ⇐ [TransactionalMessage](#TransactionalMessage) * [.preDeployTasks(metadata)](#TransactionalEmail.preDeployTasks) ⇒ TYPE.MetadataTypeItem * [.postRetrieveTasks(metadata)](#TransactionalEmail.postRetrieveTasks) ⇒ TYPE.MetadataTypeItem - - -### TransactionalEmail.retrieve(retrieveDir, [_], [__], [___], [key]) ⇒ Promise.<TYPE.MetadataTypeMapObj> -Retrieves Metadata of Mobile Keywords -Endpoint /legacy/v1/beta/mobile/code/ return all Mobile Codes with all details. - -**Kind**: static method of [TransactionalEmail](#TransactionalEmail) -**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 | -| [___] | void | unused parameter | -| [key] | string | customer key of single item to retrieve | - - - -### TransactionalEmail.retrieveForCache() ⇒ Promise.<TYPE.MetadataTypeMapObj> -Retrieves event definition metadata for caching - -**Kind**: static method of [TransactionalEmail](#TransactionalEmail) -**Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise of metadata - - -### TransactionalEmail.update(metadata) ⇒ Promise -Updates a single item - -**Kind**: static method of [TransactionalEmail](#TransactionalEmail) -**Returns**: Promise - Promise - -| Param | Type | Description | -| --- | --- | --- | -| metadata | TYPE.MetadataTypeItem | a single item | - - - -### TransactionalEmail.create(metadata) ⇒ Promise -Creates a single item - -**Kind**: static method of [TransactionalEmail](#TransactionalEmail) -**Returns**: Promise - Promise - -| Param | Type | Description | -| --- | --- | --- | -| metadata | TYPE.MetadataTypeItem | a single item | - - - -### TransactionalEmail.deleteByKey(buObject, 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 | -| --- | --- | --- | -| buObject | TYPE.BuObject | references credentials | -| key | string | Identifier of item | - ### TransactionalEmail.preDeployTasks(metadata) ⇒ TYPE.MetadataTypeItem @@ -4543,13 +4477,13 @@ Delete a metadata item from the specified business unit -## TransactionalSMS ⇐ [MetadataType](#MetadataType) +## TransactionalSMS ⇐ [TransactionalMessage](#TransactionalMessage) TransactionalSMS MetadataType **Kind**: global class -**Extends**: [MetadataType](#MetadataType) +**Extends**: [TransactionalMessage](#TransactionalMessage) -* [TransactionalSMS](#TransactionalSMS) ⇐ [MetadataType](#MetadataType) +* [TransactionalSMS](#TransactionalSMS) ⇐ [TransactionalMessage](#TransactionalMessage) * [.postDeleteTasks(buObject, customerKey)](#TransactionalSMS.postDeleteTasks) ⇒ void * [.preDeployTasks(metadata, dir)](#TransactionalSMS.preDeployTasks) ⇒ TYPE.MetadataTypeItem * [._mergeCode(metadata, deployDir, [templateName])](#TransactionalSMS._mergeCode) ⇒ Promise.<string> diff --git a/lib/metadataTypes/TransactionalEmail.js b/lib/metadataTypes/TransactionalEmail.js index 2a8db145d..c54055c03 100644 --- a/lib/metadataTypes/TransactionalEmail.js +++ b/lib/metadataTypes/TransactionalEmail.js @@ -1,118 +1,18 @@ 'use strict'; const TYPE = require('../../types/mcdev.d'); -const MetadataType = require('./MetadataType'); +const TransactionalMessage = require('./TransactionalMessage'); const Util = require('../util/util'); const cache = require('../util/cache'); /** * TransactionalEmail MetadataType * - * @augments MetadataType + * @augments TransactionalMessage */ -class TransactionalEmail extends MetadataType { - /** - * Retrieves Metadata of Mobile Keywords - * Endpoint /legacy/v1/beta/mobile/code/ return all Mobile Codes with all details. - * - * @param {string} retrieveDir Directory where retrieved metadata directory will be saved - * @param {void} [_] unused parameter - * @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 keyList; - const baseUri = '/messaging/v1/email/definitions/'; - if (!key) { - // Retrieve all - const response = this.definition.restPagination - ? await this.client.rest.getBulk(baseUri) - : await this.client.rest.get(baseUri); - const parsed = this.parseResponseBody(response); - keyList = Object.keys(parsed).filter((item) => parsed[item].status !== 'Deleted'); - Util.logger.info( - ` - Filtered ${this.definition.type} with status 'deleted': ${ - Object.keys(parsed).length - keyList.length - } (downloaded but not saved to disk)` - ); - } else { - // Retrieve single - keyList = [key]; - } - - // get all sms with additional details not given by the list endpoint - const details = ( - await Promise.all( - keyList.map(async (key) => { - try { - return await this.client.rest.get(baseUri + (key || '')); - } catch { - return null; - } - }) - ) - ).filter(Boolean); - const parsed = this.parseResponseBody({ definitions: details }); - - // * retrieveDir is mandatory in this method as it is not used for caching (there is a seperate method for that) - const savedMetadata = await this.saveResults(parsed, retrieveDir, null, null); - // defined colors for optionally printing the keys we filtered by - const color = { - reset: '\x1B[0m', - dim: '\x1B[2m', - }; - Util.logger.info( - `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` + - (key !== null ? ` ${color.dim}(Key: ${key})${color.reset}` : '') - ); - - return { metadata: savedMetadata, type: this.definition.type }; - } - - /** - * Retrieves event definition metadata for caching - * - * @returns {Promise.} Promise of metadata - */ - static retrieveForCache() { - return super.retrieveREST(null, '/messaging/v1/email/definitions/'); - } - /** - * Updates a single item - * - * @param {TYPE.MetadataTypeItem} metadata a single item - * @returns {Promise} Promise - */ - static update(metadata) { - return super.updateREST(metadata, '/messaging/v1/email/definitions'); - } +class TransactionalEmail extends TransactionalMessage { + static subType = 'email'; - /** - * Creates a single item - * - * @param {TYPE.MetadataTypeItem} metadata a single item - * @returns {Promise} Promise - */ - static create(metadata) { - return super.createREST(metadata, '/messaging/v1/email/definitions'); - } - /** - * Delete a metadata item from the specified business unit - * - * @param {TYPE.BuObject} buObject references credentials - * @param {string} key Identifier of item - * @returns {Promise.} deletion success status - */ - static deleteByKey(buObject, key) { - return super.deleteByKeyREST( - buObject, - '/messaging/v1/email/definitions/' + key, - key, - false - ); - } /** * prepares for deployment * diff --git a/lib/metadataTypes/TransactionalSMS.js b/lib/metadataTypes/TransactionalSMS.js index 84847e280..3bd032d93 100644 --- a/lib/metadataTypes/TransactionalSMS.js +++ b/lib/metadataTypes/TransactionalSMS.js @@ -10,7 +10,7 @@ const cache = require('../util/cache'); /** * TransactionalSMS MetadataType * - * @augments MetadataType + * @augments TransactionalMessage */ class TransactionalSMS extends TransactionalMessage { static subType = 'sms'; From f1abc81dde9814fe966743c01f81a9452ccc329c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 24 Nov 2022 20:43:53 +0100 Subject: [PATCH 022/103] #557: check for lists / list-path more gracefully --- lib/metadataTypes/List.js | 10 +++++++--- lib/metadataTypes/TransactionalEmail.js | 17 +++++------------ 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/lib/metadataTypes/List.js b/lib/metadataTypes/List.js index b00b57b9a..f339f50f1 100644 --- a/lib/metadataTypes/List.js +++ b/lib/metadataTypes/List.js @@ -51,8 +51,12 @@ class List extends MetadataType { */ static async retrieveForCache() { const results = await this.retrieve(null); - for (const metadataEntry in results.metadata) { - this.parseMetadata(results.metadata[metadataEntry], true); + if (cache.getCache()?.folder) { + for (const metadataEntry in results.metadata) { + this.parseMetadata(results.metadata[metadataEntry], true); + } + } else { + Util.logger.debug('folders not cached but required for list'); } return results; } @@ -95,10 +99,10 @@ class List extends MetadataType { if (!parseForCache) { delete metadata.Category; } - return metadata; } catch (ex) { Util.logger.warn(` - List ${metadata.ID}: '${metadata.CustomerKey}': ${ex.message}`); } + return metadata; } } // Assign definition to static attributes diff --git a/lib/metadataTypes/TransactionalEmail.js b/lib/metadataTypes/TransactionalEmail.js index c54055c03..ee688277f 100644 --- a/lib/metadataTypes/TransactionalEmail.js +++ b/lib/metadataTypes/TransactionalEmail.js @@ -41,16 +41,8 @@ class TransactionalEmail extends TransactionalMessage { ); } // subscriptions: list - if (metadata.subscriptions?.r__list_PathName) { - metadata.subscriptions.list = cache.getListObjectId( - metadata.r__list_PathName, - 'CustomerKey' - ); - delete metadata.subscriptions.r__list_PathName; - } else if (metadata.subscriptions?.list) { - throw new Error( - `r__list_PathName not defined but instead found List.ID. Please try re-retrieving this from your BU.` - ); + if (metadata.subscriptions?.list) { + cache.searchForField('list', metadata.subscriptions.list, 'CustomerKey', 'CustomerKey'); } // journey @@ -106,11 +98,12 @@ class TransactionalEmail extends TransactionalMessage { // subscriptions: list if (metadata.subscriptions?.list) { try { - metadata.r__list_PathName = cache.getListPathName( + cache.searchForField( + 'list', metadata.subscriptions.list, + 'CustomerKey', 'CustomerKey' ); - delete metadata.subscriptions.list; } catch (ex) { Util.logger.warn( ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${ From 4cc113583b1ff420934844f49956a43b3c5345e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 25 Nov 2022 11:33:15 +0100 Subject: [PATCH 023/103] #565: auto-cache list folder if not already cached --- docs/dist/documentation.md | 24 ++++++++++++++++-------- lib/metadataTypes/Folder.js | 36 ++++++++++++++++++++++++++++++------ lib/metadataTypes/List.js | 24 ++++++++++++++++++------ 3 files changed, 64 insertions(+), 20 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 1bb9b03f9..b340aca2a 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -2585,20 +2585,20 @@ Folder MetadataType **Extends**: [MetadataType](#MetadataType) * [Folder](#Folder) ⇐ [MetadataType](#MetadataType) - * [.retrieve(retrieveDir, [additionalFields], buObject, [___], [key])](#Folder.retrieve) ⇒ Promise - * [.retrieveForCache(buObject)](#Folder.retrieveForCache) ⇒ Promise + * [.retrieve(retrieveDir, [additionalFields], buObject, [___], [key], [contentTypeList])](#Folder.retrieve) ⇒ Promise + * [.retrieveForCache(buObject, [contentTypeList])](#Folder.retrieveForCache) ⇒ Promise * [.upsert(metadata)](#Folder.upsert) ⇒ Promise.<object> * [.create(metadataEntry)](#Folder.create) ⇒ Promise * [.update(metadataEntry)](#Folder.update) ⇒ Promise * [.preDeployTasks(metadata)](#Folder.preDeployTasks) ⇒ Promise.<TYPE.MetadataTypeItem> * [.getJsonFromFS(dir, [listBadKeys])](#Folder.getJsonFromFS) ⇒ TYPE.MetadataTypeMap - * [.retrieveHelper([additionalFields], [queryAllAccounts])](#Folder.retrieveHelper) ⇒ Promise.<object> + * [.retrieveHelper([additionalFields], [queryAllAccounts], [contentTypeList])](#Folder.retrieveHelper) ⇒ Promise.<object> * [.postRetrieveTasks(metadata)](#Folder.postRetrieveTasks) ⇒ TYPE.MetadataTypeItem * [.saveResults(results, retrieveDir, mid)](#Folder.saveResults) ⇒ Promise.<object> -### Folder.retrieve(retrieveDir, [additionalFields], buObject, [___], [key]) ⇒ Promise +### Folder.retrieve(retrieveDir, [additionalFields], buObject, [___], [key], [contentTypeList]) ⇒ Promise Retrieves metadata of metadata type into local filesystem. executes callback with retrieved metadata **Kind**: static method of [Folder](#Folder) @@ -2611,10 +2611,11 @@ Retrieves metadata of metadata type into local filesystem. executes callback wit | buObject | TYPE.BuObject | properties for auth | | [___] | void | unused parameter | | [key] | string | customer key of single item to retrieve | +| [contentTypeList] | Array.<string> | content type of folder | -### Folder.retrieveForCache(buObject) ⇒ Promise +### Folder.retrieveForCache(buObject, [contentTypeList]) ⇒ Promise Retrieves folder metadata for caching **Kind**: static method of [Folder](#Folder) @@ -2623,6 +2624,7 @@ Retrieves folder metadata for caching | Param | Type | Description | | --- | --- | --- | | buObject | TYPE.BuObject | properties for auth | +| [contentTypeList] | Array.<string> | content type of folder | @@ -2689,7 +2691,7 @@ Returns file contents mapped to their filename without '.json' ending -### Folder.retrieveHelper([additionalFields], [queryAllAccounts]) ⇒ Promise.<object> +### Folder.retrieveHelper([additionalFields], [queryAllAccounts], [contentTypeList]) ⇒ Promise.<object> Helper to retrieve the folders as promise **Kind**: static method of [Folder](#Folder) @@ -2699,6 +2701,7 @@ Helper to retrieve the folders as promise | --- | --- | --- | | [additionalFields] | Array.<string> | Returns specified fields even if their retrieve definition is not set to true | | [queryAllAccounts] | boolean | which queryAllAccounts setting to use | +| [contentTypeList] | Array.<string> | content type of folder | @@ -2913,7 +2916,7 @@ List MetadataType * [List](#List) ⇐ [MetadataType](#MetadataType) * [.retrieve(retrieveDir, [_], [__], [___], [key])](#List.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> - * [.retrieveForCache()](#List.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.retrieveForCache(buObject)](#List.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.deleteByKey(buObject, customerKey)](#List.deleteByKey) ⇒ Promise.<boolean> * [.postRetrieveTasks(list)](#List.postRetrieveTasks) ⇒ TYPE.MetadataTypeItem * [.parseMetadata(metadata, [parseForCache])](#List.parseMetadata) ⇒ TYPE.MetadataTypeItem @@ -2936,11 +2939,16 @@ Retrieves Metadata of Lists -### List.retrieveForCache() ⇒ Promise.<TYPE.MetadataTypeMapObj> +### List.retrieveForCache(buObject) ⇒ Promise.<TYPE.MetadataTypeMapObj> Gets metadata cache with limited fields and does not store value to disk **Kind**: static method of [List](#List) **Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise of metadata + +| Param | Type | Description | +| --- | --- | --- | +| buObject | TYPE.BuObject | properties for auth | + ### List.deleteByKey(buObject, customerKey) ⇒ Promise.<boolean> diff --git a/lib/metadataTypes/Folder.js b/lib/metadataTypes/Folder.js index 9d84f087c..a4ac1f614 100644 --- a/lib/metadataTypes/Folder.js +++ b/lib/metadataTypes/Folder.js @@ -21,16 +21,19 @@ class Folder extends MetadataType { * @param {TYPE.BuObject} buObject properties for auth * @param {void} [___] unused parameter * @param {string} [key] customer key of single item to retrieve + * @param {string[]} [contentTypeList] content type of folder * @returns {Promise} Promise */ - static async retrieve(retrieveDir, additionalFields, buObject, ___, key) { + static async retrieve(retrieveDir, additionalFields, buObject, ___, key, contentTypeList) { if (key) { Util.logger.error(`Folder.retrieve() does not support key parameter`); } - const queryAllFolders = await this.retrieveHelper(additionalFields, true); + const queryAllFolders = await this.retrieveHelper(additionalFields, true, contentTypeList); if (buObject.eid !== buObject.mid) { - queryAllFolders.push(...(await this.retrieveHelper(additionalFields, false))); + queryAllFolders.push( + ...(await this.retrieveHelper(additionalFields, false, contentTypeList)) + ); } const sortPairs = toposort(queryAllFolders.map((a) => [a.ParentFolder.ID, a.ID])); const idMap = {}; @@ -150,10 +153,11 @@ class Folder extends MetadataType { * Retrieves folder metadata for caching * * @param {TYPE.BuObject} buObject properties for auth + * @param {string[]} [contentTypeList] content type of folder * @returns {Promise} Promise */ - static retrieveForCache(buObject) { - return this.retrieve(null, null, buObject); + static retrieveForCache(buObject, contentTypeList) { + return this.retrieve(null, null, buObject, null, null, contentTypeList); } /** @@ -515,10 +519,30 @@ class Folder extends MetadataType { * * @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true * @param {boolean} [queryAllAccounts] which queryAllAccounts setting to use + * @param {string[]} [contentTypeList] content type of folder * @returns {Promise.} soap object */ - static async retrieveHelper(additionalFields, queryAllAccounts) { + static async retrieveHelper(additionalFields, queryAllAccounts, contentTypeList) { const options = { QueryAllAccounts: !!queryAllAccounts }; + if (contentTypeList) { + for (const contentType of contentTypeList) { + options.filter = options.filter + ? { + leftOperand: { + leftOperand: 'ContentType', + operator: 'equals', + rightOperand: contentType, + }, + operator: 'OR', + rightOperand: options.filter, + } + : { + leftOperand: 'ContentType', + operator: 'equals', + rightOperand: contentType, + }; + } + } const response = await this.client.soap.retrieveBulk( 'DataFolder', this.getFieldNamesToRetrieve(additionalFields).filter( diff --git a/lib/metadataTypes/List.js b/lib/metadataTypes/List.js index f339f50f1..6336ff637 100644 --- a/lib/metadataTypes/List.js +++ b/lib/metadataTypes/List.js @@ -2,6 +2,7 @@ const TYPE = require('../../types/mcdev.d'); const MetadataType = require('./MetadataType'); +const Folder = require('./Folder'); const Util = require('../util/util'); const cache = require('../util/cache'); @@ -47,16 +48,27 @@ class List extends MetadataType { /** * Gets metadata cache with limited fields and does not store value to disk * + * @param {TYPE.BuObject} buObject properties for auth * @returns {Promise.} Promise of metadata */ - static async retrieveForCache() { + static async retrieveForCache(buObject) { const results = await this.retrieve(null); - if (cache.getCache()?.folder) { - for (const metadataEntry in results.metadata) { - this.parseMetadata(results.metadata[metadataEntry], true); - } - } else { + if (!cache.getCache()?.folder) { Util.logger.debug('folders not cached but required for list'); + Util.logger.info(' - Caching dependent Metadata: folder'); + Folder.client = this.client; + Folder.properties = this.properties; + const result = await Folder.retrieveForCache(buObject, [ + 'list', + 'mysubs', + 'suppression_list', + 'publication', + 'contextual_suppression_list', + ]); + cache.setMetadata('folder', result.metadata); + } + for (const metadataEntry in results.metadata) { + this.parseMetadata(results.metadata[metadataEntry], true); } return results; } From 7fc273ee3eafc92b2e6cb3826454e8aad47c9efd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 25 Nov 2022 11:44:41 +0100 Subject: [PATCH 024/103] #566: get ParentBU's all subscriber list depending on BU-unsubscribe settings --- lib/metadataTypes/List.js | 65 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/lib/metadataTypes/List.js b/lib/metadataTypes/List.js index 6336ff637..a3fc8839d 100644 --- a/lib/metadataTypes/List.js +++ b/lib/metadataTypes/List.js @@ -5,6 +5,7 @@ const MetadataType = require('./MetadataType'); const Folder = require('./Folder'); const Util = require('../util/util'); const cache = require('../util/cache'); +const auth = require('../util/auth'); /** * List MetadataType @@ -52,7 +53,7 @@ class List extends MetadataType { * @returns {Promise.} Promise of metadata */ static async retrieveForCache(buObject) { - const results = await this.retrieve(null); + let results = await this.retrieve(null); if (!cache.getCache()?.folder) { Util.logger.debug('folders not cached but required for list'); Util.logger.info(' - Caching dependent Metadata: folder'); @@ -70,6 +71,68 @@ class List extends MetadataType { for (const metadataEntry in results.metadata) { this.parseMetadata(results.metadata[metadataEntry], true); } + if (buObject.eid !== buObject.mid) { + // for caching, we want to get the All Subscriber List from the Parent Account + Util.logger.debug(' - Checking MasterUnsubscribeBehavior for current BU'); + /** @type {TYPE.BuObject} */ + const buObjectParentBu = { + eid: this.properties.credentials[buObject.credential].eid, + mid: this.properties.credentials[buObject.credential].eid, + businessUnit: Util.parentBuName, + credential: buObject.credential, + }; + try { + this.client = auth.getSDK(buObjectParentBu); + } catch (ex) { + Util.logger.error(ex.message); + return; + } + const buResult = await this.client.soap.retrieve( + 'BusinessUnit', + ['MasterUnsubscribeBehavior'], + { + QueryAllAccounts: true, + filter: { + leftOperand: 'ID', + operator: 'equals', + rightOperand: this.properties.credentials[buObject.credential].eid, + }, + } + ); + const masterUnsubscribeBehavior = buResult.Results[0]?.MasterUnsubscribeBehavior; + if (masterUnsubscribeBehavior === 'ENTIRE_ENTERPRISE') { + Util.logger.debug(` - BU uses ParentBU's All Subscriber List`); + Util.logger.info(' - Caching dependent Metadata: list (on _ParentBU_)'); + const metadataParentBu = await this.retrieve( + null, + null, + null, + null, + 'All Subscribers' + ); + // manually set folder path of parent's All Subscriber List to avoid retrieving folders + for (const key of Object.keys(metadataParentBu.metadata)) { + metadataParentBu.metadata[key].r__folder_Path = 'my subscribers'; + } + // find & delete local All Subscriber list to avoid referencing the wrong one + for (const key of Object.keys(results.metadata)) { + if (results.metadata[key].ListName === 'All Subscribers') { + delete results.metadata[key]; + break; + } + } + + // make sure to overwrite parent bu DEs with local ones + results = { + metadata: { ...metadataParentBu.metadata, ...results.metadata }, + type: results.type, + }; + } else if (masterUnsubscribeBehavior === 'BUSINESS_UNIT_ONLY') { + Util.logger.debug(' - BU uses own All Subscriber List'); + } + // revert client to current default + this.client = auth.getSDK(this.buObject); + } return results; } From 2b3dec073256c3c1fb29a0370aca787aacb470c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 25 Nov 2022 11:57:23 +0100 Subject: [PATCH 025/103] #557: resolve listCustomerKey to listPathName and back would otherwise block cross BU deployments --- lib/metadataTypes/TransactionalEmail.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/metadataTypes/TransactionalEmail.js b/lib/metadataTypes/TransactionalEmail.js index ee688277f..dda541cdc 100644 --- a/lib/metadataTypes/TransactionalEmail.js +++ b/lib/metadataTypes/TransactionalEmail.js @@ -41,8 +41,11 @@ class TransactionalEmail extends TransactionalMessage { ); } // subscriptions: list - if (metadata.subscriptions?.list) { - cache.searchForField('list', metadata.subscriptions.list, 'CustomerKey', 'CustomerKey'); + if (metadata.subscriptions?.r__list_PathName) { + metadata.subscriptions.list = { + ID: cache.getListObjectId(metadata.subscriptions.r__list_PathName, 'CustomerKey'), + }; + delete metadata.subscriptions.r__list_PathName; } // journey @@ -98,12 +101,12 @@ class TransactionalEmail extends TransactionalMessage { // subscriptions: list if (metadata.subscriptions?.list) { try { - cache.searchForField( - 'list', + // List + metadata.subscriptions.r__list_PathName = cache.getListPathName( metadata.subscriptions.list, - 'CustomerKey', 'CustomerKey' ); + delete metadata.subscriptions.list; } catch (ex) { Util.logger.warn( ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${ From 323f68302ce46103a8fac84a58f16b9f428ccd33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 25 Nov 2022 14:01:00 +0100 Subject: [PATCH 026/103] #566: improve log output --- lib/metadataTypes/List.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/metadataTypes/List.js b/lib/metadataTypes/List.js index a3fc8839d..d8e99fb8a 100644 --- a/lib/metadataTypes/List.js +++ b/lib/metadataTypes/List.js @@ -102,7 +102,9 @@ class List extends MetadataType { const masterUnsubscribeBehavior = buResult.Results[0]?.MasterUnsubscribeBehavior; if (masterUnsubscribeBehavior === 'ENTIRE_ENTERPRISE') { Util.logger.debug(` - BU uses ParentBU's All Subscriber List`); - Util.logger.info(' - Caching dependent Metadata: list (on _ParentBU_)'); + Util.logger.info( + ' - Caching dependent Metadata: All Subscriber list (on _ParentBU_)' + ); const metadataParentBu = await this.retrieve( null, null, From 11b20dd7776db1d5b5ec60bf9617f0bd1a21dde8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 25 Nov 2022 14:50:32 +0100 Subject: [PATCH 027/103] #567: boost performance of folder subcaching during dataExtension caching --- lib/metadataTypes/DataExtension.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index 567962a05..fc0291e2a 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -360,7 +360,13 @@ class DataExtension extends MetadataType { Util.logger.info(' - Caching dependent Metadata: folder (shared via _ParentBU_)'); Folder.client = this.client; Folder.properties = this.properties; - const result = await Folder.retrieveForCache(buObjectParentBu); + const result = await Folder.retrieveForCache(buObjectParentBu, [ + 'shared_data', + 'synchronizeddataextension', + 'salesforcedataextension', + 'shared_dataextension', + 'dataextension', + ]); cache.mergeMetadata('folder', result.metadata, buObject.eid); // get the types and clean out non-shared ones From b26fd748cd01d4220bde244470b6b0072d4a13df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 25 Nov 2022 15:02:02 +0100 Subject: [PATCH 028/103] #556: fix transactionalMessage UPDATE definitionKey must be in URL, not in body --- lib/metadataTypes/TransactionalMessage.js | 8 ++++++-- .../definitions/TransactionalSMS.definition.js | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/metadataTypes/TransactionalMessage.js b/lib/metadataTypes/TransactionalMessage.js index 5cbd1f19a..67399324d 100644 --- a/lib/metadataTypes/TransactionalMessage.js +++ b/lib/metadataTypes/TransactionalMessage.js @@ -86,7 +86,10 @@ class TransactionalMessage extends MetadataType { * @returns {Promise} Promise */ static update(metadata) { - return super.updateREST(metadata, '/messaging/v1/' + this.subType + '/definitions'); + return super.updateREST( + metadata, + '/messaging/v1/' + this.subType + '/definitions/' + metadata[this.definition.keyField] + ); } /** @@ -116,6 +119,7 @@ class TransactionalMessage extends MetadataType { } // Assign definition to static attributes -TransactionalMessage.definition = {}; +// ! using SMS definitions here as placeholder to have auto completion +TransactionalMessage.definition = require('../MetadataTypeDefinitions').transactionalSMS; module.exports = TransactionalMessage; diff --git a/lib/metadataTypes/definitions/TransactionalSMS.definition.js b/lib/metadataTypes/definitions/TransactionalSMS.definition.js index 6f56a3155..bd4d9be5e 100644 --- a/lib/metadataTypes/definitions/TransactionalSMS.definition.js +++ b/lib/metadataTypes/definitions/TransactionalSMS.definition.js @@ -23,7 +23,7 @@ module.exports = { }, definitionKey: { isCreateable: true, - isUpdateable: true, + isUpdateable: false, retrieving: true, template: true, }, From 7d6a936381368bbba62cf5ff909fe386965eea20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 25 Nov 2022 15:05:57 +0100 Subject: [PATCH 029/103] #557: fix UPDATE to not have definitionKey in body --- lib/metadataTypes/definitions/TransactionalEmail.definition.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/metadataTypes/definitions/TransactionalEmail.definition.js b/lib/metadataTypes/definitions/TransactionalEmail.definition.js index 90d82b750..256b365b6 100644 --- a/lib/metadataTypes/definitions/TransactionalEmail.definition.js +++ b/lib/metadataTypes/definitions/TransactionalEmail.definition.js @@ -23,7 +23,7 @@ module.exports = { }, definitionKey: { isCreateable: true, - isUpdateable: true, + isUpdateable: false, retrieving: true, template: true, }, From eb726b50441515e92ff2ce29ad8c1d7fbbde7b72 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Nov 2022 14:19:13 +0000 Subject: [PATCH 030/103] Bump eslint from 8.27.0 to 8.28.0 Bumps [eslint](https://github.com/eslint/eslint) from 8.27.0 to 8.28.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.27.0...v8.28.0) --- updated-dependencies: - dependency-name: eslint 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 c64643ac8..d0d0ef0cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,7 @@ "assert": "2.0.0", "axios-mock-adapter": "1.21.2", "chai": "4.3.6", - "eslint": "8.27.0", + "eslint": "8.28.0", "eslint-config-prettier": "8.5.0", "eslint-config-ssjs": "1.1.11", "eslint-plugin-jsdoc": "39.6.2", @@ -2642,9 +2642,9 @@ } }, "node_modules/eslint": { - "version": "8.27.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.27.0.tgz", - "integrity": "sha512-0y1bfG2ho7mty+SiILVf9PfuRA49ek4Nc60Wmmu62QlobNR+CeXa4xXIJgcuwSQgZiWaPH+5BDsctpIW0PR/wQ==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.28.0.tgz", + "integrity": "sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.3.3", @@ -10710,9 +10710,9 @@ "dev": true }, "eslint": { - "version": "8.27.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.27.0.tgz", - "integrity": "sha512-0y1bfG2ho7mty+SiILVf9PfuRA49ek4Nc60Wmmu62QlobNR+CeXa4xXIJgcuwSQgZiWaPH+5BDsctpIW0PR/wQ==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.28.0.tgz", + "integrity": "sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==", "dev": true, "requires": { "@eslint/eslintrc": "^1.3.3", diff --git a/package.json b/package.json index 2cc94c2e7..55910c3b1 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "assert": "2.0.0", "axios-mock-adapter": "1.21.2", "chai": "4.3.6", - "eslint": "8.27.0", + "eslint": "8.28.0", "eslint-config-prettier": "8.5.0", "eslint-config-ssjs": "1.1.11", "eslint-plugin-jsdoc": "39.6.2", From 7a271f80e2e87840b7944e5cba9c6559d23d6eae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Nov 2022 14:24:05 +0000 Subject: [PATCH 031/103] Bump deep-equal from 2.0.5 to 2.1.0 Bumps [deep-equal](https://github.com/inspect-js/node-deep-equal) from 2.0.5 to 2.1.0. - [Release notes](https://github.com/inspect-js/node-deep-equal/releases) - [Changelog](https://github.com/inspect-js/node-deep-equal/blob/master/CHANGELOG.md) - [Commits](https://github.com/inspect-js/node-deep-equal/compare/v2.0.5...v2.1.0) --- updated-dependencies: - dependency-name: deep-equal dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 209 ++++++++++++++++++++++++++++------------------ package.json | 2 +- 2 files changed, 128 insertions(+), 83 deletions(-) diff --git a/package-lock.json b/package-lock.json index d0d0ef0cb..1713fbd52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "command-exists": "1.2.9", "conf": "10.2.0", "console.table": "0.10.0", - "deep-equal": "2.0.5", + "deep-equal": "2.1.0", "fs-extra": "10.1.0", "inquirer": "8.2.2", "json-to-table": "4.2.1", @@ -2186,25 +2186,25 @@ } }, "node_modules/deep-equal": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz", - "integrity": "sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.1.0.tgz", + "integrity": "sha512-2pxgvWu3Alv1PoWEyVg7HS8YhGlUFUV7N5oOvfL6d+7xAmLSemMwv/c8Zv/i9KFzxV5Kt5CAvQc70fLwVuf4UA==", "dependencies": { - "call-bind": "^1.0.0", - "es-get-iterator": "^1.1.1", - "get-intrinsic": "^1.0.1", - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.2", - "is-regex": "^1.1.1", + "call-bind": "^1.0.2", + "es-get-iterator": "^1.1.2", + "get-intrinsic": "^1.1.3", + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", "isarray": "^2.0.5", - "object-is": "^1.1.4", + "object-is": "^1.1.5", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "regexp.prototype.flags": "^1.3.0", - "side-channel": "^1.0.3", - "which-boxed-primitive": "^1.0.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", "which-collection": "^1.0.1", - "which-typed-array": "^1.1.2" + "which-typed-array": "^1.1.8" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2544,6 +2544,7 @@ "version": "1.19.5", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz", "integrity": "sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", @@ -2595,6 +2596,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, "dependencies": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -3384,10 +3386,13 @@ } } }, - "node_modules/foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } }, "node_modules/form-data": { "version": "4.0.0", @@ -3474,13 +3479,13 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3502,6 +3507,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.1" @@ -3649,6 +3655,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/got": { "version": "9.6.0", "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", @@ -4080,6 +4097,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, "dependencies": { "get-intrinsic": "^1.1.0", "has": "^1.0.3", @@ -4348,6 +4366,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -4441,6 +4460,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, "dependencies": { "call-bind": "^1.0.2" }, @@ -4488,14 +4508,14 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", - "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", + "for-each": "^0.3.3", + "gopd": "^1.0.1", "has-tostringtag": "^1.0.0" }, "engines": { @@ -4533,6 +4553,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, "dependencies": { "call-bind": "^1.0.2" }, @@ -6254,13 +6275,13 @@ } }, "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, "engines": { @@ -7890,6 +7911,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" @@ -7902,6 +7924,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" @@ -8261,6 +8284,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-bigints": "^1.0.2", @@ -8470,16 +8494,16 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", - "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", + "for-each": "^0.3.3", + "gopd": "^1.0.1", "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.7" + "is-typed-array": "^1.1.10" }, "engines": { "node": ">= 0.4" @@ -10343,25 +10367,25 @@ } }, "deep-equal": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz", - "integrity": "sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.1.0.tgz", + "integrity": "sha512-2pxgvWu3Alv1PoWEyVg7HS8YhGlUFUV7N5oOvfL6d+7xAmLSemMwv/c8Zv/i9KFzxV5Kt5CAvQc70fLwVuf4UA==", "requires": { - "call-bind": "^1.0.0", - "es-get-iterator": "^1.1.1", - "get-intrinsic": "^1.0.1", - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.2", - "is-regex": "^1.1.1", + "call-bind": "^1.0.2", + "es-get-iterator": "^1.1.2", + "get-intrinsic": "^1.1.3", + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", "isarray": "^2.0.5", - "object-is": "^1.1.4", + "object-is": "^1.1.5", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "regexp.prototype.flags": "^1.3.0", - "side-channel": "^1.0.3", - "which-boxed-primitive": "^1.0.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", "which-collection": "^1.0.1", - "which-typed-array": "^1.1.2" + "which-typed-array": "^1.1.8" } }, "deep-extend": { @@ -10639,6 +10663,7 @@ "version": "1.19.5", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz", "integrity": "sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA==", + "dev": true, "requires": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", @@ -10681,6 +10706,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -11247,10 +11273,13 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } }, "form-data": { "version": "4.0.0", @@ -11312,13 +11341,13 @@ "dev": true }, "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" } }, "get-stream": { @@ -11331,6 +11360,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, "requires": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.1" @@ -11434,6 +11464,14 @@ "slash": "^3.0.0" } }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + } + }, "got": { "version": "9.6.0", "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", @@ -11753,6 +11791,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, "requires": { "get-intrinsic": "^1.1.0", "has": "^1.0.3", @@ -11922,7 +11961,8 @@ "is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true }, "is-npm": { "version": "5.0.0", @@ -11977,6 +12017,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, "requires": { "call-bind": "^1.0.2" } @@ -12003,14 +12044,14 @@ } }, "is-typed-array": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", - "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", "requires": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", + "for-each": "^0.3.3", + "gopd": "^1.0.1", "has-tostringtag": "^1.0.0" } }, @@ -12033,6 +12074,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, "requires": { "call-bind": "^1.0.2" } @@ -13361,13 +13403,13 @@ "dev": true }, "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" } }, @@ -14573,6 +14615,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" @@ -14582,6 +14625,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" @@ -14855,6 +14899,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, "requires": { "call-bind": "^1.0.2", "has-bigints": "^1.0.2", @@ -15019,16 +15064,16 @@ } }, "which-typed-array": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", - "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", "requires": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", + "for-each": "^0.3.3", + "gopd": "^1.0.1", "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.7" + "is-typed-array": "^1.1.10" } }, "widest-line": { diff --git a/package.json b/package.json index 55910c3b1..99e66cb67 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "command-exists": "1.2.9", "conf": "10.2.0", "console.table": "0.10.0", - "deep-equal": "2.0.5", + "deep-equal": "2.1.0", "fs-extra": "10.1.0", "inquirer": "8.2.2", "json-to-table": "4.2.1", From 9f12d751c6a271c7c6cc7a381341c7839c9d3815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 25 Nov 2022 15:26:48 +0100 Subject: [PATCH 032/103] #560: fix UPDATE to not have definitionKey in body --- lib/metadataTypes/definitions/TransactionalPush.definition.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/metadataTypes/definitions/TransactionalPush.definition.js b/lib/metadataTypes/definitions/TransactionalPush.definition.js index 7e5a26315..e3efd5cd2 100644 --- a/lib/metadataTypes/definitions/TransactionalPush.definition.js +++ b/lib/metadataTypes/definitions/TransactionalPush.definition.js @@ -23,7 +23,7 @@ module.exports = { }, definitionKey: { isCreateable: true, - isUpdateable: true, + isUpdateable: false, retrieving: true, template: true, }, From 4efed3c4f0fb79440cc329235a0064f8012baa4d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Nov 2022 15:20:33 +0000 Subject: [PATCH 033/103] Bump chai from 4.3.6 to 4.3.7 Bumps [chai](https://github.com/chaijs/chai) from 4.3.6 to 4.3.7. - [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.6...v4.3.7) --- updated-dependencies: - dependency-name: chai dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 32 ++++++++++++++++---------------- package.json | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1713fbd52..6e9cbc0a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,7 +35,7 @@ "devDependencies": { "assert": "2.0.0", "axios-mock-adapter": "1.21.2", - "chai": "4.3.6", + "chai": "4.3.7", "eslint": "8.28.0", "eslint-config-prettier": "8.5.0", "eslint-config-ssjs": "1.1.11", @@ -1449,14 +1449,14 @@ } }, "node_modules/chai": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", "dev": true, "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", - "deep-eql": "^3.0.1", + "deep-eql": "^4.1.2", "get-func-name": "^2.0.0", "loupe": "^2.3.1", "pathval": "^1.1.1", @@ -2174,15 +2174,15 @@ } }, "node_modules/deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.2.tgz", + "integrity": "sha512-gT18+YW4CcW/DBNTwAmqTtkJh7f9qqScu2qFVlx7kCoeY9tlBu9cUcr7+I+Z/noG8INehS3xQgLpTtd/QUTn4w==", "dev": true, "dependencies": { "type-detect": "^4.0.0" }, "engines": { - "node": ">=0.12" + "node": ">=6" } }, "node_modules/deep-equal": { @@ -9808,14 +9808,14 @@ } }, "chai": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", "dev": true, "requires": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", - "deep-eql": "^3.0.1", + "deep-eql": "^4.1.2", "get-func-name": "^2.0.0", "loupe": "^2.3.1", "pathval": "^1.1.1", @@ -10358,9 +10358,9 @@ } }, "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.2.tgz", + "integrity": "sha512-gT18+YW4CcW/DBNTwAmqTtkJh7f9qqScu2qFVlx7kCoeY9tlBu9cUcr7+I+Z/noG8INehS3xQgLpTtd/QUTn4w==", "dev": true, "requires": { "type-detect": "^4.0.0" diff --git a/package.json b/package.json index 99e66cb67..caa3f9882 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "devDependencies": { "assert": "2.0.0", "axios-mock-adapter": "1.21.2", - "chai": "4.3.6", + "chai": "4.3.7", "eslint": "8.28.0", "eslint-config-prettier": "8.5.0", "eslint-config-ssjs": "1.1.11", From b1bbe492f9f08a3a85f45baf84420ed4882bb148 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 25 Nov 2022 17:15:38 +0100 Subject: [PATCH 034/103] #556: added unit test: retrieve transactionalSMS --- .../v1/beta/mobile/code/get-response.json | 32 +++++++++++++ .../v1/beta/mobile/keyword/get-response.json | 46 +++++++++++++++++++ .../v1/sms/definitions/get-response.json | 15 ++++++ .../testExisting_tsms/get-response.json | 18 ++++++++ .../transactionalSMS/get-expected.json | 15 ++++++ test/transactionalSMS.test.js | 39 ++++++++++++++++ 6 files changed, 165 insertions(+) create mode 100644 test/resources/9999999/legacy/v1/beta/mobile/code/get-response.json create mode 100644 test/resources/9999999/legacy/v1/beta/mobile/keyword/get-response.json create mode 100644 test/resources/9999999/messaging/v1/sms/definitions/get-response.json create mode 100644 test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/get-response.json create mode 100644 test/resources/9999999/transactionalSMS/get-expected.json create mode 100644 test/transactionalSMS.test.js diff --git a/test/resources/9999999/legacy/v1/beta/mobile/code/get-response.json b/test/resources/9999999/legacy/v1/beta/mobile/code/get-response.json new file mode 100644 index 000000000..5c6222f44 --- /dev/null +++ b/test/resources/9999999/legacy/v1/beta/mobile/code/get-response.json @@ -0,0 +1,32 @@ +{ + "startIndex": 0, + "itemsPerPage": 50, + "totalResults": 1, + "entry": [ + { + "id": "RXBwc0JoNk9jVTY0YWJNWFZDaGxvdzo4MTow", + "createdDate": "2017-05-05T10:33:15.953Z", + "lastUpdated": "2022-11-25T15:51:14.225Z", + "code": "4912312345678", + "countryCode": "HK", + "startDate": "2017-05-05T06:00:00Z", + "endDate": "2047-05-05T06:00:00Z", + "keywordLimit": "Unlimited", + "keywordsUsed": "0", + "codeType": "PRIVATE", + "isShortCode": false, + "keywordsUsedOther": "0", + "isGsmCharacterSetOnly": false, + "isMms": false, + "isStackIndependant": false, + "dipSwitches": 1, + "moEngineVersion": 1, + "sendableCountries": [ + { "countryCode": "HK", "vendor": "CLX", "fromNameSupported": true } + ], + "supportsConcatenation": true, + "isClientOwned": false, + "isOwner": false + } + ] +} diff --git a/test/resources/9999999/legacy/v1/beta/mobile/keyword/get-response.json b/test/resources/9999999/legacy/v1/beta/mobile/keyword/get-response.json new file mode 100644 index 000000000..4e8d8f539 --- /dev/null +++ b/test/resources/9999999/legacy/v1/beta/mobile/keyword/get-response.json @@ -0,0 +1,46 @@ +{ + "startIndex": 0, + "itemsPerPage": 50, + "totalResults": 1, + "entry": [ + { + "id": "cTVJaG5oSDJPVUNHcUh6Z3pQT2tVdzo4Njow", + "createdDate": "2018-03-27T12:37:18.12Z", + "lastUpdated": "2018-03-27T12:37:18.167Z", + "code": { + "id": "RXBwc0JoNk9jVTY0YWJNWFZDaGxvdzo4MTow", + "createdDate": "2017-05-05T10:33:15.953Z", + "lastUpdated": "2022-11-25T15:51:14.225Z", + "code": "4912312345678", + "countryCode": "HK", + "startDate": "2017-05-05T06:00:00Z", + "endDate": "2047-05-05T06:00:00Z", + "keywordLimit": "Unlimited", + "keywordsUsed": "0", + "codeType": "PRIVATE", + "isShortCode": false, + "keywordsUsedOther": "0", + "isGsmCharacterSetOnly": false, + "isMms": false, + "isStackIndependant": false, + "dipSwitches": 1, + "moEngineVersion": 1, + "sendableCountries": [ + { "countryCode": "HK", "vendor": "CLX", "fromNameSupported": true } + ], + "supportsConcatenation": true, + "isClientOwned": false, + "isOwner": false + }, + "keyword": "testExisting_keyword", + "startDate": "2018-03-27T12:37:18.197Z", + "endDate": "2038-03-27T12:37:18.197Z", + "status": "Active", + "createdBy": { "id": "abcabcabc", "name": "xyz app user" }, + "restriction": "NONE", + "keywordType": "NORMAL", + "isInherited": false, + "decodedId": "9e2192ab-f611-4039-86a8-7ce0ccf3a453" + } + ] +} diff --git a/test/resources/9999999/messaging/v1/sms/definitions/get-response.json b/test/resources/9999999/messaging/v1/sms/definitions/get-response.json new file mode 100644 index 000000000..af3233afe --- /dev/null +++ b/test/resources/9999999/messaging/v1/sms/definitions/get-response.json @@ -0,0 +1,15 @@ +{ + "requestId": "60c26a29-e1b0-4952-a566-476a937e4a5d", + "definitions": [ + { + "name": "testExisting_tsms", + "definitionKey": "testExisting_tsms", + "status": "Active", + "createdDate": "2022-11-23T02:50:00", + "modifiedDate": "2022-11-25T08:01:00" + } + ], + "count": 3, + "page": 1, + "pageSize": 50 +} diff --git a/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/get-response.json b/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/get-response.json new file mode 100644 index 000000000..7da96858a --- /dev/null +++ b/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/get-response.json @@ -0,0 +1,18 @@ +{ + "requestId": "ae8511d0-9bf0-42d0-af4f-bd9c1218c812", + "name": "testExisting_tsms", + "definitionKey": "testExisting_tsms", + "definitionId": "0a650d90-755e-ed11-b852-48df37d1df5b", + "description": "bla bla", + "status": "Active", + "createdDate": "2022-11-07T02:24:00", + "modifiedDate": "2022-11-07T02:25:00", + "content": { "message": "%%[ test = 1 ]%%" }, + "subscriptions": { + "shortCode": "4912312345678", + "countryCode": "", + "autoAddSubscriber": true, + "updateSubscriber": true, + "keyword": "testExisting_keyword" + } +} diff --git a/test/resources/9999999/transactionalSMS/get-expected.json b/test/resources/9999999/transactionalSMS/get-expected.json new file mode 100644 index 000000000..4cf9c7393 --- /dev/null +++ b/test/resources/9999999/transactionalSMS/get-expected.json @@ -0,0 +1,15 @@ +{ + "name": "testExisting_tsms", + "definitionKey": "testExisting_tsms", + "description": "bla bla", + "status": "Active", + "createdDate": "2022-11-07T02:24:00", + "modifiedDate": "2022-11-07T02:25:00", + "subscriptions": { + "shortCode": "4912312345678", + "countryCode": "", + "autoAddSubscriber": true, + "updateSubscriber": true, + "keyword": "testExisting_keyword" + } +} diff --git a/test/transactionalSMS.test.js b/test/transactionalSMS.test.js new file mode 100644 index 000000000..73f73a466 --- /dev/null +++ b/test/transactionalSMS.test.js @@ -0,0 +1,39 @@ +const assert = require('chai').assert; +const cache = require('../lib/util/cache'); +const testUtils = require('./utils'); +const handler = require('../lib/index'); + +describe('transactionalSMS', () => { + beforeEach(() => { + testUtils.mockSetup(); + }); + afterEach(() => { + testUtils.mockReset(); + }); + + describe('Retrieve ================', () => { + it('Should retrieve a transactionalSMS', async () => { + // WHEN + await handler.retrieve('testInstance/testBU', ['transactionalSMS']); + // THEN + // get results from cache + const result = cache.getCache(); + assert.equal( + result.transactionalSMS ? Object.keys(result.transactionalSMS).length : 0, + 1, + 'only one transactionalSMS expected' + ); + assert.deepEqual( + await testUtils.getActualFile('testExisting_tsms', 'transactionalSMS'), + await testUtils.getExpectedFile('9999999', 'transactionalSMS', 'get'), + 'returned metadata was not equal expected' + ); + assert.equal( + Object.values(testUtils.getAPIHistory()).flat().length, + 4, + 'Unexpected number of requests made' + ); + return; + }); + }); +}); From c230f4836e532fdd19162ee51015ec6d69fa0a74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Mon, 28 Nov 2022 14:44:56 +0100 Subject: [PATCH 035/103] #556: added unit test: deploy (create + update) transactionalSMS potentially fixed a long standing issue that should have prevented saving deploy results to retrieve folder --- lib/metadataTypes/MetadataType.js | 3 +- lib/metadataTypes/TransactionalSMS.js | 2 +- ...estExisting_tsms.transactionalSMS-meta.amp | 4 + ...stExisting_tsms.transactionalSMS-meta.json | 15 ++ .../testNew_tsms.transactionalSMS-meta.amp | 4 + .../testNew_tsms.transactionalSMS-meta.json | 15 ++ .../v1/sms/definitions/post-response.json | 17 +++ .../testExisting_tsms/get-response.json | 2 +- .../testExisting_tsms/patch-response.json | 18 +++ .../transactionalSMS/patch-expected.amp | 4 + .../transactionalSMS/patch-expected.json | 15 ++ .../transactionalSMS/post-expected.amp | 4 + .../transactionalSMS/post-expected.json | 15 ++ test/transactionalSMS.test.js | 136 ++++++++++++++++++ test/utils.js | 21 +++ 15 files changed, 272 insertions(+), 3 deletions(-) create mode 100644 test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.amp create mode 100644 test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.json create mode 100644 test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.amp create mode 100644 test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.json create mode 100644 test/resources/9999999/messaging/v1/sms/definitions/post-response.json create mode 100644 test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/patch-response.json create mode 100644 test/resources/9999999/transactionalSMS/patch-expected.amp create mode 100644 test/resources/9999999/transactionalSMS/patch-expected.json create mode 100644 test/resources/9999999/transactionalSMS/post-expected.amp create mode 100644 test/resources/9999999/transactionalSMS/post-expected.json diff --git a/lib/metadataTypes/MetadataType.js b/lib/metadataTypes/MetadataType.js index d55ccac1e..2b36cc14d 100644 --- a/lib/metadataTypes/MetadataType.js +++ b/lib/metadataTypes/MetadataType.js @@ -545,11 +545,12 @@ class MetadataType { .map((r) => r.Object); return this.parseResponseBody({ Results: metadataResults }); } else { + // likely comming from one of the many REST APIs // put in Retrieve Format for parsing // todo add handling when response does not contain items. // @ts-ignore const metadataResults = createResults.concat(updateResults).filter(Boolean); - return this.parseResponseBody({ items: metadataResults }); + return this.parseResponseBody(metadataResults); } } diff --git a/lib/metadataTypes/TransactionalSMS.js b/lib/metadataTypes/TransactionalSMS.js index 84847e280..9464d3cb3 100644 --- a/lib/metadataTypes/TransactionalSMS.js +++ b/lib/metadataTypes/TransactionalSMS.js @@ -111,7 +111,7 @@ class TransactionalSMS extends TransactionalMessage { // extract message body const codeArr = []; // keep between tags - const { fileExt, code } = this.prepExtractedCode(metadata.content.message); + const { fileExt, code } = this.prepExtractedCode(metadata.content?.message); delete metadata.content; codeArr.push({ subFolder: null, diff --git a/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.amp b/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.amp new file mode 100644 index 000000000..e4c5aff95 --- /dev/null +++ b/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.amp @@ -0,0 +1,4 @@ + +%%[ + SET @key = 'secret' +]%% diff --git a/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.json b/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.json new file mode 100644 index 000000000..4cf9c7393 --- /dev/null +++ b/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.json @@ -0,0 +1,15 @@ +{ + "name": "testExisting_tsms", + "definitionKey": "testExisting_tsms", + "description": "bla bla", + "status": "Active", + "createdDate": "2022-11-07T02:24:00", + "modifiedDate": "2022-11-07T02:25:00", + "subscriptions": { + "shortCode": "4912312345678", + "countryCode": "", + "autoAddSubscriber": true, + "updateSubscriber": true, + "keyword": "testExisting_keyword" + } +} diff --git a/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.amp b/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.amp new file mode 100644 index 000000000..e4c5aff95 --- /dev/null +++ b/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.amp @@ -0,0 +1,4 @@ + +%%[ + SET @key = 'secret' +]%% diff --git a/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.json b/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.json new file mode 100644 index 000000000..cb2fc540b --- /dev/null +++ b/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.json @@ -0,0 +1,15 @@ +{ + "name": "testNew_tsms", + "definitionKey": "testNew_tsms", + "description": "created on deploy", + "status": "Active", + "createdDate": "2022-11-07T02:24:00", + "modifiedDate": "2022-11-07T02:25:00", + "subscriptions": { + "shortCode": "4912312345678", + "countryCode": "", + "autoAddSubscriber": true, + "updateSubscriber": true, + "keyword": "testExisting_keyword" + } +} diff --git a/test/resources/9999999/messaging/v1/sms/definitions/post-response.json b/test/resources/9999999/messaging/v1/sms/definitions/post-response.json new file mode 100644 index 000000000..53288e0d6 --- /dev/null +++ b/test/resources/9999999/messaging/v1/sms/definitions/post-response.json @@ -0,0 +1,17 @@ +{ + "requestId": "582bec09-6d04-4222-bbba-cea616495596", + "name": "testNew_tsms", + "definitionKey": "testNew_tsms", + "description": "created on deploy", + "status": "Active", + "createdDate": "2022-11-07T02:24:00", + "modifiedDate": "2022-11-07T02:25:00", + "content": { "message": "\n%%[\n SET @key = 'secret'\n]%%\n" }, + "subscriptions": { + "shortCode": "4912312345678", + "countryCode": "", + "autoAddSubscriber": true, + "updateSubscriber": true, + "keyword": "testExisting_keyword" + } +} diff --git a/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/get-response.json b/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/get-response.json index 7da96858a..cfca58680 100644 --- a/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/get-response.json +++ b/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/get-response.json @@ -7,7 +7,7 @@ "status": "Active", "createdDate": "2022-11-07T02:24:00", "modifiedDate": "2022-11-07T02:25:00", - "content": { "message": "%%[ test = 1 ]%%" }, + "content": { "message": "%%[ SET @key = 'secret' ]%%" }, "subscriptions": { "shortCode": "4912312345678", "countryCode": "", diff --git a/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/patch-response.json b/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/patch-response.json new file mode 100644 index 000000000..cfca58680 --- /dev/null +++ b/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/patch-response.json @@ -0,0 +1,18 @@ +{ + "requestId": "ae8511d0-9bf0-42d0-af4f-bd9c1218c812", + "name": "testExisting_tsms", + "definitionKey": "testExisting_tsms", + "definitionId": "0a650d90-755e-ed11-b852-48df37d1df5b", + "description": "bla bla", + "status": "Active", + "createdDate": "2022-11-07T02:24:00", + "modifiedDate": "2022-11-07T02:25:00", + "content": { "message": "%%[ SET @key = 'secret' ]%%" }, + "subscriptions": { + "shortCode": "4912312345678", + "countryCode": "", + "autoAddSubscriber": true, + "updateSubscriber": true, + "keyword": "testExisting_keyword" + } +} diff --git a/test/resources/9999999/transactionalSMS/patch-expected.amp b/test/resources/9999999/transactionalSMS/patch-expected.amp new file mode 100644 index 000000000..e4c5aff95 --- /dev/null +++ b/test/resources/9999999/transactionalSMS/patch-expected.amp @@ -0,0 +1,4 @@ + +%%[ + SET @key = 'secret' +]%% diff --git a/test/resources/9999999/transactionalSMS/patch-expected.json b/test/resources/9999999/transactionalSMS/patch-expected.json new file mode 100644 index 000000000..4cf9c7393 --- /dev/null +++ b/test/resources/9999999/transactionalSMS/patch-expected.json @@ -0,0 +1,15 @@ +{ + "name": "testExisting_tsms", + "definitionKey": "testExisting_tsms", + "description": "bla bla", + "status": "Active", + "createdDate": "2022-11-07T02:24:00", + "modifiedDate": "2022-11-07T02:25:00", + "subscriptions": { + "shortCode": "4912312345678", + "countryCode": "", + "autoAddSubscriber": true, + "updateSubscriber": true, + "keyword": "testExisting_keyword" + } +} diff --git a/test/resources/9999999/transactionalSMS/post-expected.amp b/test/resources/9999999/transactionalSMS/post-expected.amp new file mode 100644 index 000000000..e4c5aff95 --- /dev/null +++ b/test/resources/9999999/transactionalSMS/post-expected.amp @@ -0,0 +1,4 @@ + +%%[ + SET @key = 'secret' +]%% diff --git a/test/resources/9999999/transactionalSMS/post-expected.json b/test/resources/9999999/transactionalSMS/post-expected.json new file mode 100644 index 000000000..cb2fc540b --- /dev/null +++ b/test/resources/9999999/transactionalSMS/post-expected.json @@ -0,0 +1,15 @@ +{ + "name": "testNew_tsms", + "definitionKey": "testNew_tsms", + "description": "created on deploy", + "status": "Active", + "createdDate": "2022-11-07T02:24:00", + "modifiedDate": "2022-11-07T02:25:00", + "subscriptions": { + "shortCode": "4912312345678", + "countryCode": "", + "autoAddSubscriber": true, + "updateSubscriber": true, + "keyword": "testExisting_keyword" + } +} diff --git a/test/transactionalSMS.test.js b/test/transactionalSMS.test.js index 73f73a466..72bc9b517 100644 --- a/test/transactionalSMS.test.js +++ b/test/transactionalSMS.test.js @@ -36,4 +36,140 @@ describe('transactionalSMS', () => { return; }); }); + describe('Deploy ================', () => { + it('Should create & upsert a transactionalSMS', async () => { + // WHEN + await handler.deploy('testInstance/testBU', ['transactionalSMS']); + // THEN + // get results from cache + const result = cache.getCache(); + assert.equal( + result.transactionalSMS ? Object.keys(result.transactionalSMS).length : 0, + 2, + 'two transactionalSMSs expected' + ); + // confirm created item + assert.deepEqual( + await testUtils.getActualFile('testNew_tsms', 'transactionalSMS'), + await testUtils.getExpectedFile('9999999', 'transactionalSMS', 'post'), + 'returned metadata was not equal expected for insert transactionalSMS' + ); + assert.deepEqual( + await testUtils.getActualFileGeneric('testNew_tsms', 'transactionalSMS', 'amp'), + await testUtils.getExpectedFileGeneric( + '9999999', + 'transactionalSMS', + 'post', + 'amp' + ), + 'returned AMPscript was not equal expected for insert transactionalSMS' + ); + // confirm updated item + assert.deepEqual( + await testUtils.getActualFile('testExisting_tsms', 'transactionalSMS'), + await testUtils.getExpectedFile('9999999', 'transactionalSMS', 'patch'), + 'returned metadata was not equal expected for update transactionalSMS' + ); + assert.deepEqual( + await testUtils.getActualFileGeneric( + 'testExisting_tsms', + 'transactionalSMS', + 'amp' + ), + await testUtils.getExpectedFileGeneric( + '9999999', + 'transactionalSMS', + 'patch', + 'amp' + ), + 'returned AMPscript was not equal expected for update transactionalSMS' + ); + assert.equal( + Object.values(testUtils.getAPIHistory()).flat().length, + 5, + 'Unexpected number of requests made' + ); + return; + }); + }); + // describe('Templating ================', () => { + // it('Should create a transactionalSMS template via retrieveAsTemplate and build it', async () => { + // // GIVEN there is a template + // const result = await handler.retrieveAsTemplate( + // 'testInstance/testBU', + // 'transactionalSMS', + // ['testExisting_tsms'], + // 'testMarket' + // ); + // // WHEN + // assert.equal( + // result.transactionalSMS ? Object.keys(result.transactionalSMS).length : 0, + // 1, + // 'only one transactionalSMS expected' + // ); + // assert.deepEqual( + // await testUtils.getActualTemplate('testExisting_tsms', 'transactionalSMS'), + // await testUtils.getExpectedFile('9999999', 'transactionalSMS', 'template'), + // 'returned template was not equal expected' + // ); + // // THEN + // await handler.buildDefinition( + // 'testInstance/testBU', + // 'transactionalSMS', + // 'testExisting_tsms', + // 'testMarket' + // ); + // assert.deepEqual( + // await testUtils.getActualDeployFile('testExisting_tsms', 'transactionalSMS'), + // await testUtils.getExpectedFile('9999999', 'transactionalSMS', 'build'), + // 'returned deployment file was not equal expected' + // ); + // assert.equal( + // Object.values(testUtils.getAPIHistory()).flat().length, + // 6, + // 'Unexpected number of requests made' + // ); + // return; + // }); + // it('Should create a transactionalSMS template via buildTemplate and build it', async () => { + // // download first before we test buildTemplate + // await handler.retrieve('testInstance/testBU', ['transactionalSMS']); + // // GIVEN there is a template + // const result = await handler.buildTemplate( + // 'testInstance/testBU', + // 'transactionalSMS', + // ['testExisting_tsms'], + // 'testMarket' + // ); + // // WHEN + // assert.equal( + // result.transactionalSMS ? Object.keys(result.transactionalSMS).length : 0, + // 1, + // 'only one transactionalSMS expected' + // ); + // assert.deepEqual( + // await testUtils.getActualTemplate('testExisting_tsms', 'transactionalSMS'), + // await testUtils.getExpectedFile('9999999', 'transactionalSMS', 'template'), + // 'returned template was not equal expected' + // ); + // // THEN + // await handler.buildDefinition( + // 'testInstance/testBU', + // 'transactionalSMS', + // 'testExisting_tsms', + // 'testMarket' + // ); + // assert.deepEqual( + // await testUtils.getActualDeployFile('testExisting_tsms', 'transactionalSMS'), + // await testUtils.getExpectedFile('9999999', 'transactionalSMS', 'build'), + // 'returned deployment file was not equal expected' + // ); + // assert.equal( + // Object.values(testUtils.getAPIHistory()).flat().length, + // 6, + // 'Unexpected number of requests made' + // ); + // return; + // }); + // }); }); diff --git a/test/utils.js b/test/utils.js index 243bd0646..e79e11a99 100644 --- a/test/utils.js +++ b/test/utils.js @@ -21,6 +21,16 @@ const resourceFactory = require('./resourceFactory'); */ exports.getActualFile = (customerKey, type) => File.readJSON(`./retrieve/testInstance/testBU/${type}/${customerKey}.${type}-meta.json`); +/** + * gets file from Retrieve folder + * + * @param {string} customerKey of metadata + * @param {string} type of metadata + * @param {string} ext file extension + * @returns {Promise.} file in string form + */ +exports.getActualFileGeneric = (customerKey, type, ext) => + File.readFile(`./retrieve/testInstance/testBU/${type}/${customerKey}.${type}-meta.${ext}`); /** * gets file from Deploy folder * @@ -50,6 +60,17 @@ exports.getActualTemplate = (customerKey, type) => */ exports.getExpectedFile = (mid, type, action) => File.readJSON(path.join('test', 'resources', mid, type, action + '-expected.json')); +/** + * gets file from resources folder which should be used for comparison + * + * @param {number} mid of Business Unit + * @param {string} type of metadata + * @param {string} action of SOAP request + * @param {string} ext file extension + * @returns {Promise.} file in string form + */ +exports.getExpectedFileGeneric = (mid, type, action, ext) => + File.readFile(path.join('test', 'resources', mid, type, action + '-expected.' + ext)); /** * setup mocks for API and FS * From 04b776bac99483805bdec554d82e9fe3a4897210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Mon, 28 Nov 2022 14:47:33 +0100 Subject: [PATCH 036/103] #556: create-test files renamed --- .../testBU/query/testExistingQuery.query-meta.json | 2 +- ...stQuery.query-meta.json => testNewQuery.query-meta.json} | 6 +++--- ...testQuery.query-meta.sql => testNewQuery.query-meta.sql} | 0 test/query.test.js | 2 +- .../patch-response.json | 2 +- .../9999999/automation/v1/queries/post-response.json | 6 +++--- test/resources/9999999/query/patch-expected.json | 2 +- test/resources/9999999/query/post-expected.json | 6 +++--- 8 files changed, 13 insertions(+), 13 deletions(-) rename test/mockRoot/deploy/testInstance/testBU/query/{testQuery.query-meta.json => testNewQuery.query-meta.json} (71%) rename test/mockRoot/deploy/testInstance/testBU/query/{testQuery.query-meta.sql => testNewQuery.query-meta.sql} (100%) diff --git a/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.json b/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.json index ed7beb5a9..04612115e 100644 --- a/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.json +++ b/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.json @@ -1,7 +1,7 @@ { "name": "testExistingQuery", "key": "testExistingQuery", - "description": "updated", + "description": "updated on deploy", "targetKey": "childBU_dataextension_test", "createdDate": "2022-04-26T15:21:16.453", "modifiedDate": "2022-04-26T16:04:15.88", diff --git a/test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.json b/test/mockRoot/deploy/testInstance/testBU/query/testNewQuery.query-meta.json similarity index 71% rename from test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.json rename to test/mockRoot/deploy/testInstance/testBU/query/testNewQuery.query-meta.json index fc647aca4..48adbd5b1 100644 --- a/test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.json +++ b/test/mockRoot/deploy/testInstance/testBU/query/testNewQuery.query-meta.json @@ -1,7 +1,7 @@ { - "name": "testQuery", - "key": "testQuery", - "description": "", + "name": "testNewQuery", + "key": "testNewQuery", + "description": "created on deploy", "targetKey": "childBU_dataextension_test", "createdDate": "2022-04-26T15:21:16.453", "modifiedDate": "2022-04-26T16:04:15.88", diff --git a/test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.sql b/test/mockRoot/deploy/testInstance/testBU/query/testNewQuery.query-meta.sql similarity index 100% rename from test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.sql rename to test/mockRoot/deploy/testInstance/testBU/query/testNewQuery.query-meta.sql diff --git a/test/query.test.js b/test/query.test.js index e2af6c9bf..b2cbf647b 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -49,7 +49,7 @@ describe('query', () => { 'two querys expected' ); assert.deepEqual( - await testUtils.getActualFile('testQuery', 'query'), + await testUtils.getActualFile('testNewQuery', 'query'), await testUtils.getExpectedFile('9999999', 'query', 'post'), 'returned metadata was not equal expected for insert query' ); diff --git a/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json b/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json index 8be361772..74e650e5a 100644 --- a/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json +++ b/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json @@ -2,7 +2,7 @@ "queryDefinitionId": "549f0568-607c-4940-afef-437965094dat", "name": "testExistingQuery", "key": "testExistingQuery", - "description": "updated", + "description": "updated on deploy", "queryText": "SELECT\n SubscriberKey as testField\nFROM\n _Subscribers ", "targetName": "childBU_dataextension_test", "targetKey": "childBU_dataextension_test", diff --git a/test/resources/9999999/automation/v1/queries/post-response.json b/test/resources/9999999/automation/v1/queries/post-response.json index c46ec2940..eb0943059 100644 --- a/test/resources/9999999/automation/v1/queries/post-response.json +++ b/test/resources/9999999/automation/v1/queries/post-response.json @@ -1,8 +1,8 @@ { "queryDefinitionId": "549f0568-607c-4940-afef-437965094dae", - "name": "testQuery", - "key": "testQuery", - "description": "", + "name": "testNewQuery", + "key": "testNewQuery", + "description": "created on deploy", "queryText": "SELECT\n SubscriberKey as testField\nFROM\n _Subscribers ", "targetName": "childBU_dataextension_test", "targetKey": "childBU_dataextension_test", diff --git a/test/resources/9999999/query/patch-expected.json b/test/resources/9999999/query/patch-expected.json index ed7beb5a9..04612115e 100644 --- a/test/resources/9999999/query/patch-expected.json +++ b/test/resources/9999999/query/patch-expected.json @@ -1,7 +1,7 @@ { "name": "testExistingQuery", "key": "testExistingQuery", - "description": "updated", + "description": "updated on deploy", "targetKey": "childBU_dataextension_test", "createdDate": "2022-04-26T15:21:16.453", "modifiedDate": "2022-04-26T16:04:15.88", diff --git a/test/resources/9999999/query/post-expected.json b/test/resources/9999999/query/post-expected.json index fc647aca4..48adbd5b1 100644 --- a/test/resources/9999999/query/post-expected.json +++ b/test/resources/9999999/query/post-expected.json @@ -1,7 +1,7 @@ { - "name": "testQuery", - "key": "testQuery", - "description": "", + "name": "testNewQuery", + "key": "testNewQuery", + "description": "created on deploy", "targetKey": "childBU_dataextension_test", "createdDate": "2022-04-26T15:21:16.453", "modifiedDate": "2022-04-26T16:04:15.88", From ebf39ee776d820227fa7d66851d54f435e292af1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Mon, 28 Nov 2022 14:54:16 +0100 Subject: [PATCH 037/103] #556: confirm actual SQL looks correct after query deploy --- test/query.test.js | 13 +++++++++++++ test/resources/9999999/query/patch-expected.sql | 4 ++++ test/resources/9999999/query/post-expected.sql | 4 ++++ test/transactionalSMS.test.js | 1 + 4 files changed, 22 insertions(+) create mode 100644 test/resources/9999999/query/patch-expected.sql create mode 100644 test/resources/9999999/query/post-expected.sql diff --git a/test/query.test.js b/test/query.test.js index b2cbf647b..278aa0c20 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -48,16 +48,29 @@ describe('query', () => { 2, 'two querys expected' ); + // confirm created item assert.deepEqual( await testUtils.getActualFile('testNewQuery', 'query'), await testUtils.getExpectedFile('9999999', 'query', 'post'), 'returned metadata was not equal expected for insert query' ); + assert.deepEqual( + await testUtils.getActualFileGeneric('testNewQuery', 'query', 'sql'), + await testUtils.getExpectedFileGeneric('9999999', 'query', 'post', 'sql'), + 'returned SQL was not equal expected for insert query' + ); + // confirm updated item assert.deepEqual( await testUtils.getActualFile('testExistingQuery', 'query'), await testUtils.getExpectedFile('9999999', 'query', 'patch'), 'returned metadata was not equal expected for insert query' ); + assert.deepEqual( + await testUtils.getActualFileGeneric('testExistingQuery', 'query', 'sql'), + await testUtils.getExpectedFileGeneric('9999999', 'query', 'patch', 'sql'), + 'returned SQL was not equal expected for insert query' + ); + // check number of API calls assert.equal( Object.values(testUtils.getAPIHistory()).flat().length, 8, diff --git a/test/resources/9999999/query/patch-expected.sql b/test/resources/9999999/query/patch-expected.sql new file mode 100644 index 000000000..9eb673f71 --- /dev/null +++ b/test/resources/9999999/query/patch-expected.sql @@ -0,0 +1,4 @@ +SELECT + SubscriberKey as testField +FROM + _Subscribers \ No newline at end of file diff --git a/test/resources/9999999/query/post-expected.sql b/test/resources/9999999/query/post-expected.sql new file mode 100644 index 000000000..9eb673f71 --- /dev/null +++ b/test/resources/9999999/query/post-expected.sql @@ -0,0 +1,4 @@ +SELECT + SubscriberKey as testField +FROM + _Subscribers \ No newline at end of file diff --git a/test/transactionalSMS.test.js b/test/transactionalSMS.test.js index 72bc9b517..4edd6d6e6 100644 --- a/test/transactionalSMS.test.js +++ b/test/transactionalSMS.test.js @@ -84,6 +84,7 @@ describe('transactionalSMS', () => { ), 'returned AMPscript was not equal expected for update transactionalSMS' ); + // check number of API calls assert.equal( Object.values(testUtils.getAPIHistory()).flat().length, 5, From bfe4542457b8de227afb265e4b1c506732115b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 29 Nov 2022 10:10:18 +0100 Subject: [PATCH 038/103] #556: allow testing nested files as well e.g. sql, ssjs, ... --- test/dataExtension.test.js | 34 +++++++++++++++++++-------------- test/query.test.js | 36 +++++++++++++++++------------------ test/transactionalSMS.test.js | 34 ++++++++++----------------------- test/utils.js | 32 +++++++++++++++++++++++++------ 4 files changed, 74 insertions(+), 62 deletions(-) diff --git a/test/dataExtension.test.js b/test/dataExtension.test.js index ee5f066a4..1b491d4cb 100644 --- a/test/dataExtension.test.js +++ b/test/dataExtension.test.js @@ -23,8 +23,8 @@ describe('dataExtension', () => { 'only one dataExtension expected' ); assert.deepEqual( - await testUtils.getActualFile('childBU_dataextension_test', 'dataExtension'), - await testUtils.getExpectedFile('9999999', 'dataExtension', 'retrieve'), + await testUtils.getActualJson('childBU_dataextension_test', 'dataExtension'), + await testUtils.getExpectedJson('9999999', 'dataExtension', 'retrieve'), 'returned metadata was not equal expected' ); @@ -50,13 +50,13 @@ describe('dataExtension', () => { 'two data extensions expected' ); assert.deepEqual( - await testUtils.getActualFile('testDataExtension', 'dataExtension'), - await testUtils.getExpectedFile('9999999', 'dataExtension', 'create'), + await testUtils.getActualJson('testDataExtension', 'dataExtension'), + await testUtils.getExpectedJson('9999999', 'dataExtension', 'create'), 'returned metadata was not equal expected for create' ); assert.deepEqual( - await testUtils.getActualFile('childBU_dataextension_test', 'dataExtension'), - await testUtils.getExpectedFile('9999999', 'dataExtension', 'update'), + await testUtils.getActualJson('childBU_dataextension_test', 'dataExtension'), + await testUtils.getExpectedJson('9999999', 'dataExtension', 'update'), 'returned metadata was not equal expected for update' ); assert.equal( @@ -84,8 +84,11 @@ describe('dataExtension', () => { 'only one dataExtension expected' ); assert.deepEqual( - await testUtils.getActualTemplate('childBU_dataextension_test', 'dataExtension'), - await testUtils.getExpectedFile('9999999', 'dataExtension', 'template'), + await testUtils.getActualTemplateJson( + 'childBU_dataextension_test', + 'dataExtension' + ), + await testUtils.getExpectedJson('9999999', 'dataExtension', 'template'), 'returned template was not equal expected' ); // THEN @@ -96,8 +99,8 @@ describe('dataExtension', () => { 'testMarket' ); assert.deepEqual( - await testUtils.getActualDeployFile('childBU_dataextension_test', 'dataExtension'), - await testUtils.getExpectedFile('9999999', 'dataExtension', 'build'), + await testUtils.getActualDeployJson('childBU_dataextension_test', 'dataExtension'), + await testUtils.getExpectedJson('9999999', 'dataExtension', 'build'), 'returned deployment file was not equal expected' ); assert.equal( @@ -124,8 +127,11 @@ describe('dataExtension', () => { 'only one dataExtension expected' ); assert.deepEqual( - await testUtils.getActualTemplate('childBU_dataextension_test', 'dataExtension'), - await testUtils.getExpectedFile('9999999', 'dataExtension', 'template'), + await testUtils.getActualTemplateJson( + 'childBU_dataextension_test', + 'dataExtension' + ), + await testUtils.getExpectedJson('9999999', 'dataExtension', 'template'), 'returned template was not equal expected' ); // THEN @@ -137,8 +143,8 @@ describe('dataExtension', () => { ); assert.deepEqual( - await testUtils.getActualDeployFile('childBU_dataextension_test', 'dataExtension'), - await testUtils.getExpectedFile('9999999', 'dataExtension', 'build'), + await testUtils.getActualDeployJson('childBU_dataextension_test', 'dataExtension'), + await testUtils.getExpectedJson('9999999', 'dataExtension', 'build'), 'returned deployment file was not equal expected' ); assert.equal( diff --git a/test/query.test.js b/test/query.test.js index 278aa0c20..000bae08c 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -24,8 +24,8 @@ describe('query', () => { 'only one query expected' ); assert.deepEqual( - await testUtils.getActualFile('testExistingQuery', 'query'), - await testUtils.getExpectedFile('9999999', 'query', 'get'), + await testUtils.getActualJson('testExistingQuery', 'query'), + await testUtils.getExpectedJson('9999999', 'query', 'get'), 'returned metadata was not equal expected' ); assert.equal( @@ -50,24 +50,24 @@ describe('query', () => { ); // confirm created item assert.deepEqual( - await testUtils.getActualFile('testNewQuery', 'query'), - await testUtils.getExpectedFile('9999999', 'query', 'post'), + await testUtils.getActualJson('testNewQuery', 'query'), + await testUtils.getExpectedJson('9999999', 'query', 'post'), 'returned metadata was not equal expected for insert query' ); assert.deepEqual( - await testUtils.getActualFileGeneric('testNewQuery', 'query', 'sql'), - await testUtils.getExpectedFileGeneric('9999999', 'query', 'post', 'sql'), + await testUtils.getActualFile('testNewQuery', 'query', 'sql'), + await testUtils.getExpectedFile('9999999', 'query', 'post', 'sql'), 'returned SQL was not equal expected for insert query' ); // confirm updated item assert.deepEqual( - await testUtils.getActualFile('testExistingQuery', 'query'), - await testUtils.getExpectedFile('9999999', 'query', 'patch'), + await testUtils.getActualJson('testExistingQuery', 'query'), + await testUtils.getExpectedJson('9999999', 'query', 'patch'), 'returned metadata was not equal expected for insert query' ); assert.deepEqual( - await testUtils.getActualFileGeneric('testExistingQuery', 'query', 'sql'), - await testUtils.getExpectedFileGeneric('9999999', 'query', 'patch', 'sql'), + await testUtils.getActualFile('testExistingQuery', 'query', 'sql'), + await testUtils.getExpectedFile('9999999', 'query', 'patch', 'sql'), 'returned SQL was not equal expected for insert query' ); // check number of API calls @@ -95,8 +95,8 @@ describe('query', () => { 'only one query expected' ); assert.deepEqual( - await testUtils.getActualTemplate('testExistingQuery', 'query'), - await testUtils.getExpectedFile('9999999', 'query', 'template'), + await testUtils.getActualTemplateJson('testExistingQuery', 'query'), + await testUtils.getExpectedJson('9999999', 'query', 'template'), 'returned template was not equal expected' ); // THEN @@ -107,8 +107,8 @@ describe('query', () => { 'testMarket' ); assert.deepEqual( - await testUtils.getActualDeployFile('testExistingQuery', 'query'), - await testUtils.getExpectedFile('9999999', 'query', 'build'), + await testUtils.getActualDeployJson('testExistingQuery', 'query'), + await testUtils.getExpectedJson('9999999', 'query', 'build'), 'returned deployment file was not equal expected' ); assert.equal( @@ -135,8 +135,8 @@ describe('query', () => { 'only one query expected' ); assert.deepEqual( - await testUtils.getActualTemplate('testExistingQuery', 'query'), - await testUtils.getExpectedFile('9999999', 'query', 'template'), + await testUtils.getActualTemplateJson('testExistingQuery', 'query'), + await testUtils.getExpectedJson('9999999', 'query', 'template'), 'returned template was not equal expected' ); // THEN @@ -147,8 +147,8 @@ describe('query', () => { 'testMarket' ); assert.deepEqual( - await testUtils.getActualDeployFile('testExistingQuery', 'query'), - await testUtils.getExpectedFile('9999999', 'query', 'build'), + await testUtils.getActualDeployJson('testExistingQuery', 'query'), + await testUtils.getExpectedJson('9999999', 'query', 'build'), 'returned deployment file was not equal expected' ); assert.equal( diff --git a/test/transactionalSMS.test.js b/test/transactionalSMS.test.js index 4edd6d6e6..24284abb8 100644 --- a/test/transactionalSMS.test.js +++ b/test/transactionalSMS.test.js @@ -24,8 +24,8 @@ describe('transactionalSMS', () => { 'only one transactionalSMS expected' ); assert.deepEqual( - await testUtils.getActualFile('testExisting_tsms', 'transactionalSMS'), - await testUtils.getExpectedFile('9999999', 'transactionalSMS', 'get'), + await testUtils.getActualJson('testExisting_tsms', 'transactionalSMS'), + await testUtils.getExpectedJson('9999999', 'transactionalSMS', 'get'), 'returned metadata was not equal expected' ); assert.equal( @@ -50,38 +50,24 @@ describe('transactionalSMS', () => { ); // confirm created item assert.deepEqual( - await testUtils.getActualFile('testNew_tsms', 'transactionalSMS'), - await testUtils.getExpectedFile('9999999', 'transactionalSMS', 'post'), + await testUtils.getActualJson('testNew_tsms', 'transactionalSMS'), + await testUtils.getExpectedJson('9999999', 'transactionalSMS', 'post'), 'returned metadata was not equal expected for insert transactionalSMS' ); assert.deepEqual( - await testUtils.getActualFileGeneric('testNew_tsms', 'transactionalSMS', 'amp'), - await testUtils.getExpectedFileGeneric( - '9999999', - 'transactionalSMS', - 'post', - 'amp' - ), + await testUtils.getActualFile('testNew_tsms', 'transactionalSMS', 'amp'), + await testUtils.getExpectedFile('9999999', 'transactionalSMS', 'post', 'amp'), 'returned AMPscript was not equal expected for insert transactionalSMS' ); // confirm updated item assert.deepEqual( - await testUtils.getActualFile('testExisting_tsms', 'transactionalSMS'), - await testUtils.getExpectedFile('9999999', 'transactionalSMS', 'patch'), + await testUtils.getActualJson('testExisting_tsms', 'transactionalSMS'), + await testUtils.getExpectedJson('9999999', 'transactionalSMS', 'patch'), 'returned metadata was not equal expected for update transactionalSMS' ); assert.deepEqual( - await testUtils.getActualFileGeneric( - 'testExisting_tsms', - 'transactionalSMS', - 'amp' - ), - await testUtils.getExpectedFileGeneric( - '9999999', - 'transactionalSMS', - 'patch', - 'amp' - ), + await testUtils.getActualFile('testExisting_tsms', 'transactionalSMS', 'amp'), + await testUtils.getExpectedFile('9999999', 'transactionalSMS', 'patch', 'amp'), 'returned AMPscript was not equal expected for update transactionalSMS' ); // check number of API calls diff --git a/test/utils.js b/test/utils.js index e79e11a99..f79a5780c 100644 --- a/test/utils.js +++ b/test/utils.js @@ -19,7 +19,7 @@ const resourceFactory = require('./resourceFactory'); * @param {string} type of metadata * @returns {Promise.} file in string form */ -exports.getActualFile = (customerKey, type) => +exports.getActualJson = (customerKey, type) => File.readJSON(`./retrieve/testInstance/testBU/${type}/${customerKey}.${type}-meta.json`); /** * gets file from Retrieve folder @@ -29,7 +29,7 @@ exports.getActualFile = (customerKey, type) => * @param {string} ext file extension * @returns {Promise.} file in string form */ -exports.getActualFileGeneric = (customerKey, type, ext) => +exports.getActualFile = (customerKey, type, ext) => File.readFile(`./retrieve/testInstance/testBU/${type}/${customerKey}.${type}-meta.${ext}`); /** * gets file from Deploy folder @@ -38,8 +38,18 @@ exports.getActualFileGeneric = (customerKey, type, ext) => * @param {string} type of metadata * @returns {Promise.} file in string form */ -exports.getActualDeployFile = (customerKey, type) => +exports.getActualDeployJson = (customerKey, type) => File.readJSON(`./deploy/testInstance/testBU/${type}/${customerKey}.${type}-meta.json`); +/** + * gets file from Deploy folder + * + * @param {string} customerKey of metadata + * @param {string} type of metadata + * @param {string} ext file extension + * @returns {Promise.} file in string form + */ +exports.getActualDeployFile = (customerKey, type, ext) => + File.readJSON(`./deploy/testInstance/testBU/${type}/${customerKey}.${type}-meta.${ext}`); /** * gets file from Template folder * @@ -47,8 +57,18 @@ exports.getActualDeployFile = (customerKey, type) => * @param {string} type of metadata * @returns {Promise.} file in string form */ -exports.getActualTemplate = (customerKey, type) => +exports.getActualTemplateJson = (customerKey, type) => File.readJSON(`./template/${type}/${customerKey}.${type}-meta.json`); +/** + * gets file from Template folder + * + * @param {string} customerKey of metadata + * @param {string} type of metadata + * @param {string} ext file extension + * @returns {Promise.} file in string form + */ +exports.getActualTemplateFile = (customerKey, type, ext) => + File.readFile(`./template/${type}/${customerKey}.${type}-meta.${ext}`); /** * gets file from resources folder which should be used for comparison @@ -58,7 +78,7 @@ exports.getActualTemplate = (customerKey, type) => * @param {string} action of SOAP request * @returns {Promise.} file in string form */ -exports.getExpectedFile = (mid, type, action) => +exports.getExpectedJson = (mid, type, action) => File.readJSON(path.join('test', 'resources', mid, type, action + '-expected.json')); /** * gets file from resources folder which should be used for comparison @@ -69,7 +89,7 @@ exports.getExpectedFile = (mid, type, action) => * @param {string} ext file extension * @returns {Promise.} file in string form */ -exports.getExpectedFileGeneric = (mid, type, action, ext) => +exports.getExpectedFile = (mid, type, action, ext) => File.readFile(path.join('test', 'resources', mid, type, action + '-expected.' + ext)); /** * setup mocks for API and FS From c8bdf97c2f91e3840b925ef02f0d893ee8d36612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 29 Nov 2022 10:27:05 +0100 Subject: [PATCH 039/103] #573: --- test/query.test.js | 33 ++++++++++++++++--- .../9999999/query/build-expected.sql | 4 +++ test/resources/9999999/query/get-expected.sql | 4 +++ .../9999999/query/template-expected.sql | 4 +++ test/utils.js | 2 +- 5 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 test/resources/9999999/query/build-expected.sql create mode 100644 test/resources/9999999/query/get-expected.sql create mode 100644 test/resources/9999999/query/template-expected.sql diff --git a/test/query.test.js b/test/query.test.js index 000bae08c..8c6f8feac 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -28,6 +28,11 @@ describe('query', () => { await testUtils.getExpectedJson('9999999', 'query', 'get'), 'returned metadata was not equal expected' ); + assert.deepEqual( + await testUtils.getActualFile('testExistingQuery', 'query', 'sql'), + await testUtils.getExpectedFile('9999999', 'query', 'get', 'sql'), + 'returned SQL was not equal expected' + ); assert.equal( Object.values(testUtils.getAPIHistory()).flat().length, 6, @@ -97,7 +102,12 @@ describe('query', () => { assert.deepEqual( await testUtils.getActualTemplateJson('testExistingQuery', 'query'), await testUtils.getExpectedJson('9999999', 'query', 'template'), - 'returned template was not equal expected' + 'returned template JSON was not equal expected' + ); + assert.deepEqual( + await testUtils.getActualTemplateFile('testExistingQuery', 'query', 'sql'), + await testUtils.getExpectedFile('9999999', 'query', 'template', 'sql'), + 'returned template SQL was not equal expected' ); // THEN await handler.buildDefinition( @@ -109,7 +119,12 @@ describe('query', () => { assert.deepEqual( await testUtils.getActualDeployJson('testExistingQuery', 'query'), await testUtils.getExpectedJson('9999999', 'query', 'build'), - 'returned deployment file was not equal expected' + 'returned deployment JSON was not equal expected' + ); + assert.deepEqual( + await testUtils.getActualDeployFile('testExistingQuery', 'query', 'sql'), + await testUtils.getExpectedFile('9999999', 'query', 'build', 'sql'), + 'returned deployment SQL was not equal expected' ); assert.equal( Object.values(testUtils.getAPIHistory()).flat().length, @@ -137,7 +152,12 @@ describe('query', () => { assert.deepEqual( await testUtils.getActualTemplateJson('testExistingQuery', 'query'), await testUtils.getExpectedJson('9999999', 'query', 'template'), - 'returned template was not equal expected' + 'returned template JSON was not equal expected' + ); + assert.deepEqual( + await testUtils.getActualTemplateFile('testExistingQuery', 'query', 'sql'), + await testUtils.getExpectedFile('9999999', 'query', 'template', 'sql'), + 'returned template SQL was not equal expected' ); // THEN await handler.buildDefinition( @@ -149,7 +169,12 @@ describe('query', () => { assert.deepEqual( await testUtils.getActualDeployJson('testExistingQuery', 'query'), await testUtils.getExpectedJson('9999999', 'query', 'build'), - 'returned deployment file was not equal expected' + 'returned deployment JSON was not equal expected' + ); + assert.deepEqual( + await testUtils.getActualDeployFile('testExistingQuery', 'query', 'sql'), + await testUtils.getExpectedFile('9999999', 'query', 'build', 'sql'), + 'returned deployment SQL was not equal expected' ); assert.equal( Object.values(testUtils.getAPIHistory()).flat().length, diff --git a/test/resources/9999999/query/build-expected.sql b/test/resources/9999999/query/build-expected.sql new file mode 100644 index 000000000..9eb673f71 --- /dev/null +++ b/test/resources/9999999/query/build-expected.sql @@ -0,0 +1,4 @@ +SELECT + SubscriberKey as testField +FROM + _Subscribers \ No newline at end of file diff --git a/test/resources/9999999/query/get-expected.sql b/test/resources/9999999/query/get-expected.sql new file mode 100644 index 000000000..9eb673f71 --- /dev/null +++ b/test/resources/9999999/query/get-expected.sql @@ -0,0 +1,4 @@ +SELECT + SubscriberKey as testField +FROM + _Subscribers \ No newline at end of file diff --git a/test/resources/9999999/query/template-expected.sql b/test/resources/9999999/query/template-expected.sql new file mode 100644 index 000000000..9eb673f71 --- /dev/null +++ b/test/resources/9999999/query/template-expected.sql @@ -0,0 +1,4 @@ +SELECT + SubscriberKey as testField +FROM + _Subscribers \ No newline at end of file diff --git a/test/utils.js b/test/utils.js index f79a5780c..def4215cd 100644 --- a/test/utils.js +++ b/test/utils.js @@ -49,7 +49,7 @@ exports.getActualDeployJson = (customerKey, type) => * @returns {Promise.} file in string form */ exports.getActualDeployFile = (customerKey, type, ext) => - File.readJSON(`./deploy/testInstance/testBU/${type}/${customerKey}.${type}-meta.${ext}`); + File.readFile(`./deploy/testInstance/testBU/${type}/${customerKey}.${type}-meta.${ext}`); /** * gets file from Template folder * From 4323e823ae29986454b0f3a9d994ce41612a4310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 29 Nov 2022 13:55:52 +0100 Subject: [PATCH 040/103] #556: add chai-files to compare nested code; add transactionalSMS templating test; enhance query templating test --- lib/metadataTypes/MetadataType.js | 3 +- lib/metadataTypes/Query.js | 1 + lib/metadataTypes/TransactionalSMS.js | 120 +++++++++++++ .../TransactionalSMS.definition.js | 2 +- package-lock.json | 148 +++++++++------ package.json | 1 + test/mockRoot/.mcdevrc.json | 13 +- .../query/testExistingQuery.query-meta.sql | 4 +- test/query.test.js | 71 ++++---- .../patch-response.json | 2 +- .../automation/v1/queries/get-response.json | 4 +- .../v1/sms/definitions/post-response.json | 2 +- .../9999999/query/build-expected.json | 4 +- .../9999999/query/build-expected.sql | 4 +- .../resources/9999999/query/get-expected.json | 2 +- test/resources/9999999/query/get-expected.sql | 4 +- .../9999999/query/patch-expected.sql | 4 +- .../9999999/query/template-expected.json | 2 +- .../9999999/query/template-expected.sql | 4 +- .../transactionalSMS/build-expected.amp | 4 + .../transactionalSMS/build-expected.json | 13 ++ .../9999999/transactionalSMS/get-expected.amp | 4 + .../transactionalSMS/post-expected.amp | 2 +- .../transactionalSMS/template-expected.amp | 4 + .../transactionalSMS/template-expected.json | 13 ++ test/transactionalSMS.test.js | 169 ++++++++---------- test/utils.js | 8 +- 27 files changed, 404 insertions(+), 208 deletions(-) create mode 100644 test/resources/9999999/transactionalSMS/build-expected.amp create mode 100644 test/resources/9999999/transactionalSMS/build-expected.json create mode 100644 test/resources/9999999/transactionalSMS/get-expected.amp create mode 100644 test/resources/9999999/transactionalSMS/template-expected.amp create mode 100644 test/resources/9999999/transactionalSMS/template-expected.json diff --git a/lib/metadataTypes/MetadataType.js b/lib/metadataTypes/MetadataType.js index 2b36cc14d..886dd161b 100644 --- a/lib/metadataTypes/MetadataType.js +++ b/lib/metadataTypes/MetadataType.js @@ -532,8 +532,8 @@ class MetadataType { (filteredByPreDeploy > 0 ? ` / ${filteredByPreDeploy} filtered` : '') ); - // if Results then parse as SOAP if (this.definition.bodyIteratorField === 'Results') { + // if Results then parse as SOAP // put in Retrieve Format for parsing // todo add handling when response does not contain items. // @ts-ignore @@ -1511,6 +1511,7 @@ class MetadataType { Util.logger.debug(JSON.stringify(ex.response.data)); return errors; } + return null; } /** diff --git a/lib/metadataTypes/Query.js b/lib/metadataTypes/Query.js index d5b99cd47..0f3fbcb95 100644 --- a/lib/metadataTypes/Query.js +++ b/lib/metadataTypes/Query.js @@ -334,6 +334,7 @@ class Query extends MetadataType { if (errors?.length > 0) { return errors.map((msg) => msg.split('Error saving the Query field.').join('')); } + return null; } } diff --git a/lib/metadataTypes/TransactionalSMS.js b/lib/metadataTypes/TransactionalSMS.js index 9464d3cb3..8aaa3e1dc 100644 --- a/lib/metadataTypes/TransactionalSMS.js +++ b/lib/metadataTypes/TransactionalSMS.js @@ -198,6 +198,126 @@ class TransactionalSMS extends TransactionalMessage { return { fileExt, code }; } + /** + * helper for {@link MetadataType.buildDefinition} + * handles extracted code if any are found for complex types + * + * @param {string} templateDir Directory where metadata templates are stored + * @param {string|string[]} targetDir (List of) Directory where built definitions will be saved + * @param {TYPE.MetadataTypeItem} metadata main JSON file that was read from file system + * @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata + * @param {string} templateName name of the template to be built + * @returns {Promise.} list of extracted files with path-parts provided as an array + */ + static buildDefinitionForNested( + templateDir, + targetDir, + metadata, + templateVariables, + templateName + ) { + return this._buildForNested( + templateDir, + targetDir, + metadata, + templateVariables, + templateName, + 'definition' + ); + } + /** + * helper for {@link MetadataType.buildTemplate} + * handles extracted code if any are found for complex types + * + * @example scripts are saved as 1 json and 1 ssjs file. both files need to be run through templating + * @param {string} templateDir Directory where metadata templates are stored + * @param {string|string[]} targetDir (List of) Directory where built definitions will be saved + * @param {TYPE.MetadataTypeItem} metadata main JSON file that was read from file system + * @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata + * @param {string} templateName name of the template to be built + * @returns {Promise.} list of extracted files with path-parts provided as an array + */ + static buildTemplateForNested( + templateDir, + targetDir, + metadata, + templateVariables, + templateName + ) { + return this._buildForNested( + templateDir, + targetDir, + metadata, + templateVariables, + templateName, + 'template' + ); + } + + /** + * helper for {@link buildTemplateForNested} / {@link buildDefinitionForNested} + * handles extracted code if any are found for complex types + * + * @param {string} templateDir Directory where metadata templates are stored + * @param {string|string[]} targetDir (List of) Directory where built definitions will be saved + * @param {TYPE.MetadataTypeItem} metadata main JSON file that was read from file system + * @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata + * @param {string} templateName name of the template to be built + * @param {'definition'|'template'} mode defines what we use this helper for + * @returns {Promise.} list of extracted files with path-parts provided as an array + */ + static async _buildForNested( + templateDir, + targetDir, + metadata, + templateVariables, + templateName, + mode + ) { + // get code from filesystem + let code = await this._mergeCode(metadata, templateDir, templateName); + const file = this.prepExtractedCode(code, metadata.name); + const fileExt = file.fileExt; + code = file.code; + // apply templating + try { + if (mode === 'definition') { + // replace template variables with their values + code = this.applyTemplateValues(code, templateVariables); + } else if (mode === 'template') { + // replace template values with corresponding variable names + code = this.applyTemplateNames(code, templateVariables); + } + } catch { + throw new Error( + `${this.definition.type}:: Error applying template variables on ${ + templateName + '.' + this.definition.type + }-meta.${fileExt}.` + ); + } + + // write to file + const targetDirArr = Array.isArray(targetDir) ? targetDir : [targetDir]; + const nestedFilePaths = []; + + // keep old name if creating templates, otherwise use new name + const fileName = mode === 'definition' ? metadata[this.definition.keyField] : templateName; + + for (const targetDir of targetDirArr) { + File.writeToFile( + [targetDir, this.definition.type], + fileName + '.' + this.definition.type + '-meta', + fileExt, + code + ); + nestedFilePaths.push([ + targetDir, + this.definition.type, + fileName + '.' + this.definition.type + '-meta.' + fileExt, + ]); + } + return nestedFilePaths; + } /** * very simplified test for HTML code in our SMS * diff --git a/lib/metadataTypes/definitions/TransactionalSMS.definition.js b/lib/metadataTypes/definitions/TransactionalSMS.definition.js index bd4d9be5e..ff9e49d5d 100644 --- a/lib/metadataTypes/definitions/TransactionalSMS.definition.js +++ b/lib/metadataTypes/definitions/TransactionalSMS.definition.js @@ -49,7 +49,7 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - template: false, + template: true, }, createdDate: { isCreateable: false, diff --git a/package-lock.json b/package-lock.json index b50936ed7..5536422cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,7 @@ "assert": "2.0.0", "axios-mock-adapter": "1.21.2", "chai": "4.3.6", + "chai-files": "1.4.0", "eslint": "8.27.0", "eslint-config-prettier": "8.5.0", "eslint-config-ssjs": "1.1.11", @@ -77,12 +78,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.9.tgz", - "integrity": "sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz", + "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==", "dev": true, "dependencies": { - "@babel/types": "^7.18.9", + "@babel/types": "^7.20.5", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -100,13 +101,13 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "dev": true, "dependencies": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -136,6 +137,15 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-validator-identifier": { "version": "7.19.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", @@ -231,9 +241,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.9.tgz", - "integrity": "sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", + "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -243,33 +253,33 @@ } }, "node_modules/@babel/template": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.6.tgz", - "integrity": "sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==", + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.6", - "@babel/types": "^7.18.6" + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.9.tgz", - "integrity": "sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz", + "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.9", + "@babel/generator": "^7.20.5", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.9", - "@babel/types": "^7.18.9", + "@babel/parser": "^7.20.5", + "@babel/types": "^7.20.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -287,12 +297,13 @@ } }, "node_modules/@babel/types": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.9.tgz", - "integrity": "sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" }, "engines": { @@ -1472,6 +1483,15 @@ "node": ">=4" } }, + "node_modules/chai-files": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/chai-files/-/chai-files-1.4.0.tgz", + "integrity": "sha512-tPTx7H2kpR+wILWHRx8RxpXcRUdc2uH8su505C9R3p5GA+eYbZBXuxWC0RZbyElYi7X7Fp/V/S2PQjkakrT1mQ==", + "dev": true, + "dependencies": { + "assertion-error": "^1.0.1" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -8747,12 +8767,12 @@ } }, "@babel/generator": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.9.tgz", - "integrity": "sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz", + "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==", "dev": true, "requires": { - "@babel/types": "^7.18.9", + "@babel/types": "^7.20.5", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" } @@ -8764,13 +8784,13 @@ "dev": true }, "@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "dev": true, "requires": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" } }, "@babel/helper-hoist-variables": { @@ -8791,6 +8811,12 @@ "@babel/types": "^7.18.6" } }, + "@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true + }, "@babel/helper-validator-identifier": { "version": "7.19.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", @@ -8867,36 +8893,36 @@ } }, "@babel/parser": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.9.tgz", - "integrity": "sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", + "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", "dev": true }, "@babel/template": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.6.tgz", - "integrity": "sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==", + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", "dev": true, "requires": { "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.6", - "@babel/types": "^7.18.6" + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" } }, "@babel/traverse": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.9.tgz", - "integrity": "sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz", + "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==", "dev": true, "requires": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.9", + "@babel/generator": "^7.20.5", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.9", - "@babel/types": "^7.18.9", + "@babel/parser": "^7.20.5", + "@babel/types": "^7.20.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -8910,12 +8936,13 @@ } }, "@babel/types": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.9.tgz", - "integrity": "sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" } }, @@ -9809,6 +9836,15 @@ "type-detect": "^4.0.5" } }, + "chai-files": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/chai-files/-/chai-files-1.4.0.tgz", + "integrity": "sha512-tPTx7H2kpR+wILWHRx8RxpXcRUdc2uH8su505C9R3p5GA+eYbZBXuxWC0RZbyElYi7X7Fp/V/S2PQjkakrT1mQ==", + "dev": true, + "requires": { + "assertion-error": "^1.0.1" + } + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", diff --git a/package.json b/package.json index b3eb03be5..57fe85eb1 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "assert": "2.0.0", "axios-mock-adapter": "1.21.2", "chai": "4.3.6", + "chai-files": "1.4.0", "eslint": "8.27.0", "eslint-config-prettier": "8.5.0", "eslint-config-ssjs": "1.1.11", diff --git a/test/mockRoot/.mcdevrc.json b/test/mockRoot/.mcdevrc.json index 7cc7db444..7595d015c 100644 --- a/test/mockRoot/.mcdevrc.json +++ b/test/mockRoot/.mcdevrc.json @@ -34,12 +34,23 @@ "templateBuilds": ["retrieve/", "deploy/"] }, "markets": { - "testMarket": { + "testSourceMarket": { "mid": "9999999", "buName": "testBU", + "secret": "secret", "sharedFolder": "/Shared Data Extensions/test", "suffix": "_test", + "description": "bla bla", "countryCodeIn": "'test'" + }, + "testTargetMarket": { + "mid": "1111111", + "buName": "testBUTarget", + "secret": "target secret", + "sharedFolder": "/Shared Data Extensions/test target", + "suffix": "_testTarget", + "description": "foobar", + "countryCodeIn": "'testTarget'" } }, "marketList": {}, diff --git a/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.sql b/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.sql index 9eb673f71..e78525408 100644 --- a/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.sql +++ b/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.sql @@ -1,4 +1,6 @@ SELECT SubscriberKey as testField FROM - _Subscribers \ No newline at end of file + _Subscribers +WHERE + country IN ('test') diff --git a/test/query.test.js b/test/query.test.js index 8c6f8feac..c0df40153 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -1,4 +1,10 @@ -const assert = require('chai').assert; +const chai = require('chai'); +const chaiFiles = require('chai-files'); +const assert = chai.assert; +chai.use(chaiFiles); +const expect = chai.expect; +const file = chaiFiles.file; +// const dir = chaiFiles.dir; const cache = require('../lib/util/cache'); const testUtils = require('./utils'); const handler = require('../lib/index'); @@ -28,10 +34,8 @@ describe('query', () => { await testUtils.getExpectedJson('9999999', 'query', 'get'), 'returned metadata was not equal expected' ); - assert.deepEqual( - await testUtils.getActualFile('testExistingQuery', 'query', 'sql'), - await testUtils.getExpectedFile('9999999', 'query', 'get', 'sql'), - 'returned SQL was not equal expected' + expect(file(testUtils.getActualFile('testExistingQuery', 'query', 'sql'))).to.equal( + file(testUtils.getExpectedFile('9999999', 'query', 'get', 'sql')) ); assert.equal( Object.values(testUtils.getAPIHistory()).flat().length, @@ -59,10 +63,8 @@ describe('query', () => { await testUtils.getExpectedJson('9999999', 'query', 'post'), 'returned metadata was not equal expected for insert query' ); - assert.deepEqual( - await testUtils.getActualFile('testNewQuery', 'query', 'sql'), - await testUtils.getExpectedFile('9999999', 'query', 'post', 'sql'), - 'returned SQL was not equal expected for insert query' + expect(file(testUtils.getActualFile('testNewQuery', 'query', 'sql'))).to.equal( + file(testUtils.getExpectedFile('9999999', 'query', 'post', 'sql')) ); // confirm updated item assert.deepEqual( @@ -70,10 +72,8 @@ describe('query', () => { await testUtils.getExpectedJson('9999999', 'query', 'patch'), 'returned metadata was not equal expected for insert query' ); - assert.deepEqual( - await testUtils.getActualFile('testExistingQuery', 'query', 'sql'), - await testUtils.getExpectedFile('9999999', 'query', 'patch', 'sql'), - 'returned SQL was not equal expected for insert query' + expect(file(testUtils.getActualFile('testExistingQuery', 'query', 'sql'))).to.equal( + file(testUtils.getExpectedFile('9999999', 'query', 'patch', 'sql')) ); // check number of API calls assert.equal( @@ -91,7 +91,7 @@ describe('query', () => { 'testInstance/testBU', 'query', ['testExistingQuery'], - 'testMarket' + 'testSourceMarket' ); // WHEN assert.equal( @@ -102,30 +102,26 @@ describe('query', () => { assert.deepEqual( await testUtils.getActualTemplateJson('testExistingQuery', 'query'), await testUtils.getExpectedJson('9999999', 'query', 'template'), - 'returned template JSON was not equal expected' - ); - assert.deepEqual( - await testUtils.getActualTemplateFile('testExistingQuery', 'query', 'sql'), - await testUtils.getExpectedFile('9999999', 'query', 'template', 'sql'), - 'returned template SQL was not equal expected' + 'returned template JSON of retrieveAsTemplate was not equal expected' ); + expect( + file(testUtils.getActualTemplateFile('testExistingQuery', 'query', 'sql')) + ).to.equal(file(testUtils.getExpectedFile('9999999', 'query', 'template', 'sql'))); // THEN await handler.buildDefinition( 'testInstance/testBU', 'query', 'testExistingQuery', - 'testMarket' + 'testTargetMarket' ); assert.deepEqual( await testUtils.getActualDeployJson('testExistingQuery', 'query'), await testUtils.getExpectedJson('9999999', 'query', 'build'), 'returned deployment JSON was not equal expected' ); - assert.deepEqual( - await testUtils.getActualDeployFile('testExistingQuery', 'query', 'sql'), - await testUtils.getExpectedFile('9999999', 'query', 'build', 'sql'), - 'returned deployment SQL was not equal expected' - ); + expect( + file(testUtils.getActualDeployFile('testExistingQuery', 'query', 'sql')) + ).to.equal(file(testUtils.getExpectedFile('9999999', 'query', 'build', 'sql'))); assert.equal( Object.values(testUtils.getAPIHistory()).flat().length, 6, @@ -141,7 +137,7 @@ describe('query', () => { 'testInstance/testBU', 'query', ['testExistingQuery'], - 'testMarket' + 'testSourceMarket' ); // WHEN assert.equal( @@ -152,30 +148,27 @@ describe('query', () => { assert.deepEqual( await testUtils.getActualTemplateJson('testExistingQuery', 'query'), await testUtils.getExpectedJson('9999999', 'query', 'template'), - 'returned template JSON was not equal expected' - ); - assert.deepEqual( - await testUtils.getActualTemplateFile('testExistingQuery', 'query', 'sql'), - await testUtils.getExpectedFile('9999999', 'query', 'template', 'sql'), - 'returned template SQL was not equal expected' + 'returned template JSON of buildTemplate was not equal expected' ); + expect( + file(testUtils.getActualTemplateFile('testExistingQuery', 'query', 'sql')) + ).to.equal(file(testUtils.getExpectedFile('9999999', 'query', 'template', 'sql'))); // THEN await handler.buildDefinition( 'testInstance/testBU', 'query', 'testExistingQuery', - 'testMarket' + 'testTargetMarket' ); assert.deepEqual( await testUtils.getActualDeployJson('testExistingQuery', 'query'), await testUtils.getExpectedJson('9999999', 'query', 'build'), 'returned deployment JSON was not equal expected' ); - assert.deepEqual( - await testUtils.getActualDeployFile('testExistingQuery', 'query', 'sql'), - await testUtils.getExpectedFile('9999999', 'query', 'build', 'sql'), - 'returned deployment SQL was not equal expected' - ); + expect( + file(testUtils.getActualDeployFile('testExistingQuery', 'query', 'sql')) + ).to.equal(file(testUtils.getExpectedFile('9999999', 'query', 'build', 'sql'))); + assert.equal( Object.values(testUtils.getAPIHistory()).flat().length, 6, diff --git a/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json b/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json index 74e650e5a..b191b4d8e 100644 --- a/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json +++ b/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json @@ -3,7 +3,7 @@ "name": "testExistingQuery", "key": "testExistingQuery", "description": "updated on deploy", - "queryText": "SELECT\n SubscriberKey as testField\nFROM\n _Subscribers ", + "queryText": "SELECT\n SubscriberKey as testField\nFROM\n _Subscribers\nWHERE\n country IN ('test')\n", "targetName": "childBU_dataextension_test", "targetKey": "childBU_dataextension_test", "targetId": "30400c03-0ec4-ec11-b83c-48df37d1de8a", diff --git a/test/resources/9999999/automation/v1/queries/get-response.json b/test/resources/9999999/automation/v1/queries/get-response.json index 8592d5da1..f404fc723 100644 --- a/test/resources/9999999/automation/v1/queries/get-response.json +++ b/test/resources/9999999/automation/v1/queries/get-response.json @@ -7,8 +7,8 @@ "queryDefinitionId": "549f0568-607c-4940-afef-437965094dat", "name": "testExistingQuery", "key": "testExistingQuery", - "description": "", - "queryText": "SELECT\n SubscriberKey as testField\nFROM\n _Subscribers ", + "description": "bla bla", + "queryText": "SELECT\n SubscriberKey as testField\nFROM\n _Subscribers\nWHERE\n country IN ('test')\n", "targetName": "childBU_dataextension_test", "targetKey": "childBU_dataextension_test", "targetId": "30400c03-0ec4-ec11-b83c-48df37d1de8a", diff --git a/test/resources/9999999/messaging/v1/sms/definitions/post-response.json b/test/resources/9999999/messaging/v1/sms/definitions/post-response.json index 53288e0d6..c66b9bda0 100644 --- a/test/resources/9999999/messaging/v1/sms/definitions/post-response.json +++ b/test/resources/9999999/messaging/v1/sms/definitions/post-response.json @@ -6,7 +6,7 @@ "status": "Active", "createdDate": "2022-11-07T02:24:00", "modifiedDate": "2022-11-07T02:25:00", - "content": { "message": "\n%%[\n SET @key = 'secret'\n]%%\n" }, + "content": { "message": "\n%%[\n SET @key = 'new secret'\n]%%\n" }, "subscriptions": { "shortCode": "4912312345678", "countryCode": "", diff --git a/test/resources/9999999/query/build-expected.json b/test/resources/9999999/query/build-expected.json index b9350fcaa..565a2b238 100644 --- a/test/resources/9999999/query/build-expected.json +++ b/test/resources/9999999/query/build-expected.json @@ -1,8 +1,8 @@ { "name": "testExistingQuery", "key": "testExistingQuery", - "description": "", - "targetKey": "childBU_dataextension_test", + "description": "foobar", + "targetKey": "childBU_dataextension_testTarget", "targetUpdateTypeName": "Overwrite", "r__folder_Path": "Query" } diff --git a/test/resources/9999999/query/build-expected.sql b/test/resources/9999999/query/build-expected.sql index 9eb673f71..276ccd7aa 100644 --- a/test/resources/9999999/query/build-expected.sql +++ b/test/resources/9999999/query/build-expected.sql @@ -1,4 +1,6 @@ SELECT SubscriberKey as testField FROM - _Subscribers \ No newline at end of file + _Subscribers +WHERE + country IN ('testTarget') diff --git a/test/resources/9999999/query/get-expected.json b/test/resources/9999999/query/get-expected.json index 42036a34e..84e4e0ff1 100644 --- a/test/resources/9999999/query/get-expected.json +++ b/test/resources/9999999/query/get-expected.json @@ -1,7 +1,7 @@ { "name": "testExistingQuery", "key": "testExistingQuery", - "description": "", + "description": "bla bla", "targetKey": "childBU_dataextension_test", "createdDate": "2022-04-26T15:21:16.453", "modifiedDate": "2022-04-26T16:02:44.01", diff --git a/test/resources/9999999/query/get-expected.sql b/test/resources/9999999/query/get-expected.sql index 9eb673f71..e78525408 100644 --- a/test/resources/9999999/query/get-expected.sql +++ b/test/resources/9999999/query/get-expected.sql @@ -1,4 +1,6 @@ SELECT SubscriberKey as testField FROM - _Subscribers \ No newline at end of file + _Subscribers +WHERE + country IN ('test') diff --git a/test/resources/9999999/query/patch-expected.sql b/test/resources/9999999/query/patch-expected.sql index 9eb673f71..e78525408 100644 --- a/test/resources/9999999/query/patch-expected.sql +++ b/test/resources/9999999/query/patch-expected.sql @@ -1,4 +1,6 @@ SELECT SubscriberKey as testField FROM - _Subscribers \ No newline at end of file + _Subscribers +WHERE + country IN ('test') diff --git a/test/resources/9999999/query/template-expected.json b/test/resources/9999999/query/template-expected.json index bbfa58ab7..5d04b46c4 100644 --- a/test/resources/9999999/query/template-expected.json +++ b/test/resources/9999999/query/template-expected.json @@ -1,7 +1,7 @@ { "name": "testExistingQuery", "key": "testExistingQuery", - "description": "", + "description": "{{{description}}}", "targetKey": "childBU_dataextension{{{suffix}}}", "targetUpdateTypeName": "Overwrite", "r__folder_Path": "Query" diff --git a/test/resources/9999999/query/template-expected.sql b/test/resources/9999999/query/template-expected.sql index 9eb673f71..0ed1dc1cc 100644 --- a/test/resources/9999999/query/template-expected.sql +++ b/test/resources/9999999/query/template-expected.sql @@ -1,4 +1,6 @@ SELECT SubscriberKey as testField FROM - _Subscribers \ No newline at end of file + _Subscribers +WHERE + country IN ({{{countryCodeIn}}}) diff --git a/test/resources/9999999/transactionalSMS/build-expected.amp b/test/resources/9999999/transactionalSMS/build-expected.amp new file mode 100644 index 000000000..006734caf --- /dev/null +++ b/test/resources/9999999/transactionalSMS/build-expected.amp @@ -0,0 +1,4 @@ + +%%[ + SET @key = 'target secret' +]%% diff --git a/test/resources/9999999/transactionalSMS/build-expected.json b/test/resources/9999999/transactionalSMS/build-expected.json new file mode 100644 index 000000000..b8985f3cd --- /dev/null +++ b/test/resources/9999999/transactionalSMS/build-expected.json @@ -0,0 +1,13 @@ +{ + "name": "testExisting_tsms", + "definitionKey": "testExisting_tsms", + "description": "foobar", + "status": "Active", + "subscriptions": { + "shortCode": "4912312345678", + "countryCode": "", + "autoAddSubscriber": true, + "updateSubscriber": true, + "keyword": "testExisting_keyword" + } +} diff --git a/test/resources/9999999/transactionalSMS/get-expected.amp b/test/resources/9999999/transactionalSMS/get-expected.amp new file mode 100644 index 000000000..e4c5aff95 --- /dev/null +++ b/test/resources/9999999/transactionalSMS/get-expected.amp @@ -0,0 +1,4 @@ + +%%[ + SET @key = 'secret' +]%% diff --git a/test/resources/9999999/transactionalSMS/post-expected.amp b/test/resources/9999999/transactionalSMS/post-expected.amp index e4c5aff95..a1f4bd04f 100644 --- a/test/resources/9999999/transactionalSMS/post-expected.amp +++ b/test/resources/9999999/transactionalSMS/post-expected.amp @@ -1,4 +1,4 @@ %%[ - SET @key = 'secret' + SET @key = 'new secret' ]%% diff --git a/test/resources/9999999/transactionalSMS/template-expected.amp b/test/resources/9999999/transactionalSMS/template-expected.amp new file mode 100644 index 000000000..29e5fcafe --- /dev/null +++ b/test/resources/9999999/transactionalSMS/template-expected.amp @@ -0,0 +1,4 @@ + +%%[ + SET @key = '{{{secret}}}' +]%% diff --git a/test/resources/9999999/transactionalSMS/template-expected.json b/test/resources/9999999/transactionalSMS/template-expected.json new file mode 100644 index 000000000..c0e2712b8 --- /dev/null +++ b/test/resources/9999999/transactionalSMS/template-expected.json @@ -0,0 +1,13 @@ +{ + "name": "testExisting_tsms", + "definitionKey": "testExisting_tsms", + "description": "{{{description}}}", + "status": "Active", + "subscriptions": { + "shortCode": "4912312345678", + "countryCode": "", + "autoAddSubscriber": true, + "updateSubscriber": true, + "keyword": "testExisting_keyword" + } +} diff --git a/test/transactionalSMS.test.js b/test/transactionalSMS.test.js index 24284abb8..b692fac25 100644 --- a/test/transactionalSMS.test.js +++ b/test/transactionalSMS.test.js @@ -1,4 +1,10 @@ -const assert = require('chai').assert; +const chai = require('chai'); +const chaiFiles = require('chai-files'); +const assert = chai.assert; +chai.use(chaiFiles); +const expect = chai.expect; +const file = chaiFiles.file; +// const dir = chaiFiles.dir; const cache = require('../lib/util/cache'); const testUtils = require('./utils'); const handler = require('../lib/index'); @@ -26,7 +32,12 @@ describe('transactionalSMS', () => { assert.deepEqual( await testUtils.getActualJson('testExisting_tsms', 'transactionalSMS'), await testUtils.getExpectedJson('9999999', 'transactionalSMS', 'get'), - 'returned metadata was not equal expected' + 'returned JSON was not equal expected' + ); + expect( + file(testUtils.getActualFile('testExisting_tsms', 'transactionalSMS', 'amp')) + ).to.equal( + file(testUtils.getExpectedFile('9999999', 'transactionalSMS', 'get', 'amp')) ); assert.equal( Object.values(testUtils.getAPIHistory()).flat().length, @@ -52,23 +63,23 @@ describe('transactionalSMS', () => { assert.deepEqual( await testUtils.getActualJson('testNew_tsms', 'transactionalSMS'), await testUtils.getExpectedJson('9999999', 'transactionalSMS', 'post'), - 'returned metadata was not equal expected for insert transactionalSMS' + 'returned JSON was not equal expected for insert transactionalSMS' ); - assert.deepEqual( - await testUtils.getActualFile('testNew_tsms', 'transactionalSMS', 'amp'), - await testUtils.getExpectedFile('9999999', 'transactionalSMS', 'post', 'amp'), - 'returned AMPscript was not equal expected for insert transactionalSMS' + expect( + file(testUtils.getActualFile('testNew_tsms', 'transactionalSMS', 'amp')) + ).to.equal( + file(testUtils.getExpectedFile('9999999', 'transactionalSMS', 'post', 'amp')) ); // confirm updated item assert.deepEqual( await testUtils.getActualJson('testExisting_tsms', 'transactionalSMS'), await testUtils.getExpectedJson('9999999', 'transactionalSMS', 'patch'), - 'returned metadata was not equal expected for update transactionalSMS' + 'returned JSON was not equal expected for update transactionalSMS' ); - assert.deepEqual( - await testUtils.getActualFile('testExisting_tsms', 'transactionalSMS', 'amp'), - await testUtils.getExpectedFile('9999999', 'transactionalSMS', 'patch', 'amp'), - 'returned AMPscript was not equal expected for update transactionalSMS' + expect( + file(testUtils.getActualFile('testExisting_tsms', 'transactionalSMS', 'amp')) + ).to.equal( + file(testUtils.getExpectedFile('9999999', 'transactionalSMS', 'patch', 'amp')) ); // check number of API calls assert.equal( @@ -79,84 +90,58 @@ describe('transactionalSMS', () => { return; }); }); - // describe('Templating ================', () => { - // it('Should create a transactionalSMS template via retrieveAsTemplate and build it', async () => { - // // GIVEN there is a template - // const result = await handler.retrieveAsTemplate( - // 'testInstance/testBU', - // 'transactionalSMS', - // ['testExisting_tsms'], - // 'testMarket' - // ); - // // WHEN - // assert.equal( - // result.transactionalSMS ? Object.keys(result.transactionalSMS).length : 0, - // 1, - // 'only one transactionalSMS expected' - // ); - // assert.deepEqual( - // await testUtils.getActualTemplate('testExisting_tsms', 'transactionalSMS'), - // await testUtils.getExpectedFile('9999999', 'transactionalSMS', 'template'), - // 'returned template was not equal expected' - // ); - // // THEN - // await handler.buildDefinition( - // 'testInstance/testBU', - // 'transactionalSMS', - // 'testExisting_tsms', - // 'testMarket' - // ); - // assert.deepEqual( - // await testUtils.getActualDeployFile('testExisting_tsms', 'transactionalSMS'), - // await testUtils.getExpectedFile('9999999', 'transactionalSMS', 'build'), - // 'returned deployment file was not equal expected' - // ); - // assert.equal( - // Object.values(testUtils.getAPIHistory()).flat().length, - // 6, - // 'Unexpected number of requests made' - // ); - // return; - // }); - // it('Should create a transactionalSMS template via buildTemplate and build it', async () => { - // // download first before we test buildTemplate - // await handler.retrieve('testInstance/testBU', ['transactionalSMS']); - // // GIVEN there is a template - // const result = await handler.buildTemplate( - // 'testInstance/testBU', - // 'transactionalSMS', - // ['testExisting_tsms'], - // 'testMarket' - // ); - // // WHEN - // assert.equal( - // result.transactionalSMS ? Object.keys(result.transactionalSMS).length : 0, - // 1, - // 'only one transactionalSMS expected' - // ); - // assert.deepEqual( - // await testUtils.getActualTemplate('testExisting_tsms', 'transactionalSMS'), - // await testUtils.getExpectedFile('9999999', 'transactionalSMS', 'template'), - // 'returned template was not equal expected' - // ); - // // THEN - // await handler.buildDefinition( - // 'testInstance/testBU', - // 'transactionalSMS', - // 'testExisting_tsms', - // 'testMarket' - // ); - // assert.deepEqual( - // await testUtils.getActualDeployFile('testExisting_tsms', 'transactionalSMS'), - // await testUtils.getExpectedFile('9999999', 'transactionalSMS', 'build'), - // 'returned deployment file was not equal expected' - // ); - // assert.equal( - // Object.values(testUtils.getAPIHistory()).flat().length, - // 6, - // 'Unexpected number of requests made' - // ); - // return; - // }); - // }); + describe('Templating ================', () => { + // it.skip('Should create a transactionalSMS template via retrieveAsTemplate and build it'); + it('Should create a transactionalSMS template via buildTemplate and build it', async () => { + // download first before we test buildTemplate + await handler.retrieve('testInstance/testBU', ['transactionalSMS']); + // buildTemplate + const result = await handler.buildTemplate( + 'testInstance/testBU', + 'transactionalSMS', + ['testExisting_tsms'], + 'testSourceMarket' + ); + assert.equal( + result.transactionalSMS ? Object.keys(result.transactionalSMS).length : 0, + 1, + 'only one transactionalSMS expected' + ); + assert.deepEqual( + await testUtils.getActualTemplateJson('testExisting_tsms', 'transactionalSMS'), + await testUtils.getExpectedJson('9999999', 'transactionalSMS', 'template'), + 'returned template JSON was not equal expected' + ); + expect( + file( + testUtils.getActualTemplateFile('testExisting_tsms', 'transactionalSMS', 'amp') + ) + ).to.equal( + file(testUtils.getExpectedFile('9999999', 'transactionalSMS', 'template', 'amp')) + ); + // buildDefinition + await handler.buildDefinition( + 'testInstance/testBU', + 'transactionalSMS', + 'testExisting_tsms', + 'testTargetMarket' + ); + assert.deepEqual( + await testUtils.getActualDeployJson('testExisting_tsms', 'transactionalSMS'), + await testUtils.getExpectedJson('9999999', 'transactionalSMS', 'build'), + 'returned deployment JSON was not equal expected' + ); + expect( + file(testUtils.getActualDeployFile('testExisting_tsms', 'transactionalSMS', 'amp')) + ).to.equal( + file(testUtils.getExpectedFile('9999999', 'transactionalSMS', 'build', 'amp')) + ); + assert.equal( + Object.values(testUtils.getAPIHistory()).flat().length, + 4, + 'Unexpected number of requests made' + ); + return; + }); + }); }); diff --git a/test/utils.js b/test/utils.js index def4215cd..2e0575f2b 100644 --- a/test/utils.js +++ b/test/utils.js @@ -30,7 +30,7 @@ exports.getActualJson = (customerKey, type) => * @returns {Promise.} file in string form */ exports.getActualFile = (customerKey, type, ext) => - File.readFile(`./retrieve/testInstance/testBU/${type}/${customerKey}.${type}-meta.${ext}`); + `./retrieve/testInstance/testBU/${type}/${customerKey}.${type}-meta.${ext}`; /** * gets file from Deploy folder * @@ -49,7 +49,7 @@ exports.getActualDeployJson = (customerKey, type) => * @returns {Promise.} file in string form */ exports.getActualDeployFile = (customerKey, type, ext) => - File.readFile(`./deploy/testInstance/testBU/${type}/${customerKey}.${type}-meta.${ext}`); + `./deploy/testInstance/testBU/${type}/${customerKey}.${type}-meta.${ext}`; /** * gets file from Template folder * @@ -68,7 +68,7 @@ exports.getActualTemplateJson = (customerKey, type) => * @returns {Promise.} file in string form */ exports.getActualTemplateFile = (customerKey, type, ext) => - File.readFile(`./template/${type}/${customerKey}.${type}-meta.${ext}`); + `./template/${type}/${customerKey}.${type}-meta.${ext}`; /** * gets file from resources folder which should be used for comparison @@ -90,7 +90,7 @@ exports.getExpectedJson = (mid, type, action) => * @returns {Promise.} file in string form */ exports.getExpectedFile = (mid, type, action, ext) => - File.readFile(path.join('test', 'resources', mid, type, action + '-expected.' + ext)); + path.join('test', 'resources', mid, type, action + '-expected.' + ext); /** * setup mocks for API and FS * From dbeb68dbabb6b2662986762a6010514d67212a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 29 Nov 2022 15:10:45 +0100 Subject: [PATCH 041/103] #556: fix how we test deploys before, we would always hardlink the deploy folder which could have interfered with other tests --- docs/dist/documentation.md | 59 +++++++++++++++++++++++++++++++++++ test/dataExtension.test.js | 3 ++ test/query.test.js | 3 ++ test/transactionalSMS.test.js | 3 ++ test/utils.js | 32 +++++++++++++------ 5 files changed, 90 insertions(+), 10 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 1d766dc1b..77362aecd 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -4450,6 +4450,9 @@ TransactionalSMS MetadataType * [._mergeCode(metadata, deployDir, [templateName])](#TransactionalSMS._mergeCode) ⇒ Promise.<string> * [.postRetrieveTasks(metadata)](#TransactionalSMS.postRetrieveTasks) ⇒ TYPE.CodeExtractItem * [.prepExtractedCode(metadataScript)](#TransactionalSMS.prepExtractedCode) ⇒ Object + * [.buildDefinitionForNested(templateDir, targetDir, metadata, templateVariables, templateName)](#TransactionalSMS.buildDefinitionForNested) ⇒ Promise.<Array.<Array.<string>>> + * [.buildTemplateForNested(templateDir, targetDir, metadata, templateVariables, templateName)](#TransactionalSMS.buildTemplateForNested) ⇒ Promise.<Array.<Array.<string>>> + * [._buildForNested(templateDir, targetDir, metadata, templateVariables, templateName, mode)](#TransactionalSMS._buildForNested) ⇒ Promise.<Array.<Array.<string>>> * [._isHTML(code)](#TransactionalSMS._isHTML) ⇒ boolean * [.getFilesToCommit(keyArr)](#TransactionalSMS.getFilesToCommit) ⇒ Array.<string> @@ -4516,6 +4519,62 @@ helper for [parseMetadata](parseMetadata) and [_buildForNested](_buildForNested) | --- | --- | --- | | metadataScript | string | the code of the file | + + +### TransactionalSMS.buildDefinitionForNested(templateDir, targetDir, metadata, templateVariables, templateName) ⇒ Promise.<Array.<Array.<string>>> +helper for [buildDefinition](#MetadataType.buildDefinition) +handles extracted code if any are found for complex types + +**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**Returns**: Promise.<Array.<Array.<string>>> - list of extracted files with path-parts provided as an array + +| Param | Type | Description | +| --- | --- | --- | +| templateDir | string | Directory where metadata templates are stored | +| targetDir | string \| Array.<string> | (List of) Directory where built definitions will be saved | +| metadata | TYPE.MetadataTypeItem | main JSON file that was read from file system | +| templateVariables | TYPE.TemplateMap | variables to be replaced in the metadata | +| templateName | string | name of the template to be built | + + + +### TransactionalSMS.buildTemplateForNested(templateDir, targetDir, metadata, templateVariables, templateName) ⇒ Promise.<Array.<Array.<string>>> +helper for [buildTemplate](#MetadataType.buildTemplate) +handles extracted code if any are found for complex types + +**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**Returns**: Promise.<Array.<Array.<string>>> - list of extracted files with path-parts provided as an array + +| Param | Type | Description | +| --- | --- | --- | +| templateDir | string | Directory where metadata templates are stored | +| targetDir | string \| Array.<string> | (List of) Directory where built definitions will be saved | +| metadata | TYPE.MetadataTypeItem | main JSON file that was read from file system | +| templateVariables | TYPE.TemplateMap | variables to be replaced in the metadata | +| templateName | string | name of the template to be built | + +**Example** +```js +scripts are saved as 1 json and 1 ssjs file. both files need to be run through templating +``` + + +### TransactionalSMS.\_buildForNested(templateDir, targetDir, metadata, templateVariables, templateName, mode) ⇒ Promise.<Array.<Array.<string>>> +helper for [buildTemplateForNested](buildTemplateForNested) / [buildDefinitionForNested](buildDefinitionForNested) +handles extracted code if any are found for complex types + +**Kind**: static method of [TransactionalSMS](#TransactionalSMS) +**Returns**: Promise.<Array.<Array.<string>>> - list of extracted files with path-parts provided as an array + +| Param | Type | Description | +| --- | --- | --- | +| templateDir | string | Directory where metadata templates are stored | +| targetDir | string \| Array.<string> | (List of) Directory where built definitions will be saved | +| metadata | TYPE.MetadataTypeItem | main JSON file that was read from file system | +| templateVariables | TYPE.TemplateMap | variables to be replaced in the metadata | +| templateName | string | name of the template to be built | +| mode | 'definition' \| 'template' | defines what we use this helper for | + ### TransactionalSMS.\_isHTML(code) ⇒ boolean diff --git a/test/dataExtension.test.js b/test/dataExtension.test.js index 1b491d4cb..845c9a211 100644 --- a/test/dataExtension.test.js +++ b/test/dataExtension.test.js @@ -37,6 +37,9 @@ describe('dataExtension', () => { }); }); describe('Deploy ================', () => { + beforeEach(() => { + testUtils.mockSetupDeploy(); + }); it('Should create & upsert a data extension', async () => { // WHEN await handler.deploy('testInstance/testBU', ['dataExtension']); diff --git a/test/query.test.js b/test/query.test.js index c0df40153..fcf7707d3 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -46,6 +46,9 @@ describe('query', () => { }); }); describe('Deploy ================', () => { + beforeEach(() => { + testUtils.mockSetupDeploy(); + }); it('Should create & upsert a query', async () => { // WHEN await handler.deploy('testInstance/testBU', ['query']); diff --git a/test/transactionalSMS.test.js b/test/transactionalSMS.test.js index b692fac25..0d351adee 100644 --- a/test/transactionalSMS.test.js +++ b/test/transactionalSMS.test.js @@ -48,6 +48,9 @@ describe('transactionalSMS', () => { }); }); describe('Deploy ================', () => { + beforeEach(() => { + testUtils.mockSetupDeploy(); + }); it('Should create & upsert a transactionalSMS', async () => { // WHEN await handler.deploy('testInstance/testBU', ['transactionalSMS']); diff --git a/test/utils.js b/test/utils.js index 2e0575f2b..990dac435 100644 --- a/test/utils.js +++ b/test/utils.js @@ -96,6 +96,15 @@ exports.getExpectedFile = (mid, type, action, ext) => * * @returns {void} */ + +const fsMockConf = { + '.prettierrc': fsmock.load(path.resolve(__dirname, '../boilerplate/files/.prettierrc')), + '.mcdevrc.json': fsmock.load(path.resolve(__dirname, 'mockRoot/.mcdevrc.json')), + '.mcdev-auth.json': fsmock.load(path.resolve(__dirname, 'mockRoot/.mcdev-auth.json')), + 'boilerplate/config.json': fsmock.load(path.resolve(__dirname, '../boilerplate/config.json')), + // deploy: fsmock.load(path.resolve(__dirname, 'mockRoot/deploy')), + test: fsmock.load(path.resolve(__dirname)), +}; exports.mockSetup = () => { Util.setLoggingLevel({ debug: true }); apimock = new MockAdapter(axios, { onNoMatch: 'throwException' }); @@ -110,16 +119,18 @@ exports.mockSetup = () => { apimock .onAny(new RegExp(`^${escapeRegExp(resourceFactory.restUrl)}`)) .reply((config) => resourceFactory.handleRESTRequest(config)); - fsmock({ - '.prettierrc': fsmock.load(path.resolve(__dirname, '../boilerplate/files/.prettierrc')), - '.mcdevrc.json': fsmock.load(path.resolve(__dirname, 'mockRoot/.mcdevrc.json')), - '.mcdev-auth.json': fsmock.load(path.resolve(__dirname, 'mockRoot/.mcdev-auth.json')), - 'boilerplate/config.json': fsmock.load( - path.resolve(__dirname, '../boilerplate/config.json') - ), - deploy: fsmock.load(path.resolve(__dirname, 'mockRoot/deploy')), - test: fsmock.load(path.resolve(__dirname)), - }); + fsmock(fsMockConf); +}; + +/** + * setup mocks for deploy test + * + * @returns {void} + */ +exports.mockSetupDeploy = () => { + const fsMockConfDeploy = { ...fsMockConf }; + fsMockConfDeploy.deploy = fsmock.load(path.resolve(__dirname, 'mockRoot/deploy')); + fsmock(fsMockConfDeploy); }; /** * resets mocks for API and FS @@ -128,6 +139,7 @@ exports.mockSetup = () => { */ exports.mockReset = () => { auth.clearSessions(); + fsmock.restore(); apimock.restore(); }; /** From 21b4f20d920c7a559319ef927eacf8532235a15f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 29 Nov 2022 15:32:27 +0100 Subject: [PATCH 042/103] #556: fix dataExtension tests for new markets --- test/dataExtension.test.js | 20 +++++++---- ...dataextension_test.dataExtension-meta.json | 34 +++++++++++++++---- .../9999999/dataExtension/build-expected.json | 4 +-- 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/test/dataExtension.test.js b/test/dataExtension.test.js index 845c9a211..71974e727 100644 --- a/test/dataExtension.test.js +++ b/test/dataExtension.test.js @@ -52,11 +52,13 @@ describe('dataExtension', () => { 2, 'two data extensions expected' ); + // insert assert.deepEqual( await testUtils.getActualJson('testDataExtension', 'dataExtension'), await testUtils.getExpectedJson('9999999', 'dataExtension', 'create'), 'returned metadata was not equal expected for create' ); + // update assert.deepEqual( await testUtils.getActualJson('childBU_dataextension_test', 'dataExtension'), await testUtils.getExpectedJson('9999999', 'dataExtension', 'update'), @@ -77,7 +79,7 @@ describe('dataExtension', () => { 'testInstance/testBU', 'dataExtension', ['childBU_dataextension_test'], - 'testMarket' + 'testSourceMarket' ); // WHEN @@ -99,10 +101,13 @@ describe('dataExtension', () => { 'testInstance/testBU', 'dataExtension', 'childBU_dataextension_test', - 'testMarket' + 'testTargetMarket' ); assert.deepEqual( - await testUtils.getActualDeployJson('childBU_dataextension_test', 'dataExtension'), + await testUtils.getActualDeployJson( + 'childBU_dataextension_testTarget', + 'dataExtension' + ), await testUtils.getExpectedJson('9999999', 'dataExtension', 'build'), 'returned deployment file was not equal expected' ); @@ -121,7 +126,7 @@ describe('dataExtension', () => { 'testInstance/testBU', 'dataExtension', ['childBU_dataextension_test'], - 'testMarket' + 'testSourceMarket' ); // WHEN assert.equal( @@ -142,11 +147,14 @@ describe('dataExtension', () => { 'testInstance/testBU', 'dataExtension', 'childBU_dataextension_test', - 'testMarket' + 'testTargetMarket' ); assert.deepEqual( - await testUtils.getActualDeployJson('childBU_dataextension_test', 'dataExtension'), + await testUtils.getActualDeployJson( + 'childBU_dataextension_testTarget', + 'dataExtension' + ), await testUtils.getExpectedJson('9999999', 'dataExtension', 'build'), 'returned deployment file was not equal expected' ); diff --git a/test/mockRoot/deploy/testInstance/testBU/dataExtension/childBU_dataextension_test.dataExtension-meta.json b/test/mockRoot/deploy/testInstance/testBU/dataExtension/childBU_dataextension_test.dataExtension-meta.json index b83b7e4db..219055a3a 100644 --- a/test/mockRoot/deploy/testInstance/testBU/dataExtension/childBU_dataextension_test.dataExtension-meta.json +++ b/test/mockRoot/deploy/testInstance/testBU/dataExtension/childBU_dataextension_test.dataExtension-meta.json @@ -1,19 +1,23 @@ { "CustomerKey": "childBU_dataextension_test", "Name": "childBU_dataextension_test", - "Description": "", - "IsSendable": false, - "IsTestable": false, + "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": "testField", + "Name": "FirstName", "DefaultValue": "", - "MaxLength": 254, - "IsRequired": true, + "MaxLength": 50, + "IsRequired": false, "IsPrimaryKey": false, "FieldType": "Text" }, @@ -21,14 +25,30 @@ "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": 40, + "MaxLength": 50, "IsRequired": true, "IsPrimaryKey": true, "FieldType": "Text" diff --git a/test/resources/9999999/dataExtension/build-expected.json b/test/resources/9999999/dataExtension/build-expected.json index 2864d267b..c1c7bca76 100644 --- a/test/resources/9999999/dataExtension/build-expected.json +++ b/test/resources/9999999/dataExtension/build-expected.json @@ -1,6 +1,6 @@ { - "CustomerKey": "childBU_dataextension_test", - "Name": "childBU_dataextension_test", + "CustomerKey": "childBU_dataextension_testTarget", + "Name": "childBU_dataextension_testTarget", "Description": "Container for my test emails", "IsSendable": true, "IsTestable": true, From 463c2e1347490223d6098d855a890294f46b5471 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Nov 2022 14:43:42 +0000 Subject: [PATCH 043/103] Bump eslint-plugin-jsdoc from 39.6.2 to 39.6.4 Bumps [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) from 39.6.2 to 39.6.4. - [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases) - [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v39.6.2...v39.6.4) --- updated-dependencies: - dependency-name: eslint-plugin-jsdoc dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 30 +++++++++++++++--------------- package.json | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6e9cbc0a8..3107d3221 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,7 +39,7 @@ "eslint": "8.28.0", "eslint-config-prettier": "8.5.0", "eslint-config-ssjs": "1.1.11", - "eslint-plugin-jsdoc": "39.6.2", + "eslint-plugin-jsdoc": "39.6.4", "eslint-plugin-mocha": "10.1.0", "eslint-plugin-prettier": "4.2.1", "eslint-plugin-unicorn": "44.0.2", @@ -326,9 +326,9 @@ } }, "node_modules/@es-joy/jsdoccomment": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.36.0.tgz", - "integrity": "sha512-u0XZyvUF6Urb2cSivSXA8qXIpT/CxkHcdtZKoWusAzgzmsTWpg0F2FpWXsolHmMUyVY3dLWaoy+0ccJ5uf2QjA==", + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.36.1.tgz", + "integrity": "sha512-922xqFsTpHs6D0BUiG4toiyPOMc8/jafnWKxz1KWgS4XzKPy2qXf1Pe6UFuNSCQqt6tOuhAWXBNuuyUhJmw9Vg==", "dev": true, "dependencies": { "comment-parser": "1.3.1", @@ -2727,12 +2727,12 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "39.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.6.2.tgz", - "integrity": "sha512-dvgY/W7eUFoAIIiaWHERIMI61ZWqcz9YFjEeyTzdPlrZc3TY/3aZm5aB91NUoTLWYZmO/vFlYSuQi15tF7uE5A==", + "version": "39.6.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.6.4.tgz", + "integrity": "sha512-fskvdLCfwmPjHb6e+xNGDtGgbF8X7cDwMtVLAP2WwSf9Htrx68OAx31BESBM1FAwsN2HTQyYQq7m4aW4Q4Nlag==", "dev": true, "dependencies": { - "@es-joy/jsdoccomment": "~0.36.0", + "@es-joy/jsdoccomment": "~0.36.1", "comment-parser": "1.3.1", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", @@ -8962,9 +8962,9 @@ } }, "@es-joy/jsdoccomment": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.36.0.tgz", - "integrity": "sha512-u0XZyvUF6Urb2cSivSXA8qXIpT/CxkHcdtZKoWusAzgzmsTWpg0F2FpWXsolHmMUyVY3dLWaoy+0ccJ5uf2QjA==", + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.36.1.tgz", + "integrity": "sha512-922xqFsTpHs6D0BUiG4toiyPOMc8/jafnWKxz1KWgS4XzKPy2qXf1Pe6UFuNSCQqt6tOuhAWXBNuuyUhJmw9Vg==", "dev": true, "requires": { "comment-parser": "1.3.1", @@ -10817,12 +10817,12 @@ "requires": {} }, "eslint-plugin-jsdoc": { - "version": "39.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.6.2.tgz", - "integrity": "sha512-dvgY/W7eUFoAIIiaWHERIMI61ZWqcz9YFjEeyTzdPlrZc3TY/3aZm5aB91NUoTLWYZmO/vFlYSuQi15tF7uE5A==", + "version": "39.6.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.6.4.tgz", + "integrity": "sha512-fskvdLCfwmPjHb6e+xNGDtGgbF8X7cDwMtVLAP2WwSf9Htrx68OAx31BESBM1FAwsN2HTQyYQq7m4aW4Q4Nlag==", "dev": true, "requires": { - "@es-joy/jsdoccomment": "~0.36.0", + "@es-joy/jsdoccomment": "~0.36.1", "comment-parser": "1.3.1", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", diff --git a/package.json b/package.json index caa3f9882..4b4448f15 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "eslint": "8.28.0", "eslint-config-prettier": "8.5.0", "eslint-config-ssjs": "1.1.11", - "eslint-plugin-jsdoc": "39.6.2", + "eslint-plugin-jsdoc": "39.6.4", "eslint-plugin-mocha": "10.1.0", "eslint-plugin-prettier": "4.2.1", "eslint-plugin-unicorn": "44.0.2", From 9b94bb306fedf6d13d67fd6857bad53b60b009ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Nov 2022 15:10:06 +0000 Subject: [PATCH 044/103] Bump prettier from 2.7.1 to 2.8.0 Bumps [prettier](https://github.com/prettier/prettier) from 2.7.1 to 2.8.0. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/2.7.1...2.8.0) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:production 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 3107d3221..bf5b5bdad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "json-to-table": "4.2.1", "mustache": "4.2.0", "p-limit": "3.1.0", - "prettier": "2.7.1", + "prettier": "2.8.0", "prettier-plugin-sql": "0.12.1", "semver": "7.3.8", "sfmc-sdk": "0.6.1", @@ -6737,9 +6737,9 @@ } }, "node_modules/prettier": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", - "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.0.tgz", + "integrity": "sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==", "bin": { "prettier": "bin-prettier.js" }, @@ -13732,9 +13732,9 @@ "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==" }, "prettier": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", - "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==" + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.0.tgz", + "integrity": "sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==" }, "prettier-eslint": { "version": "15.0.1", diff --git a/package.json b/package.json index 4b4448f15..3db9d6d8a 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "json-to-table": "4.2.1", "mustache": "4.2.0", "p-limit": "3.1.0", - "prettier": "2.7.1", + "prettier": "2.8.0", "prettier-plugin-sql": "0.12.1", "semver": "7.3.8", "sfmc-sdk": "0.6.1", From 9b6149bf2875e4baf702c6e84ce56d24be1c94eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Nov 2022 15:15:32 +0000 Subject: [PATCH 045/103] Bump jsdoc-to-markdown from 7.1.1 to 8.0.0 Bumps [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown) from 7.1.1 to 8.0.0. - [Release notes](https://github.com/jsdoc2md/jsdoc-to-markdown/releases) - [Commits](https://github.com/jsdoc2md/jsdoc-to-markdown/compare/v7.1.1...v8.0.0) --- updated-dependencies: - dependency-name: jsdoc-to-markdown dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package-lock.json | 270 ++++++++++++++++++++++++---------------------- package.json | 2 +- 2 files changed, 142 insertions(+), 130 deletions(-) diff --git a/package-lock.json b/package-lock.json index bf5b5bdad..c260818d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,7 +44,7 @@ "eslint-plugin-prettier": "4.2.1", "eslint-plugin-unicorn": "44.0.2", "husky": "8.0.1", - "jsdoc-to-markdown": "7.1.1", + "jsdoc-to-markdown": "8.0.0", "lint-staged": "13.0.3", "mocha": "10.1.0", "mock-fs": "5.2.0", @@ -465,6 +465,18 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@jsdoc/salty": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.1.tgz", + "integrity": "sha512-JXwylDNSHa549N9uceDYu8D4GMXwSo3H8CCPYEQqxhhHpxD28+lRl2b3bS/caaPj5w1YD3SWtrficJNTnUjGpg==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v12.0.0" + } + }, "node_modules/@kwsites/file-exists": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", @@ -2406,9 +2418,9 @@ "dev": true }, "node_modules/dmd": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.1.0.tgz", - "integrity": "sha512-0zQIJ873gay1scCTFZvHPWM9mVJBnaylB2NQDI8O9u8O32m00Jb6uxDKexZm8hjTRM7RiWe0FJ32pExHoXdwoQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.2.0.tgz", + "integrity": "sha512-uXWxLF1H7TkUAuoHK59/h/ts5cKavm2LnhrIgJWisip4BVzPoXavlwyoprFFn2CzcahKYgvkfaebS6oxzgflkg==", "dev": true, "dependencies": { "array-back": "^6.2.2", @@ -2416,7 +2428,7 @@ "common-sequence": "^2.0.2", "file-set": "^4.0.2", "handlebars": "^4.7.7", - "marked": "^4.0.12", + "marked": "^4.2.3", "object-get": "^2.1.1", "reduce-flatten": "^3.0.1", "reduce-unique": "^2.0.1", @@ -3423,7 +3435,7 @@ "node_modules/fs-then-native": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fs-then-native/-/fs-then-native-2.0.0.tgz", - "integrity": "sha1-GaEk2U2QwiyOBF8ujdbr6jbUjGc=", + "integrity": "sha512-X712jAOaWXkemQCAmWeg5rOT2i+KOpWz1Z/txk/cW0qlOu2oQ9H61vc5w3X/iyuUEfq/OyaFJ78/cZAQD1/bgA==", "dev": true, "engines": { "node": ">=4.0.0" @@ -4623,38 +4635,38 @@ } }, "node_modules/jsdoc": { - "version": "3.6.10", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.10.tgz", - "integrity": "sha512-IdQ8ppSo5LKZ9o3M+LKIIK8i00DIe5msDvG3G81Km+1dhy0XrOWD0Ji8H61ElgyEj/O9KRLokgKbAM9XX9CJAg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.0.tgz", + "integrity": "sha512-tzTgkklbWKrlaQL2+e3NNgLcZu3NaK2vsHRx7tyHQ+H5jcB9Gx0txSd2eJWlMC/xU1+7LQu4s58Ry0RkuaEQVg==", "dev": true, "dependencies": { "@babel/parser": "^7.9.4", + "@jsdoc/salty": "^0.2.1", "@types/markdown-it": "^12.2.3", "bluebird": "^3.7.2", "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", "js2xmlparser": "^4.0.2", - "klaw": "^4.0.1", + "klaw": "^3.0.0", "markdown-it": "^12.3.2", "markdown-it-anchor": "^8.4.1", "marked": "^4.0.10", "mkdirp": "^1.0.4", "requizzle": "^0.2.3", "strip-json-comments": "^3.1.0", - "taffydb": "2.6.2", "underscore": "~1.13.2" }, "bin": { "jsdoc": "jsdoc.js" }, "engines": { - "node": ">=8.15.0" + "node": ">=12.0.0" } }, "node_modules/jsdoc-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-7.1.1.tgz", - "integrity": "sha512-0pkuPCzVXiqsDAsVrNFXCkHzlyNepBIDVtwwehry4RJAnZmXtlAz7rh8F9FRz53u3NeynGbex+bpYWwi8lE66A==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-8.0.0.tgz", + "integrity": "sha512-Rnhor0suB1Ds1abjmFkFfKeD+kSMRN9oHMTMZoJVUrmtCGDwXty+sWMA9sa4xbe4UyxuPjhC7tavZ40mDKK6QQ==", "dev": true, "dependencies": { "array-back": "^6.2.2", @@ -4662,7 +4674,7 @@ "collect-all": "^1.0.4", "file-set": "^4.0.2", "fs-then-native": "^2.0.0", - "jsdoc": "^3.6.10", + "jsdoc": "^4.0.0", "object-to-spawn-args": "^2.0.1", "temp-path": "^1.0.0", "walk-back": "^5.1.0" @@ -4672,16 +4684,16 @@ } }, "node_modules/jsdoc-parse": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.1.0.tgz", - "integrity": "sha512-n/hDGQJa69IBun1yZAjqzV4gVR41+flZ3bIlm9fKvNe2Xjsd1/+zCo2+R9ls8LxtePgIWbpA1jU7xkB2lRdLLg==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.2.0.tgz", + "integrity": "sha512-Afu1fQBEb7QHt6QWX/6eUWvYHJofB90Fjx7FuJYF7mnG9z5BkAIpms1wsnvYLytfmqpEENHs/fax9p8gvMj7dw==", "dev": true, "dependencies": { "array-back": "^6.2.2", "lodash.omit": "^4.5.0", "lodash.pick": "^4.4.0", "reduce-extract": "^1.0.0", - "sort-array": "^4.1.4", + "sort-array": "^4.1.5", "test-value": "^3.0.0" }, "engines": { @@ -4689,17 +4701,17 @@ } }, "node_modules/jsdoc-to-markdown": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-7.1.1.tgz", - "integrity": "sha512-CI86d63xAVNO+ENumWwmJ034lYe5iGU5GwjtTA11EuphP9tpnoi4hrKgR/J8uME0D+o4KUpVfwX1fjZhc8dEtg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-8.0.0.tgz", + "integrity": "sha512-2FQvYkg491+FP6s15eFlgSSWs69CvQrpbABGYBtvAvGWy/lWo8IKKToarT283w59rQFrpcjHl3YdhHCa3l7gXg==", "dev": true, "dependencies": { "array-back": "^6.2.2", "command-line-tool": "^0.8.0", "config-master": "^3.1.0", - "dmd": "^6.1.0", - "jsdoc-api": "^7.1.1", - "jsdoc-parse": "^6.1.0", + "dmd": "^6.2.0", + "jsdoc-api": "^8.0.0", + "jsdoc-parse": "^6.2.0", "walk-back": "^5.1.0" }, "bin": { @@ -4822,12 +4834,12 @@ } }, "node_modules/klaw": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-4.0.1.tgz", - "integrity": "sha512-pgsE40/SvC7st04AHiISNewaIMUbY5V/K8b21ekiPiFoYs/EYSdsGa+FJArB1d441uq4Q8zZyIxvAzkGNlBdRw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", "dev": true, - "engines": { - "node": ">=14.14.0" + "dependencies": { + "graceful-fs": "^4.1.9" } }, "node_modules/kuler": { @@ -5080,7 +5092,7 @@ "node_modules/lodash.omit": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", - "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==", "dev": true }, "node_modules/lodash.padend": { @@ -5092,7 +5104,7 @@ "node_modules/lodash.pick": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==", "dev": true }, "node_modules/log-symbols": { @@ -5367,9 +5379,9 @@ } }, "node_modules/markdown-it-anchor": { - "version": "8.6.2", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.2.tgz", - "integrity": "sha512-JNaekTlIwwyYGBN3zifZDxgz4bSL8sbEj58fdTZGmPSMMGXBZapFjcZk2I33Jy79c1fvCKHpF7MA/67FOTjvzA==", + "version": "8.6.5", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.5.tgz", + "integrity": "sha512-PI1qEHHkTNWT+X6Ip9w+paonfIQ+QZP9sCeMYi47oqhH+EsW8CrJ8J7CzV19QVOj6il8ATGbK2nTECj22ZHGvQ==", "dev": true, "peerDependencies": { "@types/markdown-it": "*", @@ -5377,9 +5389,9 @@ } }, "node_modules/marked": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.14.tgz", - "integrity": "sha512-HL5sSPE/LP6U9qKgngIIPTthuxC0jrfxpYMZ3LdGDD3vTnLs59m2Z7r6+LNDR3ToqEQdkKd6YaaEfJhodJmijQ==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.3.tgz", + "integrity": "sha512-slWRdJkbTZ+PjkyJnE30Uid64eHwbwa1Q25INCAYfZlK4o6ylagBy/Le9eWntqJFoFT93ikUKMv47GZ4gTwHkw==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -5391,7 +5403,7 @@ "node_modules/mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "dev": true }, "node_modules/memorystream": { @@ -7150,7 +7162,7 @@ "node_modules/reduce-extract": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/reduce-extract/-/reduce-extract-1.0.0.tgz", - "integrity": "sha1-Z/I4W+2mUGG19fQxJmLosIDKFSU=", + "integrity": "sha512-QF8vjWx3wnRSL5uFMyCjDeDc5EBMiryoT9tz94VvgjKfzecHAVnqmXAwQDcr7X4JmLc2cjkjFGCVzhMqDjgR9g==", "dev": true, "dependencies": { "test-value": "^1.0.1" @@ -7162,7 +7174,7 @@ "node_modules/reduce-extract/node_modules/array-back": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, "dependencies": { "typical": "^2.6.0" @@ -7174,7 +7186,7 @@ "node_modules/reduce-extract/node_modules/test-value": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/test-value/-/test-value-1.1.0.tgz", - "integrity": "sha1-oJE29y7AQ9J8iTcHwrFZv6196T8=", + "integrity": "sha512-wrsbRo7qP+2Je8x8DsK8ovCGyxe3sYfQwOraIY/09A2gFXU9DYKiTF14W4ki/01AEh56kMzAmlj9CaHGDDUBJA==", "dev": true, "dependencies": { "array-back": "^1.0.2", @@ -7205,7 +7217,7 @@ "node_modules/reduce-without": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/reduce-without/-/reduce-without-1.0.1.tgz", - "integrity": "sha1-aK0OrRGFXJo31OglbBW7+Hly/Iw=", + "integrity": "sha512-zQv5y/cf85sxvdrKPlfcRzlDn/OqKFThNimYmsS3flmkioKvkUGn2Qg9cJVoQiEvdxFGLE0MQER/9fZ9sUqdxg==", "dev": true, "dependencies": { "test-value": "^2.0.0" @@ -7217,7 +7229,7 @@ "node_modules/reduce-without/node_modules/array-back": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, "dependencies": { "typical": "^2.6.0" @@ -7229,7 +7241,7 @@ "node_modules/reduce-without/node_modules/test-value": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz", - "integrity": "sha1-Edpv9nDzRxpztiXKTz/c97t0gpE=", + "integrity": "sha512-+1epbAxtKeXttkGFMTX9H42oqzOTufR1ceCF+GYA5aOmvaPq9wd4PUS8329fn2RRLGNeUkgRLnVpycjx8DsO2w==", "dev": true, "dependencies": { "array-back": "^1.0.3", @@ -7327,12 +7339,12 @@ "dev": true }, "node_modules/requizzle": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", - "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", "dev": true, "dependencies": { - "lodash": "^4.17.14" + "lodash": "^4.17.21" } }, "node_modules/resolve": { @@ -7822,7 +7834,7 @@ "node_modules/stream-connect": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-connect/-/stream-connect-1.0.2.tgz", - "integrity": "sha1-GLyB8u2zW4tdmoAJIAqYUxRCipc=", + "integrity": "sha512-68Kl+79cE0RGKemKkhxTSg8+6AGrqBt+cbZAXevg2iJ6Y3zX4JhA/sZeGzLpxW9cXhmqAcE7KnJCisUmIUfnFQ==", "dev": true, "dependencies": { "array-back": "^1.0.2" @@ -7834,7 +7846,7 @@ "node_modules/stream-connect/node_modules/array-back": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, "dependencies": { "typical": "^2.6.0" @@ -8053,16 +8065,10 @@ "node": ">=4" } }, - "node_modules/taffydb": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", - "dev": true - }, "node_modules/temp-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-path/-/temp-path-1.0.0.tgz", - "integrity": "sha1-JLFUOXOrRCiW2a02fdnL2/r+kYs=", + "integrity": "sha512-TvmyH7kC6ZVTYkqCODjJIbgvu0FKiwQpZ4D1aknE7xpcDf/qEOB8KZEK5ef2pfbVoiBhNWs3yx4y+ESMtNYmlg==", "dev": true }, "node_modules/test-value": { @@ -8268,9 +8274,9 @@ "dev": true }, "node_modules/uglify-js": { - "version": "3.15.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.4.tgz", - "integrity": "sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA==", + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", "dev": true, "optional": true, "bin": { @@ -8296,9 +8302,9 @@ } }, "node_modules/underscore": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.3.tgz", - "integrity": "sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA==", + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", "dev": true }, "node_modules/unique-string": { @@ -8569,7 +8575,7 @@ "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, "node_modules/wordwrapjs": { @@ -9071,6 +9077,15 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "@jsdoc/salty": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.1.tgz", + "integrity": "sha512-JXwylDNSHa549N9uceDYu8D4GMXwSo3H8CCPYEQqxhhHpxD28+lRl2b3bS/caaPj5w1YD3SWtrficJNTnUjGpg==", + "dev": true, + "requires": { + "lodash": "^4.17.21" + } + }, "@kwsites/file-exists": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", @@ -10546,9 +10561,9 @@ "dev": true }, "dmd": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.1.0.tgz", - "integrity": "sha512-0zQIJ873gay1scCTFZvHPWM9mVJBnaylB2NQDI8O9u8O32m00Jb6uxDKexZm8hjTRM7RiWe0FJ32pExHoXdwoQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.2.0.tgz", + "integrity": "sha512-uXWxLF1H7TkUAuoHK59/h/ts5cKavm2LnhrIgJWisip4BVzPoXavlwyoprFFn2CzcahKYgvkfaebS6oxzgflkg==", "dev": true, "requires": { "array-back": "^6.2.2", @@ -10556,7 +10571,7 @@ "common-sequence": "^2.0.2", "file-set": "^4.0.2", "handlebars": "^4.7.7", - "marked": "^4.0.12", + "marked": "^4.2.3", "object-get": "^2.1.1", "reduce-flatten": "^3.0.1", "reduce-unique": "^2.0.1", @@ -11304,7 +11319,7 @@ "fs-then-native": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fs-then-native/-/fs-then-native-2.0.0.tgz", - "integrity": "sha1-GaEk2U2QwiyOBF8ujdbr6jbUjGc=", + "integrity": "sha512-X712jAOaWXkemQCAmWeg5rOT2i+KOpWz1Z/txk/cW0qlOu2oQ9H61vc5w3X/iyuUEfq/OyaFJ78/cZAQD1/bgA==", "dev": true }, "fs.realpath": { @@ -12135,25 +12150,25 @@ } }, "jsdoc": { - "version": "3.6.10", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.10.tgz", - "integrity": "sha512-IdQ8ppSo5LKZ9o3M+LKIIK8i00DIe5msDvG3G81Km+1dhy0XrOWD0Ji8H61ElgyEj/O9KRLokgKbAM9XX9CJAg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.0.tgz", + "integrity": "sha512-tzTgkklbWKrlaQL2+e3NNgLcZu3NaK2vsHRx7tyHQ+H5jcB9Gx0txSd2eJWlMC/xU1+7LQu4s58Ry0RkuaEQVg==", "dev": true, "requires": { "@babel/parser": "^7.9.4", + "@jsdoc/salty": "^0.2.1", "@types/markdown-it": "^12.2.3", "bluebird": "^3.7.2", "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", "js2xmlparser": "^4.0.2", - "klaw": "^4.0.1", + "klaw": "^3.0.0", "markdown-it": "^12.3.2", "markdown-it-anchor": "^8.4.1", "marked": "^4.0.10", "mkdirp": "^1.0.4", "requizzle": "^0.2.3", "strip-json-comments": "^3.1.0", - "taffydb": "2.6.2", "underscore": "~1.13.2" }, "dependencies": { @@ -12166,9 +12181,9 @@ } }, "jsdoc-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-7.1.1.tgz", - "integrity": "sha512-0pkuPCzVXiqsDAsVrNFXCkHzlyNepBIDVtwwehry4RJAnZmXtlAz7rh8F9FRz53u3NeynGbex+bpYWwi8lE66A==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-8.0.0.tgz", + "integrity": "sha512-Rnhor0suB1Ds1abjmFkFfKeD+kSMRN9oHMTMZoJVUrmtCGDwXty+sWMA9sa4xbe4UyxuPjhC7tavZ40mDKK6QQ==", "dev": true, "requires": { "array-back": "^6.2.2", @@ -12176,38 +12191,38 @@ "collect-all": "^1.0.4", "file-set": "^4.0.2", "fs-then-native": "^2.0.0", - "jsdoc": "^3.6.10", + "jsdoc": "^4.0.0", "object-to-spawn-args": "^2.0.1", "temp-path": "^1.0.0", "walk-back": "^5.1.0" } }, "jsdoc-parse": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.1.0.tgz", - "integrity": "sha512-n/hDGQJa69IBun1yZAjqzV4gVR41+flZ3bIlm9fKvNe2Xjsd1/+zCo2+R9ls8LxtePgIWbpA1jU7xkB2lRdLLg==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.2.0.tgz", + "integrity": "sha512-Afu1fQBEb7QHt6QWX/6eUWvYHJofB90Fjx7FuJYF7mnG9z5BkAIpms1wsnvYLytfmqpEENHs/fax9p8gvMj7dw==", "dev": true, "requires": { "array-back": "^6.2.2", "lodash.omit": "^4.5.0", "lodash.pick": "^4.4.0", "reduce-extract": "^1.0.0", - "sort-array": "^4.1.4", + "sort-array": "^4.1.5", "test-value": "^3.0.0" } }, "jsdoc-to-markdown": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-7.1.1.tgz", - "integrity": "sha512-CI86d63xAVNO+ENumWwmJ034lYe5iGU5GwjtTA11EuphP9tpnoi4hrKgR/J8uME0D+o4KUpVfwX1fjZhc8dEtg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-8.0.0.tgz", + "integrity": "sha512-2FQvYkg491+FP6s15eFlgSSWs69CvQrpbABGYBtvAvGWy/lWo8IKKToarT283w59rQFrpcjHl3YdhHCa3l7gXg==", "dev": true, "requires": { "array-back": "^6.2.2", "command-line-tool": "^0.8.0", "config-master": "^3.1.0", - "dmd": "^6.1.0", - "jsdoc-api": "^7.1.1", - "jsdoc-parse": "^6.1.0", + "dmd": "^6.2.0", + "jsdoc-api": "^8.0.0", + "jsdoc-parse": "^6.2.0", "walk-back": "^5.1.0" } }, @@ -12295,10 +12310,13 @@ "dev": true }, "klaw": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-4.0.1.tgz", - "integrity": "sha512-pgsE40/SvC7st04AHiISNewaIMUbY5V/K8b21ekiPiFoYs/EYSdsGa+FJArB1d441uq4Q8zZyIxvAzkGNlBdRw==", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.9" + } }, "kuler": { "version": "2.0.0", @@ -12499,7 +12517,7 @@ "lodash.omit": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", - "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==", "dev": true }, "lodash.padend": { @@ -12511,7 +12529,7 @@ "lodash.pick": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==", "dev": true }, "log-symbols": { @@ -12716,22 +12734,22 @@ } }, "markdown-it-anchor": { - "version": "8.6.2", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.2.tgz", - "integrity": "sha512-JNaekTlIwwyYGBN3zifZDxgz4bSL8sbEj58fdTZGmPSMMGXBZapFjcZk2I33Jy79c1fvCKHpF7MA/67FOTjvzA==", + "version": "8.6.5", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.5.tgz", + "integrity": "sha512-PI1qEHHkTNWT+X6Ip9w+paonfIQ+QZP9sCeMYi47oqhH+EsW8CrJ8J7CzV19QVOj6il8ATGbK2nTECj22ZHGvQ==", "dev": true, "requires": {} }, "marked": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.14.tgz", - "integrity": "sha512-HL5sSPE/LP6U9qKgngIIPTthuxC0jrfxpYMZ3LdGDD3vTnLs59m2Z7r6+LNDR3ToqEQdkKd6YaaEfJhodJmijQ==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.3.tgz", + "integrity": "sha512-slWRdJkbTZ+PjkyJnE30Uid64eHwbwa1Q25INCAYfZlK4o6ylagBy/Le9eWntqJFoFT93ikUKMv47GZ4gTwHkw==", "dev": true }, "mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "dev": true }, "memorystream": { @@ -14046,7 +14064,7 @@ "reduce-extract": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/reduce-extract/-/reduce-extract-1.0.0.tgz", - "integrity": "sha1-Z/I4W+2mUGG19fQxJmLosIDKFSU=", + "integrity": "sha512-QF8vjWx3wnRSL5uFMyCjDeDc5EBMiryoT9tz94VvgjKfzecHAVnqmXAwQDcr7X4JmLc2cjkjFGCVzhMqDjgR9g==", "dev": true, "requires": { "test-value": "^1.0.1" @@ -14055,7 +14073,7 @@ "array-back": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, "requires": { "typical": "^2.6.0" @@ -14064,7 +14082,7 @@ "test-value": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/test-value/-/test-value-1.1.0.tgz", - "integrity": "sha1-oJE29y7AQ9J8iTcHwrFZv6196T8=", + "integrity": "sha512-wrsbRo7qP+2Je8x8DsK8ovCGyxe3sYfQwOraIY/09A2gFXU9DYKiTF14W4ki/01AEh56kMzAmlj9CaHGDDUBJA==", "dev": true, "requires": { "array-back": "^1.0.2", @@ -14088,7 +14106,7 @@ "reduce-without": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/reduce-without/-/reduce-without-1.0.1.tgz", - "integrity": "sha1-aK0OrRGFXJo31OglbBW7+Hly/Iw=", + "integrity": "sha512-zQv5y/cf85sxvdrKPlfcRzlDn/OqKFThNimYmsS3flmkioKvkUGn2Qg9cJVoQiEvdxFGLE0MQER/9fZ9sUqdxg==", "dev": true, "requires": { "test-value": "^2.0.0" @@ -14097,7 +14115,7 @@ "array-back": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, "requires": { "typical": "^2.6.0" @@ -14106,7 +14124,7 @@ "test-value": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz", - "integrity": "sha1-Edpv9nDzRxpztiXKTz/c97t0gpE=", + "integrity": "sha512-+1epbAxtKeXttkGFMTX9H42oqzOTufR1ceCF+GYA5aOmvaPq9wd4PUS8329fn2RRLGNeUkgRLnVpycjx8DsO2w==", "dev": true, "requires": { "array-back": "^1.0.3", @@ -14176,12 +14194,12 @@ "dev": true }, "requizzle": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", - "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", "dev": true, "requires": { - "lodash": "^4.17.14" + "lodash": "^4.17.21" } }, "resolve": { @@ -14546,7 +14564,7 @@ "stream-connect": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-connect/-/stream-connect-1.0.2.tgz", - "integrity": "sha1-GLyB8u2zW4tdmoAJIAqYUxRCipc=", + "integrity": "sha512-68Kl+79cE0RGKemKkhxTSg8+6AGrqBt+cbZAXevg2iJ6Y3zX4JhA/sZeGzLpxW9cXhmqAcE7KnJCisUmIUfnFQ==", "dev": true, "requires": { "array-back": "^1.0.2" @@ -14555,7 +14573,7 @@ "array-back": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", "dev": true, "requires": { "typical": "^2.6.0" @@ -14716,16 +14734,10 @@ } } }, - "taffydb": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", - "dev": true - }, "temp-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-path/-/temp-path-1.0.0.tgz", - "integrity": "sha1-JLFUOXOrRCiW2a02fdnL2/r+kYs=", + "integrity": "sha512-TvmyH7kC6ZVTYkqCODjJIbgvu0FKiwQpZ4D1aknE7xpcDf/qEOB8KZEK5ef2pfbVoiBhNWs3yx4y+ESMtNYmlg==", "dev": true }, "test-value": { @@ -14889,9 +14901,9 @@ "dev": true }, "uglify-js": { - "version": "3.15.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.4.tgz", - "integrity": "sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA==", + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", "dev": true, "optional": true }, @@ -14908,9 +14920,9 @@ } }, "underscore": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.3.tgz", - "integrity": "sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA==", + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", "dev": true }, "unique-string": { @@ -15121,7 +15133,7 @@ "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, "wordwrapjs": { diff --git a/package.json b/package.json index 3db9d6d8a..3e0e9af2a 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "eslint-plugin-prettier": "4.2.1", "eslint-plugin-unicorn": "44.0.2", "husky": "8.0.1", - "jsdoc-to-markdown": "7.1.1", + "jsdoc-to-markdown": "8.0.0", "lint-staged": "13.0.3", "mocha": "10.1.0", "mock-fs": "5.2.0", From 8cdd35fc860c9d360d0dd572db9abde5d23b0de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 29 Nov 2022 16:19:40 +0100 Subject: [PATCH 046/103] #556: code comments --- lib/metadataTypes/TransactionalMessage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/metadataTypes/TransactionalMessage.js b/lib/metadataTypes/TransactionalMessage.js index 67399324d..e17849e3d 100644 --- a/lib/metadataTypes/TransactionalMessage.js +++ b/lib/metadataTypes/TransactionalMessage.js @@ -10,6 +10,7 @@ const Util = require('../util/util'); * @augments MetadataType */ class TransactionalMessage extends MetadataType { + // define this.subType as string here for intellisense; requires to be redefined in child class static subType = ''; /** * Retrieves Metadata of Mobile Keywords From 05eec8e7155748328a93db91aa474767289bd259 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Nov 2022 15:21:06 +0000 Subject: [PATCH 047/103] Bump eslint-plugin-unicorn from 44.0.2 to 45.0.1 Bumps [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn) from 44.0.2 to 45.0.1. - [Release notes](https://github.com/sindresorhus/eslint-plugin-unicorn/releases) - [Commits](https://github.com/sindresorhus/eslint-plugin-unicorn/compare/v44.0.2...v45.0.1) --- updated-dependencies: - dependency-name: eslint-plugin-unicorn dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package-lock.json | 129 ++++++++++++++++++++++++++++++++++++++-------- package.json | 2 +- 2 files changed, 109 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index c260818d2..4d5cd91e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,7 +42,7 @@ "eslint-plugin-jsdoc": "39.6.4", "eslint-plugin-mocha": "10.1.0", "eslint-plugin-prettier": "4.2.1", - "eslint-plugin-unicorn": "44.0.2", + "eslint-plugin-unicorn": "45.0.1", "husky": "8.0.1", "jsdoc-to-markdown": "8.0.0", "lint-staged": "13.0.3", @@ -339,6 +339,21 @@ "node": "^14 || ^16 || ^17 || ^18 || ^19" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.1.2.tgz", + "integrity": "sha512-7qELuQWWjVDdVsFQ5+beUl+KPczrEDA7S3zM4QUd/bJl7oXgsmpXaEVqrRTnOBqenOV4rWf2kVZk2Ot085zPWA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/@eslint/eslintrc": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", @@ -2797,24 +2812,26 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "44.0.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-44.0.2.tgz", - "integrity": "sha512-GLIDX1wmeEqpGaKcnMcqRvMVsoabeF0Ton0EX4Th5u6Kmf7RM9WBl705AXFEsns56ESkEs0uyelLuUTvz9Tr0w==", + "version": "45.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-45.0.1.tgz", + "integrity": "sha512-tLnIw5oDJJc3ILYtlKtqOxPP64FZLTkZkgeuoN6e7x6zw+rhBjOxyvq2c7577LGxXuIhBYrwisZuKNqOOHp3BA==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.19.1", - "ci-info": "^3.4.0", + "@eslint-community/eslint-utils": "^4.1.0", + "ci-info": "^3.6.1", "clean-regexp": "^1.0.0", - "eslint-utils": "^3.0.0", "esquery": "^1.4.0", "indent-string": "^4.0.0", "is-builtin-module": "^3.2.0", + "jsesc": "^3.0.2", "lodash": "^4.17.21", "pluralize": "^8.0.0", "read-pkg-up": "^7.0.1", "regexp-tree": "^0.1.24", + "regjsparser": "^0.9.1", "safe-regex": "^2.1.1", - "semver": "^7.3.7", + "semver": "^7.3.8", "strip-indent": "^3.0.0" }, "engines": { @@ -2824,14 +2841,29 @@ "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" }, "peerDependencies": { - "eslint": ">=8.23.1" + "eslint": ">=8.28.0" } }, "node_modules/eslint-plugin-unicorn/node_modules/ci-info": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", - "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==", - "dev": true + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz", + "integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } }, "node_modules/eslint-scope": { "version": "7.1.1", @@ -7310,6 +7342,27 @@ "node": ">=8" } }, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -8978,6 +9031,15 @@ "jsdoc-type-pratt-parser": "~3.1.0" } }, + "@eslint-community/eslint-utils": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.1.2.tgz", + "integrity": "sha512-7qELuQWWjVDdVsFQ5+beUl+KPczrEDA7S3zM4QUd/bJl7oXgsmpXaEVqrRTnOBqenOV4rWf2kVZk2Ot085zPWA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, "@eslint/eslintrc": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", @@ -10866,31 +10928,39 @@ } }, "eslint-plugin-unicorn": { - "version": "44.0.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-44.0.2.tgz", - "integrity": "sha512-GLIDX1wmeEqpGaKcnMcqRvMVsoabeF0Ton0EX4Th5u6Kmf7RM9WBl705AXFEsns56ESkEs0uyelLuUTvz9Tr0w==", + "version": "45.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-45.0.1.tgz", + "integrity": "sha512-tLnIw5oDJJc3ILYtlKtqOxPP64FZLTkZkgeuoN6e7x6zw+rhBjOxyvq2c7577LGxXuIhBYrwisZuKNqOOHp3BA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.19.1", - "ci-info": "^3.4.0", + "@eslint-community/eslint-utils": "^4.1.0", + "ci-info": "^3.6.1", "clean-regexp": "^1.0.0", - "eslint-utils": "^3.0.0", "esquery": "^1.4.0", "indent-string": "^4.0.0", "is-builtin-module": "^3.2.0", + "jsesc": "^3.0.2", "lodash": "^4.17.21", "pluralize": "^8.0.0", "read-pkg-up": "^7.0.1", "regexp-tree": "^0.1.24", + "regjsparser": "^0.9.1", "safe-regex": "^2.1.1", - "semver": "^7.3.7", + "semver": "^7.3.8", "strip-indent": "^3.0.0" }, "dependencies": { "ci-info": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", - "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz", + "integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==", + "dev": true + }, + "jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true } } @@ -14171,6 +14241,23 @@ "rc": "^1.2.8" } }, + "regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true + } + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", diff --git a/package.json b/package.json index 3e0e9af2a..2412b7784 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "eslint-plugin-jsdoc": "39.6.4", "eslint-plugin-mocha": "10.1.0", "eslint-plugin-prettier": "4.2.1", - "eslint-plugin-unicorn": "44.0.2", + "eslint-plugin-unicorn": "45.0.1", "husky": "8.0.1", "jsdoc-to-markdown": "8.0.0", "lint-staged": "13.0.3", From aa0d78b004fb4d2a8cfadc9171862a3cff89722b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 29 Nov 2022 16:48:53 +0100 Subject: [PATCH 048/103] #0 auto-apply new eslint rule unicorn/no-negated-condition --- lib/Deployer.js | 8 ++--- lib/Retriever.js | 8 ++--- lib/cli.js | 1 + lib/index.js | 10 +++---- lib/metadataTypes/Automation.js | 8 ++--- lib/metadataTypes/DataExtension.js | 14 ++++----- lib/metadataTypes/DataExtensionField.js | 6 ++-- lib/metadataTypes/Discovery.js | 10 +++---- lib/metadataTypes/MetadataType.js | 26 ++++++++--------- lib/util/businessUnit.js | 2 +- lib/util/cli.js | 39 ++++++++++++------------- lib/util/config.js | 20 ++++++------- lib/util/devops.js | 8 ++--- lib/util/init.config.js | 14 ++++----- lib/util/init.git.js | 6 ++-- lib/util/util.js | 14 ++++----- 16 files changed, 97 insertions(+), 97 deletions(-) diff --git a/lib/Deployer.js b/lib/Deployer.js index 3ffe20267..471c68391 100644 --- a/lib/Deployer.js +++ b/lib/Deployer.js @@ -116,15 +116,15 @@ class Deployer { ) { const buObject = await Cli.getCredentialObject( properties, - cred !== null ? cred + '/' + bu : null, + cred === null ? null : cred + '/' + bu, null, true ); - if (buObject !== null) { + if (buObject === null) { + return; + } else { cred = buObject.credential; bu = buObject.businessUnit; - } else { - return; } } diff --git a/lib/Retriever.js b/lib/Retriever.js index 79e0799a3..44ea220b4 100644 --- a/lib/Retriever.js +++ b/lib/Retriever.js @@ -113,11 +113,11 @@ class Retriever { } Util.logger.info( `Retrieving: ${metadataType}` + - (typeKeyMap[metadataType][0] !== null - ? ` ${color.dim}(Keys: ${typeKeyMap[metadataType].join(', ')})${ + (typeKeyMap[metadataType][0] === null + ? '' + : ` ${color.dim}(Keys: ${typeKeyMap[metadataType].join(', ')})${ color.reset - }` - : '') + }`) ); result = await (changelogOnly ? MetadataTypeInfo[type].retrieveChangelog(this.buObject, null, subType) diff --git a/lib/cli.js b/lib/cli.js index 0b538b098..2c6fcc474 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -392,6 +392,7 @@ yargs * @returns {string[]} values split into an array. */ function csvToArray(csv) { + // eslint-disable-next-line unicorn/no-negated-condition return !csv ? null : csv.includes(',') diff --git a/lib/index.js b/lib/index.js index 2736816d0..d115fbe32 100644 --- a/lib/index.js +++ b/lib/index.js @@ -151,15 +151,15 @@ class Mcdev { ) { const buObject = await Cli.getCredentialObject( properties, - cred !== null ? cred + '/' + bu : null, + cred === null ? null : cred + '/' + bu, null, true ); - if (buObject !== null) { + if (buObject === null) { + return; + } else { cred = buObject.credential; bu = buObject.businessUnit; - } else { - return; } } @@ -206,7 +206,7 @@ class Mcdev { } const buObject = await Cli.getCredentialObject( properties, - cred !== null ? cred + '/' + bu : null, + cred === null ? null : cred + '/' + bu, null, true ); diff --git a/lib/metadataTypes/Automation.js b/lib/metadataTypes/Automation.js index c352cb2c1..33ff94020 100644 --- a/lib/metadataTypes/Automation.js +++ b/lib/metadataTypes/Automation.js @@ -937,10 +937,7 @@ class Automation extends MetadataType { * @returns {string[]} list of all files that need to be committed in a flat array ['path/file1.ext', 'path/file2.ext'] */ static getFilesToCommit(keyArr) { - if (!this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)) { - // document automation is not active upon retrieve, run default method instead - return super.getFilesToCommit(keyArr); - } else { + if (this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)) { // document automation is active. assume we want to commit the MD file as well const path = File.normalizePath([ this.properties.directories.retrieve, @@ -954,6 +951,9 @@ class Automation extends MetadataType { File.normalizePath([path, `${key}.${this.definition.type}-doc.md`]), ]); return fileList; + } else { + // document automation is not active upon retrieve, run default method instead + return super.getFilesToCommit(keyArr); } } } diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index 567962a05..238cb91c5 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -1025,10 +1025,10 @@ class DataExtension extends MetadataType { const { metadata } = await super.retrieveSOAP(null, null, options, additionalFields); for (const key in metadata) { // some system data extensions do not have CategoryID which throws errors in other places. These do not need to be parsed - if (!metadata[key].CategoryID) { - delete metadata[key]; - } else { + if (metadata[key].CategoryID) { metadata[key].Fields = []; + } else { + delete metadata[key]; } } return metadata; @@ -1041,10 +1041,7 @@ class DataExtension extends MetadataType { * @returns {string[]} list of all files that need to be committed in a flat array ['path/file1.ext', 'path/file2.ext'] */ static getFilesToCommit(keyArr) { - if (!this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)) { - // document dataExtension is not active upon retrieve, run default method instead - return super.getFilesToCommit(keyArr); - } else { + if (this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)) { // document dataExtension is active. assume we want to commit the MD file as well const path = File.normalizePath([ this.properties.directories.retrieve, @@ -1058,6 +1055,9 @@ class DataExtension extends MetadataType { File.normalizePath([path, `${key}.${this.definition.type}-doc.md`]), ]); return fileList; + } else { + // document dataExtension is not active upon retrieve, run default method instead + return super.getFilesToCommit(keyArr); } } } diff --git a/lib/metadataTypes/DataExtensionField.js b/lib/metadataTypes/DataExtensionField.js index 8a902be0d..f33f784c0 100644 --- a/lib/metadataTypes/DataExtensionField.js +++ b/lib/metadataTypes/DataExtensionField.js @@ -277,15 +277,15 @@ class DataExtensionField extends MetadataType { this.postDeleteTasks(buObject, customerKey); return true; } catch (ex) { - if (!handleOutside) { + if (handleOutside) { + throw ex; + } else { const errorMsg = ex.results?.length ? `${ex.results[0].StatusMessage} (Code ${ex.results[0].ErrorCode})` : ex.message; Util.logger.error( `- error deleting ${this.definition.type} '${customerKey}': ${errorMsg}` ); - } else { - throw ex; } return false; diff --git a/lib/metadataTypes/Discovery.js b/lib/metadataTypes/Discovery.js index 4d7de52b5..d737ecdd0 100644 --- a/lib/metadataTypes/Discovery.js +++ b/lib/metadataTypes/Discovery.js @@ -25,11 +25,7 @@ class Discovery extends MetadataType { if (key) { Util.logger.error('Discovery.retrieve() does not support key parameter'); } - if (buObject.eid !== buObject.mid) { - // don't run for BUs other than Parent BU - Util.logger.warn(' - Skipping Discovery retrieval on non-parent BU'); - return; - } else { + if (buObject.eid === buObject.mid) { const res = await this.client.rest.getCollection( Object.keys(this.definition.endPointMapping).map( (endpoint) => this.definition.endPointMapping[endpoint] @@ -44,6 +40,10 @@ class Discovery extends MetadataType { await super.saveResults(metadataStructure, retrieveDir, null); Util.logger.info('Downloaded: ' + this.definition.type); return { metadata: metadataStructure, type: this.definition.type }; + } else { + // don't run for BUs other than Parent BU + Util.logger.warn(' - Skipping Discovery retrieval on non-parent BU'); + return; } } } diff --git a/lib/metadataTypes/MetadataType.js b/lib/metadataTypes/MetadataType.js index 3c7a429e1..47ac047e8 100644 --- a/lib/metadataTypes/MetadataType.js +++ b/lib/metadataTypes/MetadataType.js @@ -613,7 +613,9 @@ class MetadataType { } return response; } catch (ex) { - if (!handleOutside) { + if (handleOutside) { + throw ex; + } else { const errorMsg = ex.results && ex.results.length ? `${ex.results[0].StatusMessage} (Code ${ex.results[0].ErrorCode})` @@ -623,8 +625,6 @@ class MetadataType { metadataEntry[this.definition.keyField] }': ${errorMsg}` ); - } else { - throw ex; } return {}; @@ -692,7 +692,9 @@ class MetadataType { } return response; } catch (ex) { - if (!handleOutside) { + if (handleOutside) { + throw ex; + } else { const errorMsg = ex?.json?.Results.length ? `${ex.json.Results[0].StatusMessage} (Code ${ex.json.Results[0].ErrorCode})` : ex.message; @@ -701,8 +703,6 @@ class MetadataType { metadataEntry[this.definition.keyField] }': ${errorMsg}` ); - } else { - throw ex; } return {}; @@ -785,9 +785,9 @@ class MetadataType { `Downloaded: ${overrideType || this.definition.type} (${ Object.keys(savedMetadata).length })` + - (singleRetrieve !== null - ? ` ${color.dim}(Key: ${singleRetrieve})${color.reset}` - : '') + (singleRetrieve === null + ? '' + : ` ${color.dim}(Key: ${singleRetrieve})${color.reset}`) ); } @@ -1454,7 +1454,7 @@ class MetadataType { try { // write to file - const targetDirArr = !Array.isArray(targetDir) ? [targetDir] : targetDir; + const targetDirArr = Array.isArray(targetDir) ? targetDir : [targetDir]; for (const targetDir of targetDirArr) { await File.writeJSONToFile( @@ -1580,15 +1580,15 @@ class MetadataType { return true; } catch (ex) { - if (!handleOutside) { + if (handleOutside) { + throw ex; + } else { const errorMsg = ex?.results?.length ? `${ex.results[0].StatusMessage} (Code ${ex.results[0].ErrorCode})` : ex.message; Util.logger.error( `- error deleting ${this.definition.type} '${customerKey}': ${errorMsg}` ); - } else { - throw ex; } return false; diff --git a/lib/util/businessUnit.js b/lib/util/businessUnit.js index befed3e25..a3cb3937a 100644 --- a/lib/util/businessUnit.js +++ b/lib/util/businessUnit.js @@ -68,7 +68,7 @@ const BusinessUnit = { myBuList[equalizedName] = element.ID; Util.logger.info( ` - ${element.Name} ${ - element.Name !== equalizedName ? `(${equalizedName})` : '' + element.Name === equalizedName ? '' : `(${equalizedName})` }` ); } diff --git a/lib/util/cli.js b/lib/util/cli.js index ff620f728..2cb508c68 100644 --- a/lib/util/cli.js +++ b/lib/util/cli.js @@ -37,10 +37,7 @@ const Cli = { */ async addExtraCredential(properties) { const skipInteraction = Util.skipInteraction; - if (!(await config.checkProperties(properties))) { - // return null here to avoid seeing 2 error messages for the same issue - return null; - } else { + if (await config.checkProperties(properties)) { this.logExistingCredentials(properties); Util.logger.info('\nPlease enter your new credentials'); if (skipInteraction && properties.credentials[skipInteraction.credentialName]) { @@ -50,6 +47,9 @@ const Cli = { return null; } return this._setCredential(properties, null); + } else { + // return null here to avoid seeing 2 error messages for the same issue + return null; } }, /** @@ -357,11 +357,11 @@ const Cli = { validate: (value) => { if (!value || value.trim().length < 10) { return 'Please enter a valid tenant identifier'; - } else if (!tenantRegex.test(value.trim())) { - return `Please copy the URI directly from the installed package's "API Integration" section. It looks like this: https://a1b2b3xy56z.auth.marketingcloudapis.com/`; - } else { + } else if (tenantRegex.test(value.trim())) { // all good return true; + } else { + return `Please copy the URI directly from the installed package's "API Integration" section. It looks like this: https://a1b2b3xy56z.auth.marketingcloudapis.com/`; } }, }, @@ -393,15 +393,19 @@ const Cli = { */ async selectTypes(properties, setTypesArr) { let responses; - if (!setTypesArr) { - if (Util.logger.level !== 'debug') { - Util.logger.info( - 'Run mcdev selectTypes --debug if you need to use "disabled" types.' - ); - } else { + if (setTypesArr) { + responses = { + selectedTypes: setTypesArr, + }; + } else { + if (Util.logger.level === 'debug') { Util.logger.warn( 'Debug mode enabled. Allowing selection of "disabled" types. Please be aware that these might be unstable.' ); + } else { + Util.logger.info( + 'Run mcdev selectTypes --debug if you need to use "disabled" types.' + ); } const flattenedDefinitions = []; for (const el in MetadataDefinitions) { @@ -436,9 +440,8 @@ const Cli = { ? ' \x1B[1;30;40m(non-default)\u001B[0m' : ''), value: def.type, - disabled: !(Util.logger.level === 'debug' || def.typeRetrieveByDefault) - ? 'disabled' - : false, + disabled: + Util.logger.level === 'debug' || def.typeRetrieveByDefault ? false : 'disabled', // subtypes can be activated through their main type checked: properties.metaDataTypes.retrieve.includes(def.type) || @@ -475,10 +478,6 @@ const Cli = { choices: typeChoices, }, ]); - } else { - responses = { - selectedTypes: setTypesArr, - }; } if (responses.selectedTypes) { diff --git a/lib/util/config.js b/lib/util/config.js index 6f0f2afa5..de2bda103 100644 --- a/lib/util/config.js +++ b/lib/util/config.js @@ -128,17 +128,9 @@ const config = { const missingFields = []; for (const key in defaultProps) { if (Object.prototype.hasOwnProperty.call(defaultProps, key)) { - if (!Object.prototype.hasOwnProperty.call(properties, key)) { - errorMsgs.push(`${key}{} missing`); - solutionSet.add( - `Run 'mcdev upgrade' to fix missing or changed configuration options` - ); - missingFields.push(key); - } else { + if (Object.prototype.hasOwnProperty.call(properties, key)) { if (!silent && key === 'credentials') { - if (!Object.keys(properties.credentials)) { - errorMsgs.push(`no Credential defined`); - } else { + if (Object.keys(properties.credentials)) { for (const cred in properties.credentials) { if (cred.includes('/') || cred.includes('\\')) { errorMsgs.push( @@ -176,6 +168,8 @@ const config = { solutionSet.add(`Run 'mcdev reloadBUs ${cred}'`); } } + } else { + errorMsgs.push(`no Credential defined`); } } else if (['directories', 'metaDataTypes', 'options'].includes(key)) { for (const subkey in defaultProps[key]) { @@ -222,6 +216,12 @@ const config = { } } } + } else { + errorMsgs.push(`${key}{} missing`); + solutionSet.add( + `Run 'mcdev upgrade' to fix missing or changed configuration options` + ); + missingFields.push(key); } } } diff --git a/lib/util/devops.js b/lib/util/devops.js index d721aea52..4907e1df9 100644 --- a/lib/util/devops.js +++ b/lib/util/devops.js @@ -113,13 +113,13 @@ const DevOps = { .filter((file) => filterPaths.some((path) => file.file.startsWith(path))) // ensure badly named files on unsupported metadata types are not in our subset .filter((/** @type {TYPE.DeltaPkgItem} */ file) => { - if (!MetadataType[file.type]) { + if (MetadataType[file.type]) { + return true; + } else { Util.logger.debug( `Unknown metadata-type found for (${file.file}): ` + file.type ); return false; - } else { - return true; } }) .map((/** @type {TYPE.DeltaPkgItem} */ file) => { @@ -182,7 +182,7 @@ const DevOps = { ) { Util.logger.warn( `- ❌ No changes found. Check what branch you are currently on and if the target branch name (${rangeUserInput}${ - range !== rangeUserInput ? ' converted to ' + range : '' + range === rangeUserInput ? '' : ' converted to ' + range }) was correct` ); return []; diff --git a/lib/util/init.config.js b/lib/util/init.config.js index 86251ea10..4f0ffdd02 100644 --- a/lib/util/init.config.js +++ b/lib/util/init.config.js @@ -78,10 +78,10 @@ const Init = { break; } case 'metaDataTypes.documentOnRetrieve': { - if (!properties.options.documentOnRetrieve) { - properties.metaDataTypes.documentOnRetrieve = []; - } else { + if (properties.options.documentOnRetrieve) { this._updateLeaf(properties, defaultProps, fieldName); + } else { + properties.metaDataTypes.documentOnRetrieve = []; } delete properties.options.documentOnRetrieve; upgradeMsgs.push( @@ -217,10 +217,7 @@ const Init = { Util.boilerplateDirectory, 'gitignore-template' ); - if (!(await File.pathExists(gitignoreFileName))) { - Util.logger.debug(`Dependency file not found in ${gitignoreFileName}`); - return false; - } else { + if (await File.pathExists(gitignoreFileName)) { const fileContent = await File.readFile(gitignoreFileName, 'utf8'); creationLog.push( await this._createIdeConfigFile( @@ -229,6 +226,9 @@ const Init = { fileContent ) ); + } else { + Util.logger.debug(`Dependency file not found in ${gitignoreFileName}`); + return false; } // load file list from boilerplate dir and initiate copy process diff --git a/lib/util/init.git.js b/lib/util/init.git.js index 2ee5caf47..f6a137a89 100644 --- a/lib/util/init.git.js +++ b/lib/util/init.git.js @@ -156,11 +156,11 @@ const Init = { return 'Please enter a valid remote URL'; } else if (!value.startsWith('http') && !value.startsWith('ssh')) { return `Your Git Remote URL should start with 'http' or 'ssh'`; - } else if (!value.endsWith('.git')) { - return `Your Git Remote URL should end with '.git'`; - } else { + } else if (value.endsWith('.git')) { // all good return true; + } else { + return `Your Git Remote URL should end with '.git'`; } }, }, diff --git a/lib/util/util.js b/lib/util/util.js index 394ab55fe..79603ffee 100644 --- a/lib/util/util.js +++ b/lib/util/util.js @@ -87,10 +87,7 @@ const Util = { * @returns {void} throws errors if problems were found */ verifyMarketList(mlName, properties) { - if (!properties.marketList[mlName]) { - // ML does not exist - throw new Error(`Market List ${mlName} is not defined`); - } else { + if (properties.marketList[mlName]) { // ML exists, check if it is properly set up // check if BUs in marketList are valid @@ -111,10 +108,10 @@ const Util = { marketArr = [marketArr]; } for (const market of marketArr) { - if (!properties.markets[market]) { - throw new Error(`Market '${market}' is not defined.`); - } else { + if (properties.markets[market]) { // * markets can be empty or include variables. Nothing we can test here + } else { + throw new Error(`Market '${market}' is not defined.`); } } } @@ -122,6 +119,9 @@ const Util = { if (!buCounter) { throw new Error(`No BUs defined in marketList ${mlName}`); } + } else { + // ML does not exist + throw new Error(`Market List ${mlName} is not defined`); } }, /** From a663f26fd3cae514d84cfacae77a4e5a090f2d2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 29 Nov 2022 17:02:36 +0100 Subject: [PATCH 049/103] #556: eslint --fix --- lib/metadataTypes/TransactionalMessage.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/metadataTypes/TransactionalMessage.js b/lib/metadataTypes/TransactionalMessage.js index e17849e3d..9a6820bc1 100644 --- a/lib/metadataTypes/TransactionalMessage.js +++ b/lib/metadataTypes/TransactionalMessage.js @@ -26,7 +26,10 @@ class TransactionalMessage extends MetadataType { static async retrieve(retrieveDir, _, __, ___, key) { let keyList; const baseUri = '/messaging/v1/' + this.subType + '/definitions/'; - if (!key) { + if (key) { + // Retrieve single + keyList = [key]; + } else { // Retrieve all const response = this.definition.restPagination ? await this.client.rest.getBulk(baseUri) @@ -38,9 +41,6 @@ class TransactionalMessage extends MetadataType { Object.keys(parsed).length - keyList.length } (downloaded but not saved to disk)` ); - } else { - // Retrieve single - keyList = [key]; } // get all sms with additional details not given by the list endpoint @@ -66,7 +66,7 @@ class TransactionalMessage extends MetadataType { }; Util.logger.info( `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` + - (key !== null ? ` ${color.dim}(Key: ${key})${color.reset}` : '') + (key === null ? '' : ` ${color.dim}(Key: ${key})${color.reset}`) ); return { metadata: savedMetadata, type: this.definition.type }; From 8cde08adbcdeb06e73cf769fe751a8db7938861a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 29 Nov 2022 17:52:14 +0100 Subject: [PATCH 050/103] #556: show filtered-msg only if anything was filtered --- lib/metadataTypes/TransactionalMessage.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/metadataTypes/TransactionalMessage.js b/lib/metadataTypes/TransactionalMessage.js index 9a6820bc1..d4128af66 100644 --- a/lib/metadataTypes/TransactionalMessage.js +++ b/lib/metadataTypes/TransactionalMessage.js @@ -36,11 +36,12 @@ class TransactionalMessage extends MetadataType { : await this.client.rest.get(baseUri); const parsed = this.parseResponseBody(response); keyList = Object.keys(parsed).filter((item) => parsed[item].status !== 'Deleted'); - Util.logger.info( - ` - Filtered ${this.definition.type} with status 'deleted': ${ - Object.keys(parsed).length - keyList.length - } (downloaded but not saved to disk)` - ); + const filteredCount = Object.keys(parsed).length - keyList.length; + if (filteredCount) { + Util.logger.info( + ` - Filtered ${this.definition.type} with status 'deleted': ${filteredCount} (downloaded but not saved to disk)` + ); + } } // get all sms with additional details not given by the list endpoint From 57c0473907d98fae5db1641414a00af9b2a21326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 29 Nov 2022 21:55:09 +0100 Subject: [PATCH 051/103] #557: add transactionalEmail test for RETRIEVE --- lib/metadataTypes/Asset.js | 2 +- .../TransactionalEmail.definition.js | 6 + ...isting_temail.transactionalEmail-meta.json | 24 ++ .../businessUnit/retrieve-response.xml | 33 ++ .../1111111/list/retrieve-response.xml | 39 +++ .../content/assets/query/post-response.json | 48 +++ .../9999999/dataFolder/retrieve-response.xml | 97 +++++- .../v1/interactions/get-response.json | 296 ++++++++++++++++++ .../9999999/list/retrieve-response.xml | 78 +++++ .../v1/email/definitions/get-response.json | 15 + .../testExisting_temail/get-response.json | 26 ++ .../transactionalEmail/get-expected.json | 24 ++ test/transactionalEmail.test.js | 50 +++ 13 files changed, 735 insertions(+), 3 deletions(-) create mode 100644 test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testExisting_temail.transactionalEmail-meta.json create mode 100644 test/resources/1111111/businessUnit/retrieve-response.xml create mode 100644 test/resources/1111111/list/retrieve-response.xml create mode 100644 test/resources/9999999/asset/v1/content/assets/query/post-response.json create mode 100644 test/resources/9999999/interaction/v1/interactions/get-response.json create mode 100644 test/resources/9999999/list/retrieve-response.xml create mode 100644 test/resources/9999999/messaging/v1/email/definitions/get-response.json create mode 100644 test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/get-response.json create mode 100644 test/resources/9999999/transactionalEmail/get-expected.json create mode 100644 test/transactionalEmail.test.js diff --git a/lib/metadataTypes/Asset.js b/lib/metadataTypes/Asset.js index 06ba04792..64d33f4fb 100644 --- a/lib/metadataTypes/Asset.js +++ b/lib/metadataTypes/Asset.js @@ -169,7 +169,7 @@ class Asset extends MetadataType { const subtypeIds = subTypeArray?.map( (subTypeItemName) => Asset.definition.typeMapping[subTypeItemName] ); - const uri = 'asset/v1/content/assets/query'; + const uri = '/asset/v1/content/assets/query'; const payload = { page: { page: 1, diff --git a/lib/metadataTypes/definitions/TransactionalEmail.definition.js b/lib/metadataTypes/definitions/TransactionalEmail.definition.js index 256b365b6..d7634d55d 100644 --- a/lib/metadataTypes/definitions/TransactionalEmail.definition.js +++ b/lib/metadataTypes/definitions/TransactionalEmail.definition.js @@ -87,6 +87,12 @@ module.exports = { retrieving: true, template: true, }, + 'subscriptions.r__list_PathName': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: true, + }, 'subscriptions.autoAddSubscriber': { isCreateable: true, isUpdateable: 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 new file mode 100644 index 000000000..54c87907d --- /dev/null +++ b/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testExisting_temail.transactionalEmail-meta.json @@ -0,0 +1,24 @@ +{ + "name": "testExisting_temail", + "definitionKey": "testExisting_temail", + "description": "bla bla", + "classification": "Default Transactional", + "status": "Active", + "createdDate": "2020-09-10T03:29:00", + "modifiedDate": "2020-09-10T03:29:00", + "content": { + "customerKey": "testExisting_asset_message" + }, + "subscriptions": { + "dataExtension": "childBU_dataextension_test", + "autoAddSubscriber": true, + "updateSubscriber": true, + "r__list_PathName": "my subscribers/All Subscribers" + }, + "options": { + "trackLinks": true + }, + "journey": { + "interactionKey": "testExisting_journey" + } +} diff --git a/test/resources/1111111/businessUnit/retrieve-response.xml b/test/resources/1111111/businessUnit/retrieve-response.xml new file mode 100644 index 000000000..e681ce9b6 --- /dev/null +++ b/test/resources/1111111/businessUnit/retrieve-response.xml @@ -0,0 +1,33 @@ + + + + RetrieveResponse + urn:uuid:9e5f98fa-4112-4416-be42-4f9e6f6886e4 + urn:uuid:b94fb623-e37d-40d3-8178-e6c8ee3b40b5 + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + + + 2022-11-29T19:42:20Z + 2022-11-29T19:47:20Z + + + + + + OK + 233a302f-6c1f-4fea-8b32-82a843ae806d + + + + None + + ENTIRE_ENTERPRISE + + + + diff --git a/test/resources/1111111/list/retrieve-response.xml b/test/resources/1111111/list/retrieve-response.xml new file mode 100644 index 000000000..6b43f89e8 --- /dev/null +++ b/test/resources/1111111/list/retrieve-response.xml @@ -0,0 +1,39 @@ + + + + RetrieveResponse + urn:uuid:26648e52-3bac-4eca-a5e1-ad71d38b56fe + urn:uuid:8552cfbd-466c-447e-a232-13d945e44a5c + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + + + 2022-11-29T19:42:21Z + 2022-11-29T19:47:21Z + + + + + + OK + 11e9f8a8-e5e6-4082-93bd-5840060512ce + + + 2017-01-24T06:32:34.453 + 2017-01-24T06:32:34.453 + 15 + 1f793b8b-cb23-4bb4-9ec4-1e0bf9511960 + All Subscribers - 277 + All Subscribers + 277 + Private + Contains all subscribers + ExactTargetList + + + + diff --git a/test/resources/9999999/asset/v1/content/assets/query/post-response.json b/test/resources/9999999/asset/v1/content/assets/query/post-response.json new file mode 100644 index 000000000..e80d2232a --- /dev/null +++ b/test/resources/9999999/asset/v1/content/assets/query/post-response.json @@ -0,0 +1,48 @@ +{ + "count": 1, + "page": 1, + "pageSize": 50, + "links": {}, + "items": [ + { + "id": 808714, + "customerKey": "testExisting_asset_message", + "assetType": { "id": 208, "name": "htmlemail", "displayName": "HTML Email" }, + "name": "testExisting_asset_message", + "createdDate": "2021-01-31T14:11:01.423-06:00", + "createdBy": { + "id": 700304523, + "email": "", + "name": "SFMC DEVOPS app user", + "userId": "700304523" + }, + "modifiedDate": "2021-01-31T14:13:30-06:00", + "modifiedBy": { + "id": 700304523, + "name": "SFMC DEVOPS app user", + "userId": "700304523" + }, + "status": { "id": 1, "name": "Draft" }, + "category": { "id": 290833, "name": "Content Builder", "parentId": 0 }, + "availableViews": ["html", "text", "subjectline", "preheader"], + "data": { + "email": { + "options": { "characterEncoding": "us-ascii" }, + "legacy": { + "legacyId": 531213, + "legacyKey": "testExisting_asset_message", + "legacyType": "email", + "legacyCategoryId": 290835 + } + } + }, + "legacyData": { + "legacyId": 531213, + "legacyKey": "testExisting_asset_message", + "legacyType": "email", + "legacyCategoryId": 290835 + }, + "modelVersion": 2 + } + ] +} diff --git a/test/resources/9999999/dataFolder/retrieve-response.xml b/test/resources/9999999/dataFolder/retrieve-response.xml index 4ce07129d..a48d37473 100644 --- a/test/resources/9999999/dataFolder/retrieve-response.xml +++ b/test/resources/9999999/dataFolder/retrieve-response.xml @@ -1,5 +1,10 @@ - + RetrieveResponse urn:uuid:f36f3303-3b5a-4641-8109-b26447634d91 @@ -60,6 +65,94 @@ false true + + + 9999999 + + + 2017-02-16T01:59:27.18 + 2017-02-16T01:59:27.18 + 412 + + mysubs_default + + + 0 + + + my subscribers + + mysubs + true + false + true + + + + 9999999 + + + 2017-02-16T01:59:27.177 + 2017-02-16T01:59:27.177 + 404 + + list_default + + + 0 + + + my lists + + list + true + false + true + + + + 9999999 + + + 2017-02-16T01:59:34.023 + 2017-02-16T01:59:34.023 + 424 + + publication_default + + + 0 + + + Publication Lists + + publication + true + false + true + + + + 9999999 + + + 2017-02-16T01:59:32.323 + 2017-02-16T01:59:32.323 + 419 + + suppression_list_default + + + 0 + + + Suppression Lists + + suppression_list + true + false + true + - \ No newline at end of file + diff --git a/test/resources/9999999/interaction/v1/interactions/get-response.json b/test/resources/9999999/interaction/v1/interactions/get-response.json new file mode 100644 index 000000000..1f87d37a3 --- /dev/null +++ b/test/resources/9999999/interaction/v1/interactions/get-response.json @@ -0,0 +1,296 @@ +{ + "count": 1, + "page": 1, + "pageSize": 50, + "links": {}, + "summary": { + "totalInteractions": 371, + "totalRunningVersionsWithDefinedGoal": 1, + "totalRunningVersionsMeetingGoal": 0, + "cumulativePopulation": 961860 + }, + "items": [ + { + "id": "233d4413-922c-4568-85a5-e5cc77efc3be", + "key": "testExisting_interaction", + "name": "testExisting_interaction", + "lastPublishedDate": "2017-04-12T08:07:48", + "description": "", + "version": 1, + "workflowApiVersion": 1, + "createdDate": "2017-04-12T05:38:05.837", + "modifiedDate": "2017-04-12T08:07:48.333", + "activities": [ + { + "id": "69213026-bd2c-433b-8332-5f52d3e87ca5", + "key": "WAIT-1", + "name": "", + "description": "", + "type": "WAIT", + "outcomes": [ + { + "key": "bd3dff6b-565c-4b56-b9cb-60cd5b6d080b", + "next": "DATAEXTENSIONUPDATE-1", + "arguments": {}, + "metaData": {} + } + ], + "arguments": { + "waitDefinitionId": "546704c0-9384-4a41-b20e-97615cc6cc6a", + "waitForEventId": "", + "executionMode": "{{Context.ExecutionMode}}", + "startActivityKey": "{{Context.StartActivityKey}}", + "waitQueueId": "{{Context.WaitQueueId}}" + }, + "configurationArguments": { + "waitDuration": 1, + "waitUnit": "DAYS", + "specifiedTime": "", + "timeZone": "", + "description": "", + "waitForEventKey": "" + }, + "metaData": { "waitType": "duration" }, + "schema": { + "arguments": { + "endDate": { + "dataType": "Date", + "isNullable": false, + "direction": "Out", + "readOnly": false, + "access": "Hidden" + }, + "waitEndDateAttributeDataBound": { + "dataType": "Text", + "isNullable": true, + "direction": "In", + "readOnly": false, + "access": "Hidden" + }, + "waitDefinitionId": { + "dataType": "Text", + "isNullable": false, + "direction": "In", + "readOnly": false, + "access": "Hidden" + }, + "waitForEventId": { + "dataType": "Text", + "isNullable": true, + "direction": "In", + "readOnly": false, + "access": "Hidden" + }, + "executionMode": { + "dataType": "Text", + "isNullable": false, + "direction": "In", + "readOnly": false, + "access": "Hidden" + }, + "startActivityKey": { + "dataType": "Text", + "isNullable": true, + "direction": "In", + "readOnly": false, + "access": "Hidden" + }, + "waitQueueId": { + "dataType": "LongNumber", + "isNullable": true, + "direction": "In", + "readOnly": false, + "access": "Hidden" + } + } + } + }, + { + "id": "5d93cda9-2015-4c07-a1db-0d5853d25bf6", + "key": "WAIT-2", + "name": "", + "description": "", + "type": "WAIT", + "outcomes": [ + { + "key": "e3d3258a-891b-4838-b5a6-af37f8cb769a", + "arguments": {}, + "metaData": {} + } + ], + "arguments": { + "waitDefinitionId": "deed4c9d-f487-4371-bfd9-76fca44ec49b", + "waitForEventId": "", + "executionMode": "{{Context.ExecutionMode}}", + "startActivityKey": "{{Context.StartActivityKey}}", + "waitQueueId": "{{Context.WaitQueueId}}" + }, + "configurationArguments": { + "waitDuration": 1, + "waitUnit": "DAYS", + "specifiedTime": "", + "timeZone": "", + "description": "", + "waitForEventKey": "" + }, + "metaData": { "waitType": "duration" }, + "schema": { + "arguments": { + "endDate": { + "dataType": "Date", + "isNullable": false, + "direction": "Out", + "readOnly": false, + "access": "Hidden" + }, + "waitEndDateAttributeDataBound": { + "dataType": "Text", + "isNullable": true, + "direction": "In", + "readOnly": false, + "access": "Hidden" + }, + "waitDefinitionId": { + "dataType": "Text", + "isNullable": false, + "direction": "In", + "readOnly": false, + "access": "Hidden" + }, + "waitForEventId": { + "dataType": "Text", + "isNullable": true, + "direction": "In", + "readOnly": false, + "access": "Hidden" + }, + "executionMode": { + "dataType": "Text", + "isNullable": false, + "direction": "In", + "readOnly": false, + "access": "Hidden" + }, + "startActivityKey": { + "dataType": "Text", + "isNullable": true, + "direction": "In", + "readOnly": false, + "access": "Hidden" + }, + "waitQueueId": { + "dataType": "LongNumber", + "isNullable": true, + "direction": "In", + "readOnly": false, + "access": "Hidden" + } + } + } + }, + { + "id": "ef4db13e-83f0-4d41-981d-4bf5810c0daa", + "key": "DATAEXTENSIONUPDATE-1", + "name": "", + "description": "", + "type": "DATAEXTENSIONUPDATE", + "outcomes": [ + { + "key": "a49ecf04-4612-4582-85fe-d7193f872fa8", + "next": "WAIT-2", + "arguments": {}, + "metaData": {} + } + ], + "arguments": { "contactKey": "{{Contact.Key}}", "value": "{{DateTime.Now}}" }, + "configurationArguments": { + "dataExtensionId": "ace267f0-b81d-e711-80cc-1402ec7222b4", + "field": "4e875b26-0317-4525-bfa0-50c8d1b7a7b5" + }, + "metaData": { + "dataExtensionName": "test_TestData", + "cachedFieldName": "JourneyConfirmed", + "cachedDisplayValue": "" + }, + "schema": { + "arguments": { + "contactKey": { + "dataType": "Text", + "isNullable": false, + "direction": "In", + "readOnly": false, + "access": "Hidden" + }, + "value": { + "dataType": "Text", + "isNullable": false, + "direction": "In", + "readOnly": false, + "access": "Hidden" + } + } + } + } + ], + "triggers": [ + { + "id": "a7f8cd93-e0a6-40a5-8557-5bd6fa76c0ea", + "key": "TRIGGER", + "name": "test_TESTDataentry", + "description": "", + "type": "APIEvent", + "outcomes": [], + "arguments": { + "filterResult": "{{Contact.FilterId.e4ddb887-6f2d-46b1-9f64-9a020f217339}}" + }, + "configurationArguments": { + "schemaVersionId": 133, + "criteria": "", + "filterDefinitionId": "e4ddb887-6f2d-46b1-9f64-9a020f217339" + }, + "metaData": { + "scheduleState": "No Schedule", + "sourceInteractionId": "00000000-0000-0000-0000-000000000000", + "eventDefinitionId": "33b4dbc5-4b58-4a54-ab57-24388f1eefe4", + "eventDefinitionKey": "APIEvent-60130566-e2fe-eb29-4140-15c27093a80b", + "chainType": "None", + "configurationRequired": false, + "iconUrl": "/events/images/icon_journeyBuilder-event-api-blue.svg", + "title": "", + "category": "Event" + } + } + ], + "goals": [], + "exits": [], + "notifiers": [], + "stats": { + "currentPopulation": 5848, + "cumulativePopulation": 3019, + "metGoal": 0, + "metExitCriteria": 0, + "goalPerformance": 0 + }, + "healthStats": { + "currentlyInCount": 0, + "delayedContactCount": 0, + "delayedMinuteInterval": 60 + }, + "entryMode": "OnceAndDone", + "definitionType": "Multistep", + "channel": "", + "defaults": { + "email": [ + "{{Event.APIEvent-60130566-e2fe-eb29-4140-15c27093a80b.\"emailAddress\"}}" + ], + "properties": {} + }, + "metaData": {}, + "executionMode": "Production", + "categoryId": 6298, + "status": "Published", + "definitionId": "233d4413-922c-4568-85a5-e5cc77efc3be", + "scheduledStatus": "Draft" + } + ] +} diff --git a/test/resources/9999999/list/retrieve-response.xml b/test/resources/9999999/list/retrieve-response.xml new file mode 100644 index 000000000..a90976cae --- /dev/null +++ b/test/resources/9999999/list/retrieve-response.xml @@ -0,0 +1,78 @@ + + + + RetrieveResponse + urn:uuid:fc7c8368-c8f0-4b86-a4ac-c420bb2517cf + urn:uuid:eaad475d-1129-4cf5-bbe6-a1d90a80f6da + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + + + 2022-11-29T19:42:18Z + 2022-11-29T19:47:18Z + + + + + + OK + e5b8151e-8780-4543-9856-971f3023769f + + + 2017-02-16T01:59:27.29 + 2017-02-16T01:59:27.29 + 23 + 95e7f560-dca9-4ad2-8bc6-850ea7506e88 + All Subscribers - 412 + All Subscribers + 412 + Private + Contains all subscribers + ExactTargetList + + + + 2017-06-13T06:03:10.8 + 2017-06-13T06:03:10.8 + 109 + 3d03a584-453e-41e0-9ed7-3ef418d5f79c + Test_List - 404 + Test_List + 404 + Private + Test_List + ExactTargetList + + + + 2017-06-26T04:46:40.887 + 2017-06-26T04:46:40.887 + 114 + b3e9f648-0588-4cc4-90a5-26624cdd2e1e + Demo Publication List - 424 + Demo Publication List + 424 + Public + + PublicationList + + + + 2022-02-17T11:58:08.273 + 2022-02-17T11:59:40.01 + 1120 + ebe40007-8776-46d7-9c9f-dc4578ac08b7 + sfmcTest - 419 + sfmcTest + 419 + Private + + SuppressionList + + + + diff --git a/test/resources/9999999/messaging/v1/email/definitions/get-response.json b/test/resources/9999999/messaging/v1/email/definitions/get-response.json new file mode 100644 index 000000000..3575ca537 --- /dev/null +++ b/test/resources/9999999/messaging/v1/email/definitions/get-response.json @@ -0,0 +1,15 @@ +{ + "requestId": "b0a6c5aa-83fa-43c2-a6a8-1e2055c85a43", + "definitions": [ + { + "name": "testExisting_temail", + "definitionKey": "testExisting_temail", + "status": "Active", + "createdDate": "2020-08-17T01:37:00", + "modifiedDate": "2020-08-17T01:37:00" + } + ], + "count": 1, + "page": 1, + "pageSize": 50 +} diff --git a/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/get-response.json b/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/get-response.json new file mode 100644 index 000000000..a2ba8a5ed --- /dev/null +++ b/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/get-response.json @@ -0,0 +1,26 @@ +{ + "requestId": "ae8511d0-9bf0-42d0-af4f-bd9c1218c812", + "name": "testExisting_temail", + "definitionKey": "testExisting_temail", + "definitionId": "0a650d90-755e-ed11-b852-48df37d1df5b", + "description": "bla bla", + "classification": "Default Transactional", + "status": "Active", + "createdDate": "2020-09-10T03:29:00", + "modifiedDate": "2020-09-10T03:29:00", + "content": { + "customerKey": "testExisting_asset_message" + }, + "subscriptions": { + "dataExtension": "childBU_dataextension_test", + "autoAddSubscriber": true, + "updateSubscriber": true, + "r__list_PathName": "my subscribers/All Subscribers" + }, + "options": { + "trackLinks": true + }, + "journey": { + "interactionKey": "testExisting_journey" + } +} diff --git a/test/resources/9999999/transactionalEmail/get-expected.json b/test/resources/9999999/transactionalEmail/get-expected.json new file mode 100644 index 000000000..54c87907d --- /dev/null +++ b/test/resources/9999999/transactionalEmail/get-expected.json @@ -0,0 +1,24 @@ +{ + "name": "testExisting_temail", + "definitionKey": "testExisting_temail", + "description": "bla bla", + "classification": "Default Transactional", + "status": "Active", + "createdDate": "2020-09-10T03:29:00", + "modifiedDate": "2020-09-10T03:29:00", + "content": { + "customerKey": "testExisting_asset_message" + }, + "subscriptions": { + "dataExtension": "childBU_dataextension_test", + "autoAddSubscriber": true, + "updateSubscriber": true, + "r__list_PathName": "my subscribers/All Subscribers" + }, + "options": { + "trackLinks": true + }, + "journey": { + "interactionKey": "testExisting_journey" + } +} diff --git a/test/transactionalEmail.test.js b/test/transactionalEmail.test.js new file mode 100644 index 000000000..1d5997ffa --- /dev/null +++ b/test/transactionalEmail.test.js @@ -0,0 +1,50 @@ +const chai = require('chai'); +const chaiFiles = require('chai-files'); +const assert = chai.assert; +chai.use(chaiFiles); +const expect = chai.expect; +const file = chaiFiles.file; +// const dir = chaiFiles.dir; +const cache = require('../lib/util/cache'); +const testUtils = require('./utils'); +const handler = require('../lib/index'); + +describe('transactionalEmail', () => { + beforeEach(() => { + testUtils.mockSetup(); + }); + afterEach(() => { + testUtils.mockReset(); + }); + + describe('Retrieve ================', () => { + it('Should retrieve a transactionalEmail', async () => { + // WHEN + await handler.retrieve('testInstance/testBU', ['transactionalEmail']); + // THEN + // get results from cache + const result = cache.getCache(); + assert.equal( + result.transactionalEmail ? Object.keys(result.transactionalEmail).length : 0, + 1, + 'only one transactionalEmail expected' + ); + assert.deepEqual( + await testUtils.getActualJson('testExisting_tsms', 'transactionalEmail'), + await testUtils.getExpectedJson('9999999', 'transactionalEmail', 'get'), + 'returned JSON was not equal expected' + ); + expect( + file(testUtils.getActualFile('testExisting_tsms', 'transactionalEmail', 'amp')) + ).to.equal( + file(testUtils.getExpectedFile('9999999', 'transactionalEmail', 'get', 'amp')) + ); + assert.equal( + Object.values(testUtils.getAPIHistory()).flat().length, + 4, + 'Unexpected number of requests made' + ); + return; + }); + }); +}); From 4223e0d4aea184dedb3efbb89c9ed68e769b4294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 29 Nov 2022 21:58:58 +0100 Subject: [PATCH 052/103] #556: fixed count --- .../9999999/messaging/v1/sms/definitions/get-response.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/resources/9999999/messaging/v1/sms/definitions/get-response.json b/test/resources/9999999/messaging/v1/sms/definitions/get-response.json index af3233afe..8e4f28a3f 100644 --- a/test/resources/9999999/messaging/v1/sms/definitions/get-response.json +++ b/test/resources/9999999/messaging/v1/sms/definitions/get-response.json @@ -9,7 +9,7 @@ "modifiedDate": "2022-11-25T08:01:00" } ], - "count": 3, + "count": 1, "page": 1, "pageSize": 50 } From 084c645aae2ca1e3e2edf74397e9e59df4deeb02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 30 Nov 2022 15:11:16 +0100 Subject: [PATCH 053/103] #556 Apply suggestions from code review Co-authored-by: Doug Midgley --- lib/metadataTypes/MetadataType.js | 1 - lib/metadataTypes/Query.js | 1 - lib/metadataTypes/TransactionalMessage.js | 2 +- test/transactionalSMS.test.js | 1 - test/utils.js | 1 - 5 files changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/metadataTypes/MetadataType.js b/lib/metadataTypes/MetadataType.js index b4a8f5bcc..60bdd6ccd 100644 --- a/lib/metadataTypes/MetadataType.js +++ b/lib/metadataTypes/MetadataType.js @@ -1511,7 +1511,6 @@ class MetadataType { Util.logger.debug(JSON.stringify(ex.response.data)); return errors; } - return null; } /** diff --git a/lib/metadataTypes/Query.js b/lib/metadataTypes/Query.js index 0f3fbcb95..d5b99cd47 100644 --- a/lib/metadataTypes/Query.js +++ b/lib/metadataTypes/Query.js @@ -334,7 +334,6 @@ class Query extends MetadataType { if (errors?.length > 0) { return errors.map((msg) => msg.split('Error saving the Query field.').join('')); } - return null; } } diff --git a/lib/metadataTypes/TransactionalMessage.js b/lib/metadataTypes/TransactionalMessage.js index d4128af66..67c22ee68 100644 --- a/lib/metadataTypes/TransactionalMessage.js +++ b/lib/metadataTypes/TransactionalMessage.js @@ -11,7 +11,7 @@ const Util = require('../util/util'); */ class TransactionalMessage extends MetadataType { // define this.subType as string here for intellisense; requires to be redefined in child class - static subType = ''; + static subType; /** * Retrieves Metadata of Mobile Keywords * Endpoint /legacy/v1/beta/mobile/code/ return all Mobile Codes with all details. diff --git a/test/transactionalSMS.test.js b/test/transactionalSMS.test.js index 0d351adee..938f5416b 100644 --- a/test/transactionalSMS.test.js +++ b/test/transactionalSMS.test.js @@ -4,7 +4,6 @@ const assert = chai.assert; chai.use(chaiFiles); const expect = chai.expect; const file = chaiFiles.file; -// const dir = chaiFiles.dir; const cache = require('../lib/util/cache'); const testUtils = require('./utils'); const handler = require('../lib/index'); diff --git a/test/utils.js b/test/utils.js index 990dac435..a6da0df34 100644 --- a/test/utils.js +++ b/test/utils.js @@ -102,7 +102,6 @@ const fsMockConf = { '.mcdevrc.json': fsmock.load(path.resolve(__dirname, 'mockRoot/.mcdevrc.json')), '.mcdev-auth.json': fsmock.load(path.resolve(__dirname, 'mockRoot/.mcdev-auth.json')), 'boilerplate/config.json': fsmock.load(path.resolve(__dirname, '../boilerplate/config.json')), - // deploy: fsmock.load(path.resolve(__dirname, 'mockRoot/deploy')), test: fsmock.load(path.resolve(__dirname)), }; exports.mockSetup = () => { From 02915880f92ebae06817fa071d17e0be6fe8f003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 30 Nov 2022 15:20:57 +0100 Subject: [PATCH 054/103] #556: code review fixes --- lib/metadataTypes/TransactionalSMS.js | 4 +--- test/dataExtension.test.js | 2 +- test/query.test.js | 2 +- test/transactionalSMS.test.js | 2 +- test/utils.js | 33 ++++++++++++--------------- 5 files changed, 19 insertions(+), 24 deletions(-) diff --git a/lib/metadataTypes/TransactionalSMS.js b/lib/metadataTypes/TransactionalSMS.js index 8aaa3e1dc..401dc3096 100644 --- a/lib/metadataTypes/TransactionalSMS.js +++ b/lib/metadataTypes/TransactionalSMS.js @@ -83,7 +83,6 @@ class TransactionalSMS extends TransactionalMessage { */ static async _mergeCode(metadata, deployDir, templateName) { templateName = templateName || metadata[this.definition.keyField]; - let code; const codePath = File.normalizePath([ deployDir, this.definition.type, @@ -91,7 +90,7 @@ class TransactionalSMS extends TransactionalMessage { ]); if (await File.pathExists(codePath + '.amp')) { - code = await File.readFilteredFilename( + return await File.readFilteredFilename( [deployDir, this.definition.type], templateName + '.' + this.definition.type + '-meta', 'amp' @@ -99,7 +98,6 @@ class TransactionalSMS extends TransactionalMessage { } else { throw new Error(`Could not find ${codePath}.amp`); } - return code; } /** * manages post retrieve steps diff --git a/test/dataExtension.test.js b/test/dataExtension.test.js index 71974e727..acd16fbb7 100644 --- a/test/dataExtension.test.js +++ b/test/dataExtension.test.js @@ -38,7 +38,7 @@ describe('dataExtension', () => { }); describe('Deploy ================', () => { beforeEach(() => { - testUtils.mockSetupDeploy(); + testUtils.mockSetup(true); }); it('Should create & upsert a data extension', async () => { // WHEN diff --git a/test/query.test.js b/test/query.test.js index fcf7707d3..76f4682fa 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -47,7 +47,7 @@ describe('query', () => { }); describe('Deploy ================', () => { beforeEach(() => { - testUtils.mockSetupDeploy(); + testUtils.mockSetup(true); }); it('Should create & upsert a query', async () => { // WHEN diff --git a/test/transactionalSMS.test.js b/test/transactionalSMS.test.js index 938f5416b..79a6d28f4 100644 --- a/test/transactionalSMS.test.js +++ b/test/transactionalSMS.test.js @@ -48,7 +48,7 @@ describe('transactionalSMS', () => { }); describe('Deploy ================', () => { beforeEach(() => { - testUtils.mockSetupDeploy(); + testUtils.mockSetup(true); }); it('Should create & upsert a transactionalSMS', async () => { // WHEN diff --git a/test/utils.js b/test/utils.js index a6da0df34..d2d1ab5c6 100644 --- a/test/utils.js +++ b/test/utils.js @@ -94,17 +94,11 @@ exports.getExpectedFile = (mid, type, action, ext) => /** * setup mocks for API and FS * + * @param {boolean} [isDeploy] if true, will mock deploy folder * @returns {void} */ -const fsMockConf = { - '.prettierrc': fsmock.load(path.resolve(__dirname, '../boilerplate/files/.prettierrc')), - '.mcdevrc.json': fsmock.load(path.resolve(__dirname, 'mockRoot/.mcdevrc.json')), - '.mcdev-auth.json': fsmock.load(path.resolve(__dirname, 'mockRoot/.mcdev-auth.json')), - 'boilerplate/config.json': fsmock.load(path.resolve(__dirname, '../boilerplate/config.json')), - test: fsmock.load(path.resolve(__dirname)), -}; -exports.mockSetup = () => { +exports.mockSetup = (isDeploy) => { Util.setLoggingLevel({ debug: true }); apimock = new MockAdapter(axios, { onNoMatch: 'throwException' }); // set access_token to mid to allow for autorouting of mock to correct resources @@ -118,19 +112,22 @@ exports.mockSetup = () => { apimock .onAny(new RegExp(`^${escapeRegExp(resourceFactory.restUrl)}`)) .reply((config) => resourceFactory.handleRESTRequest(config)); + const fsMockConf = { + '.prettierrc': fsmock.load(path.resolve(__dirname, '../boilerplate/files/.prettierrc')), + '.mcdevrc.json': fsmock.load(path.resolve(__dirname, 'mockRoot/.mcdevrc.json')), + '.mcdev-auth.json': fsmock.load(path.resolve(__dirname, 'mockRoot/.mcdev-auth.json')), + 'boilerplate/config.json': fsmock.load( + path.resolve(__dirname, '../boilerplate/config.json') + ), + test: fsmock.load(path.resolve(__dirname)), + }; + if (isDeploy) { + // load files we manually prepared for a direct test of `deploy` command + fsMockConf.deploy = fsmock.load(path.resolve(__dirname, 'mockRoot/deploy')); + } fsmock(fsMockConf); }; -/** - * setup mocks for deploy test - * - * @returns {void} - */ -exports.mockSetupDeploy = () => { - const fsMockConfDeploy = { ...fsMockConf }; - fsMockConfDeploy.deploy = fsmock.load(path.resolve(__dirname, 'mockRoot/deploy')); - fsmock(fsMockConfDeploy); -}; /** * resets mocks for API and FS * From c563819fe519a36100068184ef2fc838697f481d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 30 Nov 2022 15:56:16 +0100 Subject: [PATCH 055/103] #557: fix transactionalEmail RETRIEVE test --- test/transactionalEmail.test.js | 86 ++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 8 deletions(-) diff --git a/test/transactionalEmail.test.js b/test/transactionalEmail.test.js index 1d5997ffa..49d9818eb 100644 --- a/test/transactionalEmail.test.js +++ b/test/transactionalEmail.test.js @@ -2,9 +2,6 @@ const chai = require('chai'); const chaiFiles = require('chai-files'); const assert = chai.assert; chai.use(chaiFiles); -const expect = chai.expect; -const file = chaiFiles.file; -// const dir = chaiFiles.dir; const cache = require('../lib/util/cache'); const testUtils = require('./utils'); const handler = require('../lib/index'); @@ -30,14 +27,87 @@ describe('transactionalEmail', () => { 'only one transactionalEmail expected' ); assert.deepEqual( - await testUtils.getActualJson('testExisting_tsms', 'transactionalEmail'), + await testUtils.getActualJson('testExisting_temail', 'transactionalEmail'), await testUtils.getExpectedJson('9999999', 'transactionalEmail', 'get'), 'returned JSON was not equal expected' ); - expect( - file(testUtils.getActualFile('testExisting_tsms', 'transactionalEmail', 'amp')) - ).to.equal( - file(testUtils.getExpectedFile('9999999', 'transactionalEmail', 'get', 'amp')) + assert.equal( + Object.values(testUtils.getAPIHistory()).flat().length, + 12, + 'Unexpected number of requests made' + ); + return; + }); + }); + describe('Deploy ================', () => { + beforeEach(() => { + testUtils.mockSetup(true); + }); + it('Should create & upsert a transactionalEmail', async () => { + // WHEN + await handler.deploy('testInstance/testBU', ['transactionalEmail']); + // THEN + // get results from cache + const result = cache.getCache(); + assert.equal( + result.transactionalEmail ? Object.keys(result.transactionalEmail).length : 0, + 2, + 'two transactionalEmails expected' + ); + // confirm created item + assert.deepEqual( + await testUtils.getActualJson('testNew_temail', 'transactionalEmail'), + await testUtils.getExpectedJson('9999999', 'transactionalEmail', 'post'), + 'returned JSON was not equal expected for insert transactionalEmail' + ); + // confirm updated item + assert.deepEqual( + await testUtils.getActualJson('testExisting_temail', 'transactionalEmail'), + await testUtils.getExpectedJson('9999999', 'transactionalEmail', 'patch'), + 'returned JSON was not equal expected for update transactionalEmail' + ); + // check number of API calls + assert.equal( + Object.values(testUtils.getAPIHistory()).flat().length, + 5, + 'Unexpected number of requests made' + ); + return; + }); + }); + describe('Templating ================', () => { + // it.skip('Should create a transactionalEmail template via retrieveAsTemplate and build it'); + it('Should create a transactionalEmail template via buildTemplate and build it', async () => { + // download first before we test buildTemplate + await handler.retrieve('testInstance/testBU', ['transactionalEmail']); + // buildTemplate + const result = await handler.buildTemplate( + 'testInstance/testBU', + 'transactionalEmail', + ['testExisting_temail'], + 'testSourceMarket' + ); + assert.equal( + result.transactionalEmail ? Object.keys(result.transactionalEmail).length : 0, + 1, + 'only one transactionalEmail expected' + ); + assert.deepEqual( + await testUtils.getActualTemplateJson('testExisting_temail', 'transactionalEmail'), + await testUtils.getExpectedJson('9999999', 'transactionalEmail', 'template'), + 'returned template JSON was not equal expected' + ); + // buildDefinition + await handler.buildDefinition( + 'testInstance/testBU', + 'transactionalEmail', + 'testExisting_temail', + 'testTargetMarket' + ); + assert.deepEqual( + await testUtils.getActualDeployJson('testExisting_temail', 'transactionalEmail'), + await testUtils.getExpectedJson('9999999', 'transactionalEmail', 'build'), + 'returned deployment JSON was not equal expected' ); assert.equal( Object.values(testUtils.getAPIHistory()).flat().length, From 19c50bb6bc0311dc37b36697ead10c55388787c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 30 Nov 2022 16:09:00 +0100 Subject: [PATCH 056/103] #557: improve RETRIEVE transactionalEmail test to find interaction/journey --- .../v1/email/definitions/testExisting_temail/get-response.json | 2 +- test/resources/9999999/transactionalEmail/get-expected.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/get-response.json b/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/get-response.json index a2ba8a5ed..203dd8d90 100644 --- a/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/get-response.json +++ b/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/get-response.json @@ -21,6 +21,6 @@ "trackLinks": true }, "journey": { - "interactionKey": "testExisting_journey" + "interactionKey": "testExisting_interaction" } } diff --git a/test/resources/9999999/transactionalEmail/get-expected.json b/test/resources/9999999/transactionalEmail/get-expected.json index 54c87907d..c6f0dfcd7 100644 --- a/test/resources/9999999/transactionalEmail/get-expected.json +++ b/test/resources/9999999/transactionalEmail/get-expected.json @@ -19,6 +19,6 @@ "trackLinks": true }, "journey": { - "interactionKey": "testExisting_journey" + "interactionKey": "testExisting_interaction" } } From 4c0638ad898241728356c93724159e2a6c7d1160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 30 Nov 2022 19:37:19 +0100 Subject: [PATCH 057/103] #557: remove undone tests --- test/transactionalEmail.test.js | 78 --------------------------------- 1 file changed, 78 deletions(-) diff --git a/test/transactionalEmail.test.js b/test/transactionalEmail.test.js index 49d9818eb..bf71a6178 100644 --- a/test/transactionalEmail.test.js +++ b/test/transactionalEmail.test.js @@ -39,82 +39,4 @@ describe('transactionalEmail', () => { return; }); }); - describe('Deploy ================', () => { - beforeEach(() => { - testUtils.mockSetup(true); - }); - it('Should create & upsert a transactionalEmail', async () => { - // WHEN - await handler.deploy('testInstance/testBU', ['transactionalEmail']); - // THEN - // get results from cache - const result = cache.getCache(); - assert.equal( - result.transactionalEmail ? Object.keys(result.transactionalEmail).length : 0, - 2, - 'two transactionalEmails expected' - ); - // confirm created item - assert.deepEqual( - await testUtils.getActualJson('testNew_temail', 'transactionalEmail'), - await testUtils.getExpectedJson('9999999', 'transactionalEmail', 'post'), - 'returned JSON was not equal expected for insert transactionalEmail' - ); - // confirm updated item - assert.deepEqual( - await testUtils.getActualJson('testExisting_temail', 'transactionalEmail'), - await testUtils.getExpectedJson('9999999', 'transactionalEmail', 'patch'), - 'returned JSON was not equal expected for update transactionalEmail' - ); - // check number of API calls - assert.equal( - Object.values(testUtils.getAPIHistory()).flat().length, - 5, - 'Unexpected number of requests made' - ); - return; - }); - }); - describe('Templating ================', () => { - // it.skip('Should create a transactionalEmail template via retrieveAsTemplate and build it'); - it('Should create a transactionalEmail template via buildTemplate and build it', async () => { - // download first before we test buildTemplate - await handler.retrieve('testInstance/testBU', ['transactionalEmail']); - // buildTemplate - const result = await handler.buildTemplate( - 'testInstance/testBU', - 'transactionalEmail', - ['testExisting_temail'], - 'testSourceMarket' - ); - assert.equal( - result.transactionalEmail ? Object.keys(result.transactionalEmail).length : 0, - 1, - 'only one transactionalEmail expected' - ); - assert.deepEqual( - await testUtils.getActualTemplateJson('testExisting_temail', 'transactionalEmail'), - await testUtils.getExpectedJson('9999999', 'transactionalEmail', 'template'), - 'returned template JSON was not equal expected' - ); - // buildDefinition - await handler.buildDefinition( - 'testInstance/testBU', - 'transactionalEmail', - 'testExisting_temail', - 'testTargetMarket' - ); - assert.deepEqual( - await testUtils.getActualDeployJson('testExisting_temail', 'transactionalEmail'), - await testUtils.getExpectedJson('9999999', 'transactionalEmail', 'build'), - 'returned deployment JSON was not equal expected' - ); - assert.equal( - Object.values(testUtils.getAPIHistory()).flat().length, - 4, - 'Unexpected number of requests made' - ); - return; - }); - }); }); From e4d817a595e16516597d96ec465dc94ca7bffe5f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Dec 2022 20:06:22 +0000 Subject: [PATCH 058/103] Bump eslint from 8.28.0 to 8.29.0 Bumps [eslint](https://github.com/eslint/eslint) from 8.28.0 to 8.29.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.28.0...v8.29.0) --- updated-dependencies: - dependency-name: eslint 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 cc0c98043..afabee841 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,7 @@ "axios-mock-adapter": "1.21.2", "chai": "4.3.7", "chai-files": "1.4.0", - "eslint": "8.28.0", + "eslint": "8.29.0", "eslint-config-prettier": "8.5.0", "eslint-config-ssjs": "1.1.11", "eslint-plugin-jsdoc": "39.6.4", @@ -2687,9 +2687,9 @@ } }, "node_modules/eslint": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.28.0.tgz", - "integrity": "sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==", + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", + "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.3.3", @@ -10843,9 +10843,9 @@ "dev": true }, "eslint": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.28.0.tgz", - "integrity": "sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==", + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", + "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", "dev": true, "requires": { "@eslint/eslintrc": "^1.3.3", diff --git a/package.json b/package.json index 7ad79ca1c..e4ff8993c 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "axios-mock-adapter": "1.21.2", "chai": "4.3.7", "chai-files": "1.4.0", - "eslint": "8.28.0", + "eslint": "8.29.0", "eslint-config-prettier": "8.5.0", "eslint-config-ssjs": "1.1.11", "eslint-plugin-jsdoc": "39.6.4", From 4fb8e2a8a17cb93f5fe87ac47f4f742c51b55f32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 6 Dec 2022 10:42:53 +0100 Subject: [PATCH 059/103] #567: allow caching / retrieving only certain folder types --- docs/dist/documentation.md | 24 ++++++++++++++++-------- lib/metadataTypes/Folder.js | 36 ++++++++++++++++++++++++++++++------ 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index f8a2bf74a..9b9d7a58e 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -2569,20 +2569,20 @@ Folder MetadataType **Extends**: [MetadataType](#MetadataType) * [Folder](#Folder) ⇐ [MetadataType](#MetadataType) - * [.retrieve(retrieveDir, [additionalFields], buObject, [___], [key])](#Folder.retrieve) ⇒ Promise - * [.retrieveForCache(buObject)](#Folder.retrieveForCache) ⇒ Promise + * [.retrieve(retrieveDir, [additionalFields], buObject, [___], [key], [contentTypeList])](#Folder.retrieve) ⇒ Promise + * [.retrieveForCache(buObject, [contentTypeList])](#Folder.retrieveForCache) ⇒ Promise * [.upsert(metadata)](#Folder.upsert) ⇒ Promise.<object> * [.create(metadataEntry)](#Folder.create) ⇒ Promise * [.update(metadataEntry)](#Folder.update) ⇒ Promise * [.preDeployTasks(metadata)](#Folder.preDeployTasks) ⇒ Promise.<TYPE.MetadataTypeItem> * [.getJsonFromFS(dir, [listBadKeys])](#Folder.getJsonFromFS) ⇒ TYPE.MetadataTypeMap - * [.retrieveHelper([additionalFields], [queryAllAccounts])](#Folder.retrieveHelper) ⇒ Promise.<object> + * [.retrieveHelper([additionalFields], [queryAllAccounts], [contentTypeList])](#Folder.retrieveHelper) ⇒ Promise.<object> * [.postRetrieveTasks(metadata)](#Folder.postRetrieveTasks) ⇒ TYPE.MetadataTypeItem * [.saveResults(results, retrieveDir, mid)](#Folder.saveResults) ⇒ Promise.<object> -### Folder.retrieve(retrieveDir, [additionalFields], buObject, [___], [key]) ⇒ Promise +### Folder.retrieve(retrieveDir, [additionalFields], buObject, [___], [key], [contentTypeList]) ⇒ Promise Retrieves metadata of metadata type into local filesystem. executes callback with retrieved metadata **Kind**: static method of [Folder](#Folder) @@ -2595,10 +2595,11 @@ Retrieves metadata of metadata type into local filesystem. executes callback wit | buObject | TYPE.BuObject | properties for auth | | [___] | void | unused parameter | | [key] | string | customer key of single item to retrieve | +| [contentTypeList] | Array.<string> | content type of folder | -### Folder.retrieveForCache(buObject) ⇒ Promise +### Folder.retrieveForCache(buObject, [contentTypeList]) ⇒ Promise Retrieves folder metadata for caching **Kind**: static method of [Folder](#Folder) @@ -2607,6 +2608,7 @@ Retrieves folder metadata for caching | Param | Type | Description | | --- | --- | --- | | buObject | TYPE.BuObject | properties for auth | +| [contentTypeList] | Array.<string> | content type of folder | @@ -2673,7 +2675,7 @@ Returns file contents mapped to their filename without '.json' ending -### Folder.retrieveHelper([additionalFields], [queryAllAccounts]) ⇒ Promise.<object> +### Folder.retrieveHelper([additionalFields], [queryAllAccounts], [contentTypeList]) ⇒ Promise.<object> Helper to retrieve the folders as promise **Kind**: static method of [Folder](#Folder) @@ -2683,6 +2685,7 @@ Helper to retrieve the folders as promise | --- | --- | --- | | [additionalFields] | Array.<string> | Returns specified fields even if their retrieve definition is not set to true | | [queryAllAccounts] | boolean | which queryAllAccounts setting to use | +| [contentTypeList] | Array.<string> | content type of folder | @@ -2897,7 +2900,7 @@ List MetadataType * [List](#List) ⇐ [MetadataType](#MetadataType) * [.retrieve(retrieveDir, [_], [__], [___], [key])](#List.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> - * [.retrieveForCache()](#List.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.retrieveForCache(buObject)](#List.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.deleteByKey(buObject, customerKey)](#List.deleteByKey) ⇒ Promise.<boolean> * [.postRetrieveTasks(list)](#List.postRetrieveTasks) ⇒ TYPE.MetadataTypeItem * [.parseMetadata(metadata, [parseForCache])](#List.parseMetadata) ⇒ TYPE.MetadataTypeItem @@ -2920,11 +2923,16 @@ Retrieves Metadata of Lists -### List.retrieveForCache() ⇒ Promise.<TYPE.MetadataTypeMapObj> +### List.retrieveForCache(buObject) ⇒ Promise.<TYPE.MetadataTypeMapObj> Gets metadata cache with limited fields and does not store value to disk **Kind**: static method of [List](#List) **Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise of metadata + +| Param | Type | Description | +| --- | --- | --- | +| buObject | TYPE.BuObject | properties for auth | + ### List.deleteByKey(buObject, customerKey) ⇒ Promise.<boolean> diff --git a/lib/metadataTypes/Folder.js b/lib/metadataTypes/Folder.js index 9d84f087c..a4ac1f614 100644 --- a/lib/metadataTypes/Folder.js +++ b/lib/metadataTypes/Folder.js @@ -21,16 +21,19 @@ class Folder extends MetadataType { * @param {TYPE.BuObject} buObject properties for auth * @param {void} [___] unused parameter * @param {string} [key] customer key of single item to retrieve + * @param {string[]} [contentTypeList] content type of folder * @returns {Promise} Promise */ - static async retrieve(retrieveDir, additionalFields, buObject, ___, key) { + static async retrieve(retrieveDir, additionalFields, buObject, ___, key, contentTypeList) { if (key) { Util.logger.error(`Folder.retrieve() does not support key parameter`); } - const queryAllFolders = await this.retrieveHelper(additionalFields, true); + const queryAllFolders = await this.retrieveHelper(additionalFields, true, contentTypeList); if (buObject.eid !== buObject.mid) { - queryAllFolders.push(...(await this.retrieveHelper(additionalFields, false))); + queryAllFolders.push( + ...(await this.retrieveHelper(additionalFields, false, contentTypeList)) + ); } const sortPairs = toposort(queryAllFolders.map((a) => [a.ParentFolder.ID, a.ID])); const idMap = {}; @@ -150,10 +153,11 @@ class Folder extends MetadataType { * Retrieves folder metadata for caching * * @param {TYPE.BuObject} buObject properties for auth + * @param {string[]} [contentTypeList] content type of folder * @returns {Promise} Promise */ - static retrieveForCache(buObject) { - return this.retrieve(null, null, buObject); + static retrieveForCache(buObject, contentTypeList) { + return this.retrieve(null, null, buObject, null, null, contentTypeList); } /** @@ -515,10 +519,30 @@ class Folder extends MetadataType { * * @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true * @param {boolean} [queryAllAccounts] which queryAllAccounts setting to use + * @param {string[]} [contentTypeList] content type of folder * @returns {Promise.} soap object */ - static async retrieveHelper(additionalFields, queryAllAccounts) { + static async retrieveHelper(additionalFields, queryAllAccounts, contentTypeList) { const options = { QueryAllAccounts: !!queryAllAccounts }; + if (contentTypeList) { + for (const contentType of contentTypeList) { + options.filter = options.filter + ? { + leftOperand: { + leftOperand: 'ContentType', + operator: 'equals', + rightOperand: contentType, + }, + operator: 'OR', + rightOperand: options.filter, + } + : { + leftOperand: 'ContentType', + operator: 'equals', + rightOperand: contentType, + }; + } + } const response = await this.client.soap.retrieveBulk( 'DataFolder', this.getFieldNamesToRetrieve(additionalFields).filter( From d9c3fafb5f5e03be37c85bb75776d8ae2ac0a391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 6 Dec 2022 10:46:03 +0100 Subject: [PATCH 060/103] #567: only cache folders relevant for dataExtensions --- lib/metadataTypes/DataExtension.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index 238cb91c5..1645db7f0 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -360,7 +360,13 @@ class DataExtension extends MetadataType { Util.logger.info(' - Caching dependent Metadata: folder (shared via _ParentBU_)'); Folder.client = this.client; Folder.properties = this.properties; - const result = await Folder.retrieveForCache(buObjectParentBu); + const result = await Folder.retrieveForCache(buObjectParentBu, [ + 'shared_data', + 'synchronizeddataextension', + 'salesforcedataextension', + 'shared_dataextension', + 'dataextension', + ]); cache.mergeMetadata('folder', result.metadata, buObject.eid); // get the types and clean out non-shared ones From 4b0b8eae811eb425163de6ad51ed680eaa21c36b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 6 Dec 2022 10:55:27 +0100 Subject: [PATCH 061/103] #566: get all subscriber list from ParentBU dependening on BU settings --- lib/metadataTypes/List.js | 70 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/lib/metadataTypes/List.js b/lib/metadataTypes/List.js index b00b57b9a..1e990cb96 100644 --- a/lib/metadataTypes/List.js +++ b/lib/metadataTypes/List.js @@ -4,6 +4,7 @@ const TYPE = require('../../types/mcdev.d'); const MetadataType = require('./MetadataType'); const Util = require('../util/util'); const cache = require('../util/cache'); +const auth = require('../util/auth'); /** * List MetadataType @@ -47,13 +48,78 @@ class List extends MetadataType { /** * Gets metadata cache with limited fields and does not store value to disk * + * @param {TYPE.BuObject} buObject properties for auth * @returns {Promise.} Promise of metadata */ - static async retrieveForCache() { - const results = await this.retrieve(null); + static async retrieveForCache(buObject) { + let results = await this.retrieve(null); for (const metadataEntry in results.metadata) { this.parseMetadata(results.metadata[metadataEntry], true); } + if (buObject.eid !== buObject.mid) { + // for caching, we want to get the All Subscriber List from the Parent Account + Util.logger.debug(' - Checking MasterUnsubscribeBehavior for current BU'); + /** @type {TYPE.BuObject} */ + const buObjectParentBu = { + eid: this.properties.credentials[buObject.credential].eid, + mid: this.properties.credentials[buObject.credential].eid, + businessUnit: Util.parentBuName, + credential: buObject.credential, + }; + try { + this.client = auth.getSDK(buObjectParentBu); + } catch (ex) { + Util.logger.error(ex.message); + return; + } + const buResult = await this.client.soap.retrieve( + 'BusinessUnit', + ['MasterUnsubscribeBehavior'], + { + QueryAllAccounts: true, + filter: { + leftOperand: 'ID', + operator: 'equals', + rightOperand: this.properties.credentials[buObject.credential].eid, + }, + } + ); + const masterUnsubscribeBehavior = buResult.Results[0]?.MasterUnsubscribeBehavior; + if (masterUnsubscribeBehavior === 'ENTIRE_ENTERPRISE') { + Util.logger.debug(` - BU uses ParentBU's All Subscriber List`); + Util.logger.info( + ' - Caching dependent Metadata: All Subscriber list (on _ParentBU_)' + ); + const metadataParentBu = await this.retrieve( + null, + null, + null, + null, + 'All Subscribers' + ); + // manually set folder path of parent's All Subscriber List to avoid retrieving folders + for (const key of Object.keys(metadataParentBu.metadata)) { + metadataParentBu.metadata[key].r__folder_Path = 'my subscribers'; + } + // find & delete local All Subscriber list to avoid referencing the wrong one + for (const key of Object.keys(results.metadata)) { + if (results.metadata[key].ListName === 'All Subscribers') { + delete results.metadata[key]; + break; + } + } + + // make sure to overwrite parent bu DEs with local ones + results = { + metadata: { ...metadataParentBu.metadata, ...results.metadata }, + type: results.type, + }; + } else if (masterUnsubscribeBehavior === 'BUSINESS_UNIT_ONLY') { + Util.logger.debug(' - BU uses own All Subscriber List'); + } + // revert client to current default + this.client = auth.getSDK(this.buObject); + } return results; } From 28a3f0c45840f2c84110c8d6ebeaba4fa8bcc0e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 6 Dec 2022 10:58:27 +0100 Subject: [PATCH 062/103] #565: ensure we cache list folders if not found in cache already --- lib/metadataTypes/List.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/metadataTypes/List.js b/lib/metadataTypes/List.js index 1e990cb96..915d40822 100644 --- a/lib/metadataTypes/List.js +++ b/lib/metadataTypes/List.js @@ -2,6 +2,7 @@ const TYPE = require('../../types/mcdev.d'); const MetadataType = require('./MetadataType'); +const Folder = require('./Folder'); const Util = require('../util/util'); const cache = require('../util/cache'); const auth = require('../util/auth'); @@ -53,6 +54,20 @@ class List extends MetadataType { */ static async retrieveForCache(buObject) { let results = await this.retrieve(null); + if (!cache.getCache()?.folder) { + Util.logger.debug('folders not cached but required for list'); + Util.logger.info(' - Caching dependent Metadata: folder'); + Folder.client = this.client; + Folder.properties = this.properties; + const result = await Folder.retrieveForCache(buObject, [ + 'list', + 'mysubs', + 'suppression_list', + 'publication', + 'contextual_suppression_list', + ]); + cache.setMetadata('folder', result.metadata); + } for (const metadataEntry in results.metadata) { this.parseMetadata(results.metadata[metadataEntry], true); } From 736faee47035bc9fd8aeb89ca7621e5ce170ae81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 6 Dec 2022 11:02:46 +0100 Subject: [PATCH 063/103] #579: dont filter lists with broken folder links --- lib/metadataTypes/List.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/metadataTypes/List.js b/lib/metadataTypes/List.js index 915d40822..d8e99fb8a 100644 --- a/lib/metadataTypes/List.js +++ b/lib/metadataTypes/List.js @@ -176,10 +176,10 @@ class List extends MetadataType { if (!parseForCache) { delete metadata.Category; } - return metadata; } catch (ex) { Util.logger.warn(` - List ${metadata.ID}: '${metadata.CustomerKey}': ${ex.message}`); } + return metadata; } } // Assign definition to static attributes From d852ee1e4edf3ebe476981afd3ff8eec27c01ecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 6 Dec 2022 12:07:02 +0100 Subject: [PATCH 064/103] #580: Ensure deploy-checks on DE-fields know if DE has data --- lib/metadataTypes/DataExtensionField.js | 47 +++++++++++++++++++++---- test/dataExtension.test.js | 2 +- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/lib/metadataTypes/DataExtensionField.js b/lib/metadataTypes/DataExtensionField.js index f33f784c0..90b96d1ff 100644 --- a/lib/metadataTypes/DataExtensionField.js +++ b/lib/metadataTypes/DataExtensionField.js @@ -100,6 +100,19 @@ class DataExtensionField extends MetadataType { * @returns {Object.} existing fields by their original name to allow re-adding FieldType after update */ static async prepareDeployColumnsOnUpdate(deployColumns, deKey) { + // get row count to know which field restrictions apply + let hasData = false; + try { + const rowset = await this.client.rest.get( + `/data/v1/customobjectdata/key/${deKey}/rowset?$page=1&$pagesize=1` + ); + const rowCount = rowset.count; + hasData = rowCount > 0; + Util.logger.debug(`dataExtension ${deKey} row count: ${rowCount}`); + } catch (ex) { + Util.logger.debug(`Could not retrieve rowcount for ${deKey}: ${ex.message}`); + } + // retrieve existing fields to enable updating them const response = await this.retrieveForCache( { @@ -129,6 +142,7 @@ class DataExtensionField extends MetadataType { // Updating to a new FieldType will result in an error; warn & afterwards remove it if (itemOld.FieldType !== item.FieldType) { + // applicable: with or without data but simply ignored by API Util.logger.warn( ` - The Field Type of an existing field cannot be changed. Keeping the original value for [${deKey}].[${item.Name}]: '${itemOld.FieldType}'` ); @@ -141,14 +155,16 @@ class DataExtensionField extends MetadataType { delete item.FieldType; if (itemOld.MaxLength > item.MaxLength) { + // applicable: with or without data (Code 310007) Util.logger.warn( ` - The length of an existing field cannot be decreased. Keeping the original value for [${deKey}].[${item.Name}]: '${itemOld.MaxLength}'` ); item.MaxLength = itemOld.MaxLength; } if (Util.isFalse(itemOld.IsRequired) && Util.isTrue(item.IsRequired)) { + // applicable: with or without data (Code 310007) Util.logger.warn( - ` - A field cannot be changed to be required on update after it was created to allow nulls: [${deKey}].[${item.Name}]` + ` - A field cannot be changed to be required on update after it was created to allow nulls. Resetting to not equired: [${deKey}].[${item.Name}]` ); item.IsRequired = itemOld.IsRequired; } @@ -179,11 +195,20 @@ class DataExtensionField extends MetadataType { item.ObjectID = itemOld.ObjectID; } else { // field is getting added --- - if (Util.isTrue(item.IsRequired) && item.DefaultValue === '') { - Util.logger.warn( - ` - Adding new fields to an existing table requires that these fields are either not-required (nullable) or have a default value set. Changing [${deKey}].[${item.Name}] to be not-required` - ); - item.IsRequired = false; + + if (hasData && Util.isTrue(item.IsRequired) && item.DefaultValue === '') { + // applicable: with data only + if (Util.isFalse(item.IsPrimaryKey)) { + Util.logger.warn( + ` - Adding new fields to an existing table requires that these fields are either not-required (nullable) or have a default value set. Changing [${deKey}].[${item.Name}] to be not-required` + ); + item.IsRequired = false; + } else { + Util.logger.error( + `- You cannot add a new primary key field to an existing table that has data. Removing [${deKey}].[${item.Name}] from deployment` + ); + deployColumns.splice(i, 1); + } } if (item.Name_new) { Util.logger.warn( @@ -194,6 +219,15 @@ class DataExtensionField extends MetadataType { // Field doesn't exist in target, therefore Remove ObjectID if present delete item.ObjectID; } + if (Util.isTrue(item.IsPrimaryKey) && Util.isFalse(item.IsRequired)) { + // applicable: with or without data + Util.logger.warn( + `- Primary Key field [${deKey}].[${item.Name}] cannot be not-required (nullable). Changing field to be required!` + ); + item.IsRequired = true; + } + + // filter bad manual changes to the json if (!Util.isTrue(item.IsRequired) && !Util.isFalse(item.IsRequired)) { Util.logger.error( `- Invalid value for 'IsRequired' of [${deKey}].[${item.Name}]. Found '${item.IsRequired}' instead of 'true'/'false'. Removing field from deploy!` @@ -207,6 +241,7 @@ class DataExtensionField extends MetadataType { deployColumns.splice(i, 1); } } + Util.logger.debug( `${deployColumns.length} Fields added/updated for [${deKey}]${ deployColumns.length ? ': ' : '' diff --git a/test/dataExtension.test.js b/test/dataExtension.test.js index acd16fbb7..6e3c1320d 100644 --- a/test/dataExtension.test.js +++ b/test/dataExtension.test.js @@ -66,7 +66,7 @@ describe('dataExtension', () => { ); assert.equal( Object.values(testUtils.getAPIHistory()).flat().length, - 11, + 12, 'Unexpected number of requests made' ); return; From 0e37dd1d328dca0c49f148d9a057370b82d6a395 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Dec 2022 11:17:40 +0000 Subject: [PATCH 065/103] Bump simple-git from 3.14.1 to 3.15.1 Bumps [simple-git](https://github.com/steveukx/git-js/tree/HEAD/simple-git) from 3.14.1 to 3.15.1. - [Release notes](https://github.com/steveukx/git-js/releases) - [Changelog](https://github.com/steveukx/git-js/blob/main/simple-git/CHANGELOG.md) - [Commits](https://github.com/steveukx/git-js/commits/simple-git@3.15.1/simple-git) --- updated-dependencies: - dependency-name: simple-git dependency-type: direct:production 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 afabee841..8fe31f274 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "prettier-plugin-sql": "0.12.1", "semver": "7.3.8", "sfmc-sdk": "0.6.1", - "simple-git": "3.14.1", + "simple-git": "3.15.1", "toposort": "2.0.2", "update-notifier": "5.1.0", "winston": "3.8.2", @@ -7718,9 +7718,9 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/simple-git": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.14.1.tgz", - "integrity": "sha512-1ThF4PamK9wBORVGMK9HK5si4zoGS2GpRO7tkAFObA4FZv6dKaCVHLQT+8zlgiBm6K2h+wEU9yOaFCu/SR3OyA==", + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.15.1.tgz", + "integrity": "sha512-73MVa5984t/JP4JcQt0oZlKGr42ROYWC3BcUZfuHtT3IHKPspIvL0cZBnvPXF7LL3S/qVeVHVdYYmJ3LOTw4Rg==", "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", @@ -14530,9 +14530,9 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "simple-git": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.14.1.tgz", - "integrity": "sha512-1ThF4PamK9wBORVGMK9HK5si4zoGS2GpRO7tkAFObA4FZv6dKaCVHLQT+8zlgiBm6K2h+wEU9yOaFCu/SR3OyA==", + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.15.1.tgz", + "integrity": "sha512-73MVa5984t/JP4JcQt0oZlKGr42ROYWC3BcUZfuHtT3IHKPspIvL0cZBnvPXF7LL3S/qVeVHVdYYmJ3LOTw4Rg==", "requires": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", diff --git a/package.json b/package.json index e4ff8993c..7985dde1d 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "prettier-plugin-sql": "0.12.1", "semver": "7.3.8", "sfmc-sdk": "0.6.1", - "simple-git": "3.14.1", + "simple-git": "3.15.1", "toposort": "2.0.2", "update-notifier": "5.1.0", "winston": "3.8.2", From f71bc629dc31fb06be5085b1050b41315b720a1b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Dec 2022 11:24:03 +0000 Subject: [PATCH 066/103] Bump lint-staged from 13.0.3 to 13.1.0 Bumps [lint-staged](https://github.com/okonet/lint-staged) from 13.0.3 to 13.1.0. - [Release notes](https://github.com/okonet/lint-staged/releases) - [Commits](https://github.com/okonet/lint-staged/compare/v13.0.3...v13.1.0) --- updated-dependencies: - dependency-name: lint-staged dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 129 +++++++++++++++++++--------------------------- package.json | 2 +- 2 files changed, 53 insertions(+), 78 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8fe31f274..59d7c3612 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,7 +47,7 @@ "eslint-plugin-unicorn": "45.0.1", "husky": "8.0.1", "jsdoc-to-markdown": "8.0.0", - "lint-staged": "13.0.3", + "lint-staged": "13.1.0", "mocha": "10.1.0", "mock-fs": "5.2.0", "npm-check": "6.0.1", @@ -1950,9 +1950,9 @@ } }, "node_modules/commander": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.3.0.tgz", - "integrity": "sha512-hv95iU5uXPbK83mjrJKuZyFM/LBAoCV/XhVGkS5Je6tl7sxr6A0ITMw5WoRV46/UaJ46Nllm3Xt7IaJhXTIkzw==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", + "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", "dev": true, "engines": { "node": "^12.20.0 || >=14" @@ -2534,20 +2534,6 @@ "once": "^1.4.0" } }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/entities": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", @@ -4920,9 +4906,9 @@ } }, "node_modules/lilconfig": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", - "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", "dev": true, "engines": { "node": ">=10" @@ -4944,24 +4930,24 @@ } }, "node_modules/lint-staged": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.0.3.tgz", - "integrity": "sha512-9hmrwSCFroTSYLjflGI8Uk+GWAwMB4OlpU4bMJEAT5d/llQwtYKoim4bLOyLCuWFAhWEupE0vkIFqtw/WIsPug==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.1.0.tgz", + "integrity": "sha512-pn/sR8IrcF/T0vpWLilih8jmVouMlxqXxKuAojmbiGX5n/gDnz+abdPptlj0vYnbfE0SQNl3CY/HwtM0+yfOVQ==", "dev": true, "dependencies": { "cli-truncate": "^3.1.0", - "colorette": "^2.0.17", - "commander": "^9.3.0", + "colorette": "^2.0.19", + "commander": "^9.4.1", "debug": "^4.3.4", "execa": "^6.1.0", - "lilconfig": "2.0.5", - "listr2": "^4.0.5", + "lilconfig": "2.0.6", + "listr2": "^5.0.5", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "object-inspect": "^1.12.2", "pidtree": "^0.6.0", "string-argv": "^0.3.1", - "yaml": "^2.1.1" + "yaml": "^2.1.3" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -4974,22 +4960,22 @@ } }, "node_modules/listr2": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", - "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.6.tgz", + "integrity": "sha512-u60KxKBy1BR2uLJNTWNptzWQ1ob/gjMzIJPZffAENzpZqbMZ/5PrXXOomDcevIS/+IB7s1mmCEtSlT2qHWMqag==", "dev": true, "dependencies": { "cli-truncate": "^2.1.0", - "colorette": "^2.0.16", + "colorette": "^2.0.19", "log-update": "^4.0.0", "p-map": "^4.0.0", "rfdc": "^1.3.0", - "rxjs": "^7.5.5", + "rxjs": "^7.5.7", "through": "^2.3.8", "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=12" + "node": "^14.13.1 || >=16.0.0" }, "peerDependencies": { "enquirer": ">= 2.3.0 < 3" @@ -7533,9 +7519,9 @@ } }, "node_modules/rxjs": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", - "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.6.0.tgz", + "integrity": "sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ==", "dependencies": { "tslib": "^2.1.0" } @@ -8744,9 +8730,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.1.tgz", - "integrity": "sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz", + "integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==", "dev": true, "engines": { "node": ">= 14" @@ -10267,9 +10253,9 @@ } }, "commander": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.3.0.tgz", - "integrity": "sha512-hv95iU5uXPbK83mjrJKuZyFM/LBAoCV/XhVGkS5Je6tl7sxr6A0ITMw5WoRV46/UaJ46Nllm3Xt7IaJhXTIkzw==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", + "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", "dev": true }, "comment-parser": { @@ -10726,17 +10712,6 @@ "once": "^1.4.0" } }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, "entities": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", @@ -12442,9 +12417,9 @@ } }, "lilconfig": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", - "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", "dev": true }, "lines-and-columns": { @@ -12463,38 +12438,38 @@ } }, "lint-staged": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.0.3.tgz", - "integrity": "sha512-9hmrwSCFroTSYLjflGI8Uk+GWAwMB4OlpU4bMJEAT5d/llQwtYKoim4bLOyLCuWFAhWEupE0vkIFqtw/WIsPug==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.1.0.tgz", + "integrity": "sha512-pn/sR8IrcF/T0vpWLilih8jmVouMlxqXxKuAojmbiGX5n/gDnz+abdPptlj0vYnbfE0SQNl3CY/HwtM0+yfOVQ==", "dev": true, "requires": { "cli-truncate": "^3.1.0", - "colorette": "^2.0.17", - "commander": "^9.3.0", + "colorette": "^2.0.19", + "commander": "^9.4.1", "debug": "^4.3.4", "execa": "^6.1.0", - "lilconfig": "2.0.5", - "listr2": "^4.0.5", + "lilconfig": "2.0.6", + "listr2": "^5.0.5", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "object-inspect": "^1.12.2", "pidtree": "^0.6.0", "string-argv": "^0.3.1", - "yaml": "^2.1.1" + "yaml": "^2.1.3" } }, "listr2": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", - "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.6.tgz", + "integrity": "sha512-u60KxKBy1BR2uLJNTWNptzWQ1ob/gjMzIJPZffAENzpZqbMZ/5PrXXOomDcevIS/+IB7s1mmCEtSlT2qHWMqag==", "dev": true, "requires": { "cli-truncate": "^2.1.0", - "colorette": "^2.0.16", + "colorette": "^2.0.19", "log-update": "^4.0.0", "p-map": "^4.0.0", "rfdc": "^1.3.0", - "rxjs": "^7.5.5", + "rxjs": "^7.5.7", "through": "^2.3.8", "wrap-ansi": "^7.0.0" }, @@ -14394,9 +14369,9 @@ } }, "rxjs": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", - "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.6.0.tgz", + "integrity": "sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ==", "requires": { "tslib": "^2.1.0" } @@ -15331,9 +15306,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yaml": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.1.tgz", - "integrity": "sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz", + "integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==", "dev": true }, "yargs": { diff --git a/package.json b/package.json index 7985dde1d..a8506f93e 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "eslint-plugin-unicorn": "45.0.1", "husky": "8.0.1", "jsdoc-to-markdown": "8.0.0", - "lint-staged": "13.0.3", + "lint-staged": "13.1.0", "mocha": "10.1.0", "mock-fs": "5.2.0", "npm-check": "6.0.1", From 854bbe0cab5a574774b3c2c7db22e9abdf2b5ca1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Dec 2022 11:30:02 +0000 Subject: [PATCH 067/103] Bump fs-extra from 10.1.0 to 11.1.0 Bumps [fs-extra](https://github.com/jprichardson/node-fs-extra) from 10.1.0 to 11.1.0. - [Release notes](https://github.com/jprichardson/node-fs-extra/releases) - [Changelog](https://github.com/jprichardson/node-fs-extra/blob/master/CHANGELOG.md) - [Commits](https://github.com/jprichardson/node-fs-extra/compare/10.1.0...11.1.0) --- updated-dependencies: - dependency-name: fs-extra dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package-lock.json | 16 ++++++++-------- package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 59d7c3612..daa308d9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "conf": "10.2.0", "console.table": "0.10.0", "deep-equal": "2.1.0", - "fs-extra": "10.1.0", + "fs-extra": "11.1.0", "inquirer": "8.2.2", "json-to-table": "4.2.1", "mustache": "4.2.0", @@ -3454,16 +3454,16 @@ } }, "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", + "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">=14.14" } }, "node_modules/fs-then-native": { @@ -11382,9 +11382,9 @@ } }, "fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", + "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", "requires": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", diff --git a/package.json b/package.json index a8506f93e..ac9dfeb27 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "conf": "10.2.0", "console.table": "0.10.0", "deep-equal": "2.1.0", - "fs-extra": "10.1.0", + "fs-extra": "11.1.0", "inquirer": "8.2.2", "json-to-table": "4.2.1", "mustache": "4.2.0", From 26aa91577de31c0a54ba675a9d15109f7fe0dc82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 6 Dec 2022 13:54:01 +0100 Subject: [PATCH 068/103] #557: add transactionalEmail test for DEPLOY --- docs/dist/documentation.md | 13 +++++++ lib/metadataTypes/TransactionalEmail.js | 27 +++++++++++--- .../TransactionalEmail.definition.js | 12 +++++-- ...isting_temail.transactionalEmail-meta.json | 4 +-- ...estNew_temail.transactionalEmail-meta.json | 24 +++++++++++++ .../v1/email/definitions/post-response.json | 19 ++++++++++ .../testExisting_temail/get-response.json | 4 +-- .../testExisting_temail/patch-response.json | 26 ++++++++++++++ .../transactionalEmail/patch-expected.json | 24 +++++++++++++ .../transactionalEmail/post-expected.json | 24 +++++++++++++ test/transactionalEmail.test.js | 36 +++++++++++++++++++ 11 files changed, 202 insertions(+), 11 deletions(-) create mode 100644 test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testNew_temail.transactionalEmail-meta.json create mode 100644 test/resources/9999999/messaging/v1/email/definitions/post-response.json create mode 100644 test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/patch-response.json create mode 100644 test/resources/9999999/transactionalEmail/patch-expected.json create mode 100644 test/resources/9999999/transactionalEmail/post-expected.json diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index efe168eea..06c83581f 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -4367,9 +4367,22 @@ TransactionalEmail MetadataType **Extends**: [TransactionalMessage](#TransactionalMessage) * [TransactionalEmail](#TransactionalEmail) ⇐ [TransactionalMessage](#TransactionalMessage) + * [.update(metadata)](#TransactionalEmail.update) ⇒ Promise * [.preDeployTasks(metadata)](#TransactionalEmail.preDeployTasks) ⇒ TYPE.MetadataTypeItem * [.postRetrieveTasks(metadata)](#TransactionalEmail.postRetrieveTasks) ⇒ TYPE.MetadataTypeItem + + +### TransactionalEmail.update(metadata) ⇒ Promise +Updates a single item + +**Kind**: static method of [TransactionalEmail](#TransactionalEmail) +**Returns**: Promise - Promise + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.MetadataTypeItem | a single item | + ### TransactionalEmail.preDeployTasks(metadata) ⇒ TYPE.MetadataTypeItem diff --git a/lib/metadataTypes/TransactionalEmail.js b/lib/metadataTypes/TransactionalEmail.js index dda541cdc..a609ac3b1 100644 --- a/lib/metadataTypes/TransactionalEmail.js +++ b/lib/metadataTypes/TransactionalEmail.js @@ -13,6 +13,20 @@ const cache = require('../util/cache'); class TransactionalEmail extends TransactionalMessage { static subType = 'email'; + /** + * Updates a single item + * + * @param {TYPE.MetadataTypeItem} metadata a single item + * @returns {Promise} Promise + */ + static update(metadata) { + if (metadata.options?.createJourney) { + // only send this during create or else we might end up with an unexpected outcome + delete metadata.options.createJourney; + } + return super.update(metadata); + } + /** * prepares for deployment * @@ -42,15 +56,20 @@ class TransactionalEmail extends TransactionalMessage { } // subscriptions: list if (metadata.subscriptions?.r__list_PathName) { - metadata.subscriptions.list = { - ID: cache.getListObjectId(metadata.subscriptions.r__list_PathName, 'CustomerKey'), - }; + metadata.subscriptions.list = cache.getListObjectId( + metadata.subscriptions.r__list_PathName, + 'CustomerKey' + ); delete metadata.subscriptions.r__list_PathName; } // journey if (metadata.journey?.interactionKey) { - cache.searchForField('interaction', metadata.journey.interactionKey, 'key', 'key'); + // ! update & create enpoints dont accept journey.interactionKey. They only allow to create a new journey + // cache.searchForField('interaction', metadata.journey.interactionKey, 'key', 'key'); + metadata.options = 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; } return metadata; diff --git a/lib/metadataTypes/definitions/TransactionalEmail.definition.js b/lib/metadataTypes/definitions/TransactionalEmail.definition.js index d7634d55d..1916a25df 100644 --- a/lib/metadataTypes/definitions/TransactionalEmail.definition.js +++ b/lib/metadataTypes/definitions/TransactionalEmail.definition.js @@ -123,15 +123,21 @@ module.exports = { retrieving: true, template: true, }, - journey: { + 'options.createJourney': { isCreateable: true, isUpdateable: true, + retrieving: false, + template: false, + }, + journey: { + isCreateable: false, + isUpdateable: false, retrieving: true, template: true, }, 'journey.interactionKey': { - isCreateable: true, - isUpdateable: true, + 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 54c87907d..9d6c10fea 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 @@ -1,7 +1,7 @@ { "name": "testExisting_temail", "definitionKey": "testExisting_temail", - "description": "bla bla", + "description": "updated via deploy", "classification": "Default Transactional", "status": "Active", "createdDate": "2020-09-10T03:29:00", @@ -19,6 +19,6 @@ "trackLinks": true }, "journey": { - "interactionKey": "testExisting_journey" + "interactionKey": "testExisting_interaction" } } 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 new file mode 100644 index 000000000..9193a68fe --- /dev/null +++ b/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testNew_temail.transactionalEmail-meta.json @@ -0,0 +1,24 @@ +{ + "name": "testNew_temail", + "definitionKey": "testNew_temail", + "description": "created on deploy", + "classification": "Default Transactional", + "status": "Active", + "createdDate": "2020-09-10T03:29:00", + "modifiedDate": "2020-09-10T03:29:00", + "content": { + "customerKey": "testExisting_asset_message" + }, + "subscriptions": { + "dataExtension": "childBU_dataextension_test", + "autoAddSubscriber": true, + "updateSubscriber": true, + "r__list_PathName": "my subscribers/All Subscribers" + }, + "options": { + "trackLinks": true + }, + "journey": { + "interactionKey": "testExisting_interaction" + } +} diff --git a/test/resources/9999999/messaging/v1/email/definitions/post-response.json b/test/resources/9999999/messaging/v1/email/definitions/post-response.json new file mode 100644 index 000000000..0b2f8e238 --- /dev/null +++ b/test/resources/9999999/messaging/v1/email/definitions/post-response.json @@ -0,0 +1,19 @@ +{ + "requestId": "51f09ca1-1e5e-444a-8ddc-8e4ff80a9fa1", + "name": "testNew_temail", + "definitionKey": "testNew_temail", + "description": "created on deploy", + "classification": "Default Transactional", + "status": "Active", + "createdDate": "2022-12-06T06:08:00", + "modifiedDate": "2022-12-06T06:08:00", + "content": { "customerKey": "testExisting_asset_message" }, + "subscriptions": { + "dataExtension": "childBU_dataextension_test", + "list": "All Subscribers - 277", + "autoAddSubscriber": true, + "updateSubscriber": true + }, + "options": { "trackLinks": true }, + "journey": { "interactionKey": "1f936b41-cf1b-4207-aa33-715b3bf9eab7" } +} diff --git a/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/get-response.json b/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/get-response.json index 203dd8d90..6eb37bb17 100644 --- a/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/get-response.json +++ b/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/get-response.json @@ -12,10 +12,10 @@ "customerKey": "testExisting_asset_message" }, "subscriptions": { + "list": "All Subscribers - 277", "dataExtension": "childBU_dataextension_test", "autoAddSubscriber": true, - "updateSubscriber": true, - "r__list_PathName": "my subscribers/All Subscribers" + "updateSubscriber": true }, "options": { "trackLinks": true diff --git a/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/patch-response.json b/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/patch-response.json new file mode 100644 index 000000000..2e07e5e93 --- /dev/null +++ b/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/patch-response.json @@ -0,0 +1,26 @@ +{ + "requestId": "ae8511d0-9bf0-42d0-af4f-bd9c1218c812", + "name": "testExisting_temail", + "definitionKey": "testExisting_temail", + "definitionId": "0a650d90-755e-ed11-b852-48df37d1df5b", + "description": "updated via deploy", + "classification": "Default Transactional", + "status": "Active", + "createdDate": "2020-09-10T03:29:00", + "modifiedDate": "2020-09-10T03:29:00", + "content": { + "customerKey": "testExisting_asset_message" + }, + "subscriptions": { + "list": "All Subscribers - 277", + "dataExtension": "childBU_dataextension_test", + "autoAddSubscriber": true, + "updateSubscriber": true + }, + "options": { + "trackLinks": true + }, + "journey": { + "interactionKey": "testExisting_interaction" + } +} diff --git a/test/resources/9999999/transactionalEmail/patch-expected.json b/test/resources/9999999/transactionalEmail/patch-expected.json new file mode 100644 index 000000000..9d6c10fea --- /dev/null +++ b/test/resources/9999999/transactionalEmail/patch-expected.json @@ -0,0 +1,24 @@ +{ + "name": "testExisting_temail", + "definitionKey": "testExisting_temail", + "description": "updated via deploy", + "classification": "Default Transactional", + "status": "Active", + "createdDate": "2020-09-10T03:29:00", + "modifiedDate": "2020-09-10T03:29:00", + "content": { + "customerKey": "testExisting_asset_message" + }, + "subscriptions": { + "dataExtension": "childBU_dataextension_test", + "autoAddSubscriber": true, + "updateSubscriber": true, + "r__list_PathName": "my subscribers/All Subscribers" + }, + "options": { + "trackLinks": true + }, + "journey": { + "interactionKey": "testExisting_interaction" + } +} diff --git a/test/resources/9999999/transactionalEmail/post-expected.json b/test/resources/9999999/transactionalEmail/post-expected.json new file mode 100644 index 000000000..5f94f6127 --- /dev/null +++ b/test/resources/9999999/transactionalEmail/post-expected.json @@ -0,0 +1,24 @@ +{ + "name": "testNew_temail", + "definitionKey": "testNew_temail", + "description": "created on deploy", + "classification": "Default Transactional", + "status": "Active", + "createdDate": "2022-12-06T06:08:00", + "modifiedDate": "2022-12-06T06:08:00", + "content": { + "customerKey": "testExisting_asset_message" + }, + "subscriptions": { + "dataExtension": "childBU_dataextension_test", + "autoAddSubscriber": true, + "updateSubscriber": true, + "r__list_PathName": "my subscribers/All Subscribers" + }, + "options": { + "trackLinks": true + }, + "journey": { + "interactionKey": "1f936b41-cf1b-4207-aa33-715b3bf9eab7" + } +} diff --git a/test/transactionalEmail.test.js b/test/transactionalEmail.test.js index bf71a6178..011f8fb20 100644 --- a/test/transactionalEmail.test.js +++ b/test/transactionalEmail.test.js @@ -39,4 +39,40 @@ describe('transactionalEmail', () => { return; }); }); + describe('Deploy ================', () => { + beforeEach(() => { + testUtils.mockSetup(true); + }); + it('Should create & upsert a transactionalEmail', async () => { + // WHEN + await handler.deploy('testInstance/testBU', ['transactionalEmail']); + // THEN + // get results from cache + const result = cache.getCache(); + assert.equal( + result.transactionalEmail ? Object.keys(result.transactionalEmail).length : 0, + 2, + 'two transactionalEmails expected' + ); + // confirm created item + assert.deepEqual( + await testUtils.getActualJson('testNew_temail', 'transactionalEmail'), + await testUtils.getExpectedJson('9999999', 'transactionalEmail', 'post'), + 'returned JSON was not equal expected for insert transactionalEmail' + ); + // confirm updated item + assert.deepEqual( + await testUtils.getActualJson('testExisting_temail', 'transactionalEmail'), + await testUtils.getExpectedJson('9999999', 'transactionalEmail', 'patch'), + 'returned JSON was not equal expected for update transactionalEmail' + ); + // check number of API calls + assert.equal( + Object.values(testUtils.getAPIHistory()).flat().length, + 13, + 'Unexpected number of requests made' + ); + return; + }); + }); }); From 48f43b0f811884e42674f3cc9bfaa32fce43a7d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 6 Dec 2022 15:20:26 +0100 Subject: [PATCH 069/103] #557: add transactionalEmail test for TEMPLATING --- docs/dist/documentation.md | 2 +- .../TransactionalEmail.definition.js | 2 +- .../transactionalEmail/build-expected.json | 22 ++++++++++ .../transactionalEmail/template-expected.json | 22 ++++++++++ test/transactionalEmail.test.js | 42 +++++++++++++++++++ 5 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 test/resources/9999999/transactionalEmail/build-expected.json create mode 100644 test/resources/9999999/transactionalEmail/template-expected.json diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 06c83581f..a467619e9 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -4381,7 +4381,7 @@ Updates a single item | Param | Type | Description | | --- | --- | --- | -| metadata | TYPE.MetadataTypeItem | a single item | +| metadata | TYPE.MetadataTypeItem | how the item shall look after the update | diff --git a/lib/metadataTypes/definitions/TransactionalEmail.definition.js b/lib/metadataTypes/definitions/TransactionalEmail.definition.js index 1916a25df..8b8cd08e6 100644 --- a/lib/metadataTypes/definitions/TransactionalEmail.definition.js +++ b/lib/metadataTypes/definitions/TransactionalEmail.definition.js @@ -49,7 +49,7 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - template: false, + template: true, }, createdDate: { isCreateable: false, diff --git a/test/resources/9999999/transactionalEmail/build-expected.json b/test/resources/9999999/transactionalEmail/build-expected.json new file mode 100644 index 000000000..e3b7dad79 --- /dev/null +++ b/test/resources/9999999/transactionalEmail/build-expected.json @@ -0,0 +1,22 @@ +{ + "name": "testExisting_temail", + "definitionKey": "testExisting_temail", + "description": "foobar", + "classification": "Default Transactional", + "status": "Active", + "content": { + "customerKey": "testExisting_asset_message" + }, + "subscriptions": { + "dataExtension": "childBU_dataextension_testTarget", + "autoAddSubscriber": true, + "updateSubscriber": true, + "r__list_PathName": "my subscribers/All Subscribers" + }, + "options": { + "trackLinks": true + }, + "journey": { + "interactionKey": "testExisting_interaction" + } +} diff --git a/test/resources/9999999/transactionalEmail/template-expected.json b/test/resources/9999999/transactionalEmail/template-expected.json new file mode 100644 index 000000000..905f1162d --- /dev/null +++ b/test/resources/9999999/transactionalEmail/template-expected.json @@ -0,0 +1,22 @@ +{ + "name": "testExisting_temail", + "definitionKey": "testExisting_temail", + "description": "{{{description}}}", + "classification": "Default Transactional", + "status": "Active", + "content": { + "customerKey": "testExisting_asset_message" + }, + "subscriptions": { + "dataExtension": "childBU_dataextension{{{suffix}}}", + "autoAddSubscriber": true, + "updateSubscriber": true, + "r__list_PathName": "my subscribers/All Subscribers" + }, + "options": { + "trackLinks": true + }, + "journey": { + "interactionKey": "testExisting_interaction" + } +} diff --git a/test/transactionalEmail.test.js b/test/transactionalEmail.test.js index 011f8fb20..489535791 100644 --- a/test/transactionalEmail.test.js +++ b/test/transactionalEmail.test.js @@ -75,4 +75,46 @@ describe('transactionalEmail', () => { return; }); }); + describe('Templating ================', () => { + // it.skip('Should create a transactionalEmail template via retrieveAsTemplate and build it'); + it('Should create a transactionalEmail template via buildTemplate and build it', async () => { + // download first before we test buildTemplate + await handler.retrieve('testInstance/testBU', ['transactionalEmail']); + // buildTemplate + const result = await handler.buildTemplate( + 'testInstance/testBU', + 'transactionalEmail', + ['testExisting_temail'], + 'testSourceMarket' + ); + assert.equal( + result.transactionalEmail ? Object.keys(result.transactionalEmail).length : 0, + 1, + 'only one transactionalEmail expected' + ); + assert.deepEqual( + await testUtils.getActualTemplateJson('testExisting_temail', 'transactionalEmail'), + await testUtils.getExpectedJson('9999999', 'transactionalEmail', 'template'), + 'returned template JSON was not equal expected' + ); + // buildDefinition + await handler.buildDefinition( + 'testInstance/testBU', + 'transactionalEmail', + 'testExisting_temail', + 'testTargetMarket' + ); + assert.deepEqual( + await testUtils.getActualDeployJson('testExisting_temail', 'transactionalEmail'), + await testUtils.getExpectedJson('9999999', 'transactionalEmail', 'build'), + 'returned deployment JSON was not equal expected' + ); + assert.equal( + Object.values(testUtils.getAPIHistory()).flat().length, + 12, + 'Unexpected number of requests made' + ); + return; + }); + }); }); From 97b67317a097f066ab5c2cd6035170565350bbd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 6 Dec 2022 15:21:57 +0100 Subject: [PATCH 070/103] #557: warn user that we do not allow updating journey link on transactionalEmails --- lib/metadataTypes/TransactionalEmail.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/metadataTypes/TransactionalEmail.js b/lib/metadataTypes/TransactionalEmail.js index a609ac3b1..c9e2bb652 100644 --- a/lib/metadataTypes/TransactionalEmail.js +++ b/lib/metadataTypes/TransactionalEmail.js @@ -16,12 +16,17 @@ class TransactionalEmail extends TransactionalMessage { /** * Updates a single item * - * @param {TYPE.MetadataTypeItem} metadata a single item + * @param {TYPE.MetadataTypeItem} metadata how the item shall look after the update * @returns {Promise} Promise */ static update(metadata) { if (metadata.options?.createJourney) { // only send this during create or else we might end up with an unexpected outcome + Util.logger.warn( + ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${ + metadata[this.definition.keyField] + }): Cannot update journey link during update. If you need to relink this item to a new journey please delete and recreate it.` + ); delete metadata.options.createJourney; } return super.update(metadata); From 7da91b4f575ce8f8231c08c91e50840fa5452962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 6 Dec 2022 15:38:51 +0100 Subject: [PATCH 071/103] #560: eslint --fix --- lib/metadataTypes/TransactionalPush.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/metadataTypes/TransactionalPush.js b/lib/metadataTypes/TransactionalPush.js index b65102236..ff486e7c3 100644 --- a/lib/metadataTypes/TransactionalPush.js +++ b/lib/metadataTypes/TransactionalPush.js @@ -24,15 +24,15 @@ class TransactionalPush extends MetadataType { static async retrieve(retrieveDir, _, __, ___, key) { let keyList; const baseUri = '/messaging/v1/push/definitions/'; - if (!key) { + if (key) { + // Retrieve single + keyList = [key]; + } else { // Retrieve all const response = this.definition.restPagination ? await this.client.rest.getBulk(baseUri) : await this.client.rest.get(baseUri); keyList = Object.keys(this.parseResponseBody(response)); - } else { - // Retrieve single - keyList = [key]; } // get all sms with additional details not given by the list endpoint From ff2e430a87ec82930f13bd7fbe7df25fb7559315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 6 Dec 2022 17:33:09 +0100 Subject: [PATCH 072/103] #560: base commit of class --- docs/dist/documentation.md | 79 ++--------------------- lib/metadataTypes/TransactionalPush.js | 89 ++------------------------ 2 files changed, 10 insertions(+), 158 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index e6514dc14..d6c4251dc 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -107,8 +107,8 @@ Provides default functionality that can be overwritten by child metadata type cl
TransactionalMessageMetadataType

TransactionalMessage MetadataType

-
TransactionalPushMetadataType
-

TransactionalPush MetadataType

+
TransactionalPushTransactionalMessage
+

TransactionalPush TransactionalMessage

TransactionalSMSTransactionalMessage

TransactionalSMS MetadataType

@@ -4488,80 +4488,11 @@ Delete a metadata item from the specified business unit -## TransactionalPush ⇐ [MetadataType](#MetadataType) -TransactionalPush MetadataType +## TransactionalPush ⇐ [TransactionalMessage](#TransactionalMessage) +TransactionalPush TransactionalMessage **Kind**: global class -**Extends**: [MetadataType](#MetadataType) - -* [TransactionalPush](#TransactionalPush) ⇐ [MetadataType](#MetadataType) - * [.retrieve(retrieveDir, [_], [__], [___], [key])](#TransactionalPush.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> - * [.retrieveForCache()](#TransactionalPush.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> - * [.update(metadata)](#TransactionalPush.update) ⇒ Promise - * [.create(metadata)](#TransactionalPush.create) ⇒ Promise - * [.deleteByKey(buObject, key)](#TransactionalPush.deleteByKey) ⇒ Promise.<boolean> - - - -### TransactionalPush.retrieve(retrieveDir, [_], [__], [___], [key]) ⇒ Promise.<TYPE.MetadataTypeMapObj> -Retrieves Metadata of Mobile Keywords -Endpoint /legacy/v1/beta/mobile/code/ return all Mobile Codes with all details. - -**Kind**: static method of [TransactionalPush](#TransactionalPush) -**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 | -| [___] | void | unused parameter | -| [key] | string | customer key of single item to retrieve | - - - -### TransactionalPush.retrieveForCache() ⇒ Promise.<TYPE.MetadataTypeMapObj> -Retrieves event definition metadata for caching - -**Kind**: static method of [TransactionalPush](#TransactionalPush) -**Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise of metadata - - -### TransactionalPush.update(metadata) ⇒ Promise -Updates a single item - -**Kind**: static method of [TransactionalPush](#TransactionalPush) -**Returns**: Promise - Promise - -| Param | Type | Description | -| --- | --- | --- | -| metadata | TYPE.MetadataTypeItem | a single item | - - - -### TransactionalPush.create(metadata) ⇒ Promise -Creates a single item - -**Kind**: static method of [TransactionalPush](#TransactionalPush) -**Returns**: Promise - Promise - -| Param | Type | Description | -| --- | --- | --- | -| metadata | TYPE.MetadataTypeItem | a single item | - - - -### TransactionalPush.deleteByKey(buObject, key) ⇒ Promise.<boolean> -Delete a metadata item from the specified business unit - -**Kind**: static method of [TransactionalPush](#TransactionalPush) -**Returns**: Promise.<boolean> - deletion success status - -| Param | Type | Description | -| --- | --- | --- | -| buObject | TYPE.BuObject | references credentials | -| key | string | Identifier of item | - +**Extends**: [TransactionalMessage](#TransactionalMessage) ## TransactionalSMS ⇐ [TransactionalMessage](#TransactionalMessage) diff --git a/lib/metadataTypes/TransactionalPush.js b/lib/metadataTypes/TransactionalPush.js index ff486e7c3..50b9f62d9 100644 --- a/lib/metadataTypes/TransactionalPush.js +++ b/lib/metadataTypes/TransactionalPush.js @@ -1,93 +1,14 @@ 'use strict'; -const TYPE = require('../../types/mcdev.d'); -const MetadataType = require('./MetadataType'); -const Util = require('../util/util'); +const TransactionalMessage = require('./TransactionalMessage'); /** - * TransactionalPush MetadataType + * TransactionalPush TransactionalMessage * - * @augments MetadataType + * @augments TransactionalMessage */ -class TransactionalPush extends MetadataType { - /** - * Retrieves Metadata of Mobile Keywords - * Endpoint /legacy/v1/beta/mobile/code/ return all Mobile Codes with all details. - * - * @param {string} retrieveDir Directory where retrieved metadata directory will be saved - * @param {void} [_] unused parameter - * @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 keyList; - const baseUri = '/messaging/v1/push/definitions/'; - if (key) { - // Retrieve single - keyList = [key]; - } else { - // Retrieve all - const response = this.definition.restPagination - ? await this.client.rest.getBulk(baseUri) - : await this.client.rest.get(baseUri); - keyList = Object.keys(this.parseResponseBody(response)); - } - - // get all sms with additional details not given by the list endpoint - const details = keyList - ? await Promise.all(keyList.map((key) => this.client.rest.get(baseUri + (key || '')))) - : []; - - const parsed = this.parseResponseBody({ definitions: details }); - - // * retrieveDir is mandatory in this method as it is not used for caching (there is a seperate method for that) - const savedMetadata = await this.saveResults(parsed, retrieveDir, null, null); - Util.logger.info( - `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` - ); - - return { metadata: savedMetadata, type: this.definition.type }; - } - - /** - * Retrieves event definition metadata for caching - * - * @returns {Promise.} Promise of metadata - */ - static retrieveForCache() { - return super.retrieveREST(null, '/messaging/v1/push/definitions/'); - } - /** - * Updates a single item - * - * @param {TYPE.MetadataTypeItem} metadata a single item - * @returns {Promise} Promise - */ - static update(metadata) { - return super.updateREST(metadata, '/messaging/v1/push/definitions'); - } - - /** - * Creates a single item - * - * @param {TYPE.MetadataTypeItem} metadata a single item - * @returns {Promise} Promise - */ - static create(metadata) { - return super.createREST(metadata, '/messaging/v1/push/definitions'); - } - /** - * Delete a metadata item from the specified business unit - * - * @param {TYPE.BuObject} buObject references credentials - * @param {string} key Identifier of item - * @returns {Promise.} deletion success status - */ - static deleteByKey(buObject, key) { - return super.deleteByKeyREST(buObject, '/messaging/v1/push/definitions/' + key, key, false); - } +class TransactionalPush extends TransactionalMessage { + static subType = 'push'; } // Assign definition to static attributes From 29179d3c56ad8a9c3de59d65c4d2d7b62283ac9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 6 Dec 2022 17:50:59 +0100 Subject: [PATCH 073/103] #582: ensure only credential failure stops retrieval, not issues with a single type --- lib/Retriever.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/Retriever.js b/lib/Retriever.js index 44ea220b4..ae71bd587 100644 --- a/lib/Retriever.js +++ b/lib/Retriever.js @@ -163,9 +163,13 @@ class Retriever { } } } catch (ex) { - Util.logger.errorStack(ex, `Retrieving ${metadataType} failed`); - // do not continue retrieving if one type failed. simply skip processing the rest of the for-loop - break; + if (ex.message.startsWith('Client authentication failed.')) { + // do not continue retrieving if we logged an authentication issue + Util.logger.error(ex.message); + break; + } else { + Util.logger.errorStack(ex, ` - Retrieving ${metadataType} failed`); + } } } return retrieveChangelog; From a47558963d56d03bf3138b2b186ed8036658fa92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Tue, 6 Dec 2022 17:59:19 +0100 Subject: [PATCH 074/103] #582: check for invalid credentials more future proof --- lib/Retriever.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Retriever.js b/lib/Retriever.js index ae71bd587..08083d86a 100644 --- a/lib/Retriever.js +++ b/lib/Retriever.js @@ -163,7 +163,10 @@ class Retriever { } } } catch (ex) { - if (ex.message.startsWith('Client authentication failed.')) { + if ( + ex.code === 'invalid_client' || + ex.message.startsWith('Client authentication failed.') + ) { // do not continue retrieving if we logged an authentication issue Util.logger.error(ex.message); break; From b14248d1a69e91995806afcdc6963df90c7de8df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 7 Dec 2022 10:40:48 +0100 Subject: [PATCH 075/103] #560: add postRetrieve & preDeploy tasks for dependencies --- docs/dist/documentation.md | 29 +++++++++ lib/metadataTypes/TransactionalPush.js | 60 +++++++++++++++++++ .../TransactionalPush.definition.js | 14 ++++- 3 files changed, 102 insertions(+), 1 deletion(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index d6c4251dc..eac121c64 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -4493,6 +4493,35 @@ TransactionalPush TransactionalMessage **Kind**: global class **Extends**: [TransactionalMessage](#TransactionalMessage) + +* [TransactionalPush](#TransactionalPush) ⇐ [TransactionalMessage](#TransactionalMessage) + * [.preDeployTasks(metadata)](#TransactionalPush.preDeployTasks) ⇒ TYPE.MetadataTypeItem + * [.postRetrieveTasks(metadata)](#TransactionalPush.postRetrieveTasks) ⇒ TYPE.MetadataTypeItem + + + +### TransactionalPush.preDeployTasks(metadata) ⇒ TYPE.MetadataTypeItem +prepares for deployment + +**Kind**: static method of [TransactionalPush](#TransactionalPush) +**Returns**: TYPE.MetadataTypeItem - Promise + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.MetadataTypeItem | a single item | + + + +### TransactionalPush.postRetrieveTasks(metadata) ⇒ TYPE.MetadataTypeItem +manages post retrieve steps + +**Kind**: static method of [TransactionalPush](#TransactionalPush) +**Returns**: TYPE.MetadataTypeItem - a single item + +| Param | Type | Description | +| --- | --- | --- | +| metadata | TYPE.MetadataTypeItem | a single item | + ## TransactionalSMS ⇐ [TransactionalMessage](#TransactionalMessage) diff --git a/lib/metadataTypes/TransactionalPush.js b/lib/metadataTypes/TransactionalPush.js index 50b9f62d9..1b97b6922 100644 --- a/lib/metadataTypes/TransactionalPush.js +++ b/lib/metadataTypes/TransactionalPush.js @@ -1,6 +1,9 @@ 'use strict'; +const TYPE = require('../../types/mcdev.d'); const TransactionalMessage = require('./TransactionalMessage'); +const Util = require('../util/util'); +const cache = require('../util/cache'); /** * TransactionalPush TransactionalMessage @@ -9,6 +12,63 @@ const TransactionalMessage = require('./TransactionalMessage'); */ class TransactionalPush extends TransactionalMessage { static subType = 'push'; + + /** + * prepares for deployment + * + * @param {TYPE.MetadataTypeItem} metadata a single item + * @returns {TYPE.MetadataTypeItem} Promise + */ + static async preDeployTasks(metadata) { + // asset + if (metadata.content?.customerKey) { + // we merely want to be able to show an error if it does not exist + cache.searchForField( + 'asset', + metadata.content.customerKey, + 'customerKey', + 'customerKey' + ); + } + if (metadata.options?.badge && typeof metadata.options?.badge !== 'string') { + // ensure it's a string, or else the API will return an error. Our SDK turns numbers in strings into actual numbers + metadata.options.badge += ''; + } + + return metadata; + } + /** + * manages post retrieve steps + * + * @param {TYPE.MetadataTypeItem} metadata a single item + * @returns {TYPE.MetadataTypeItem} a single item + */ + static postRetrieveTasks(metadata) { + // asset + if (metadata.content?.customerKey) { + try { + // we merely want to be able to show an error if it does not exist + cache.searchForField( + 'asset', + metadata.content.customerKey, + 'customerKey', + 'customerKey' + ); + } catch (ex) { + Util.logger.warn( + ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${ + metadata[this.definition.keyField] + }): ${ex.message}.` + ); + } + } + if (metadata.options?.badge && typeof metadata.options?.badge !== 'string') { + // ensure it's a string, or else the API will return an error. Our SDK turns numbers in strings into actual numbers + metadata.options.badge += ''; + } + + return metadata; + } } // Assign definition to static attributes diff --git a/lib/metadataTypes/definitions/TransactionalPush.definition.js b/lib/metadataTypes/definitions/TransactionalPush.definition.js index e3efd5cd2..21caee96e 100644 --- a/lib/metadataTypes/definitions/TransactionalPush.definition.js +++ b/lib/metadataTypes/definitions/TransactionalPush.definition.js @@ -1,6 +1,6 @@ module.exports = { bodyIteratorField: 'definitions', - dependencies: [], + dependencies: ['asset-asset'], hasExtended: false, idField: 'definitionId', keyField: 'definitionKey', @@ -87,6 +87,18 @@ module.exports = { retrieving: true, template: true, }, + 'options.customKeys[].value': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'options.customKeys[].key': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, applicationId: { isCreateable: true, isUpdateable: true, From e7d73a6d2753cfafa6a6ca9624d36d4a7e23781d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 7 Dec 2022 10:45:19 +0100 Subject: [PATCH 076/103] #560: add transactionalPush test for DEPLOY --- ...Existing_tpush.transactionalPush-meta.json | 18 +++ .../testNew_tpush.transactionalPush-meta.json | 18 +++ .../content/assets/query/post-response.json | 26 +++- .../v1/push/definitions/get-response.json | 15 +++ .../v1/push/definitions/post-response.json | 13 ++ .../testExisting_tpush/patch-response.json | 13 ++ .../transactionalPush/patch-expected.json | 16 +++ .../transactionalPush/post-expected.json | 16 +++ test/transactionalPush.test.js | 120 ++++++++++++++++++ 9 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 test/mockRoot/deploy/testInstance/testBU/transactionalPush/testExisting_tpush.transactionalPush-meta.json create mode 100644 test/mockRoot/deploy/testInstance/testBU/transactionalPush/testNew_tpush.transactionalPush-meta.json create mode 100644 test/resources/9999999/messaging/v1/push/definitions/get-response.json create mode 100644 test/resources/9999999/messaging/v1/push/definitions/post-response.json create mode 100644 test/resources/9999999/messaging/v1/push/definitions/testExisting_tpush/patch-response.json create mode 100644 test/resources/9999999/transactionalPush/patch-expected.json create mode 100644 test/resources/9999999/transactionalPush/post-expected.json create mode 100644 test/transactionalPush.test.js diff --git a/test/mockRoot/deploy/testInstance/testBU/transactionalPush/testExisting_tpush.transactionalPush-meta.json b/test/mockRoot/deploy/testInstance/testBU/transactionalPush/testExisting_tpush.transactionalPush-meta.json new file mode 100644 index 000000000..39407d688 --- /dev/null +++ b/test/mockRoot/deploy/testInstance/testBU/transactionalPush/testExisting_tpush.transactionalPush-meta.json @@ -0,0 +1,18 @@ +{ + "definitionKey": "testExisting_tpush", + "name": "testExisting_tpush", + "status": "Active", + "applicationId": "ffbab4c9-fbf6-4b87-9c9a-10c6ce86e81d", + "description": "updated via deploy; note that applicationId can only be manually set up in Setup - Mobile Push", + "content": { + "customerKey": "mobileMessage_test" + }, + "options": { + "sound": "temp.wmv", + "badge": "1", + "customKeys": [ + { "key": "key1", "value": "value1" }, + { "key": "key2", "value": "value2" } + ] + } +} diff --git a/test/mockRoot/deploy/testInstance/testBU/transactionalPush/testNew_tpush.transactionalPush-meta.json b/test/mockRoot/deploy/testInstance/testBU/transactionalPush/testNew_tpush.transactionalPush-meta.json new file mode 100644 index 000000000..e3c0301cf --- /dev/null +++ b/test/mockRoot/deploy/testInstance/testBU/transactionalPush/testNew_tpush.transactionalPush-meta.json @@ -0,0 +1,18 @@ +{ + "definitionKey": "testNew_tpush", + "name": "testNew_tpush", + "status": "Active", + "applicationId": "ffbab4c9-fbf6-4b87-9c9a-10c6ce86e81d", + "description": "created on deploy; note that applicationId can only be manually set up in Setup - Mobile Push", + "content": { + "customerKey": "mobileMessage_test" + }, + "options": { + "sound": "temp.wmv", + "badge": "1", + "customKeys": [ + { "key": "key1", "value": "value1" }, + { "key": "key2", "value": "value2" } + ] + } +} diff --git a/test/resources/9999999/asset/v1/content/assets/query/post-response.json b/test/resources/9999999/asset/v1/content/assets/query/post-response.json index e80d2232a..7bb8d6ea8 100644 --- a/test/resources/9999999/asset/v1/content/assets/query/post-response.json +++ b/test/resources/9999999/asset/v1/content/assets/query/post-response.json @@ -1,5 +1,5 @@ { - "count": 1, + "count": 2, "page": 1, "pageSize": 50, "links": {}, @@ -43,6 +43,30 @@ "legacyCategoryId": 290835 }, "modelVersion": 2 + }, + { + "id": 1209971, + "customerKey": "mobileMessage_test", + "assetType": { "id": 230, "name": "jsonmessage", "displayName": "JSON Message" }, + "name": "mobileMessage_test", + "createdDate": "2022-12-07T02:49:36.857-06:00", + "createdBy": { + "id": 700301950, + "email": "joern.berkefeld@accenture.com", + "name": "J├Ârn Berkefeld (ASGR)", + "userId": "700301950" + }, + "modifiedDate": "2022-12-07T02:49:57.44-06:00", + "modifiedBy": { + "id": 700301950, + "email": "joern.berkefeld@accenture.com", + "name": "J├Ârn Berkefeld (ASGR)", + "userId": "700301950" + }, + "status": { "id": 1, "name": "Draft" }, + "category": { "id": 283608, "name": "Content Builder", "parentId": 0 }, + "availableViews": ["push"], + "modelVersion": 2 } ] } diff --git a/test/resources/9999999/messaging/v1/push/definitions/get-response.json b/test/resources/9999999/messaging/v1/push/definitions/get-response.json new file mode 100644 index 000000000..2ee2ff921 --- /dev/null +++ b/test/resources/9999999/messaging/v1/push/definitions/get-response.json @@ -0,0 +1,15 @@ +{ + "requestId": "4a89b42d-9e71-4ddd-9b4e-a3b00edbfdfb", + "definitions": [ + { + "name": "testExisting_tpush", + "definitionKey": "testExisting_tpush", + "status": "Active", + "createdDate": "2022-12-07T02:56:00", + "modifiedDate": "2022-12-07T02:56:00" + } + ], + "count": 1, + "page": 1, + "pageSize": 50 +} diff --git a/test/resources/9999999/messaging/v1/push/definitions/post-response.json b/test/resources/9999999/messaging/v1/push/definitions/post-response.json new file mode 100644 index 000000000..fcb7834ce --- /dev/null +++ b/test/resources/9999999/messaging/v1/push/definitions/post-response.json @@ -0,0 +1,13 @@ +{ + "requestId": "ee81df03-ab61-454d-a54a-025675cf2b41", + "name": "testNew_tpush", + "definitionKey": "testNew_tpush", + "definitionId": "2106d3f9-0c76-ed11-b849-48df37d1de8b", + "description": "created on deploy; note that applicationId can only be manually set up in Setup - Mobile Push", + "applicationId": "ffbab4c9-fbf6-4b87-9c9a-10c6ce86e81d", + "status": "Active", + "createdDate": "2022-12-07T02:56:00", + "modifiedDate": "2022-12-07T03:26:00", + "content": { "customerKey": "mobileMessage_test" }, + "options": { "sound": "temp.wmv", "badge": "1" } +} diff --git a/test/resources/9999999/messaging/v1/push/definitions/testExisting_tpush/patch-response.json b/test/resources/9999999/messaging/v1/push/definitions/testExisting_tpush/patch-response.json new file mode 100644 index 000000000..8565f8f97 --- /dev/null +++ b/test/resources/9999999/messaging/v1/push/definitions/testExisting_tpush/patch-response.json @@ -0,0 +1,13 @@ +{ + "requestId": "6d796b32-dff3-4564-ae36-f51edb0e5ec6", + "name": "testExisting_tpush", + "definitionKey": "testExisting_tpush", + "definitionId": "2106d3f9-0c76-ed11-b849-48df37d1de8b", + "description": "updated via deploy; note that applicationId can only be manually set up in Setup - Mobile Push", + "applicationId": "ffbab4c9-fbf6-4b87-9c9a-10c6ce86e81d", + "status": "Active", + "createdDate": "2022-12-07T02:56:00", + "modifiedDate": "2022-12-07T03:26:00", + "content": { "customerKey": "mobileMessage_test" }, + "options": { "sound": "temp.wmv", "badge": "1" } +} diff --git a/test/resources/9999999/transactionalPush/patch-expected.json b/test/resources/9999999/transactionalPush/patch-expected.json new file mode 100644 index 000000000..2cf1ecd9b --- /dev/null +++ b/test/resources/9999999/transactionalPush/patch-expected.json @@ -0,0 +1,16 @@ +{ + "definitionKey": "testExisting_tpush", + "name": "testExisting_tpush", + "status": "Active", + "applicationId": "ffbab4c9-fbf6-4b87-9c9a-10c6ce86e81d", + "description": "updated via deploy; note that applicationId can only be manually set up in Setup - Mobile Push", + "createdDate": "2022-12-07T02:56:00", + "modifiedDate": "2022-12-07T03:26:00", + "content": { + "customerKey": "mobileMessage_test" + }, + "options": { + "sound": "temp.wmv", + "badge": "1" + } +} diff --git a/test/resources/9999999/transactionalPush/post-expected.json b/test/resources/9999999/transactionalPush/post-expected.json new file mode 100644 index 000000000..7f87af359 --- /dev/null +++ b/test/resources/9999999/transactionalPush/post-expected.json @@ -0,0 +1,16 @@ +{ + "definitionKey": "testNew_tpush", + "name": "testNew_tpush", + "status": "Active", + "applicationId": "ffbab4c9-fbf6-4b87-9c9a-10c6ce86e81d", + "description": "created on deploy; note that applicationId can only be manually set up in Setup - Mobile Push", + "createdDate": "2022-12-07T02:56:00", + "modifiedDate": "2022-12-07T03:26:00", + "content": { + "customerKey": "mobileMessage_test" + }, + "options": { + "sound": "temp.wmv", + "badge": "1" + } +} diff --git a/test/transactionalPush.test.js b/test/transactionalPush.test.js new file mode 100644 index 000000000..c6a438268 --- /dev/null +++ b/test/transactionalPush.test.js @@ -0,0 +1,120 @@ +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('transactionalPush', () => { + beforeEach(() => { + testUtils.mockSetup(); + }); + afterEach(() => { + testUtils.mockReset(); + }); + + // describe('Retrieve ================', () => { + // it('Should retrieve a transactionalPush', async () => { + // // WHEN + // await handler.retrieve('testInstance/testBU', ['transactionalPush']); + // // THEN + // // get results from cache + // const result = cache.getCache(); + // assert.equal( + // result.transactionalPush ? Object.keys(result.transactionalPush).length : 0, + // 1, + // 'only one transactionalPush expected' + // ); + // assert.deepEqual( + // await testUtils.getActualJson('testExisting_tpush', 'transactionalPush'), + // await testUtils.getExpectedJson('9999999', 'transactionalPush', 'get'), + // 'returned JSON was not equal expected' + // ); + // assert.equal( + // Object.values(testUtils.getAPIHistory()).flat().length, + // 12, + // 'Unexpected number of requests made' + // ); + // return; + // }); + // }); + describe('Deploy ================', () => { + beforeEach(() => { + testUtils.mockSetup(true); + }); + it('Should create & upsert a transactionalPush', async () => { + // WHEN + await handler.deploy('testInstance/testBU', ['transactionalPush']); + // THEN + // get results from cache + const result = cache.getCache(); + assert.equal( + result.transactionalPush ? Object.keys(result.transactionalPush).length : 0, + 2, + 'two transactionalPushs expected' + ); + // confirm created item + assert.deepEqual( + await testUtils.getActualJson('testNew_tpush', 'transactionalPush'), + await testUtils.getExpectedJson('9999999', 'transactionalPush', 'post'), + 'returned JSON was not equal expected for insert transactionalPush' + ); + // confirm updated item + assert.deepEqual( + await testUtils.getActualJson('testExisting_tpush', 'transactionalPush'), + await testUtils.getExpectedJson('9999999', 'transactionalPush', 'patch'), + 'returned JSON was not equal expected for update transactionalPush' + ); + // check number of API calls + assert.equal( + Object.values(testUtils.getAPIHistory()).flat().length, + 4, + 'Unexpected number of requests made' + ); + return; + }); + }); + // describe('Templating ================', () => { + // // it.skip('Should create a transactionalPush template via retrieveAsTemplate and build it'); + // it('Should create a transactionalPush template via buildTemplate and build it', async () => { + // // download first before we test buildTemplate + // await handler.retrieve('testInstance/testBU', ['transactionalPush']); + // // buildTemplate + // const result = await handler.buildTemplate( + // 'testInstance/testBU', + // 'transactionalPush', + // ['testExisting_tpush'], + // 'testSourceMarket' + // ); + // assert.equal( + // result.transactionalPush ? Object.keys(result.transactionalPush).length : 0, + // 1, + // 'only one transactionalPush expected' + // ); + // assert.deepEqual( + // await testUtils.getActualTemplateJson('testExisting_tpush', 'transactionalPush'), + // await testUtils.getExpectedJson('9999999', 'transactionalPush', 'template'), + // 'returned template JSON was not equal expected' + // ); + // // buildDefinition + // await handler.buildDefinition( + // 'testInstance/testBU', + // 'transactionalPush', + // 'testExisting_tpush', + // 'testTargetMarket' + // ); + // assert.deepEqual( + // await testUtils.getActualDeployJson('testExisting_tpush', 'transactionalPush'), + // await testUtils.getExpectedJson('9999999', 'transactionalPush', 'build'), + // 'returned deployment JSON was not equal expected' + // ); + // assert.equal( + // Object.values(testUtils.getAPIHistory()).flat().length, + // 12, + // 'Unexpected number of requests made' + // ); + // return; + // }); + // }); +}); From a064667ecf5a1ae23dae1703b67710cf968939fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 7 Dec 2022 10:55:22 +0100 Subject: [PATCH 077/103] #560: add transactionalPush test for RETRIEVE --- .../testExisting_tpush/get-response.json | 13 +++++ .../transactionalPush/get-expected.json | 11 ++++ test/transactionalPush.test.js | 50 +++++++++---------- 3 files changed, 49 insertions(+), 25 deletions(-) create mode 100644 test/resources/9999999/messaging/v1/push/definitions/testExisting_tpush/get-response.json create mode 100644 test/resources/9999999/transactionalPush/get-expected.json diff --git a/test/resources/9999999/messaging/v1/push/definitions/testExisting_tpush/get-response.json b/test/resources/9999999/messaging/v1/push/definitions/testExisting_tpush/get-response.json new file mode 100644 index 000000000..217217f71 --- /dev/null +++ b/test/resources/9999999/messaging/v1/push/definitions/testExisting_tpush/get-response.json @@ -0,0 +1,13 @@ +{ + "requestId": "64dac58f-f6d0-4bea-9489-eb058c491a87", + "name": "testExisting_tpush", + "definitionKey": "testExisting_tpush", + "definitionId": "2106d3f9-0c76-ed11-b849-48df37d1de8b", + "description": "note that applicationId can only be manually set up in Setup - Mobile Push", + "applicationId": "ffbab4c9-fbf6-4b87-9c9a-10c6ce86e81d", + "status": "Active", + "createdDate": "2022-12-07T02:56:00", + "modifiedDate": "2022-12-07T02:56:00", + "content": { "customerKey": "mobileMessage_test" }, + "options": { "sound": "temp.wmv", "badge": "1" } +} diff --git a/test/resources/9999999/transactionalPush/get-expected.json b/test/resources/9999999/transactionalPush/get-expected.json new file mode 100644 index 000000000..ff58fe20f --- /dev/null +++ b/test/resources/9999999/transactionalPush/get-expected.json @@ -0,0 +1,11 @@ +{ + "name": "testExisting_tpush", + "definitionKey": "testExisting_tpush", + "description": "note that applicationId can only be manually set up in Setup - Mobile Push", + "applicationId": "ffbab4c9-fbf6-4b87-9c9a-10c6ce86e81d", + "status": "Active", + "createdDate": "2022-12-07T02:56:00", + "modifiedDate": "2022-12-07T02:56:00", + "content": { "customerKey": "mobileMessage_test" }, + "options": { "sound": "temp.wmv", "badge": "1" } +} diff --git a/test/transactionalPush.test.js b/test/transactionalPush.test.js index c6a438268..a11babc70 100644 --- a/test/transactionalPush.test.js +++ b/test/transactionalPush.test.js @@ -14,31 +14,31 @@ describe('transactionalPush', () => { testUtils.mockReset(); }); - // describe('Retrieve ================', () => { - // it('Should retrieve a transactionalPush', async () => { - // // WHEN - // await handler.retrieve('testInstance/testBU', ['transactionalPush']); - // // THEN - // // get results from cache - // const result = cache.getCache(); - // assert.equal( - // result.transactionalPush ? Object.keys(result.transactionalPush).length : 0, - // 1, - // 'only one transactionalPush expected' - // ); - // assert.deepEqual( - // await testUtils.getActualJson('testExisting_tpush', 'transactionalPush'), - // await testUtils.getExpectedJson('9999999', 'transactionalPush', 'get'), - // 'returned JSON was not equal expected' - // ); - // assert.equal( - // Object.values(testUtils.getAPIHistory()).flat().length, - // 12, - // 'Unexpected number of requests made' - // ); - // return; - // }); - // }); + describe('Retrieve ================', () => { + it('Should retrieve a transactionalPush', async () => { + // WHEN + await handler.retrieve('testInstance/testBU', ['transactionalPush']); + // THEN + // get results from cache + const result = cache.getCache(); + assert.equal( + result.transactionalPush ? Object.keys(result.transactionalPush).length : 0, + 1, + 'only one transactionalPush expected' + ); + assert.deepEqual( + await testUtils.getActualJson('testExisting_tpush', 'transactionalPush'), + await testUtils.getExpectedJson('9999999', 'transactionalPush', 'get'), + 'returned JSON was not equal expected' + ); + assert.equal( + Object.values(testUtils.getAPIHistory()).flat().length, + 3, + 'Unexpected number of requests made' + ); + return; + }); + }); describe('Deploy ================', () => { beforeEach(() => { testUtils.mockSetup(true); From 6cfe3803c33f52fa5c6794b77cc4ab86c2e6f20d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 7 Dec 2022 11:04:13 +0100 Subject: [PATCH 078/103] #560: add transactionalPush test for TEMPLATING --- .../testExisting_tpush/get-response.json | 2 +- .../transactionalPush/build-expected.json | 8 ++ .../transactionalPush/get-expected.json | 2 +- .../transactionalPush/template-expected.json | 8 ++ test/transactionalPush.test.js | 84 +++++++++---------- 5 files changed, 60 insertions(+), 44 deletions(-) create mode 100644 test/resources/9999999/transactionalPush/build-expected.json create mode 100644 test/resources/9999999/transactionalPush/template-expected.json diff --git a/test/resources/9999999/messaging/v1/push/definitions/testExisting_tpush/get-response.json b/test/resources/9999999/messaging/v1/push/definitions/testExisting_tpush/get-response.json index 217217f71..bb588cc0f 100644 --- a/test/resources/9999999/messaging/v1/push/definitions/testExisting_tpush/get-response.json +++ b/test/resources/9999999/messaging/v1/push/definitions/testExisting_tpush/get-response.json @@ -3,7 +3,7 @@ "name": "testExisting_tpush", "definitionKey": "testExisting_tpush", "definitionId": "2106d3f9-0c76-ed11-b849-48df37d1de8b", - "description": "note that applicationId can only be manually set up in Setup - Mobile Push", + "description": "bla bla. note that applicationId can only be manually set up in Setup - Mobile Push", "applicationId": "ffbab4c9-fbf6-4b87-9c9a-10c6ce86e81d", "status": "Active", "createdDate": "2022-12-07T02:56:00", diff --git a/test/resources/9999999/transactionalPush/build-expected.json b/test/resources/9999999/transactionalPush/build-expected.json new file mode 100644 index 000000000..89036d8a9 --- /dev/null +++ b/test/resources/9999999/transactionalPush/build-expected.json @@ -0,0 +1,8 @@ +{ + "name": "testExisting_tpush", + "definitionKey": "testExisting_tpush", + "description": "foobar. note that applicationId can only be manually set up in Setup - Mobile Push", + "applicationId": "ffbab4c9-fbf6-4b87-9c9a-10c6ce86e81d", + "content": { "customerKey": "mobileMessage_testTarget" }, + "options": { "sound": "temp.wmv", "badge": "1" } +} diff --git a/test/resources/9999999/transactionalPush/get-expected.json b/test/resources/9999999/transactionalPush/get-expected.json index ff58fe20f..242a9a809 100644 --- a/test/resources/9999999/transactionalPush/get-expected.json +++ b/test/resources/9999999/transactionalPush/get-expected.json @@ -1,7 +1,7 @@ { "name": "testExisting_tpush", "definitionKey": "testExisting_tpush", - "description": "note that applicationId can only be manually set up in Setup - Mobile Push", + "description": "bla bla. note that applicationId can only be manually set up in Setup - Mobile Push", "applicationId": "ffbab4c9-fbf6-4b87-9c9a-10c6ce86e81d", "status": "Active", "createdDate": "2022-12-07T02:56:00", diff --git a/test/resources/9999999/transactionalPush/template-expected.json b/test/resources/9999999/transactionalPush/template-expected.json new file mode 100644 index 000000000..26266ff35 --- /dev/null +++ b/test/resources/9999999/transactionalPush/template-expected.json @@ -0,0 +1,8 @@ +{ + "name": "testExisting_tpush", + "definitionKey": "testExisting_tpush", + "description": "{{{description}}}. note that applicationId can only be manually set up in Setup - Mobile Push", + "applicationId": "ffbab4c9-fbf6-4b87-9c9a-10c6ce86e81d", + "content": { "customerKey": "mobileMessage{{{suffix}}}" }, + "options": { "sound": "temp.wmv", "badge": "1" } +} diff --git a/test/transactionalPush.test.js b/test/transactionalPush.test.js index a11babc70..4fa770568 100644 --- a/test/transactionalPush.test.js +++ b/test/transactionalPush.test.js @@ -75,46 +75,46 @@ describe('transactionalPush', () => { return; }); }); - // describe('Templating ================', () => { - // // it.skip('Should create a transactionalPush template via retrieveAsTemplate and build it'); - // it('Should create a transactionalPush template via buildTemplate and build it', async () => { - // // download first before we test buildTemplate - // await handler.retrieve('testInstance/testBU', ['transactionalPush']); - // // buildTemplate - // const result = await handler.buildTemplate( - // 'testInstance/testBU', - // 'transactionalPush', - // ['testExisting_tpush'], - // 'testSourceMarket' - // ); - // assert.equal( - // result.transactionalPush ? Object.keys(result.transactionalPush).length : 0, - // 1, - // 'only one transactionalPush expected' - // ); - // assert.deepEqual( - // await testUtils.getActualTemplateJson('testExisting_tpush', 'transactionalPush'), - // await testUtils.getExpectedJson('9999999', 'transactionalPush', 'template'), - // 'returned template JSON was not equal expected' - // ); - // // buildDefinition - // await handler.buildDefinition( - // 'testInstance/testBU', - // 'transactionalPush', - // 'testExisting_tpush', - // 'testTargetMarket' - // ); - // assert.deepEqual( - // await testUtils.getActualDeployJson('testExisting_tpush', 'transactionalPush'), - // await testUtils.getExpectedJson('9999999', 'transactionalPush', 'build'), - // 'returned deployment JSON was not equal expected' - // ); - // assert.equal( - // Object.values(testUtils.getAPIHistory()).flat().length, - // 12, - // 'Unexpected number of requests made' - // ); - // return; - // }); - // }); + describe('Templating ================', () => { + // it.skip('Should create a transactionalPush template via retrieveAsTemplate and build it'); + it('Should create a transactionalPush template via buildTemplate and build it', async () => { + // download first before we test buildTemplate + await handler.retrieve('testInstance/testBU', ['transactionalPush']); + // buildTemplate + const result = await handler.buildTemplate( + 'testInstance/testBU', + 'transactionalPush', + ['testExisting_tpush'], + 'testSourceMarket' + ); + assert.equal( + result.transactionalPush ? Object.keys(result.transactionalPush).length : 0, + 1, + 'only one transactionalPush expected' + ); + assert.deepEqual( + await testUtils.getActualTemplateJson('testExisting_tpush', 'transactionalPush'), + await testUtils.getExpectedJson('9999999', 'transactionalPush', 'template'), + 'returned template JSON was not equal expected' + ); + // buildDefinition + await handler.buildDefinition( + 'testInstance/testBU', + 'transactionalPush', + 'testExisting_tpush', + 'testTargetMarket' + ); + assert.deepEqual( + await testUtils.getActualDeployJson('testExisting_tpush', 'transactionalPush'), + await testUtils.getExpectedJson('9999999', 'transactionalPush', 'build'), + 'returned deployment JSON was not equal expected' + ); + assert.equal( + Object.values(testUtils.getAPIHistory()).flat().length, + 3, + 'Unexpected number of requests made' + ); + return; + }); + }); }); From a73bc0cdb65d74d93627b2bb1cd0578d9c796631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 7 Dec 2022 11:43:17 +0100 Subject: [PATCH 079/103] #560: how to handle applicationId in transactionalPush JSON --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index af680c436..c1c0d81cc 100644 --- a/README.md +++ b/README.md @@ -51,10 +51,11 @@ Accenture Salesforce Marketing Cloud DevTools (mcdev) is a rapid deployment/roll - [6.2.8. createDeltaPkg](#628-createdeltapkg) - [7. Advanced Configuration](#7-advanced-configuration) - [7.1. Config Options](#71-config-options) - - [7.2. Metadata-specific settings & options](#72-metadata-specific-settings--options) + - [7.2. Metadata-specific settings \& options](#72-metadata-specific-settings--options) - [7.2.1. Retention Policy fields in Data Extensions](#721-retention-policy-fields-in-data-extensions) - [7.2.2. Adding/Updating Fields on existing Data Extensions](#722-addingupdating-fields-on-existing-data-extensions) - [7.2.3. Renaming fields of a Data Extensions](#723-renaming-fields-of-a-data-extensions) + - [7.2.4. Mobile Push Application](#724-mobile-push-application) - [7.3. Market Configuration](#73-market-configuration) - [7.4. Market List Configuration](#74-market-list-configuration) - [8. Examples](#8-examples) @@ -1244,6 +1245,12 @@ Imagine you wanted to rename `BillingCountry` to `BillingZip` for some reason. P All you have to do is deploy the data extension again with Name_new specified for each field that needs to be renamed. +#### 7.2.4. Mobile Push Application + +When creating / updating `transactionalPush` you will notice that there is a field for the Mobile Application ID (`applicationId`). This cannot be found in your downloaded BUs but only in SFMC Setup > Mobile Push. Mobile Applications can be created and maintained solely via GUI and not via API. + +If you plan to deploy `transactionalPush` messages to another BU, ensure that as a pre-deployment step, you created the respective Mobile App on the target BU and that you replaced the application ID in your deployment package with that new ID. In a CI/CD environment where the deployment would happen automatically based on your development version, you will have to create a new variable in your source & target market which stores the application Id for the respective BUs. + ### 7.3. Market Configuration From 22e758cb23d90c4283afe00fa46ca1150384165d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 7 Dec 2022 12:51:48 +0100 Subject: [PATCH 080/103] #584: moved readme into wiki --- README.md | 1556 +---------------------------------------------------- 1 file changed, 9 insertions(+), 1547 deletions(-) diff --git a/README.md b/README.md index 2ae589fe9..d6430de40 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # Accenture SFMC DevTools - - [![view on npm](https://badgen.net/github/release/Accenture/sfmc-devtools)](https://www.npmjs.org/package/mcdev) [![view on npm](https://badgen.net/npm/node/mcdev)](https://www.npmjs.org/package/mcdev) [![license](https://badgen.net/npm/license/mcdev)](https://www.npmjs.org/package/mcdev) @@ -11,1560 +9,24 @@ Accenture Salesforce Marketing Cloud DevTools (mcdev) is a rapid deployment/rollout, backup and development tool for Salesforce Marketing Cloud. It allows you to retrieve and deploy configuration and code across Business Units and instances. - - -- [1. Changelog](#1-changelog) -- [2. Getting Started](#2-getting-started) - - [2.1. Pre-requisites](#21-pre-requisites) - - [2.1.1. Install Node.js with npm](#211-install-nodejs-with-npm) - - [2.1.2. Install the Git Command Line](#212-install-the-git-command-line) - - [2.2. Install Accenture SFMC DevTools](#22-install-accenture-sfmc-devtools) - - [2.3. Initial project setup](#23-initial-project-setup) - - [2.4. Joining a project that was set up before](#24-joining-a-project-that-was-set-up-before) - - [2.5. Recommended additional installs](#25-recommended-additional-installs) - - [2.6 Using mcdev in other node packages](#26-using-mcdev-in-other-node-packages) -- [3. Updating Accenture SFMC DevTools](#3-updating-accenture-sfmc-devtools) -- [4. Troubleshoot Install/Update](#4-troubleshoot-installupdate) - - [4.1. Installing a specific version](#41-installing-a-specific-version) - - [4.2. Using custom CLIs](#42-using-custom-clis) - - [4.3. Missing write access to...on MacOS](#43-missing-write-access-toon-macos) - - [4.4. ...running scripts is disabled on this system](#44-running-scripts-is-disabled-on-this-system) - - [4.5. Operation not permitted OR No such file or directory](#45-operation-not-permitted-or-no-such-file-or-directory) -- [5. Metadata Type Support](#5-metadata-type-support) -- [6. Command Overview](#6-command-overview) - - [6.1. Maintenance and setup commands](#61-maintenance-and-setup-commands) - - [6.1.1. init](#611-init) - - [6.1.2. upgrade](#612-upgrade) - - [6.1.3. reloadBUs](#613-reloadbus) - - [6.1.4. badKeys](#614-badkeys) - - [6.1.5. document](#615-document) - - [6.1.6. selectTypes](#616-selecttypes) - - [6.1.7. explainTypes](#617-explaintypes) - - [6.2. Operational commands](#62-operational-commands) - - [6.2.1. retrieve](#621-retrieve) - - [6.2.2. deploy](#622-deploy) - - [6.2.3. delete](#623-delete) - - [6.2.4. retrieveAsTemplate](#624-retrieveastemplate) - - [6.2.5. buildTemplate](#625-buildtemplate) - - [6.2.6. buildDefinition](#626-builddefinition) - - [6.2.7. buildDefinitionBulk](#627-builddefinitionbulk) - - [6.2.8. createDeltaPkg](#628-createdeltapkg) -- [7. Advanced Configuration](#7-advanced-configuration) - - [7.1. Config Options](#71-config-options) - - [7.2. Metadata-specific settings & options](#72-metadata-specific-settings--options) - - [7.2.1. Retention Policy fields in Data Extensions](#721-retention-policy-fields-in-data-extensions) - - [7.2.2. Adding/Updating Fields on existing Data Extensions](#722-addingupdating-fields-on-existing-data-extensions) - - [7.2.3. Renaming fields of a Data Extensions](#723-renaming-fields-of-a-data-extensions) - - [7.3. Market Configuration](#73-market-configuration) - - [7.4. Market List Configuration](#74-market-list-configuration) -- [8. Examples](#8-examples) - - [8.1. Retrieve and deploy Data Extension](#81-retrieve-and-deploy-data-extension) - - [8.2. Metadata Retrieving/Backup](#82-metadata-retrievingbackup) - - [8.3. Automation Deployment](#83-automation-deployment) -- [9. Contribute](#9-contribute) - - [9.1. Install Guide for Developers](#91-install-guide-for-developers) - - [9.2. Local install](#92-local-install) - - [9.3. NPM Scripts](#93-npm-scripts) - - [9.4. Developer Documentation](#94-developer-documentation) - - - -## 1. Changelog - - - -Find info on the latest changes in our [Changelog](CHANGELOG.md). - -## 2. Getting Started - - - -Accenture SFMC DevTools can be installed as Node.JS package. The following guide will demonstrate how you can get started within 10 minutes or less. - - - -### 2.1. Pre-requisites - - - -#### 2.1.1. Install Node.js with npm - - - -1. To check if it is already installed, at the OS command prompt, type: `node --version` - - If this command reports Node version 14.16.x or later, you’re done—proceed to the next installation. If the reported version is earlier than 14.16.x, continue to step 2. - - If you get a “command not found” error message, continue to step 2. -2. In a web browser, go to [nodejs.org](https://nodejs.org) -3. Download and run the latest **LTS** installer for your operating system. -4. When the installer finishes, try step 1 again. If it fails, please restart your terminal. If it still does not work, reboot your computer and try the version check then. - -#### 2.1.2. Install the Git Command Line - - - -1. To check if git is already installed, at the OS command prompt, type: `git version` - - If this command reports a git version such as “git version 2.31.0” (or "git version 2.31.0.windows.1" on Windows), you’re done. Proceed to native Android or iOS environment setup. - - If you get a “command not found” error message, continue to step 2. -2. Go to [git-scm.com/downloads](https://git-scm.com/downloads). -3. Under Downloads, click the icon for your operating system. -4. Run the installer. -5. When the installer finishes, try step 1 again. If it fails, please restart your terminal. If it still does not work, reboot your computer and try the version check then. - - - -### 2.2. Install Accenture SFMC DevTools - - - -If you experience issues installing Accenture SFMC DevTools, please check out the [Troubleshoot Install/Update](#troubleshoot-installupdate) section in this manual. - -**How to:** - -1. Install Accenture SFMC DevTools by running `npm install -g mcdev` (prefix with `sudo` on MacOS) - - If you get an error, please see the below troubleshooting section. - -When completed run `mcdev --version` and it will show you which version you installed (e.g. `4.1.12`). - -> **_Side note for proud nerds_:** -> -> The `-g` part behind `npm install` triggers a so-called "global installation". This is a term coined by Package Managers, in this case Node's package manager (npm) and it means that Accenture SFMC DevTools is installed on your computer and usable across projects. The alternative is a "local installation" would install Accenture SFMC DevTools only for the current project folder. This local install is a technique used to ensure everyone in the team is using the same version of a tool. Nevertheless, we strongly recommend going with the global installation to be able to use the full feature-set of Accenture SFMC DevTools. -> -> You may choose to install mcdev globally or locally. Global install runs faster and allows you to initialize new projects by running `mcdev init` in any directory. If your project does require a local installation, please refer to the [Local Install Guide](#local-install). - -### 2.3. Initial project setup - - - -After the successful installation, you will now need to set up the connection to your Marketing Cloud instance. - -1. In your Marketing Cloud instance - 1. Ensure that you **selected** your **Parent/Global Business Unit**. - 2. Go to `Setup -> Apps -> Installed Packages`. - 3. Create a new "installed package" and name it "Accenture SFMC DevTools Deployment Tool" - > Note: On some older SFMC instances it will ask you if you want the enhanced version. Please enable this option. - 4. Click on the _Add Component_ button and select `API Integration` with `Server-To-Server` mode. - 5. Make sure you grant all available rights. - 6. Go to the access tab and grant it access to all Business Units that you want to use it for, but ensure that the Parent/Global Business Unit is among these. - - _Why?_ Shared Data Extensions, roles, users, Business Unit info and some other metadata is internally stored solely on the parent Business Unit and hence can only be retrieved and updated via that BU. - 7. Note down _EID_ (Parent MID), _Client Id_, _Client Secret_ and _Authentication Base URI_. -2. In your project folder - 1. Open a CLI in your project folder (e.g. `C:\repos\MyProject\` on Windows or `~/repos/MyProject/` on Mac) - 2. Run `mcdev init` to start the interactive setup wizard. - 1. If not found yet, default configuration files will be copied into your project folder, copied by initializing an npm project and a local Git repository. - 2. The wizard will ask you to name your credential. The name you choose here will be used by all team members because the config is shared via Git. - > Being specific here reduces the chance of deploying to the wrong server (instance) in stressful situations. We suggest you use something like `ClientName`, or `ClientName-ProjectName` if you have multiple projects with the same client. In case your project uses multiples instances you might like to use something like `Client-ProjectName-Sandbox` and `Client-ProjectName-PROD`. - 3. It will then continue to ask for the EID (Parent MID), client ID, client secret and Authentication Base URI. - 4. The credentials will be automatically tested & your list of BUs downloaded until finally the central configuration file `.mcdevrc.json` gets created in your project folder. - 5. The last step is to download an initial backup and commit it into git. No worries - the wizard does that for you! - 3. If this is the first time you set up Accenture SFMC DevTools or you recently upgraded Accenture SFMC DevTools, please restart VS Code now! A pop-up will likely appear in the lower right corner prompting you to install recommended extensions. - 4. Done. -3. Sharing the project with your team - 1. Make sure you have a Git repo (Bitbucket, GitHub, GitLab) set up somewhere. If you are an SI partner, usually, your client will have to do this for you. - > While running `mcdev init`, the tool already made sure to set up a local Git repo for you. Now, you need to upload ("push") it to the online repo: - 2. Open the URL of your online repo and find the "CLONE" button. This will likely show you a normal URL, ending on ".git" - 3. Add this as your repository remote named "origin". If you use a GUI-based tool, that should be fairly simple, otherwise, execute `git remote add origin YOUR-URL` in your project folder. - 4. Now run `git push -u origin master` to start the upload. - -### 2.4. Joining a project that was set up before - - - -If Accenture SFMC DevTools was already used to set up the project by somebody in your team, including all of the steps in the above chapter [Initial project configuration](#initial-project-setup), then basically you are in luck. Things are much faster from here on: - -1. Make sure you went through the chapters [Pre-requisites](#pre-requisites) and [Install Accenture SFMC DevTools](#install-mcdev). Do skip [Initial project configuration](#initial-project-setup)! -2. Acquire the URL to your Git repo and Clone it to your computer. It should end on `.git`. Also ask your team lead for `EID (Parent MID)`, `Client ID`, `Client Secret` and the `Authentication Base URI`. You will need this later. - > We recommend you create a folder like `C:\repos\` and clone the repo for your current project into that. By default, the repo name will be suggested as a sub-folder name which you should keep in place. That way you will always have one folder in which all your projects can be found in with their respective sub-folders. -3. Open your main repo folder (e.g. `C:\repos\`) in the CLI of your choice (e.g. PowerShell on Windows) -4. now execute `git clone YOUR-REPO-URL`. This will create a sub-folder with the name of the repo and download everything for you into it (e.g. `C:\repos\YOUR-REPO\`) -5. Still in the command prompt, execute `cd YOUR-REPO`. This will switch your current folder (visible before the command prompt) to the new repo folder (`C:\repos\YOUR-REPO\`). -6. Assuming you installed Accenture SFMC DevTools globally (recommended!), now execute `mcdev init`. -7. At this point, the system will recognize the previously set up project and ask you for `EID (Parent MID)`, `Client ID`, `Client Secret` and the `Authentication Base URI`. -8. Done. - -### 2.5. Recommended additional installs - - - -The following seeks to enhance your daily process. Our guide assumes that you are using [Visual Studio Code](https://code.visualstudio.com/download) to develop, backup and deploy your project. For smooth operations, we highly recommend the following Marketing Cloud specific plugins for it. - -Nevertheless, Accenture SFMC DevTools will run without them and is not associated with the development of these publicly available apps & plugins. - -> **Note:** The following lists are automatically installed when you run `mcdev init` or `mcdev upgrade`. - -**Visual Studio Code extensions:** - -When you run `mcdev init` or `mcdev upgrade` we add/update the file `.vscode/extensions.json` in your project directory with a list of recommended VSCode extensions that will help your daily workflow with Salesforce Marketing Cloud. - -Restart VS Code after cloning a prepared repo or after you've run `init` / `upgrade` and VS Code will prompt you in the lower right corner with: - -![Recommended extension prompt](img/README.md/vscode-recommendations-1.png) - -Click on `Install All` to quickly get things ready or review the recommendations first via `Show Recommendations`. If you opt for the second option, be aware that there are 2 kinds of recommendations: - -![Recommended extension prompt](img/README.md/vscode-recommendations-2.png) - -The "Workspace Recommendations" were defined by Accenture SFMC DevTools. Clicking on the little cloud icon will install all at once. The "Other Recommendations" are auto-generated by VS Code and are not controlled by Accenture SFMC DevTools. You _may_ look through those as well, but they might also be completely irrelevant to you. - -**Node modules:** - -- [eslint](https://npmjs.com/package/eslint): code linting -- [eslint-config-prettier](http://npmjs.com/package/eslint-config-prettier): ensures that prettier and eslint do not have conflicting rules -- [eslint-config-ssjs](http://npmjs.com/package/eslint-config-ssjs): allows you to have accurate code linting in \*.SSJS files -- [eslint-plugin-jsdoc](http://npmjs.com/package/eslint-plugin-jsdoc): will help you write proper jsdoc comments in your SSJS code -- [eslint-plugin-prettier](http://npmjs.com/package/eslint-plugin-prettier): runs prettier and ESlint together -- [prettier](https://prettier.io): opinionated code formatter -- [npm-check](http://npmjs.com/package/npm-check): makes it easier to keep your node modules up-to-date -- [sfmc-boilerplate](http://npmjs.com/package/sfmc-boilerplate): build tool for your more complex email, cloudpage and automation code. - -Please note that Visual Studio Code might warn you about using the local installation of ESLint with a pop-up like the following. Please confirm this with `Allow` or, if you are certain about what you are doing, with `Allow Everywhere`. Inside of Accenture SFMC DevTools project folders this warning is normal because we ask to install the VSCode extension and the node module for ESLint. - -![VSCode Eslint install warning](img/README.md/vscode-eslint-allow_everywhere.jpg) - -### 2.6 Using mcdev in other node packages - -Install it locally first via the following (or with a [specific version](#41-installing-specific-version)): - -```bash -npm install --save mcdev -``` - -And then require it in your code: - -```javascript -const mcdev = require('mcdev'); - -// download all metadata from your instance's Parent BU -mcdev.retrieve('MyCredential/_ParentBU_'); - -// or download all metadata from your instance's Parent BU -mcdev.retrieve('MyCredential/_ParentBU_', 'dataExtension'); -``` - -For more details on the available methods look out for what Intellisense will return or refer to the [developer documentation](docs/dist/documentation.md). - - - -## 3. Updating Accenture SFMC DevTools - - - -If you have mcdev already installed you can update your installation in a simplified way: - -```bash -npm update -g mcdev -``` - - - -## 4. Troubleshoot Install/Update - - - -### 4.1. Installing a specific version - - - -To work with our **developer-version** or to install a **specific older version** you can select any branch or tag from our git repository during install to do so: - -**Most recent developer version (using the GitHub repo & branch name):** - -```bash -npm install -g accenture/sfmc-devtools#develop -``` - -_Note: Regardless of which tag or branch you install_ - -**Install specific version (using a version tag on npm):** - -```bash -npm install -g mcdev@4.1.12 -``` - -**Warning**: When you used the above method to install Accenture SFMC DevTools for a specific version or tag, trying to [update Accenture SFMC DevTools](#updating-mcdev) might not download the most recently published official version but instead stay on the version or branch you previously selected (in the above examples: develop, 4.1.12)! - -> **Note**: The version is currently _not_ updated on the developer branch until a new release is published. Hence, you will not see a change if you run `mcdev --version`. - -### 4.2. Using custom CLIs - - - -Some users of Accenture SFMC DevTools prefer to use git bash or other CLIs instead of the operating system's default. Please note that some of the functionality of Accenture SFMC DevTools but also other tools like the Node package manager (npm) do not necessarily function properly in these. - -If you encounter problems, we strongly recommend first trying it in the default CLI. - - - -### 4.3. Missing write access to...on MacOS - - - -Depending on your setup, the default global installs & updates might error out with "Missing write access to /usr/local/lib/node_modules". In this case, prefix your command with `sudo`: - -```bash -sudo npm install -g mcdev -``` - -```bash -sudo npm update -g mcdev -``` - -![Mac sudo](img/README.md/troubleshoot-mac-sudo.png) - -### 4.4. ...running scripts is disabled on this system - - - -If you see the below error then your system's security settings are rather strict. - -![Execution Policy](img/README.md/troubleshoot-execution_policy.jpg) - -Steps to solve this: - -1. Start Windows PowerShell with the "Run as Administrator" option. -2. Input the following and then hit ENTER: `set-executionpolicy remotesigned` -3. This will likely show a lengthy message with a question to confirm the change (screenshot below). Please type `y` (="yes") and confirm with `Enter`. - -![Execution Policy](img/README.md/troubleshoot-execution_policy-2.png) - -Please note that this change is global and not just for your current folder. - -### 4.5. Operation not permitted OR No such file or directory - - - -If you encounter out of the 3 following errors you will have to completely remove Node.JS and install it again afterwards. - -**Error 1:** Cannot find module index.js - -![index.js not found](img/README.md/troubleshoot-nodejs-index.jpg) - -**Error 2:** Operation not permitted - -![operation not permitted](img/README.md/troubleshoot-nodejs-permission.jpg) - -**How to completely remove Node.js from Windows:** - -1. Run npm cache clean --force - -2. Uninstall it via the system's `Add or remove programs` (find it by searching in the start menu). - -3. Reboot your computer. - -4. Look for these folders and remove them (and their contents) if any still exist. Depending on the version you installed, UAC settings, and CPU architecture, these may or may not exist: - - - C:\Program Files (x86)\Nodejs - - C:\Program Files\Nodejs - - C:\Users\\{User}\AppData\Roaming\npm - - (or %appdata%\npm) - - C:\Users\\{User}\AppData\Roaming\npm-cache - - (or %appdata%\npm-cache) - - C:\Users\\{User}\.npmrc (and possibly check for that without the . prefix too) - - C:\Users\\{User}\npmrc - - C:\Users\\{User}\AppData\Local\Temp\npm-\* - -5. Check your %PATH% environment variable to ensure no references to Nodejs or npm exist. - -6. If it's still not uninstalled, type `where node` at the command prompt and you'll see where it resides -- delete that (and probably the parent directory) too. - -7. Reboot again! - -**Re-install Node.js and Accenture SFMC DevTools:** - -Now, please follow the guides above in the [Pre-requisites](#pre-requisites) section to Install Node.js again and afterwards try again to [install Accenture SFMC DevTools](#install-mcdev). - - - -## 5. Metadata Type Support +## Quick start - - -The following metadata types are currently supported: - -| MetadataType | CLI Argument | Retrieve | Deploy | Template | Retrieved by Default | Description | -| ---------------------------------- | ------------------------- | -------- | ---------- | ---------- | -------------------- | ------------------------------------------------------------------------------------------------------------------ | -| API Discovery | `discovery` | Yes | - | - | - | Description of all API endpoints accessible via REST API; only relevant for developers of Accenture SFMC DevTools. | -| Asset | `asset` | Yes | Yes | Yes | Yes | Assets from Content Builder grouped into subtypes. | -| Automation | `automation` | Yes | Yes | Yes | Yes | Used via Automation Studio directly - or indirectly via Journey Builder & MC Connect. | -| Automation: Data Extract Activity | `dataExtract` | Yes | Yes | Yes | Yes | Creates zipped files in your FTP directory or convert XML into CSV. | -| Automation: File Transfer Activity | `fileTransfer` | Yes | Yes | Yes | Yes | Unzip, decrypt a file or move a file from secure location into FTP directory. | -| Automation: Filter Activity | `filter` | Beta | in backlog | in backlog | - | Part of how filtered Data Extensions are created. Depends on type "FilterDefinitions". | -| Automation: Import File Activity | `importFile` | Yes | Yes | Yes | Yes | Reads files in FTP directory for further processing. | -| Automation: SQL Query Activity | `query` | Yes | Yes | Yes | Yes | Select & transform data using SQL. | -| Automation: Script Activity | `script` | Yes | Yes | Yes | Yes | Execute more complex tasks via SSJS or AMPScript. | -| Campaign Tag | `campaign` | Yes | in backlog | in backlog | Yes | Way of tagging/categorizing emails, journeys and alike. | -| Content Area (Classic) | `contentArea` | Yes | - | - | - | **DEPRECATED**: Old way of saving Content Blocks; please migrate these to new Content Blocks (`Asset: ...`). | -| Data Designer Attribute Groups | `attributeGroup` | Beta | in backlog | in backlog | - | Groupings of Set Definitions (Data Extensions) in Data Designer. | -| Data Designer Set Definitions | `setDefinition` | Beta | in backlog | in backlog | - | Data Extensions linked to Attribute Groups in Data Designer. | -| Data Extension | `dataExtension` | Yes | Yes | Yes | Yes | Database table schemas. | -| Data Extension Template | `dataExtensionTemplate` | Yes | - | - | - | OOTB Database table schemas used for special cases like Transactional Journeys. | -| Data Extract Type | `dataExtractType` | Yes | - | - | - | Types of Data Extracts enabled for a specific business unit. This normally should not be stored. | -| E-Mail (Classic) | `email` | Yes | - | - | - | **DEPRECATED**: Old way of saving E-Mails; please migrate these to new E-Mail (`Asset: message`). | -| E-Mail Send Definition | `emailSendDefinition` | Yes | Yes | Yes (`bt`) | Yes | Mainly used in Automations as "Send Email Activity". | -| Folder | `folder` | Yes | Yes | Yes (`bt`) | - | Used to structure all kinds of other metadata. | -| FTPLocation | `ftpLocation` | Yes | - | - | Yes | A File Location which can be used for export or import of files to/from Marketing Cloud. | -| Journey | `interaction` | Yes | in backlog | in backlog | - | Journey from Builder (internally called "Interaction"). | -| Journey: Entry Event Definition | `eventDefinition` | Yes | Yes | Yes | - | Used in Journeys (Interactions) to define Entry Events. | -| List | `list` | Yes | in backlog | - | Yes | Old way of storing data. Still used for central Email Subscriber DB. | -| Mobile Connect Code | `mobileCode` | Yes | No | No | - | Mobile Connect Shore or Long Codes used for sending. First 50 per BU are retrieved | -| Mobile Connect Keyword | `mobileKeyword` | Yes | Yes | Yes | - | Mobile Connect keywords configured within the Business UNit. First 50 per BU are retrieved | -| Role | `role` | Yes | Yes | Yes (`bt`) | Yes | User Roles define groups that are used to grant users access to SFMC systems. | -| Transactional Email | `transactionalEmail` | Yes | Yes | Yes | Yes | Lets you send immediate Email messages via API events | -| Transactional SMS | `transactionalSMS` | Yes | Yes | Yes | Yes | Lets you send immediate SMS messages via API events | -| Triggered Send | `triggeredSendDefinition` | Yes | Yes | Yes (`bt`) | Yes | **DEPRECATED**: Sends emails via API or DataExtension Event. | -| User | `accountUser` | Yes | in backlog | - | - | Users and Installed Packages including their assigned Roles, BUs and personal permissions | - -## 6. Command Overview - - - -If you installed mcdev globally as described above you can run mcdev in any directory. See our [install Accenture SFMC DevTools](#install-mcdev) chapter for more details. - -_Example (global install):_ - -```bash -mcdev retrieve -``` - -If you installed Accenture SFMC DevTools as a local dependency (**not recommended**) to the current directory then you will have to add `npx` in front of each command. See our [Local install](#local-install) chapter for more details. - -_Example (local install):_ - -```bash -npx mcdev retrieve -``` - -The following description will assume a global installation for simplicity reasons. - -_Note:_ Parameters listed below in between square brackets = `[...]` are optional parameters. Required parameters are listed in between less-than / greater-than signs = `<...>`. - -_Note:_ Credentials and Business Unit names can always be selected interactively. Try inputting a question mark = `?` in their place if more parameters follow, or omit them completely if no other parameters are required for a command. - -### 6.1. Maintenance and setup commands - - - -#### 6.1.1. init - - - -_Command:_ `mcdev init` - -_Alias:_ - - -Creates the basic configuration file `.mcdevrc.json` and `.mcdev-auth.json` in your project directory. You may add more credentials by re-running the same command again, e.g. to add production and sandbox credentials next to each other. - -In addition, it initializes an npm package for you, installs recommended npm dependencies and places our default IDE configuration files for ESLint, Prettier, Git and VSCode into your project directory. - -The initialization ends with the creation of your Git repository and the first backup of your SFMC instance. - -_Example - initialize project / add additional credentials:_ - -```bash -mcdev init -``` - -_Example - update credentials:_ - -```bash -mcdev init yourCredentialName -``` - -The interactive setup will ask you for an `EID (Parent MID)`, `Client ID` and `Client Secret` of an installed package. It also asks for the `Authentication Base Uri`. Each installed package on a given SFMC instance shares the same tenant sub-domain and always shows you 3 domains (Auth, REST and SOAP). - -Example URL: `https://mcg123abcysykllg-0321cbs8bbt64.auth.marketingcloudapis.com` - -> **Note to CLI experts:** -> -> You can run this command without the interactive wizard asking questions using the `--skipInteraction` (or short`--yes`/`--y`) flag. In this case, you need to provide a few values in the command: -> -> ```bash -> mcdev init --y.credentialName "yourCustomCredentialName" --y.client_id "yourClientIdHere" --y.client_secret "yourClientSecretHere" --y.auth_url "https://yourTenantSubdomainHere.auth.marketingcloudapis.com/" --y.gitRemoteUrl "https://my.git.server.com/myrepo.git" --y.account_id 00000000 --y.downloadBUs "true" --y.gitPush "true" -> ``` - -| CLI Argument | Description | -| -------------------- | ---------------------------------------- | -| `--y.credentialName` | Credential name of your Business Unit | -| `--y.client_id` | Salesforce Marketing Cloud client ID | -| `--y.client_secret` | Salesforce Marketing Cloud client secret | -| `--y.auth_url` | Marketing cloud auth url | -| `--y.gitRemoteUrl` | URL of your git remote repo | -| `--y.account_id` | MID of the parent BU | -| `--y.downloadBUs` | Download all the BUs: `true` or `false` | -| `--y.gitPush` | Do the first git push: `true` or `false` | - -#### 6.1.2. upgrade - - - -_Command:_ `mcdev upgrade` - -_Alias:_ `mcdev up` - -This upgrades older Accenture SFMC DevTools projects to the newest standard: Outdated Accenture SFMC DevTools configuration files are upgraded and the right npm dependencies are installed. It also copies the right IDE configuration files into your project folder. See [init](#init) for more details. - -_Example:_ - -```bash -mcdev upgrade -``` - -#### 6.1.3. reloadBUs - - - -_Command:_ `mcdev reloadBUs [credential]` - -_Alias:_ `mcdev rb` - -Use this to synchronize your local list of available Business Units for a given credential. This is useful if you've added, deleted or renamed Business Units on your SFMC instance and want to make sure that your local setup reflects that - or if you accidentally changed your config file and want to restore it. - -_Example:_ - -```bash -mcdev reloadBUs MyProject -``` - -#### 6.1.4. badKeys - - - -_Command:_ `mcdev badKeys [business unit]` - -_Alias:_ - - -Lists all metadata for which the External key is not in sync with the name to enable you to update them quickly. - -_Example:_ - -```bash -mcdev badKeys MyProject/DEV -``` - -#### 6.1.5. document - - - -_Command:_ `mcdev document ` - -_Alias:_ `mcdev doc` - -Creates human-readable documentation for your metadata. This command is executed by default for supported types unless you changed your config manually (`metaDataTypes.documentOnRetrieve`). Therefore, running it manually is typically not required. You can choose to generate **HTML** (`html`) or **Markdown** (`md`) docs via `options.documentType`. - -The default format is set to `md` as Markdown renders nicely in Git as well as in VSCode's Markdown preview and can be copied from there into Confluence and other applications without losing the formatting. - -As standard roles are often not used by projects, we have the optional setting `options.documentStandardRoles` which is by default set to false - -Currently supported types: - -| Name | CLI Argument | -| -------------- | --------------- | -| Automation | `automation` | -| Data Extension | `dataExtension` | -| Role | `role` | -| User | `accountUser` | - -_Example:_ - -```bash -mcdev document myServer role -``` - -#### 6.1.6. selectTypes - - - -_Command:_ `mcdev selectTypes` - -_Alias:_ `mcdev st` - -Allows you to interactively select which metadata is retrieved when you run the `retrieve` command. Try out `explainTypes` first to understand what each type means. - -_Example:_ - -```bash -mcdev selectTypes -``` - -_Note:_ You may select non-standard types if you run `mcdev selectTypes --debug`. This may be needed in edge cases but is not recommended in most situations. - -#### 6.1.7. explainTypes - - - -_Command:_ `mcdev explainTypes` - -_Alias:_ `mcdev et` - -A helper command for `selectTypes`. It prints out a table that defines what the various types are. - -Types marked as not-default should be ignored. These are either under development or merely meant to support contributing to Accenture SFMC DevTools. - -_Example:_ - -```bash -mcdev explainTypes -``` - -### 6.2. Operational commands - - - -#### 6.2.1. retrieve - - - -_Command:_ `mcdev retrieve [business unit] [metadata type] [metadata key]` - -_Alias:_ `mcdev r` - -Retrieves all metadata from the specified Business Unit. You can limit what types are retrieved by default using the `selectTypes` command or by changing the list in the config file directly. - -_Example:_ - -```bash -mcdev retrieve MyProject/DEV -``` - -You can omit the Business Unit which will trigger an interactive mode based on your config: - -_Example:_ - -```bash -mcdev retrieve -``` - -If you already know the credentials name but want to only select the Business Unit interactively try this -_Example:_ - -```bash -mcdev retrieve MyProject -``` - -**retrieve specific type:** - -If you want to retrieve only a certain metadata type, let's say `script`, then pass this type in as a second parameter. The other types will remain untouched and in place, if you've previously retrieved them. - -Similarly, you can pass in multiple comma-separated types but make sure to put them in double quotes to work on all systems. - -_Example:_ +Run the following to install Accenture SFMC DevTools on your computer: ```bash -mcdev retrieve MyProject/DEV script -mcdev retrieve MyProject/DEV "script,query,automation" +npm install -g mcdev ``` -**retrieve specific type and key:** - -If you wish you may also specify the exact keys that need to be retrieved, filtering down on what's in your retrieve folder even further. Specified keys apply as a filter for all types you specify. If your naming convention does not allow for such an aggregation then please run separate commands for each type. - -_Example:_ +## Documentation -```bash -mcdev retrieve MyProject/DEV dataExtension "key1" -mcdev retrieve MyProject/DEV dataExtension "key1,key2" -mcdev retrieve MyProject/DEV "script,dataExtension,importFile" "key1,key2" -``` +Please checkout the [GitHub wiki](/Accenture/sfmc-devtools/wiki) for the full documentation. -_Note:_ This is not supported by types `discovery` and `folder`. +## Changelog -**retrieve all BUs:** +Find info on the latest releases with a detailed changelog in the [GitHub Releases tab](/Accenture/sfmc-devtools/releases). -A special variant of this command allows you to retrieve all Business Units of a given credential at once. -_Example:_ - -```bash -mcdev retrieve MyProject/* -``` - -or even - -```bash -mcdev retrieve "*" -``` - -> Note: retrieve-all will fail in some CLIs if you do not wrap the asterix (\*) in quotes. This is due to the special meaning of \* as a parameter in these CLIs. - -#### 6.2.2. deploy - - - -_Command:_ `mcdev deploy [business unit] [metadata type] [metadata key] [deploy from retrieve]` - -_Alias:_ `mcdev d` - -Deploys metadata to the specified Business Unit. -_Example:_ - -```bash -mcdev deploy MyProject/DEV -``` - -Only metadata that you copied into the **deploy** directory will be deployed. Please keep in mind that the folder structure needs to be similar to what the retrieve command creates in the retrieve folder, including the credentials and Business Unit name. - -Similarly to `mcdev retrieve` you can also use the interactive mode to select credential and/or Business Unit. - -**deploy sepcific type:** - -If you want to deploy only a certain metadata type, let's say `dataExtension`, then pass this type in as a second parameter. If there are other types in the current BU's deploy folder, these will be ignored and hence _not_ uploaded. - -Similarly, you can pass in multiple comma-separated types but make sure to put them in double quotes to work on all systems. - -_Example:_ - -```bash -mcdev deploy MyProject/DEV dataExtension -mcdev deploy MyProject/DEV "script,dataExtension,importFile" -``` - -**deploy specific type and key:** - -If you wish you may also specify the exact keys that need to be deployed, filtering down on what's in your deploy folder even further. Specified keys apply as a filter for all types you specify. If your naming convention does not allow for such an aggregation then please run separate commands for each type. - -_Example:_ - -```bash -mcdev deploy MyProject/DEV dataExtension "key1" -mcdev deploy MyProject/DEV dataExtension "key1,key2" -mcdev deploy MyProject/DEV "script,dataExtension,importFile" "key1,key2" -``` - -**deploy from retrieve folder:** - -Sometimes it's convenient to deploy right from the retrieve folder when you are using mcdev as a developer tool rather than only for deployments to other BUs. For this scenario we added the 4th parameter. In that case, it does not look into `deploy/` but into `retrieve/` finding what it needs to deploy. - -_Example:_ - -```bash -mcdev deploy MyProject/DEV dataExtension "key1" true -mcdev deploy MyProject/DEV dataExtension "key1,key2" true -mcdev deploy MyProject/DEV "script,dataExtension,importFile" "key1,key2" true -``` - -**deploy all BUs:** - -A special variant of this command allows you to deploy all Business Units of a given credential at once. -_Example:_ - -```bash -mcdev deploy MyProject/* -``` - -or even - -```bash -mcdev deploy "*" -``` - -> Note: deploy-all will fail in some CLIs if you do not wrap the asterix (\*) in quotes. This is due to the special meaning of \* as a parameter in these CLIs. - -#### 6.2.3. delete - - - -_Command:_ `mcdev delete ` - -_Alias:_ `mcdev del` - -Deletes the given metadata from your server. This needs to be run with care as any **data** stored in the deleted _meta_-data **will be lost**. - -Currently supported types: - -| Name | CLI Argument | -| --------------------- | ------------------------- | -| Data Extension | `dataExtension` | -| Data Extension Field | `dataExtensionField` | -| Email Send Definition | `Email Send Definition` | -| List | `list` | -| Transactional Email | `transactionalEmail` | -| Transactional SMS | `transactionalSMS` | -| Triggered Send | `triggeredSendDefinition` | - -_Example:_ - -```bash -mcdev delete MyProject/_ParentBU_ dataExtension MyUserTable - -mcdev delete MyProject/_ParentBU_ dataExtensionField MyUserTable.MyFieldName -``` - -#### 6.2.4. retrieveAsTemplate - - - -_Command:_ `mcdev retrieveAsTemplate ` - -_Alias:_ `mcdev rt` - -The `rt` command retrieves metadata from the server and uses your `market` configuration in `.mcdevrc.json` to replace strings with variables. The result is then stored in your `template/` folder. Please note that files stored here will keep their original name, despite this possibly containing market-specific suffixes or similar. Also note, that contrary to the deploy & retrieve folders, you will not see credential- or Business Unit-sub-folders here. - -This command is a prerequisite for the `buildDefintion` command. Alternatively, you can copy-paste retrieved metadata from your `retrieve/` folder to your `template/` folder and update it manually - or even create it from scratch. - -> **Note**: Before using this command, you need to configure your markets first! Check out our guide on [Market Configuration](#market-configuration) to understand how to use templating and prepare your market config. - -Currently supported types: Check out [Metadata Type Support](#metadata-type-support). - -_Example:_ - -```bash -mcdev rt MyProject/DEV dataExtension MyUserTable pilotMarketDEV1 -``` - -This will result in `MyUserTable.dataExtension-meta.json` being created in your `template/` directory: - -**retrieveAsTemplate for multiple sources:** - -You can also create multiple templates with multiple sources at once. Simply specify them in a comma-separated list and put that list in quotes: - -```bash -mcdev rt MyProject/DEV dataExtension "table1,table2,table3" pilotMarketDEV1 -``` - -This will result in the following files being created in your `template/` directory: - -- `table1.dataExtension-meta.json` -- `table2.dataExtension-meta.json` -- `table3.dataExtension-meta.json` - -#### 6.2.5. buildTemplate - - - -_Command:_ `mcdev buildTemplate ` - -_Alias:_ `mcdev bt` - -The `bt` command uses previously retrieved metadata on your local computer and uses your `market` configuration in `.mcdevrc.json` to replace strings with variables. The result is then stored in your `template/` folder. Please note that files stored here will keep their original name, despite this possibly containing market-specific suffixes or similar. Also note, that contrary to the deploy & retrieve folders, you will not see credential- or Business Unit-sub-folders here. - -This command is a prerequisite for the `buildDefintion` command. Alternatively, you can copy-paste retrieved metadata from your `retrieve/` folder to your `template/` folder and update it manually - or even create it from scratch. - -> **Note**: Before using this command, you need to configure your markets first! Check out our guide on [Market Configuration](#market-configuration) to understand how to use templating and prepare your market config. - -Currently supported types: Check out [Metadata Type Support](#metadata-type-support). - -_Example:_ - -```bash -mcdev bt MyProject/DEV dataExtension MyUserTable pilotMarketDEV1 -``` - -This will result in `MyUserTable.dataExtension-meta.json` being created in your `template/` directory: - -**buildTemplate for multiple sources:** - -You can also create multiple templates with multiple sources at once. Simply specify them in a comma-separated list and put that list in quotes: - -```bash -mcdev bt MyProject/DEV dataExtension "table1,table2,table3" pilotMarketDEV1 -``` - -This will result in the following files being created in your `template/` directory: - -- `table1.dataExtension-meta.json` -- `table2.dataExtension-meta.json` -- `table3.dataExtension-meta.json` - -#### 6.2.6. buildDefinition - - - -_Command:_ `mcdev buildDefinition ` - -_Alias:_ `mcdev bd` - -The `buildDefinition` command allows to prepare the deployments to one or multiple targets based on templates and [Market Configuration](#market-configuration). -After you have created your templates via `retrieveAsTemplate` (or manually) in your `template/dataExtension/` folder run this command to create the final deployable files in your respective `retrieve//` folder. - -This allows you to double-check if you changed something by comparing the before and after using your favorite Git client. You then have to manually copy the files you want to deploy into the respective `deploy/` folder. - -> **Note**: Before using this command, you need to configure your markets first! Check out our guide on [Market Configuration](#market-configuration) to understand how to use templating and prepare your market config. - -Currently supported types: Check out [Metadata Type Support](#metadata-type-support). - -_Example:_ - -```bash -mcdev bd MyProject/QA dataExtension MyUserTable pilotMarketQA1 -``` - -This will result in the following files being created in your `retrieve/MyProject/QA/dataExtension/` directory: - -- `MyUserTable.dataExtension-meta.json` - -**buildDefinition for multiple sources:** - -You can also create definitions based on multiple templates at once. Simply specify them in a comma-separated list and put that list in quotes: - -```bash -mcdev bd MyProject/QA dataExtension "table1,table2,table3" pilotMarketDEV1 -``` - -This will result in the following files being created in your `retrieve/MyProject/QA/dataExtension/` directory: - -- `table1.dataExtension-meta.json` -- `table2.dataExtension-meta.json` -- `table3.dataExtension-meta.json` - -#### 6.2.7. buildDefinitionBulk - - - -_Command:_ `mcdev buildDefinitionBulk ` - -_Alias:_ `mcdev bdb` - -With `buildDefinitionBulk` you get a very powerful command that wraps around `buildDefinition`, which it executes internally. It allows you to create the definitions for multiple Business Units and multiple markets at once. - -Instead of passing in the name of credentials, Business Units and markets, you simply refer to a pre-defined market list in your `.mcdevrc.json`. This enables you to re-use the same configs over and over again, across multiple deployments and share it with your team. - -**Note**: Before using this command, you need to configure your markets first! Check out our guide on [Market List Configuration](#market-list-configuration) and [Market Configuration](#market-configuration) to understand how to use **bulk** templating and prepare your market config. - -_Example:_ - -```bash -mcdev bdb pilotMarketsQA dataExtension MyUserTable -``` - -#### 6.2.8. createDeltaPkg - - - -_Command:_ `mcdev createDeltaPkg [range] [filter]` - -_Alias:_ `mcdev cdp` - -This command is rather versatile and hence can be used in multiple ways. The most powerful option presents itself when you configure `options.deployment.sourceTargetMapping` to point to a source `marketList` (usually for DEV-BU with a DEV market) and a target `marketList` (e.g. to a QA BU-market combo and a Production BU-market combo). Given this is configured, it can create all deployable files using Accenture SFMC DevTools's templating engine on the fly for you. - -The **minimum configuration** you need to have in your config could look something like the following: - -> Note: the following example does not show all necessary parts of the config, just those that are needed for createDeltaPkg. -> -> The example tells createDeltaPkg that the source BU is called `MyProject/DEV` and that the there are two target for deployment, `MyProject/QA` and `MyProject/PROD`. Furthermore it associates the markets `dev`, `qa` and `prod` to these BUs to ensure templating is applied - however, the markets in this example are empty which means no actual string replacement will occur. This is normal for most basic cases in which you would expect 1:1 copies on DEV, QA and PROD. -> -> When MC Connect is used, one would expect to see the External Keys of the Synchronized DataExtensions to be mentioned in the markets for easy auto-replacement. - -```json -{ - "credentials": { - "MyProject": { - "businessUnits": { - "DEV": "1235", - "QA": "1236", - "PROD": "1237" - } - } - }, - "options": { - "deployment": { - "commitHistory": 10, - "sourceTargetMapping": { - "deployment-source": "deployment-target" - } - } - }, - "directories": { - "deltaPackage": "docs/deltaPackage/", - "deploy": "deploy/", - "retrieve": "retrieve/", - "template": "template/", - "templateBuilds": ["retrieve/", "deploy/"] - }, - "markets": { - "dev": {}, - "qa": {}, - "prod": {} - }, - "marketList": { - "deployment-source": { - "description": "Define one 1:1 BU-Market combo here to as source for automated creation of deployment packages; you can create more than one source market list", - "MyProject/DEV": "dev" - }, - "deployment-target": { - "description": "Define n BU-Market combo here to as target for automated creation of deployment packages; you can create more than one target market list and they can be as complex as you like", - "MyProject/QA": "qa", - "MyProject/PROD": "prod" - } - } -} -``` - -> **Detailed Background infos:** `createDeltaPkg` internally first compares the 2 commits in Git to find the differences, then executes `retrieveAsTemplate` for all of these found differences, which creates files in your `template/` directory. Finally, it runs `buildDefinitionBulk` for everything, updating your `retrieve/` directory as well as, optionally, your `deploy/` directory, depending on your Accenture SFMC DevTools config's `directories.templateBuilds` value. The update to the first folder enables you to update you branch, while the second folder gives you the right files to immediately deploy to your BUs afterwards (not part of `createDeltaPkg). - -**Interactive commit selection:** - -This allows you to compare your latest commit (**not** what's still only unstaged/staged) with previous commits. This approach is especially useful if you are in charge of the deployment and simply want to compare the latest commits to your master / release branch with the commit that was last deployed. - -> **Important**: Make sure you are on the branch corresponding to the environment you want to deploy to, e.g. the master branch. - -```bash -mcdev createDeltaPkg -``` - -The output will look something like this: - -![interactive commit selection](img/README.md/cdp-1.jpg) - -> _Note: The amount of displayed past commits depends on your Accenture SFMC DevTools configs settings in `options.deployment.commitHistory`. The default value is 10._ - -**Manual commit selection:** - -This is what you would do when you work on a feature branch and want to include the proposed changes for the target BUs already in the branch. - -```bash -# Option 1 - RECOMMENDED -# compare based on what you last committed -# you just committed all your changes and want to create the deployment package for master/Production; -# Recommendation: run this before creating a pull request to include the changes in your PR. - -mcdev createDeltaPkg master # resolves to master..HEAD -``` - -Alternatives: - -```bash -# the same example with another branch name: -mcdev createDeltaPkg "release/sprint-14" # resolves to release/sprint-14..HEAD - -# or even use a commit ID instead of a branch name -mcdev createDeltaPkg d21b4221 # resolves to d21b4221..HEAD - - -# Option 2 - full git range -# Treat this option with caution because createDeltaPkg always runs retrieveAsTemplate and hence downloads from your DEV BU, not from the git branch. -mcdev createDeltaPkg master..develop -``` - -**Manual commit selection without templating:** - -If you don't want to use templating, you may instead provide the optional `filter` parameter. This will limit - -```bash -mcdev createDeltaPkg d21b4221..HEAD 'MyProject/BU1' -``` - -Range and multiple filters (without templating): - -```bash -mcdev createDeltaPkg d21b4221..HEAD 'MyProject/BU1,MyProject/BU3' -``` - -> **Note to CLI experts:** -> -> If you provide a range you can run this command without the interactive wizard asking questions using the `--skipInteraction` (or short`--yes`/`--y`) flag. This will automatically empty your deploy folder before adding new files. Your command will look like this: -> -> ```bash -> mcdev createDeltaPkg [filter] --y -> ``` - -## 7. Advanced Configuration - - - -The tool's configuration can be changed in the file `.mcdevrc.json` located in the root of your project folder. - -It contains [Market Configuration](#market-configuration) (`markets: { ... }`), [Market List Configuration](#market-list-configuration) (`marketList: { ... }`) the list of usable Business Units per credentials, `directories`, as well as other `options`. - -You will also find the configuration for what metadata shall be retrieved here in `metaDataTypes.retrieve: [ ... ]`. - -You will also find a secondary file named `.mcdev-auth.json` containing your credentials. **Do not commit this to your repository!** You should only commit `.mcdevrc.json` as this file contains project-wide settings that do not compromise security. - -### 7.1. Config Options - - - -The central config in `.mcdevrc.json` holds multiple adjustable settings: - -```json -{ - "options": { - "deployment": { - "commitHistory": 10, - "sourceTargetMapping": { - "deployment-source": "deployment-target" - }, - "targetBranchBuMapping": { - "release/*": "MySandbox/QA-DE", - "master": ["MyProduction/PROD-DE", "MyProduction/PROD-NL"] - } - }, - "documentType": "md", - "documentStandardRoles": true, - "exclude": { - "role": { - "CustomerKey": ["excludedRoleKey","excludedOtherRoleKey"] - } - }, - "include": { - "asset": { - "r__folder_Path": ["Content Builder/only/assets/in/here"] - }, - }, - "serverTimeOffset": -6 - }, - "directories": { - "businessUnits": "businessUnits/", - "deploy": "deploy/", - "docs": "docs/", - "retrieve": "retrieve/", - "template": "template/", - "templateBuilds": ["retrieve/", "deploy/"] - }, - "metaDataTypes": { - "documentOnRetrieve": ["accountUser", "automation", "dataExtension", "role"], - "retrieve": [...] - } -} -``` - -| Setting | Default | Description | -| ---------------------------------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | -| options.deployment.commitHistory | 10 | Configures how many commits `createDeltaPkg` will display if no parameters are given | -| options.deployment.sourceTargetMapping | `{"deployment-source": "deployment-target"}` | Configuration of 1 or many source-target marketList combos for `mcdev createDeltaPkg` | -| options.deployment.targetBranchBuMapping | `{"release/*": "...","master": ["..."]}` | Can be used by CI/CD pipelines to know what BUs shall be deployed to upon a merge into one of the specified branches | -| options.documentType | 'md' | Defines the format for documentation ('md', 'html', 'both') | -| options.documentStandardRoles | false | Optionally skip standard role documentation by setting to false | -| options.exclude.`type`.`field` | [] | Allows you to filter out metadata on retrieve based on their field values, e.g. CustomerKey (previously `options.filter`) | -| options.include.`type`.`field` | [] | Allows you to filter out metadata on retrieve based on their field values, e.g. CustomerKey | -| options.serverTimeOffset | -6 | Used to work around quirks in how SFMC handles timezones; For stack4: set to -7 (US Mountain time); others: -6 (US Central) | -| directories.businessUnits | 'businessUnits/' | Directory to save BU base details in | -| directories.deploy | 'deploy/' | Where `deploy` searches for files to deploy | -| directories.docs | 'docs/' | Directory for `document` output | -| directories.retrieve | 'retrieve/' | Where `retrieve` stores downloaded files | -| directories.template | 'template/' | Where `rt` stores downloaded templates & `bd` retrieves them from | -| directories.templateBuilds | ['retrieve/','deploy/'] | Where `bd` saves final deployment versions in. This can hold multiple directories, e.g. ['retrieve/','deploy/'] | -| metaDataTypes.documentOnRetrieve | ['role','dataExtension'] | automatically executes `document` for selected types | -| metaDataTypes.retrieve | _changes with each release_ | check [Metadata Type Support](#metadata-type-support) for current list | - -### 7.2. Metadata-specific settings & options - - - -#### 7.2.1. Retention Policy fields in Data Extensions - -The way the retention policy is saved is a bit misleading and hence we wanted to provide a bit of guidance if you ever need to do a deep dive here. - -| Field | Description | Values | -| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ------------ | -| **DataRetentionPeriod** | this field should print the value of the unit of measure but it unfortunately is off by one (e.g. showing "weeks" instead of "months"). Also, it seems to have no impact on what's stored.
We therefore excluded it from retrieve/deploy | - | -| **DataRetentionPeriodUnitOfMeasure** | represents drop-down for "period after" selection | 6: years
5: months
4: weeks
2: days | -| **DataRetentionPeriodLength** | represents number field for "period after" selection | min: 1
max: 999 | -| **RowBasedRetention** | only true if "delete individual records" is selected, otherwise false | true / false | -| **ResetRetentionPeriodOnImport** | true if "Reset period on import" is checked. | This option is always false if "delete individual records" is selected | true / false | -| **DeleteAtEndOfRetentionPeriod** | true if "delete all records" is selected, otherwise false | true / false | -| **RetainUntil** | Normally, this should only be filled if a date, rather than a period was set.

This is empty for "delete individual records", but filled with a (calculated) date for the other 2 delete options even if "period after" was used.
Warning: trying to update a DE is denied when "period after" fields & this is provided | ""
or date in US format (m/d/Y H:m:s") "12/6/2021 12:00:00 AM") | - -To **disable retention completely**, ensure that you have the 3 booleans set to false, RetainUntil set to an empty string and no DataRetentionPeriod set (=those 2 attributes not present in file). - -To enable "delete All records and data extensions" you have to set RowBasedRetention:false and DeleteAtEndOfRetentionPeriod:false while at the same time providing a date in RetainUntil field or a DataRetentionPeriod via the 2 associated fields. - -It seems the 2 other modes were added on top later and hence "all records and data extension" is the default retention mode. - -#### 7.2.2. Adding/Updating Fields on existing Data Extensions - - - -There are a few rules to keep in mind when playing with Data Extensions fields: - -- The `FieldType` cannot be changed on existing fields; the API returns in error is the attribute is even provided unchanged during an update -- `MaxLength` can be increased or kept on the same value but never decreased during an update -- A Non-Required/Nullable field cannot be set to be required during an UPDATE -- When new fields are added, they can be required, but then also have to have a `DefaultValue` set -- The value for `IsRequired` should be 'true' or 'false' -- The value for `IsPrimary` should be 'true' or 'false' - -#### 7.2.3. Renaming fields of a Data Extensions - -With a small addition to the Data Extension's JSON, it is possible to rename fields via MC DevTools. Imagine the following Data Extension: - -```json -{ - "CustomerKey": "Account", - "Name": "Account", - "Description": "", - "IsSendable": "false", - "IsTestable": "false", - "Fields": [ - { - "Name": "BillingCity", - "Scale": "0", - "DefaultValue": "", - "MaxLength": "40", - "IsRequired": "false", - "IsPrimaryKey": "true", - "FieldType": "Text" - }, - { - "Name": "BillingCountry", - "Scale": "0", - "DefaultValue": "", - "MaxLength": "80", - "IsRequired": "false", - "IsPrimaryKey": "false", - "FieldType": "Text" - } - ], - "r__folder_Path": "Data Extensions" -} -``` - -Imagine you wanted to rename `BillingCountry` to `BillingZip` for some reason. Previously, you could either go into the GUI or delete & recreate the field. Now, MC DevTools allows you to specify `Name_new` on the field and the tool will take care of the rest during **deployment**: - -```json -{ - "CustomerKey": "Account", - "Name": "Account", - "Description": "", - "IsSendable": "false", - "IsTestable": "false", - "Fields": [ - { - "Name": "BillingCity", - "Scale": "0", - "DefaultValue": "", - "MaxLength": "40", - "IsRequired": "false", - "IsPrimaryKey": "true", - "FieldType": "Text" - }, - { - "Name": "BillingCountry" /* old name, keep here for reference during the update! */, - "Name_new": "BillingZip" /* new name */, - "Scale": "0", - "DefaultValue": "", - "MaxLength": "80", - "IsRequired": "false", - "IsPrimaryKey": "false", - "FieldType": "Text" - } - ], - "r__folder_Path": "Data Extensions" -} -``` - -All you have to do is deploy the data extension again with Name_new specified for each field that needs to be renamed. - -### 7.3. Market Configuration - - - -You will want to set up configs for variable parts that change in between Business Units. We advise starting this _after_ you've first run the `retrieveAsTemplate` command. This might sound counterintuitive but when you review what was copied into your template folder you will likely spot these variable parts the fastest and can then start setting up your market config. Please consider this an iterative process as you will likely run `rt` followed by another update of your config multiple times until you got it right. - -We advise clustering your logical approach into variable things on the **instance parent** (=`_ParentBU_` in Accenture SFMC DevTools), the **environment parent** (under which you cluster your child Business Units for DEV, QA and PROD respectively), and **child Business Units**. Ideally, the instance parent is only used to deploy _Shared Data Extensions_, the environment parent is used for integrations with external services and to separate incoming data via Automations into the respective _Shared Data Extensions_. The child Business Units are then reserved for everything that is run on a market-by-market basis. - -_Note:_ We do see it often that instance parent and environment parent are the same. This is depending on your client's setup since Business Units are not for free and clients sometimes decide to save the extra money. Sometimes, you even end up with only one BU for DEV activities, no QA environment - and share the instance parent between DEV and production... This is not the recommended approach for multiple reasons, including security, but it is the reality in some of our projects. - -Here is a simple example with one DEV BU, 1 QA BU and 2 PROD BUs: - -```json -// example market config in your .mcdevrc.json -"markets": { - "DEV-NL": { - "mid": "12345", - "buName": "DEV - Child NL", - "sharedFolder": "/Shared Data Extensions/DEV/NL", - "suffix": "_DEV_NL", - "countryCodeIn": "'NL'" - }, - "QA-DE": { - "mid": "12346", - "buName": "QA - Child DE", - "sharedFolder": "/Shared Data Extensions/QA/DE", - "suffix": "_QA_DE", - "countryCodeIn": "'DE'" - }, - "PROD-DE": { - "mid": "12349", - "buName": "DE - Germany", - "sharedFolder": "/Shared Data Extensions/DE - Germany", - "suffix": "_DE", - "countryCodeIn": "'DE'" - }, - "PROD-NL": { - "mid": "12351", - "buName": "NL - Netherlands", - "sharedFolder": "/Shared Data Extensions/NL - Netherlands", - "suffix": "_NL", - "countryCodeIn": "'NL'" - } -} -``` - -Way more complex example with dedicated "Parent" BUs per environment (DEV, QA, PROD) and multiple country-specific BUs for QA and PROD: - -```json -// example market config in your .mcdevrc.json -"markets": { - "DEV-Parent": { - "Account_Salesforce": "Account_Salesforce_1", - "suffix": "_DEV" - }, - "DEV-NL": { - "mid": "12345", - "buName": "DEV - Child NL", - "sharedFolder": "Shared Items/Shared Data Extensions/DEV/NL", - "suffix": "_DEV_NL", - "countryCodeIn": "'NL'" - }, - "QA-Parent": { - "Account_Salesforce": "Account_Salesforce_2", - "suffix": "_QA" - }, - "QA-DE": { - "mid": "12346", - "buName": "QA - Child DE", - "sharedFolder": "Shared Items/Shared Data Extensions/QA/DE", - "suffix": "_QA_DE", - "countryCodeIn": "'DE'" - }, - "QA-GULF": { - "mid": "12347", - "buName": "QA - Child GULF", - "sharedFolder": "Shared Items/Shared Data Extensions/QA/GULF", - "suffix": "_QA_GULF", - "countryCodeIn": "'AE', 'BH', 'IQ', 'KW', 'OM', 'QA', 'SA'" - }, - "PROD-Parent": { - "Account_Salesforce": "Account_Salesforce", - "suffix": "" - }, - "PROD-AT": { - "mid": "123458", - "buName": "AT - Austria", - "sharedFolder": "Shared Items/Shared Data Extensions/AT - Austria", - "suffix": "_AT", - "countryCodeIn": "'AT'" - }, - "PROD-DE": { - "mid": "12349", - "buName": "DE - Germany", - "sharedFolder": "Shared Items/Shared Data Extensions/DE - Germany", - "suffix": "_DE", - "countryCodeIn": "'DE'" - }, - "PROD-CH": { - "mid": "12352", - "buName": "CH - Switzerland", - "sharedFolder": "Shared Items/Shared Data Extensions/CH - Switzerland", - "suffix": "_CH", - "countryCodeIn": "'CH'" - }, - "PROD-GULF": { - "mid": "12350", - "buName": "GULF - Arab states of the Persian Gulf", - "sharedFolder": "Shared Items/Shared Data Extensions/GULF - Arab states of the Persian Gulf", - "suffix": "_IC_GULF", - "countryCodeIn": "'AE', 'BH', 'IQ', 'KW', 'OM', 'QA', 'SA'" - }, - "PROD-NL": { - "mid": "12351", - "buName": "NL - Netherlands", - "sharedFolder": "Shared Items/Shared Data Extensions/NL - Netherlands", - "suffix": "_NL", - "countryCodeIn": "'NL'" - } -} -``` - - - -### 7.4. Market List Configuration - - - -Market Lists are very powerful and you will quickly notice how much time they can save you during your deployment preparation. -Let's first look at an example list config: - -```json -// this is a market-list-config based on the example market-config listed above! -"markets": {...}, -"marketList": { - "deployment-source": { - "description": "Define one 1:1 BU-Market combo here to as source for automated creation of deployment packages; you can create more than one source market list", - "MySandbox/DEV-NL": "DEV-NL" - }, - "deployment-target": { - "description": "Define n BU-Market combo here to as target for automated creation of deployment packages; you can create more than one target market list and they can be as complex as you like", - "MySandbox/QA-DE": "QA-DE", - "MyProduction/PROD-DE": "PROD-DE", - "MyProduction/PROD-NL": "PROD-NL" - } -} -``` - -The above can be used together with `createDeltaPkg` command. - -Way more complex example: - -```json -// this is a market-list-config based on the example market-config listed above! -"markets": {...}, -"marketList": { - "Parent-shared": { - "description": "used to deploy shared data extensions", - "MySandbox/_ParentBU_": ["QA-DE", "QA-GULF"], - "MyProduction/_ParentBU_": ["PROD-AT", "PROD-CH", "PROD-DE", "PROD-GULF", "PROD-NL"] - }, - "Parent-medium": { - "description": "if you use the instance's parent BU also for imports", - "MySandbox/_ParentBU_": "QA-Parent", - "MyProduction/_ParentBU_": "PROD-Parent" - }, - "Parent-large": { - "description": "very large projects often decide against using the instance parent to get more order into the chaos and define their own 'parents' instead", - "MySandbox/QA-_Parent_": "QA-Parent", - "MyProduction/ProjectX-_Parent_": "PROD-Parent" - }, - "Parent-medium-multi": { - "description": "equal to Parent-shared" - }, - "Parent-large-multi": { - "description": "use to deploy market-adapted queries to your parent BUs", - "MySandbox/QA-_Parent_": ["QA-DE", "QA-GULF"], - "MyProduction/ProjectX-_Parent_": [ - "PROD-AT", - "PROD-CH", - "PROD-DE", - "PROD-GULF", - "PROD-NL" - ] - }, - "Children": { - "description": "use this to deploy to your market BUs", - "MySandbox/QA-DE": "QA-DE", - "MySandbox/QA-GULF": "QA-GULF", - "MyProduction/PROD-Child_AT": "PROD-AT", - "MyProduction/PROD-Child_CH": "PROD-CH", - "MyProduction/PROD-Child_DE": "PROD-DE", - "MyProduction/PROD-Child_GULF": "PROD-GULF", - "MyProduction/PROD-Child_NL": "PROD-NL" - } -} -``` - -First off, we don't see DEV in here. If you have more than one market in DEV then this might deviate but in general, you don't want to bulk-deploy to DEV as this is your single source of truth. -Apart from that, we can see 4 types of lists here: - -1. `Parent-shared` (_instance parent_): This would be used to deploy the Shared Data Extensions to the instance parent. The child-configs are listed in an array to ensure we end up with one file per child in our parent BU folder. -2. `Parent-medium`/`Parent-large` (medium:_instance parent_; large:_environment parent_): A 1:1 config that handles automations and the part of your solution that only runs on the parent. -3. `Parent-medium-multi`/`Parent-large-multi` (medium:_instance parent_; large:_environment parent_): Any scripts, queries, automations that are executed on the parent but require one per child (e.g. query to fill country-specific Shared Data Extensions) -4. `Children` (_child BUs_): everything that is needed on the market-specific Business Units. - -## 8. Examples - - - -### 8.1. Retrieve and deploy Data Extension - - - -1. Retrieve metadata by running `mcdev retrieve ` (where the BU corresponds to a credential-Business Unit combo in the **.mcdevrc.json**) -2. Create a directory called `deploy/` in the root directory -3. Create a directory called `dataExtension/` in the `deploy/` directory -4. Copy a single dataExtension directory from the `retrieve///dataExtension/` directory into `deploy///dataExtension/` -5. Run `mcdev deploy ` to deploy everything in the **deploy** folder to the specified Business Unit - -### 8.2. Metadata Retrieving/Backup - - - -Metadata of a Business Unit can be retrieved by running the following command: - -`mcdev retrieve ` - -where `` needs to be replaced with `credentialName/businessUnit-Name` that is defined in **.mcdevrc.json**. - -Run this command for each of your defined Business Units and this will result in a **retrieve** directory with a sub-directory for each Business Unit. Each sub-directory contains the metadata from this Business Unit that is currently supported to **retrieve**. - -This folder structure can be committed into a git repository and used as a backup. - -### 8.3. Automation Deployment - - - -Now we want to deploy an Automation with its related metadata. Select a retrieved Automation and copy it into the deploy folder. (`deploy///automation/myAutomation.meta-automation.json`) - -Copy all related activity metadata of this automation into the deploy folder. (_Example:_ `deploy///query/myquery.meta-query.json` and `deploy///query/myquery.meta-query.sql`) - -To start the deployment run the following command: - -`mcdev deploy ` - -## 9. Contribute - - +## Contribute If you want to enhance Accenture SFMC DevTools you are welcome to fork the repo and create a pull request. Please understand that we will have to conduct a code review before accepting your changes. -### 9.1. Install Guide for Developers - - - -Instead of installing Accenture SFMC DevTools as an npm dependency from our git repo, we recommend cloning our repo and then linking it locally: - -Assuming you cloned Accenture SFMC DevTools into `C:\repos\sfmc-devtools\` (or `~/repos/sfmc-devtools/` on Mac): - -1. Open a terminal in your repo folder (`repos/sfmc-devtools/`) -2. Execute `npm install` to download all the dependencies. -3. Execute `npx husky install` to enable our git hooks. -4. Execute `npm install -g "C:\repos\sfmc-devtools"` (this installs mcdev globally on your computer based on your cloned repo folder. Any changes you make in there will take immediate effect without the need for publishing or re-installing it). - -This should tell npm to create a symlink to your cloned local directory, allowing you to see updates you make in your mcdev repo instantly. - -To test your new **global** developer setup, run `mcdev --version` in CLI which should return the current version (e.g. `4.1.12`). Then, go into your mcdev repo and update the version with the suffix `-dev`, e.g. to `4.1.12-dev` and then run `mcdev --version` again to verify that your change propagates instantly. - -> **Not recommended:** Alternatively, you can install it locally only by opening a terminal in your project directory and executing `npm install --save-dev "C:\repos\sfmc-devtools"` -> To run the local version you need to prepend "npx" before your commands, e.g. `npx mcdev --version` - -**Note:** On MacOS you might need to prepend `sudo` to elevate your command. - -**Local vs. Global developer installation:** - -If you use Accenture SFMC DevTools in your team it is recommended to install your developer version **globally**, while the project's package.json should point to our Git repo in its devDependency section. Otherwise, other team members would have trouble due to potentially different paths. - -If you do need to install it locally, make sure you don't commit your project's package.json with this change or you might break mcdev for other developers in your team who either didn't clone the Accenture SFMC DevTools repo or stored it in a different directory. - - - -### 9.2. Local install - - - -> **Warning:** local installation (leading to the use of [npx](https://github.com/npm/npx#readme)) causes issues when spaces are used in keys/names and is therefore not recommended. -> You will also make setting up projects much harder if you choose the local installation as you cannot use `mcdev init` to automatically setup your entire project. - -The following explains how you _could_ install it locally for certain edge cases: - -1. Create a new folder for your upcoming SFMC project. Let's assume you named it `MyProject/` - > _Note:_ It is best practice to create a separate project folder for each of your client projects to avoid accidentally overwriting the wrong BU. -2. Now, open a command line interface (CLI) for that folder. - - In Windows, you can easily do that by pressing SHIFT + Right-click into that folder and then selecting the option "Open PowerShell window here". - - Alternatively, you could use any other CLI. We recommend opting for [Visual Studio Code](https://code.visualstudio.com/download)'s "Terminal" as you can benefit from this later. - - Your CLI prompt should look something like `PS C:\repos\MyProject>` on Windows or `~/repos/MyProject/` on Mac. -3. Initialize your new SFMC project by running `npm init`. - > _Note:_ npm is the "package manager" of Node.js and we use it to bundle Accenture SFMC DevTools together with other tools. - > - > If you are not familiar with node-projects or npm yet, we found this [blog post on nodesource.com](https://nodesource.com/blog/an-absolute-beginners-guide-to-using-npm/) to be helpful for beginners. - > In short, a package manager provides you with an easy name-based way of installing and updating tools you want to use as a team and ensuring that you are all using the same version. -4. Afterwards, install Accenture SFMC DevTools by running `npm install --save-dev mcdev` - - If you get an error, please see the below troubleshooting section. - -When completed run `mcdev --version` and it will show you which version you installed (e.g. `4.1.12`). - -### 9.3. NPM Scripts - - - -- `start`: Main entry point -- `mcdev`: alias for `start` -- `build`: Runs documentation and linting scripts -- `debug`: start debugging session -- `docs`: Generates jsdocs API documentation as markdown -- `lint`: Runs eslint with autofix and prettier -- `test`: Runs mocha tests - outdated -- `upgrade`: run npm-check to test for updated depdencies - -### 9.4. Developer Documentation - - - -- [Link to API Documentation](docs/dist/documentation.md) -- [Link to Considerations & Findings Documentation](docs/dist/considerations.md) +More details on how to best do that are described in our [wiki](/Accenture/sfmc-devtools/wiki/9.-Contribute). From 7a4d91c7a381929b517a6160d2ce85cc4c4c010a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 7 Dec 2022 13:55:33 +0100 Subject: [PATCH 081/103] #584: updated readme.md reference to point to Wiki; removed release notes from checklist (auto-generated) --- .github/PULL_REQUEST_TEMPLATE.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 97f680fbb..feb0c040e 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -25,8 +25,7 @@ _Please delete options that are not relevant._ ## Checklist - [ ] I have performed a self-review of my own code -- [ ] ran `npm run test` to with 0 tests failing - [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] ran `npm run test` to with 0 tests failing - [ ] ESLint & Prettier are not outputting errors or warnings -- [ ] README.md updated (if applicable) -- [ ] Release Notes updated +- [ ] Wiki updated (if applicable) From 0227bdfac240bf10d68f10f8075ad4364be9d814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 7 Dec 2022 14:54:35 +0100 Subject: [PATCH 082/103] #584: added include-in-package section --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index d6430de40..015aa9e32 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,30 @@ Accenture Salesforce Marketing Cloud DevTools (mcdev) is a rapid deployment/roll ## Quick start +### Install + Run the following to install Accenture SFMC DevTools on your computer: ```bash npm install -g mcdev ``` +### Include in your package + +First, install it as dependency: + +```bash +npm install mcdev --save +``` + +You can then include it in your code with: + +```javascript +const mcdev = require('mcdev'); +``` + +That will load `node_packages/mcdev/lib/index.js`. It can make sense to directly include other files if you have a special scenario. We've done that in our example for [retrieveChangelog.js](/Accenture/sfmc-devtools/blob/main/lib/retrieveChangelog.js) or in more detail, in our child-project [sfmc-devtools-copado](/Accenture/sfmc-devtools-copado) to get full control over certain aspects. + ## Documentation Please checkout the [GitHub wiki](/Accenture/sfmc-devtools/wiki) for the full documentation. From 3ab19aefd6e06421edc48217ea4bc831f971db19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 7 Dec 2022 15:16:19 +0100 Subject: [PATCH 083/103] #584: add main contacts & copyright; made links absolute --- README.md | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 015aa9e32..178299595 100644 --- a/README.md +++ b/README.md @@ -33,18 +33,40 @@ You can then include it in your code with: const mcdev = require('mcdev'); ``` -That will load `node_packages/mcdev/lib/index.js`. It can make sense to directly include other files if you have a special scenario. We've done that in our example for [retrieveChangelog.js](/Accenture/sfmc-devtools/blob/main/lib/retrieveChangelog.js) or in more detail, in our child-project [sfmc-devtools-copado](/Accenture/sfmc-devtools-copado) to get full control over certain aspects. +That will load `node_packages/mcdev/lib/index.js`. It can make sense to directly include other files if you have a special scenario. We've done that in our example for [retrieveChangelog.js](https://github.com/Accenture/sfmc-devtools/blob/main/lib/retrieveChangelog.js) or in more detail, in our child-project [sfmc-devtools-copado](https://github.com/Accenture/sfmc-devtools-copado) to get full control over certain aspects. ## Documentation -Please checkout the [GitHub wiki](/Accenture/sfmc-devtools/wiki) for the full documentation. +Please checkout the [GitHub wiki](https://github.com/Accenture/sfmc-devtools/wiki) for the full documentation. ## Changelog -Find info on the latest releases with a detailed changelog in the [GitHub Releases tab](/Accenture/sfmc-devtools/releases). +Find info on the latest releases with a detailed changelog in the [GitHub Releases tab](https://github.com/Accenture/sfmc-devtools/releases). ## Contribute If you want to enhance Accenture SFMC DevTools you are welcome to fork the repo and create a pull request. Please understand that we will have to conduct a code review before accepting your changes. -More details on how to best do that are described in our [wiki](/Accenture/sfmc-devtools/wiki/9.-Contribute). +More details on how to best do that are described in our [wiki](https://github.com/Accenture/sfmc-devtools/wiki/9.-Contribute). + +## Main Contacts + +The people that lead this project: + +
+ +
+Jörn Berkefeld +

+GitHub profile +
+ +
+Doug Midgley +

+GitHub profile +
+ +## Copyright + +Copyright (c) 2020-2022 Accenture. [MIT licensed](https://github.com/Accenture/sfmc-devtools/blob/main/LICENSE). From 1ff2e6e3afdb11eb0196220432a59b9755f35627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Wed, 7 Dec 2022 16:13:21 +0100 Subject: [PATCH 084/103] #586: enhance folder log message to reduce confusion --- lib/Deployer.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/Deployer.js b/lib/Deployer.js index 471c68391..08aa90519 100644 --- a/lib/Deployer.js +++ b/lib/Deployer.js @@ -328,11 +328,11 @@ class Deployer { const allowedDeFolderContentTypes = ['dataextension', 'shared_dataextension']; for (const metadataType of metadataTypeArr) { if (!MetadataTypeInfo[metadataType].definition.dependencies.includes('folder')) { - Util.logger.debug(` ☇ skipping ${metadataType}: folder not a dependency`); + Util.logger.debug(` ☇ skipping ${metadataType} folders: folder not a dependency`); continue; } if (!MetadataTypeInfo[metadataType].definition.folderType) { - Util.logger.debug(` ☇ skipping ${metadataType}: folderType not set`); + Util.logger.debug(` ☇ skipping ${metadataType} folders: folderType not set`); continue; } if ( @@ -341,11 +341,13 @@ class Deployer { ) ) { Util.logger.warn( - ` ☇ skipping ${metadataType}: folderType ${MetadataTypeInfo[metadataType].definition.folderType} not supported for deployment` + ` ☇ skipping ${metadataType} folders: folderType ${MetadataTypeInfo[metadataType].definition.folderType} not supported for deployment. Please consider creating folders for this type manually as a pre-deployment step, if you see errors about missing dependent folders for this type later in this log.` ); continue; } - Util.logger.debug(`Creating relevant folders for ${metadataType} in deploy dir`); + Util.logger.debug( + ` - create ${metadataType} folders: Creating relevant folders in deploy dir` + ); const allFolders = Object.keys(metadata[metadataType]) .filter( From 0ffbb4831c62ec066a8a40d3bcd12c5dda7dd071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 8 Dec 2022 10:02:48 +0100 Subject: [PATCH 085/103] #590: fix multiple git user names shown --- docs/dist/documentation.md | 49 ++++++++++++++++++++++++++++++++++---- lib/util/init.git.js | 11 +++++++-- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index eac121c64..b95979bc5 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -176,6 +176,8 @@ Provides default functionality that can be overwritten by child metadata type cl
csvToArray(csv)Array.<string>

helper to convert CSVs into an array. if only one value was given, it's also returned as an array

+
systemSync(cmd)
+
getUserName(userList, item, fieldname)string
setupSDK(sessionKey, authObject)SDK
@@ -4867,7 +4869,7 @@ CLI entry for SFMC DevTools * [.inverseGet(objs, val)](#Util.inverseGet) ⇒ string * [.getMetadataHierachy(metadataTypes)](#Util.getMetadataHierachy) ⇒ Array.<string> * [.resolveObjPath(path, obj)](#Util.resolveObjPath) ⇒ any - * [.execSync(cmd, [args], [hideOutput])](#Util.execSync) ⇒ undefined + * [.execSync(cmd, [args], [hideOutput])](#Util.execSync) ⇒ string * [.templateSearchResult(results, keyToSearch, searchValue)](#Util.templateSearchResult) ⇒ TYPE.MetadataTypeItem * [.setLoggingLevel(argv)](#Util.setLoggingLevel) ⇒ void @@ -5064,10 +5066,11 @@ let's you dynamically walk down an object and get a value -### Util.execSync(cmd, [args], [hideOutput]) ⇒ undefined +### Util.execSync(cmd, [args], [hideOutput]) ⇒ string helper to run other commands as if run manually by user **Kind**: static method of [Util](#Util) +**Returns**: string - output of command if hideOutput is true | Param | Type | Description | | --- | --- | --- | @@ -5709,6 +5712,7 @@ CLI helper class * [._updateGitConfigUser()](#Init._updateGitConfigUser) ⇒ void * [._getGitConfigUser()](#Init._getGitConfigUser) ⇒ Promise.<{'user.name': string, 'user.email': string}> * [.initProject(properties, credentialName)](#Init.initProject) ⇒ Promise.<void> + * [._initMarkets()](#Init._initMarkets) * [._downloadAllBUs(bu, gitStatus)](#Init._downloadAllBUs) ⇒ Promise.<void> * [.upgradeProject(properties, [initial], [repoName])](#Init.upgradeProject) ⇒ Promise.<boolean> * [._getMissingCredentials(properties)](#Init._getMissingCredentials) ⇒ Array.<string> @@ -5831,6 +5835,12 @@ Creates template file for properties.json | properties | TYPE.Mcdevrc | config file's json | | credentialName | string | identifying name of the installed package / project | + + +### Init.\_initMarkets() +helper for @initProject that optionally creates markets and market lists for all BUs + +**Kind**: static method of [Init](#Init) ### Init.\_downloadAllBUs(bu, gitStatus) ⇒ Promise.<void> @@ -5916,6 +5926,7 @@ CLI helper class * [._updateGitConfigUser()](#Init._updateGitConfigUser) ⇒ void * [._getGitConfigUser()](#Init._getGitConfigUser) ⇒ Promise.<{'user.name': string, 'user.email': string}> * [.initProject(properties, credentialName)](#Init.initProject) ⇒ Promise.<void> + * [._initMarkets()](#Init._initMarkets) * [._downloadAllBUs(bu, gitStatus)](#Init._downloadAllBUs) ⇒ Promise.<void> * [.upgradeProject(properties, [initial], [repoName])](#Init.upgradeProject) ⇒ Promise.<boolean> * [._getMissingCredentials(properties)](#Init._getMissingCredentials) ⇒ Array.<string> @@ -6038,6 +6049,12 @@ Creates template file for properties.json | properties | TYPE.Mcdevrc | config file's json | | credentialName | string | identifying name of the installed package / project | + + +### Init.\_initMarkets() +helper for @initProject that optionally creates markets and market lists for all BUs + +**Kind**: static method of [Init](#Init) ### Init.\_downloadAllBUs(bu, gitStatus) ⇒ Promise.<void> @@ -6123,6 +6140,7 @@ CLI helper class * [._updateGitConfigUser()](#Init._updateGitConfigUser) ⇒ void * [._getGitConfigUser()](#Init._getGitConfigUser) ⇒ Promise.<{'user.name': string, 'user.email': string}> * [.initProject(properties, credentialName)](#Init.initProject) ⇒ Promise.<void> + * [._initMarkets()](#Init._initMarkets) * [._downloadAllBUs(bu, gitStatus)](#Init._downloadAllBUs) ⇒ Promise.<void> * [.upgradeProject(properties, [initial], [repoName])](#Init.upgradeProject) ⇒ Promise.<boolean> * [._getMissingCredentials(properties)](#Init._getMissingCredentials) ⇒ Array.<string> @@ -6245,6 +6263,12 @@ Creates template file for properties.json | properties | TYPE.Mcdevrc | config file's json | | credentialName | string | identifying name of the installed package / project | + + +### Init.\_initMarkets() +helper for @initProject that optionally creates markets and market lists for all BUs + +**Kind**: static method of [Init](#Init) ### Init.\_downloadAllBUs(bu, gitStatus) ⇒ Promise.<void> @@ -6330,6 +6354,7 @@ CLI helper class * [._updateGitConfigUser()](#Init._updateGitConfigUser) ⇒ void * [._getGitConfigUser()](#Init._getGitConfigUser) ⇒ Promise.<{'user.name': string, 'user.email': string}> * [.initProject(properties, credentialName)](#Init.initProject) ⇒ Promise.<void> + * [._initMarkets()](#Init._initMarkets) * [._downloadAllBUs(bu, gitStatus)](#Init._downloadAllBUs) ⇒ Promise.<void> * [.upgradeProject(properties, [initial], [repoName])](#Init.upgradeProject) ⇒ Promise.<boolean> * [._getMissingCredentials(properties)](#Init._getMissingCredentials) ⇒ Array.<string> @@ -6452,6 +6477,12 @@ Creates template file for properties.json | properties | TYPE.Mcdevrc | config file's json | | credentialName | string | identifying name of the installed package / project | + + +### Init.\_initMarkets() +helper for @initProject that optionally creates markets and market lists for all BUs + +**Kind**: static method of [Init](#Init) ### Init.\_downloadAllBUs(bu, gitStatus) ⇒ Promise.<void> @@ -6542,7 +6573,7 @@ Util that contains logger and simple util methods * [.inverseGet(objs, val)](#Util.inverseGet) ⇒ string * [.getMetadataHierachy(metadataTypes)](#Util.getMetadataHierachy) ⇒ Array.<string> * [.resolveObjPath(path, obj)](#Util.resolveObjPath) ⇒ any - * [.execSync(cmd, [args], [hideOutput])](#Util.execSync) ⇒ undefined + * [.execSync(cmd, [args], [hideOutput])](#Util.execSync) ⇒ string * [.templateSearchResult(results, keyToSearch, searchValue)](#Util.templateSearchResult) ⇒ TYPE.MetadataTypeItem * [.setLoggingLevel(argv)](#Util.setLoggingLevel) ⇒ void @@ -6739,10 +6770,11 @@ let's you dynamically walk down an object and get a value -### Util.execSync(cmd, [args], [hideOutput]) ⇒ undefined +### Util.execSync(cmd, [args], [hideOutput]) ⇒ string helper to run other commands as if run manually by user **Kind**: static method of [Util](#Util) +**Returns**: string - output of command if hideOutput is true | Param | Type | Description | | --- | --- | --- | @@ -6790,6 +6822,15 @@ helper to convert CSVs into an array. if only one value was given, it's also ret | --- | --- | --- | | csv | string | potentially comma-separated value or null | + + +## systemSync(cmd) +**Kind**: global function + +| Param | +| --- | +| cmd | + ## getUserName(userList, item, fieldname) ⇒ string diff --git a/lib/util/init.git.js b/lib/util/init.git.js index f6a137a89..91951ffcd 100644 --- a/lib/util/init.git.js +++ b/lib/util/init.git.js @@ -258,15 +258,22 @@ const Init = { for (const file of Object.keys(gitConfigs.values)) { if (gitConfigs.values[file]['user.name']) { - result['user.name'] = gitConfigs.values[file]['user.name']; + // sometimes the config has more than one entry per key - take the last one + result['user.name'] = Array.isArray(gitConfigs.values[file]['user.name']) + ? gitConfigs.values[file]['user.name'].pop() + : gitConfigs.values[file]['user.name']; } if (gitConfigs.values[file]['user.email']) { - result['user.email'] = gitConfigs.values[file]['user.email']; + // sometimes the config has more than one entry per key - take the last one + result['user.email'] = Array.isArray(gitConfigs.values[file]['user.email']) + ? gitConfigs.values[file]['user.email'].pop() + : gitConfigs.values[file]['user.email']; } } if (!result['user.name'] || !result['user.email']) { return null; } + return result; }, }; From 022628188e90641337e82cb732cc58f0055cba6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 8 Dec 2022 10:16:24 +0100 Subject: [PATCH 086/103] #554: auto-add markets & marketLists --- boilerplate/config.json | 8 ++---- lib/util/init.js | 55 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/boilerplate/config.json b/boilerplate/config.json index 61e2f4050..930983d53 100644 --- a/boilerplate/config.json +++ b/boilerplate/config.json @@ -62,14 +62,10 @@ }, "marketList": { "deployment-source": { - "description": "Define one 1:1 BU-Market combo here to as source for automated creation of deployment packages; you can create more than one source market list", - "MySandbox/DEV-NL": "DEV-NL" + "description": "Define one 1:1 BU-Market combo here to as source for automated creation of deployment packages; you can create more than one source market list" }, "deployment-target": { - "description": "Define n BU-Market combo here to as target for automated creation of deployment packages; you can create more than one target market list and they can be as complex as you like", - "MySandbox/QA-DE": "QA-DE", - "MyProduction/PROD-DE": "PROD-DE", - "MyProduction/PROD-NL": "PROD-NL" + "description": "Define n BU-Market combo here to as target for automated creation of deployment packages; you can create more than one target market list and they can be as complex as you like" } }, "metaDataTypes": { diff --git a/lib/util/init.js b/lib/util/init.js index 26ab0c8b4..9bcf1a66e 100644 --- a/lib/util/init.js +++ b/lib/util/init.js @@ -161,6 +161,9 @@ const Init = { return; } + // set up markets and market lists initially + await Init._initMarkets(); + // create first commit to backup the project configuration if (initGit.status === 'init') { Util.logger.info(`Committing initial setup to Git:`); @@ -182,6 +185,58 @@ const Init = { ); } }, + + /** + * helper for @initProject that optionally creates markets and market lists for all BUs + */ + async _initMarkets() { + const skipInteraction = Util.skipInteraction; + const properties = await config.getProperties(true); + + // get list of business units + const firstCredentialName = Object.keys(properties.credentials)[0]; + const businessUnits = Object.keys( + properties.credentials[firstCredentialName].businessUnits + ); + + // set up empty markets for them + const markets = {}; + for (const bu of businessUnits) { + markets[bu] = { suffix: '_' + bu }; + } + properties.markets = markets; + + let sourceBuName; + // set up default deployment market lists + if (skipInteraction) { + // don't ask, list all BUs in deployment-target and set deployment-source to ??? + sourceBuName = '???'; + Util.logger.info( + 'Market List "deployment-source" will need to be set up manually. Marking all BUs as target BUs in "deployment-target".' + ); + } else { + const responses = await inquirer.prompt([ + { + type: 'list', + name: 'developmentBu', + message: 'Please select your development business unit:', + choices: businessUnits.map((bu) => ({ name: bu, value: bu })), + }, + ]); + sourceBuName = responses.developmentBu; + } + // set source list + properties.marketList['deployment-source'][firstCredentialName + '/' + sourceBuName] = + sourceBuName; + + // set target list + for (const bu of businessUnits) { + if (bu !== sourceBuName && bu !== '_ParentBU_') { + properties.marketList['deployment-target'][firstCredentialName + '/' + bu] = bu; + } + } + await File.saveConfigFile(properties); + }, /** * helper for {@link Init.initProject} * From 376003287d670a1fffa6831027b06e31cdf4e940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 8 Dec 2022 10:18:40 +0100 Subject: [PATCH 087/103] #590: allow parsing execSync output manually --- lib/util/util.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/util/util.js b/lib/util/util.js index 79603ffee..478460f5e 100644 --- a/lib/util/util.js +++ b/lib/util/util.js @@ -355,16 +355,24 @@ const Util = { * @param {string} cmd to be executed command * @param {string[]} [args] list of arguments * @param {boolean} [hideOutput] if true, output of command will be hidden from CLI - * @returns {undefined} + * @returns {string} output of command if hideOutput is true */ execSync(cmd, args, hideOutput) { args = args || []; Util.logger.info('⚡ ' + cmd + ' ' + args.join(' ')); - // the following options ensure the user sees the same output and - // interaction options as if the command was manually run - const options = hideOutput ? {} : { stdio: [0, 1, 2] }; - return child_process.execSync(cmd + ' ' + args.join(' '), options); + if (hideOutput) { + // no output displayed to user but instead returned to parsed elsewhere + return child_process + .execSync(cmd + ' ' + args.join(' ')) + .toString() + .trim(); + } else { + // the following options ensure the user sees the same output and + // interaction options as if the command was manually run + child_process.execSync(cmd + ' ' + args.join(' '), { stdio: [0, 1, 2] }); + return null; + } }, /** * standardize check to ensure only one result is returned from template search From a89cb75f173a748b865cee683e34863a580f6af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 8 Dec 2022 11:38:36 +0100 Subject: [PATCH 088/103] #554: handle unattended mode better --- docs/dist/documentation.md | 11 ----------- lib/util/init.js | 17 +++++++++++++---- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index b95979bc5..c95fd02a1 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -176,8 +176,6 @@ Provides default functionality that can be overwritten by child metadata type cl
csvToArray(csv)Array.<string>

helper to convert CSVs into an array. if only one value was given, it's also returned as an array

-
systemSync(cmd)
-
getUserName(userList, item, fieldname)string
setupSDK(sessionKey, authObject)SDK
@@ -6822,15 +6820,6 @@ helper to convert CSVs into an array. if only one value was given, it's also ret | --- | --- | --- | | csv | string | potentially comma-separated value or null | - - -## systemSync(cmd) -**Kind**: global function - -| Param | -| --- | -| cmd | - ## getUserName(userList, item, fieldname) ⇒ string diff --git a/lib/util/init.js b/lib/util/init.js index 9bcf1a66e..368c8b3b5 100644 --- a/lib/util/init.js +++ b/lib/util/init.js @@ -210,10 +210,18 @@ const Init = { // set up default deployment market lists if (skipInteraction) { // don't ask, list all BUs in deployment-target and set deployment-source to ??? - sourceBuName = '???'; - Util.logger.info( - 'Market List "deployment-source" will need to be set up manually. Marking all BUs as target BUs in "deployment-target".' - ); + if (!businessUnits.includes(skipInteraction.developmentBu)) { + Util.logger.warn( + `Could not find developmentBu=${skipInteraction.developmentBu} in business units. Skipping.` + ); + delete skipInteraction.developmentBu; + } + sourceBuName = skipInteraction.developmentBu || '???'; + if (!skipInteraction.developmentBu) { + Util.logger.info( + 'Market List "deployment-source" will need to be set up manually. Marking all BUs as target BUs in "deployment-target".' + ); + } } else { const responses = await inquirer.prompt([ { @@ -231,6 +239,7 @@ const Init = { // set target list for (const bu of businessUnits) { + // filter out source BU & parent BU to ensure they dont get deployed to automatically if (bu !== sourceBuName && bu !== '_ParentBU_') { properties.marketList['deployment-target'][firstCredentialName + '/' + bu] = bu; } From 6f5cb4d0d44e58d0d69d8127742d17ba2bd3d875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 8 Dec 2022 11:54:50 +0100 Subject: [PATCH 089/103] #554: fix exception when gitRemoteUrl is provided but without value --- lib/util/init.git.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/util/init.git.js b/lib/util/init.git.js index 91951ffcd..0446d2a28 100644 --- a/lib/util/init.git.js +++ b/lib/util/init.git.js @@ -167,10 +167,11 @@ const Init = { ]); } /* eslint-enable unicorn/prefer-ternary */ - - responses.gitRemoteUrl = responses.gitRemoteUrl.trim(); - Util.execSync('git', ['remote', 'add', 'origin', responses.gitRemoteUrl]); - return responses.gitRemoteUrl.split('/').pop().split('.')[0]; + if (typeof responses.gitRemoteUrl === 'string') { + responses.gitRemoteUrl = responses.gitRemoteUrl.trim(); + Util.execSync('git', ['remote', 'add', 'origin', responses.gitRemoteUrl]); + return responses.gitRemoteUrl.split('/').pop().split('.')[0]; + } } }, /** From fe104aaff14d1f7f9a1a893bc09f60ce775c60f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 8 Dec 2022 13:52:06 +0100 Subject: [PATCH 090/103] #566: cache all subscriber list from ParentBU even when we retrieve list actively --- docs/dist/documentation.md | 20 +++++++++++++++++--- lib/metadataTypes/List.js | 24 ++++++++++++++++++------ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index c95fd02a1..f150b6b07 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -2905,15 +2905,16 @@ List MetadataType **Extends**: [MetadataType](#MetadataType) * [List](#List) ⇐ [MetadataType](#MetadataType) - * [.retrieve(retrieveDir, [_], [__], [___], [key])](#List.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [.retrieve(retrieveDir, [_], buObject, [___], [key])](#List.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.retrieveForCache(buObject)](#List.retrieveForCache) ⇒ Promise.<TYPE.MetadataTypeMapObj> + * [._retrieveParentAllSubs(buObject, results)](#List._retrieveParentAllSubs) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.deleteByKey(buObject, customerKey)](#List.deleteByKey) ⇒ Promise.<boolean> * [.postRetrieveTasks(list)](#List.postRetrieveTasks) ⇒ TYPE.MetadataTypeItem * [.parseMetadata(metadata, [parseForCache])](#List.parseMetadata) ⇒ TYPE.MetadataTypeItem -### List.retrieve(retrieveDir, [_], [__], [___], [key]) ⇒ Promise.<TYPE.MetadataTypeMapObj> +### List.retrieve(retrieveDir, [_], buObject, [___], [key]) ⇒ Promise.<TYPE.MetadataTypeMapObj> Retrieves Metadata of Lists **Kind**: static method of [List](#List) @@ -2923,7 +2924,7 @@ Retrieves Metadata of Lists | --- | --- | --- | | retrieveDir | string | Directory where retrieved metadata directory will be saved | | [_] | void | unused parameter | -| [__] | void | unused parameter | +| buObject | TYPE.BuObject | properties for auth | | [___] | void | unused parameter | | [key] | string | customer key of single item to retrieve | @@ -2939,6 +2940,19 @@ Gets metadata cache with limited fields and does not store value to disk | --- | --- | --- | | buObject | TYPE.BuObject | properties for auth | + + +### List.\_retrieveParentAllSubs(buObject, results) ⇒ Promise.<TYPE.MetadataTypeMapObj> +helper for @link retrieveForCache and @link retrieve + +**Kind**: static method of [List](#List) +**Returns**: Promise.<TYPE.MetadataTypeMapObj> - Promise + +| Param | Type | Description | +| --- | --- | --- | +| buObject | TYPE.BuObject | properties for auth | +| results | TYPE.MetadataTypeMapObj | metadata from retrieve for current BU | + ### List.deleteByKey(buObject, customerKey) ⇒ Promise.<boolean> diff --git a/lib/metadataTypes/List.js b/lib/metadataTypes/List.js index d8e99fb8a..17ef48a88 100644 --- a/lib/metadataTypes/List.js +++ b/lib/metadataTypes/List.js @@ -18,12 +18,12 @@ class List extends MetadataType { * * @param {string} retrieveDir Directory where retrieved metadata directory will be saved * @param {void} [_] unused parameter - * @param {void} [__] unused parameter + * @param {TYPE.BuObject} buObject properties for auth * @param {void} [___] unused parameter * @param {string} [key] customer key of single item to retrieve * @returns {Promise.} Promise */ - static retrieve(retrieveDir, _, __, ___, key) { + static async retrieve(retrieveDir, _, buObject, ___, key) { /** @type {TYPE.SoapRequestParams} */ let requestParams = null; if (key) { @@ -44,7 +44,8 @@ class List extends MetadataType { }, }; } - return super.retrieveSOAP(retrieveDir, null, requestParams); + const results = await super.retrieveSOAP(retrieveDir, null, requestParams); + return await this._retrieveParentAllSubs(buObject, results); } /** * Gets metadata cache with limited fields and does not store value to disk @@ -53,7 +54,7 @@ class List extends MetadataType { * @returns {Promise.} Promise of metadata */ static async retrieveForCache(buObject) { - let results = await this.retrieve(null); + const results = await this.retrieve(null, null, buObject); if (!cache.getCache()?.folder) { Util.logger.debug('folders not cached but required for list'); Util.logger.info(' - Caching dependent Metadata: folder'); @@ -71,6 +72,17 @@ class List extends MetadataType { for (const metadataEntry in results.metadata) { this.parseMetadata(results.metadata[metadataEntry], true); } + return await this._retrieveParentAllSubs(buObject, results); + } + + /** + * helper for @link retrieveForCache and @link retrieve + * + * @param {TYPE.BuObject} buObject properties for auth + * @param {TYPE.MetadataTypeMapObj} results metadata from retrieve for current BU + * @returns {Promise.} Promise + */ + static async _retrieveParentAllSubs(buObject, results) { if (buObject.eid !== buObject.mid) { // for caching, we want to get the All Subscriber List from the Parent Account Util.logger.debug(' - Checking MasterUnsubscribeBehavior for current BU'); @@ -108,7 +120,7 @@ class List extends MetadataType { const metadataParentBu = await this.retrieve( null, null, - null, + buObjectParentBu, null, 'All Subscribers' ); @@ -125,7 +137,7 @@ class List extends MetadataType { } // make sure to overwrite parent bu DEs with local ones - results = { + return { metadata: { ...metadataParentBu.metadata, ...results.metadata }, type: results.type, }; From a15997b7af3b3d035bc7fee24071f83b1b42ceb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 8 Dec 2022 14:24:20 +0100 Subject: [PATCH 091/103] #566: fix recursive call to get parent all subs --- lib/metadataTypes/List.js | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/metadataTypes/List.js b/lib/metadataTypes/List.js index 17ef48a88..a13146000 100644 --- a/lib/metadataTypes/List.js +++ b/lib/metadataTypes/List.js @@ -72,7 +72,7 @@ class List extends MetadataType { for (const metadataEntry in results.metadata) { this.parseMetadata(results.metadata[metadataEntry], true); } - return await this._retrieveParentAllSubs(buObject, results); + return results; } /** @@ -178,18 +178,23 @@ class List extends MetadataType { * @returns {TYPE.MetadataTypeItem} Array with one metadata object and one sql string */ static parseMetadata(metadata, parseForCache) { - try { - metadata.r__folder_Path = cache.searchForField( - 'folder', - metadata.Category, - 'ID', - 'Path' - ); - if (!parseForCache) { - delete metadata.Category; + if (!metadata.r__folder_Path) { + // if we cached all subs from parent bu, we don't need to parse the folder path again here + try { + metadata.r__folder_Path = cache.searchForField( + 'folder', + metadata.Category, + 'ID', + 'Path' + ); + if (!parseForCache) { + delete metadata.Category; + } + } catch (ex) { + Util.logger.warn( + ` - List ${metadata.ID}: '${metadata.CustomerKey}': ${ex.message}` + ); } - } catch (ex) { - Util.logger.warn(` - List ${metadata.ID}: '${metadata.CustomerKey}': ${ex.message}`); } return metadata; } From c4fd3930857a560b3cc8a54a653d54d94a102e65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Thu, 8 Dec 2022 16:12:40 +0100 Subject: [PATCH 092/103] #594: handle SOAP errors happening during create --- docs/dist/documentation.md | 13 ++++++++ lib/metadataTypes/MetadataType.js | 50 ++++++++++++++----------------- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index c95fd02a1..438f1c07c 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -3011,6 +3011,7 @@ Provides default functionality that can be overwritten by child metadata type cl * [.createSOAP(metadataEntry, [overrideType], [handleOutside])](#MetadataType.createSOAP) ⇒ Promise * [.updateREST(metadataEntry, uri)](#MetadataType.updateREST) ⇒ Promise * [.updateSOAP(metadataEntry, [overrideType], [handleOutside])](#MetadataType.updateSOAP) ⇒ Promise + * [._handleSOAPErrors(ex, metadataEntry, msg, [handleOutside])](#MetadataType._handleSOAPErrors) * [.retrieveSOAP(retrieveDir, buObject, [requestParams], [additionalFields])](#MetadataType.retrieveSOAP) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.retrieveREST(retrieveDir, uri, [overrideType], [templateVariables], [singleRetrieve])](#MetadataType.retrieveREST) ⇒ Promise.<{metadata: (TYPE.MetadataTypeMap\|TYPE.MetadataTypeItem), type: string}> * [.parseResponseBody(body, [singleRetrieve])](#MetadataType.parseResponseBody) ⇒ TYPE.MetadataTypeMap @@ -3339,6 +3340,18 @@ Updates a single metadata entry via fuel-soap (generic lib not wrapper) | [overrideType] | string | can be used if the API type differs from the otherwise used type identifier | | [handleOutside] | boolean | if the API reponse is irregular this allows you to handle it outside of this generic method | + + +### MetadataType.\_handleSOAPErrors(ex, metadataEntry, msg, [handleOutside]) +**Kind**: static method of [MetadataType](#MetadataType) + +| Param | Type | Description | +| --- | --- | --- | +| ex | Error | error that occured | +| metadataEntry | TYPE.MetadataTypeItem | single metadata entry | +| msg | 'creating' \| 'updating' | what to print in the log | +| [handleOutside] | boolean | if the API reponse is irregular this allows you to handle it outside of this generic method | + ### MetadataType.retrieveSOAP(retrieveDir, buObject, [requestParams], [additionalFields]) ⇒ Promise.<TYPE.MetadataTypeMapObj> diff --git a/lib/metadataTypes/MetadataType.js b/lib/metadataTypes/MetadataType.js index 60bdd6ccd..c2970e354 100644 --- a/lib/metadataTypes/MetadataType.js +++ b/lib/metadataTypes/MetadataType.js @@ -614,20 +614,7 @@ class MetadataType { } return response; } catch (ex) { - if (handleOutside) { - throw ex; - } else { - const errorMsg = - ex.results && ex.results.length - ? `${ex.results[0].StatusMessage} (Code ${ex.results[0].ErrorCode})` - : ex.message; - Util.logger.error( - ` ☇ error creating ${this.definition.type} '${ - metadataEntry[this.definition.keyField] - }': ${errorMsg}` - ); - } - + this._handleSOAPErrors(ex, metadataEntry, 'creating', handleOutside); return {}; } } @@ -693,22 +680,31 @@ class MetadataType { } return response; } catch (ex) { - if (handleOutside) { - throw ex; - } else { - const errorMsg = ex?.json?.Results.length - ? `${ex.json.Results[0].StatusMessage} (Code ${ex.json.Results[0].ErrorCode})` - : ex.message; - Util.logger.error( - ` ☇ error updating ${this.definition.type} '${ - metadataEntry[this.definition.keyField] - }': ${errorMsg}` - ); - } - + this._handleSOAPErrors(ex, metadataEntry, 'updating', handleOutside); return {}; } } + /** + * + * @param {Error} ex error that occured + * @param {TYPE.MetadataTypeItem} metadataEntry single metadata entry + * @param {'creating'|'updating'} msg what to print in the log + * @param {boolean} [handleOutside] if the API reponse is irregular this allows you to handle it outside of this generic method + */ + static _handleSOAPErrors(ex, metadataEntry, msg, handleOutside) { + if (handleOutside) { + throw ex; + } else { + const errorMsg = ex?.json?.Results?.length + ? `${ex.json.Results[0].StatusMessage} (Code ${ex.json.Results[0].ErrorCode})` + : ex.message; + Util.logger.error( + ` ☇ error ${msg} ${this.definition.type} '${ + metadataEntry[this.definition.keyField] + }': ${errorMsg}` + ); + } + } /** * Retrieves SOAP via generic fuel-soap wrapper based metadata of metadata type into local filesystem. executes callback with retrieved metadata * From 7e7ade54e1e0ebe834f67076c9c6e4ea4807ebe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 9 Dec 2022 14:40:22 +0100 Subject: [PATCH 093/103] #594: handle SOAP retrieve errors related: https://github.com/DougMidgley/SFMC-SDK/pull/147 --- docs/dist/documentation.md | 6 +++--- lib/metadataTypes/MetadataType.js | 31 +++++++++++++++++-------------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index 438f1c07c..088177b11 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -3011,7 +3011,7 @@ Provides default functionality that can be overwritten by child metadata type cl * [.createSOAP(metadataEntry, [overrideType], [handleOutside])](#MetadataType.createSOAP) ⇒ Promise * [.updateREST(metadataEntry, uri)](#MetadataType.updateREST) ⇒ Promise * [.updateSOAP(metadataEntry, [overrideType], [handleOutside])](#MetadataType.updateSOAP) ⇒ Promise - * [._handleSOAPErrors(ex, metadataEntry, msg, [handleOutside])](#MetadataType._handleSOAPErrors) + * [._handleSOAPErrors(ex, msg, [metadataEntry], [handleOutside])](#MetadataType._handleSOAPErrors) * [.retrieveSOAP(retrieveDir, buObject, [requestParams], [additionalFields])](#MetadataType.retrieveSOAP) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.retrieveREST(retrieveDir, uri, [overrideType], [templateVariables], [singleRetrieve])](#MetadataType.retrieveREST) ⇒ Promise.<{metadata: (TYPE.MetadataTypeMap\|TYPE.MetadataTypeItem), type: string}> * [.parseResponseBody(body, [singleRetrieve])](#MetadataType.parseResponseBody) ⇒ TYPE.MetadataTypeMap @@ -3342,14 +3342,14 @@ Updates a single metadata entry via fuel-soap (generic lib not wrapper) -### MetadataType.\_handleSOAPErrors(ex, metadataEntry, msg, [handleOutside]) +### MetadataType.\_handleSOAPErrors(ex, msg, [metadataEntry], [handleOutside]) **Kind**: static method of [MetadataType](#MetadataType) | Param | Type | Description | | --- | --- | --- | | ex | Error | error that occured | -| metadataEntry | TYPE.MetadataTypeItem | single metadata entry | | msg | 'creating' \| 'updating' | what to print in the log | +| [metadataEntry] | TYPE.MetadataTypeItem | single metadata entry | | [handleOutside] | boolean | if the API reponse is irregular this allows you to handle it outside of this generic method | diff --git a/lib/metadataTypes/MetadataType.js b/lib/metadataTypes/MetadataType.js index c2970e354..cc1070dcb 100644 --- a/lib/metadataTypes/MetadataType.js +++ b/lib/metadataTypes/MetadataType.js @@ -614,7 +614,7 @@ class MetadataType { } return response; } catch (ex) { - this._handleSOAPErrors(ex, metadataEntry, 'creating', handleOutside); + this._handleSOAPErrors(ex, 'creating', metadataEntry, handleOutside); return {}; } } @@ -680,29 +680,26 @@ class MetadataType { } return response; } catch (ex) { - this._handleSOAPErrors(ex, metadataEntry, 'updating', handleOutside); + this._handleSOAPErrors(ex, 'updating', metadataEntry, handleOutside); return {}; } } /** * * @param {Error} ex error that occured - * @param {TYPE.MetadataTypeItem} metadataEntry single metadata entry * @param {'creating'|'updating'} msg what to print in the log + * @param {TYPE.MetadataTypeItem} [metadataEntry] single metadata entry * @param {boolean} [handleOutside] if the API reponse is irregular this allows you to handle it outside of this generic method */ - static _handleSOAPErrors(ex, metadataEntry, msg, handleOutside) { + static _handleSOAPErrors(ex, msg, metadataEntry, handleOutside) { if (handleOutside) { throw ex; } else { const errorMsg = ex?.json?.Results?.length ? `${ex.json.Results[0].StatusMessage} (Code ${ex.json.Results[0].ErrorCode})` : ex.message; - Util.logger.error( - ` ☇ error ${msg} ${this.definition.type} '${ - metadataEntry[this.definition.keyField] - }': ${errorMsg}` - ); + const name = metadataEntry ? ` '${metadataEntry[this.definition.keyField]}'` : ''; + Util.logger.error(` ☇ error ${msg} ${this.definition.type}${name}: ${errorMsg}`); } } /** @@ -717,11 +714,17 @@ class MetadataType { static async retrieveSOAP(retrieveDir, buObject, requestParams, additionalFields) { requestParams = requestParams || {}; const fields = this.getFieldNamesToRetrieve(additionalFields); - const response = await this.client.soap.retrieveBulk( - this.definition.type, - fields, - requestParams - ); + let response; + try { + response = await this.client.soap.retrieveBulk( + this.definition.type, + fields, + requestParams + ); + } catch (ex) { + this._handleSOAPErrors(ex, 'retrieving'); + return {}; + } const metadata = this.parseResponseBody(response); if (retrieveDir) { From 75e875f1d99c3acbd42cd507f931eab219d0067a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 9 Dec 2022 14:47:17 +0100 Subject: [PATCH 094/103] #599: in mcdev init use local user name/email if found, otherwise global ones --- lib/util/init.git.js | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/lib/util/init.git.js b/lib/util/init.git.js index 0446d2a28..2d815750f 100644 --- a/lib/util/init.git.js +++ b/lib/util/init.git.js @@ -252,30 +252,10 @@ const Init = { * @returns {Promise.<{'user.name': string, 'user.email': string}>} user.name and user.email */ async _getGitConfigUser() { - const gitConfigs = await git.listConfig(); - // remove local config - delete gitConfigs.values['.git/config']; - const result = {}; + const name = Util.execSync('git', ['config', 'user.name'], true); + const email = Util.execSync('git', ['config', 'user.email'], true); - for (const file of Object.keys(gitConfigs.values)) { - if (gitConfigs.values[file]['user.name']) { - // sometimes the config has more than one entry per key - take the last one - result['user.name'] = Array.isArray(gitConfigs.values[file]['user.name']) - ? gitConfigs.values[file]['user.name'].pop() - : gitConfigs.values[file]['user.name']; - } - if (gitConfigs.values[file]['user.email']) { - // sometimes the config has more than one entry per key - take the last one - result['user.email'] = Array.isArray(gitConfigs.values[file]['user.email']) - ? gitConfigs.values[file]['user.email'].pop() - : gitConfigs.values[file]['user.email']; - } - } - if (!result['user.name'] || !result['user.email']) { - return null; - } - - return result; + return { 'user.name': name || '', 'user.email': email || '' }; }, }; From e595c039e9ad07befbb1419b102cce815d84b320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 9 Dec 2022 15:55:13 +0100 Subject: [PATCH 095/103] #596: list ID is optional on TSDs --- lib/metadataTypes/TriggeredSendDefinition.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/metadataTypes/TriggeredSendDefinition.js b/lib/metadataTypes/TriggeredSendDefinition.js index 9e718d5be..2853c1608 100644 --- a/lib/metadataTypes/TriggeredSendDefinition.js +++ b/lib/metadataTypes/TriggeredSendDefinition.js @@ -169,14 +169,16 @@ class TriggeredSendDefinition extends MetadataType { ); } } - // List - try { - metadata.r__list_PathName = cache.getListPathName(metadata.List.ID, 'ID'); - delete metadata.List; - } catch (ex) { - Util.logger.warn( - ` - ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': ${ex.message}` - ); + // List (optional) + if (metadata.List) { + try { + metadata.r__list_PathName = cache.getListPathName(metadata.List.ID, 'ID'); + delete metadata.List; + } catch (ex) { + Util.logger.warn( + ` - ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': ${ex.message}` + ); + } } return metadata; @@ -227,7 +229,7 @@ class TriggeredSendDefinition extends MetadataType { `r__assetMessage_Key / r__email_Name not defined but instead found Email.ID. Please try re-retrieving this TSD from your BU.` ); } - // get List + // get List (optional) if (metadata.r__list_PathName) { metadata.List = { ID: cache.getListObjectId(metadata.r__list_PathName, 'ID'), From 7599e3c999e7925eafc6d799333379239b619719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 9 Dec 2022 15:58:43 +0100 Subject: [PATCH 096/103] #596: ensure retrieved fields that can be upserted are also used for templating --- .../TriggeredSendDefinition.definition.js | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/lib/metadataTypes/definitions/TriggeredSendDefinition.definition.js b/lib/metadataTypes/definitions/TriggeredSendDefinition.definition.js index da48afecc..705aa5059 100644 --- a/lib/metadataTypes/definitions/TriggeredSendDefinition.definition.js +++ b/lib/metadataTypes/definitions/TriggeredSendDefinition.definition.js @@ -22,41 +22,41 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, AutoAddSubscribers: { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, AutoUpdateSubscribers: { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, BatchInterval: { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, BccEmail: { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, CategoryID: { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, CCEmail: { - isCreateable: true, - isUpdateable: true, + isCreateable: false, + isUpdateable: false, retrieving: false, templating: false, }, @@ -88,7 +88,7 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, DataSchemas: { isCreateable: false, @@ -100,7 +100,7 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, 'DeliveryProfile.ObjectID': { isCreateable: true, @@ -112,7 +112,7 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, DisableOnEmailBuildError: { isCreateable: false, @@ -130,13 +130,13 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, 'Email.ID': { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, 'Email.PartnerKey': { isCreateable: false, @@ -154,7 +154,7 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, ExclusionFilter: { isCreateable: false, @@ -172,7 +172,7 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, FooterSalutationSource: { isCreateable: false, @@ -184,19 +184,19 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, FromName: { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, 'HeaderContentArea.ID': { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, HeaderSalutationSource: { isCreateable: false, @@ -226,7 +226,7 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, IsPlatformObject: { isCreateable: true, @@ -244,7 +244,7 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, KeepExistingEmailSubject: { isCreateable: false, @@ -256,13 +256,13 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, 'List.ID': { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, 'List.PartnerKey': { isCreateable: false, @@ -286,13 +286,13 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, NewSlotTrigger: { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, ObjectID: { isCreateable: false, @@ -352,13 +352,13 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, 'PrivateDomain.ObjectID': { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, 'PrivateDomain.PartnerKey': { isCreateable: false, @@ -370,7 +370,7 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, 'PrivateIP.PartnerKey': { isCreateable: false, @@ -406,7 +406,7 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, 'SendClassification.ObjectID': { isCreateable: false, @@ -424,7 +424,7 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, 'SenderProfile.ObjectID': { isCreateable: false, @@ -442,7 +442,7 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, SendSourceCustomerKey: { isCreateable: false, @@ -460,7 +460,7 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, SendWindowDelete: { isCreateable: false, @@ -472,7 +472,7 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, SourceAddressType: { isCreateable: false, @@ -484,13 +484,13 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, TestEmailAddr: { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, TriggeredSendClass: { isCreateable: false, @@ -502,7 +502,7 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, TriggeredSendSubClass: { isCreateable: false, @@ -514,7 +514,7 @@ module.exports = { isCreateable: true, isUpdateable: true, retrieving: true, - templating: false, + templating: true, }, TriggeredSendVersionID: { isCreateable: false, From 5ab5ce3b0afe4e325c5b0b6deedc3987169cfe01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 9 Dec 2022 16:19:57 +0100 Subject: [PATCH 097/103] #597: fix Cannot read properties of undefined (reading 'Role') --- lib/metadataTypes/AccountUser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/metadataTypes/AccountUser.js b/lib/metadataTypes/AccountUser.js index aaa6d3b6b..ed6adcec9 100644 --- a/lib/metadataTypes/AccountUser.js +++ b/lib/metadataTypes/AccountUser.js @@ -377,7 +377,7 @@ class AccountUser extends MetadataType { metadata.AssociatedBusinessUnits__c = this.userIdBuMap[metadata.ID] || []; let roles; - if (metadata.Roles.Role) { + if (metadata.Roles?.Role) { // normalize to always use array if (!metadata.Roles.Role.length) { metadata.Roles.Role = [metadata.Roles.Role]; From be69e8159e562e05b23fedf901d1852256131069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Fri, 9 Dec 2022 16:22:14 +0100 Subject: [PATCH 098/103] #597: improve logging --- lib/metadataTypes/AccountUser.js | 2 +- lib/util/auth.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/metadataTypes/AccountUser.js b/lib/metadataTypes/AccountUser.js index ed6adcec9..b8b22d41c 100644 --- a/lib/metadataTypes/AccountUser.js +++ b/lib/metadataTypes/AccountUser.js @@ -112,7 +112,7 @@ class AccountUser extends MetadataType { }, }; } - + Util.logger.info(` - Loading ${this.definition.type}. This might take a while...`); return super.retrieveSOAP(retrieveDir, buObject, requestParams); } /** diff --git a/lib/util/auth.js b/lib/util/auth.js index 71fa18bd2..e69169b36 100644 --- a/lib/util/auth.js +++ b/lib/util/auth.js @@ -93,7 +93,7 @@ function setupSDK(sessionKey, authObject) { eventHandlers: { onLoop: (type, req) => { Util.logger.info( - `- Requesting next batch (currently ${req.Results.length} records)` + ` - Requesting next batch (currently ${req.Results.length} records)` ); }, onRefresh: (authObject) => { @@ -102,7 +102,7 @@ function setupSDK(sessionKey, authObject) { }, onConnectionError: (ex, remainingAttempts) => { Util.logger.warn( - `- Connection problem (Code: ${ex.code}). Retrying ${remainingAttempts} time${ + ` - Connection problem (Code: ${ex.code}). Retrying ${remainingAttempts} time${ remainingAttempts > 1 ? 's' : '' }` ); From 54c4d2c10d8c015c21be6f489ab294abacbf1ddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Mon, 12 Dec 2022 15:16:52 +0100 Subject: [PATCH 099/103] #599: replaced cli with simple-git approach --- lib/util/init.git.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/util/init.git.js b/lib/util/init.git.js index 2d815750f..e91de4b58 100644 --- a/lib/util/init.git.js +++ b/lib/util/init.git.js @@ -252,10 +252,10 @@ const Init = { * @returns {Promise.<{'user.name': string, 'user.email': string}>} user.name and user.email */ async _getGitConfigUser() { - const name = Util.execSync('git', ['config', 'user.name'], true); - const email = Util.execSync('git', ['config', 'user.email'], true); + const names = await git.getConfig('user.name'); + const emails = await git.getConfig('user.email'); - return { 'user.name': name || '', 'user.email': email || '' }; + return { 'user.name': names.value || '', 'user.email': emails.value || '' }; }, }; From f51d0cdcd308e2e460da3af27306795b4eaed5f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Mon, 12 Dec 2022 16:22:32 +0100 Subject: [PATCH 100/103] #0: add npm version scripts --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index ac9dfeb27..7f21b1860 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,10 @@ "lint-test": "eslint test/**/*.js", "upgrade": "npm-check --update", "manual-prepare": "husky install", - "test": "mocha" + "test": "mocha", + "version:major": "npm version --no-commit-hooks major", + "version:minor": "npm version --no-commit-hooks minor", + "version:patch": "npm version --no-commit-hooks patch" }, "dependencies": { "beauty-amp-core": "0.3.7", From 22be80099811aeb4ceed2e0c88dd44f37c51ccb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Mon, 12 Dec 2022 16:27:06 +0100 Subject: [PATCH 101/103] 4.2.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index daa308d9c..c92bdf645 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mcdev", - "version": "4.1.12", + "version": "4.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "mcdev", - "version": "4.1.12", + "version": "4.2.0", "license": "MIT", "dependencies": { "beauty-amp-core": "0.3.7", diff --git a/package.json b/package.json index 7f21b1860..943befc77 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mcdev", - "version": "4.1.12", + "version": "4.2.0", "description": "Accenture Salesforce Marketing Cloud DevTools", "author": "Accenture: joern.berkefeld, douglas.midgley, robert.zimmermann, maciej.barnas", "license": "MIT", From 97ca0272df2b8ff15a6b77f2cde558d29eee4102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Mon, 12 Dec 2022 16:36:21 +0100 Subject: [PATCH 102/103] #0: prep 4.2.0 release --- .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 d2b992299..ad75bd4dd 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -39,6 +39,7 @@ body: label: Version description: What version of our software are you running? (mcdev --version) options: + - 4.2.0 - 4.1.12 - 4.1.11 - 4.1.10 diff --git a/test/mockRoot/.mcdevrc.json b/test/mockRoot/.mcdevrc.json index 7595d015c..c67fae6db 100644 --- a/test/mockRoot/.mcdevrc.json +++ b/test/mockRoot/.mcdevrc.json @@ -74,5 +74,5 @@ "triggeredSendDefinition" ] }, - "version": "4.1.12" + "version": "4.2.0" } From 926b5f2aa17c16af441481210c6249a5284d0aef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Berkefeld?= Date: Mon, 12 Dec 2022 17:04:04 +0100 Subject: [PATCH 103/103] #550: added Coverage check via NYC --- .gitignore | 1 + .nycrc.json | 5 + package-lock.json | 2014 ++++++++++++++++++++++++++++++++++++++++++--- package.json | 2 + 4 files changed, 1928 insertions(+), 94 deletions(-) create mode 100644 .nycrc.json diff --git a/.gitignore b/.gitignore index 0cbd207c7..0fc9c04ab 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ tmp/ .mcdevrc.json .vscode/tasks.json !test/mockRoot/* +/.nyc_output/ diff --git a/.nycrc.json b/.nycrc.json new file mode 100644 index 000000000..eed922e02 --- /dev/null +++ b/.nycrc.json @@ -0,0 +1,5 @@ +{ + "all": true, + "include": ["lib/**"], + "exclude": ["lib/metadataTypes/definitions/*.js", "test/**", "types/**"] +} diff --git a/package-lock.json b/package-lock.json index c92bdf645..a9f9efff3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52,6 +52,7 @@ "mock-fs": "5.2.0", "npm-check": "6.0.1", "npm-run-all": "4.1.5", + "nyc": "15.1.0", "prettier-eslint": "15.0.1" }, "engines": { @@ -65,6 +66,32 @@ "fsevents": "*" } }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@ampproject/remapping/node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -77,13 +104,61 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/compat-data": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz", + "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz", + "integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-module-transforms": "^7.20.2", + "@babel/helpers": "^7.20.5", + "@babel/parser": "^7.20.5", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "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==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/generator": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.9.tgz", - "integrity": "sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz", + "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==", "dev": true, "dependencies": { - "@babel/types": "^7.18.9", + "@babel/types": "^7.20.5", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -91,6 +166,33 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.20.0", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "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==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/helper-environment-visitor": { "version": "7.18.9", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", @@ -101,13 +203,13 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "dev": true, "dependencies": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -125,6 +227,49 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz", + "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-split-export-declaration": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", @@ -137,6 +282,15 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-validator-identifier": { "version": "7.19.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", @@ -146,6 +300,29 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz", + "integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==", + "dev": true, + "dependencies": { + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/highlight": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", @@ -232,9 +409,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.9.tgz", - "integrity": "sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", + "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -244,33 +421,33 @@ } }, "node_modules/@babel/template": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.6.tgz", - "integrity": "sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==", + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.6", - "@babel/types": "^7.18.6" + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.9.tgz", - "integrity": "sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz", + "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.9", + "@babel/generator": "^7.20.5", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.9", - "@babel/types": "^7.18.9", + "@babel/parser": "^7.20.5", + "@babel/types": "^7.20.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -288,12 +465,13 @@ } }, "node_modules/@babel/types": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.9.tgz", - "integrity": "sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" }, "engines": { @@ -434,6 +612,123 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", @@ -985,6 +1280,24 @@ "node": ">= 8" } }, + "node_modules/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "dependencies": { + "default-require-extensions": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1219,6 +1532,34 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "node_modules/browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -1316,6 +1657,21 @@ "node": ">=8" } }, + "node_modules/caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "dependencies": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -1470,6 +1826,22 @@ "node": ">=6" } }, + "node_modules/caniuse-lite": { + "version": "1.0.30001439", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz", + "integrity": "sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, "node_modules/catharsis": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", @@ -1985,6 +2357,12 @@ "node": ">=4.0.0" } }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2070,6 +2448,12 @@ "node": "> 0.10" } }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, "node_modules/cosmiconfig": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", @@ -2267,6 +2651,30 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/default-require-extensions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", + "dev": true, + "dependencies": { + "strip-bom": "^4.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-require-extensions/node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/defaults": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", @@ -2516,6 +2924,12 @@ "wcwidth": ">=1.0.1" } }, + "node_modules/electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "dev": true + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -2638,6 +3052,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, "node_modules/es6-object-assign": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", @@ -3239,6 +3659,87 @@ "node": ">=8" } }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-cache-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-cache-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/find-replace": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", @@ -3440,6 +3941,19 @@ "is-callable": "^1.1.3" } }, + "node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -3453,6 +3967,26 @@ "node": ">= 6" } }, + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/fs-extra": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", @@ -3507,6 +4041,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -3537,6 +4080,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -3877,6 +4429,31 @@ "node": ">=8" } }, + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasha/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -3980,6 +4557,12 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, "node_modules/http-cache-semantics": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", @@ -4595,45 +5178,169 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "dependencies": { + "append-transform": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "dev": true, + "dependencies": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "aggregate-error": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } }, "node_modules/js-sdsl": { "version": "4.1.5", @@ -5117,6 +5824,12 @@ "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", "dev": true }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "dev": true + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -5887,6 +6600,24 @@ "lodash": "^4.17.21" } }, + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "dependencies": { + "process-on-spawn": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, "node_modules/node-sql-parser": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/node-sql-parser/-/node-sql-parser-4.5.1.tgz", @@ -6274,6 +7005,195 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "dependencies": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "bin": { + "nyc": "bin/nyc.js" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/nyc/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/nyc/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/nyc/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nyc/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/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==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/nyc/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/object-get": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/object-get/-/object-get-2.1.1.tgz", @@ -6492,6 +7412,21 @@ "node": ">=6" } }, + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/package-json": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", @@ -6889,6 +7824,18 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "node_modules/process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "dependencies": { + "fromentries": "^1.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -7365,6 +8312,18 @@ "jsesc": "bin/jsesc" } }, + "node_modules/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "dev": true, + "dependencies": { + "es6-error": "^4.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7381,6 +8340,12 @@ "node": ">=0.10.0" } }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, "node_modules/require-package-name": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/require-package-name/-/require-package-name-2.0.1.tgz", @@ -7644,6 +8609,12 @@ "randombytes": "^2.1.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, "node_modules/sfmc-sdk": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/sfmc-sdk/-/sfmc-sdk-0.6.1.tgz", @@ -7822,6 +8793,23 @@ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", "dev": true }, + "node_modules/spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "dependencies": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -8126,6 +9114,20 @@ "integrity": "sha512-TvmyH7kC6ZVTYkqCODjJIbgvu0FKiwQpZ4D1aknE7xpcDf/qEOB8KZEK5ef2pfbVoiBhNWs3yx4y+ESMtNYmlg==", "dev": true }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/test-value": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/test-value/-/test-value-3.0.0.tgz", @@ -8381,6 +9383,32 @@ "node": ">= 10.0.0" } }, + "node_modules/update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/update-notifier": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", @@ -8446,6 +9474,15 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -8541,6 +9578,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "dev": true + }, "node_modules/which-pm": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-pm/-/which-pm-2.0.0.tgz", @@ -8816,6 +9859,28 @@ } }, "dependencies": { + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, "@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -8825,17 +9890,74 @@ "@babel/highlight": "^7.18.6" } }, + "@babel/compat-data": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz", + "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", + "dev": true + }, + "@babel/core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz", + "integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-module-transforms": "^7.20.2", + "@babel/helpers": "^7.20.5", + "@babel/parser": "^7.20.5", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "@babel/generator": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.9.tgz", - "integrity": "sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz", + "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==", "dev": true, "requires": { - "@babel/types": "^7.18.9", + "@babel/types": "^7.20.5", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" } }, + "@babel/helper-compilation-targets": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.0", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "@babel/helper-environment-visitor": { "version": "7.18.9", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", @@ -8843,13 +9965,13 @@ "dev": true }, "@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "dev": true, "requires": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" } }, "@babel/helper-hoist-variables": { @@ -8861,6 +9983,40 @@ "@babel/types": "^7.18.6" } }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz", + "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2" + } + }, + "@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dev": true, + "requires": { + "@babel/types": "^7.20.2" + } + }, "@babel/helper-split-export-declaration": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", @@ -8870,12 +10026,35 @@ "@babel/types": "^7.18.6" } }, + "@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true + }, "@babel/helper-validator-identifier": { "version": "7.19.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "dev": true }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz", + "integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==", + "dev": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" + } + }, "@babel/highlight": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", @@ -8946,36 +10125,36 @@ } }, "@babel/parser": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.9.tgz", - "integrity": "sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", + "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", "dev": true }, "@babel/template": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.6.tgz", - "integrity": "sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==", + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", "dev": true, "requires": { "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.6", - "@babel/types": "^7.18.6" + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" } }, "@babel/traverse": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.9.tgz", - "integrity": "sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz", + "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==", "dev": true, "requires": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.9", + "@babel/generator": "^7.20.5", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.9", - "@babel/types": "^7.18.9", + "@babel/parser": "^7.20.5", + "@babel/types": "^7.20.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -8989,12 +10168,13 @@ } }, "@babel/types": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.9.tgz", - "integrity": "sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" } }, @@ -9102,6 +10282,95 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, "@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", @@ -9525,6 +10794,21 @@ "picomatch": "^2.0.4" } }, + "append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "requires": { + "default-require-extensions": "^3.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -9700,6 +10984,18 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + } + }, "buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -9763,6 +11059,18 @@ } } }, + "caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "requires": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + } + }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -9882,6 +11190,12 @@ } } }, + "caniuse-lite": { + "version": "1.0.30001439", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz", + "integrity": "sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==", + "dev": true + }, "catharsis": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", @@ -10276,6 +11590,12 @@ "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", "dev": true }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -10347,6 +11667,12 @@ "easy-table": "1.1.0" } }, + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, "cosmiconfig": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", @@ -10492,6 +11818,23 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "default-require-extensions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", + "dev": true, + "requires": { + "strip-bom": "^4.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + } + } + }, "defaults": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", @@ -10694,6 +12037,12 @@ "wcwidth": ">=1.0.1" } }, + "electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "dev": true + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -10795,6 +12144,12 @@ "is-symbol": "^1.0.2" } }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, "es6-object-assign": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", @@ -11217,15 +12572,74 @@ } } }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + } + } + }, "find-replace": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", @@ -11371,6 +12785,16 @@ "is-callable": "^1.1.3" } }, + "foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + } + }, "form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -11381,6 +12805,12 @@ "mime-types": "^2.1.12" } }, + "fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true + }, "fs-extra": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", @@ -11419,6 +12849,12 @@ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -11440,6 +12876,12 @@ "has-symbols": "^1.0.3" } }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, "get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -11681,6 +13123,24 @@ "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" }, + "hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "requires": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -11768,6 +13228,12 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, "http-cache-semantics": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", @@ -12178,6 +13644,12 @@ "get-intrinsic": "^1.1.1" } }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, "is-yarn-global": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", @@ -12194,6 +13666,98 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "requires": { + "append-transform": "^2.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "dev": true, + "requires": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" + }, + "dependencies": { + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, "js-sdsl": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", @@ -12583,6 +14147,12 @@ "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", "dev": true }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "dev": true + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -13167,6 +14737,21 @@ "lodash": "^4.17.21" } }, + "node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "requires": { + "process-on-spawn": "^1.0.0" + } + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, "node-sql-parser": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/node-sql-parser/-/node-sql-parser-4.5.1.tgz", @@ -13464,6 +15049,158 @@ } } }, + "nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "requires": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "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==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, "object-get": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/object-get/-/object-get-2.1.1.tgz", @@ -13617,6 +15354,18 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, + "package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } + }, "package-json": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", @@ -13906,6 +15655,15 @@ } } }, + "process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "requires": { + "fromentries": "^1.2.0" + } + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -14263,6 +16021,15 @@ } } }, + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "dev": true, + "requires": { + "es6-error": "^4.0.1" + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -14273,6 +16040,12 @@ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, "require-package-name": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/require-package-name/-/require-package-name-2.0.1.tgz", @@ -14458,6 +16231,12 @@ "randombytes": "^2.1.0" } }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, "sfmc-sdk": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/sfmc-sdk/-/sfmc-sdk-0.6.1.tgz", @@ -14595,6 +16374,20 @@ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", "dev": true }, + "spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "requires": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + } + }, "spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -14832,6 +16625,17 @@ "integrity": "sha512-TvmyH7kC6ZVTYkqCODjJIbgvu0FKiwQpZ4D1aknE7xpcDf/qEOB8KZEK5ef2pfbVoiBhNWs3yx4y+ESMtNYmlg==", "dev": true }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, "test-value": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/test-value/-/test-value-3.0.0.tgz", @@ -15030,6 +16834,16 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" }, + "update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, "update-notifier": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", @@ -15086,6 +16900,12 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -15157,6 +16977,12 @@ "is-weakset": "^2.0.1" } }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "dev": true + }, "which-pm": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-pm/-/which-pm-2.0.0.tgz", diff --git a/package.json b/package.json index 943befc77..c6bc4400c 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "upgrade": "npm-check --update", "manual-prepare": "husky install", "test": "mocha", + "coverage": "nyc mocha", "version:major": "npm version --no-commit-hooks major", "version:minor": "npm version --no-commit-hooks minor", "version:patch": "npm version --no-commit-hooks patch" @@ -83,6 +84,7 @@ "mock-fs": "5.2.0", "npm-check": "6.0.1", "npm-run-all": "4.1.5", + "nyc": "15.1.0", "prettier-eslint": "15.0.1" }, "optionalDependencies": {