From dbbd271475e7a657d9a009fde4d4fecaf5c58bf4 Mon Sep 17 00:00:00 2001 From: Nico Rehwaldt Date: Fri, 2 Dec 2022 10:14:09 +0100 Subject: [PATCH 1/4] deps!: update to bpmn-js@11 / diagram-js@11 Incorporates new popup menu. BREAKING CHANGES: * Slight changes in popup menu look --- package-lock.json | 124 ++++++++++++---------------------------------- package.json | 6 +-- 2 files changed, 34 insertions(+), 96 deletions(-) diff --git a/package-lock.json b/package-lock.json index aeefb90e..c3b73ff0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,13 +11,13 @@ "dependencies": { "@bpmn-io/align-to-origin": "^0.7.0", "@bpmn-io/element-templates-icons-renderer": "^0.3.0", - "bpmn-js": "^11.0.0", + "bpmn-js": "^11.0.1", "bpmn-js-executable-fix": "^0.2.0", "camunda-bpmn-js-behaviors": "^0.4.0", "camunda-bpmn-moddle": "^7.0.1", - "diagram-js": "^11.0.0", + "diagram-js": "^11.1.1", "diagram-js-minimap": "^3.0.0", - "diagram-js-origin": "^1.3.4", + "diagram-js-origin": "^1.4.0", "inherits": "^2.0.4", "min-dash": "^4.0.0", "zeebe-bpmn-moddle": "^0.17.0" @@ -474,12 +474,6 @@ "bpmn-js": ">= 8" } }, - "node_modules/@bpmn-io/element-template-chooser/node_modules/tiny-svg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tiny-svg/-/tiny-svg-3.0.0.tgz", - "integrity": "sha512-+u6VomQO7MbI7CQe5q1IwNePpbVKG/HVdUQBmaEpSCdP/QmeyjhrS6WKFsNetXlvf9LWu/f5woRqjMdxBMe/0w==", - "dev": true - }, "node_modules/@bpmn-io/element-templates-icons-renderer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@bpmn-io/element-templates-icons-renderer/-/element-templates-icons-renderer-0.3.0.tgz", @@ -493,11 +487,6 @@ "diagram-js": ">= 7" } }, - "node_modules/@bpmn-io/element-templates-icons-renderer/node_modules/tiny-svg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tiny-svg/-/tiny-svg-3.0.0.tgz", - "integrity": "sha512-+u6VomQO7MbI7CQe5q1IwNePpbVKG/HVdUQBmaEpSCdP/QmeyjhrS6WKFsNetXlvf9LWu/f5woRqjMdxBMe/0w==" - }, "node_modules/@bpmn-io/element-templates-validator": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@bpmn-io/element-templates-validator/-/element-templates-validator-0.11.0.tgz", @@ -2226,13 +2215,13 @@ "dev": true }, "node_modules/bpmn-js": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/bpmn-js/-/bpmn-js-11.0.0.tgz", - "integrity": "sha512-XO08/hCpOm0X///Zk9VvQ/IwfwcVJHY3ztM0KhsbcfFtW8FR4VhJMu0EvGOpm2R0Z5ORiyyctft0SqrtewixPg==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/bpmn-js/-/bpmn-js-11.0.1.tgz", + "integrity": "sha512-ZY7XXfjuY1U3XOmiOrAVr4Whv0ENh1Z5tKyd11/gpgEWRr6r/Zo/zg8/s1TYXbw+iv5IdeBTZ64hH6b1sVMe8w==", "dependencies": { "bpmn-moddle": "^8.0.0", "css.escape": "^1.5.1", - "diagram-js": "^11.1.0", + "diagram-js": "^11.1.1", "diagram-js-direct-editing": "^2.0.0", "ids": "^1.0.0", "inherits-browser": "^0.1.0", @@ -2273,11 +2262,6 @@ "diagram-js": ">= 7" } }, - "node_modules/bpmn-js/node_modules/tiny-svg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tiny-svg/-/tiny-svg-3.0.0.tgz", - "integrity": "sha512-+u6VomQO7MbI7CQe5q1IwNePpbVKG/HVdUQBmaEpSCdP/QmeyjhrS6WKFsNetXlvf9LWu/f5woRqjMdxBMe/0w==" - }, "node_modules/bpmn-moddle": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/bpmn-moddle/-/bpmn-moddle-8.0.0.tgz", @@ -3121,9 +3105,9 @@ "dev": true }, "node_modules/diagram-js": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/diagram-js/-/diagram-js-11.1.0.tgz", - "integrity": "sha512-LDfSPHpFaXiu2LkElf2iIBmM0M9iyN189Tt0xk/GsMqm468BUXsDKBSZEqwSUPGOPd5+afCtZIS4SRlr/vgZwg==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/diagram-js/-/diagram-js-11.1.1.tgz", + "integrity": "sha512-T2xzwQLeMfqT66Dup7cluozGbZ8R2Pmt3DXjLhaGAdjqpBoxg6huUTdpLEgP4uRUos3mhZqtTm8NOpAKSJg5pA==", "dependencies": { "@bpmn-io/diagram-js-ui": "^0.2.0", "clsx": "^1.2.1", @@ -3161,27 +3145,17 @@ "tiny-svg": "^3.0.0" } }, - "node_modules/diagram-js-minimap/node_modules/tiny-svg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tiny-svg/-/tiny-svg-3.0.0.tgz", - "integrity": "sha512-+u6VomQO7MbI7CQe5q1IwNePpbVKG/HVdUQBmaEpSCdP/QmeyjhrS6WKFsNetXlvf9LWu/f5woRqjMdxBMe/0w==" - }, "node_modules/diagram-js-origin": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/diagram-js-origin/-/diagram-js-origin-1.3.4.tgz", - "integrity": "sha512-0cSuf8iHG9IUC+rJpI2cGba0HEW17W5dv0JQWuGbFAG6Y12YUCDzFkMB44VhD+X/cvSmNRgyaDVxGw3U1LSpIQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/diagram-js-origin/-/diagram-js-origin-1.4.0.tgz", + "integrity": "sha512-qMX6jWsn26DJ4JMTXb+mIcuryosSZEsDaX2BrOk3roZ9peHLITOt4kg467/I6zVQZRWnQDq1PEuf1lIXCMDLkg==", "dependencies": { - "tiny-svg": "^2.0.0" + "tiny-svg": "^3.0.0" }, "peerDependencies": { "diagram-js": "*" } }, - "node_modules/diagram-js/node_modules/tiny-svg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tiny-svg/-/tiny-svg-3.0.0.tgz", - "integrity": "sha512-+u6VomQO7MbI7CQe5q1IwNePpbVKG/HVdUQBmaEpSCdP/QmeyjhrS6WKFsNetXlvf9LWu/f5woRqjMdxBMe/0w==" - }, "node_modules/didi": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/didi/-/didi-9.0.0.tgz", @@ -9234,9 +9208,9 @@ "dev": true }, "node_modules/tiny-svg": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/tiny-svg/-/tiny-svg-2.2.4.tgz", - "integrity": "sha512-NOi39lBknf4UdDEahNkbEAJnzhu1ZcN2j75IS2vLRmIhsfxdZpTChfLKBcN1ShplVmPIXJAIafk6YY5/Aa80lQ==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tiny-svg/-/tiny-svg-3.0.0.tgz", + "integrity": "sha512-+u6VomQO7MbI7CQe5q1IwNePpbVKG/HVdUQBmaEpSCdP/QmeyjhrS6WKFsNetXlvf9LWu/f5woRqjMdxBMe/0w==" }, "node_modules/tmp": { "version": "0.2.1", @@ -10139,14 +10113,6 @@ "min-dom": "^4.0.2", "preact": "^10.11.0", "tiny-svg": "^3.0.0" - }, - "dependencies": { - "tiny-svg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tiny-svg/-/tiny-svg-3.0.0.tgz", - "integrity": "sha512-+u6VomQO7MbI7CQe5q1IwNePpbVKG/HVdUQBmaEpSCdP/QmeyjhrS6WKFsNetXlvf9LWu/f5woRqjMdxBMe/0w==", - "dev": true - } } }, "@bpmn-io/element-templates-icons-renderer": { @@ -10156,13 +10122,6 @@ "requires": { "inherits": "^2.0.4", "tiny-svg": "^3.0.0" - }, - "dependencies": { - "tiny-svg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tiny-svg/-/tiny-svg-3.0.0.tgz", - "integrity": "sha512-+u6VomQO7MbI7CQe5q1IwNePpbVKG/HVdUQBmaEpSCdP/QmeyjhrS6WKFsNetXlvf9LWu/f5woRqjMdxBMe/0w==" - } } }, "@bpmn-io/element-templates-validator": { @@ -11560,13 +11519,13 @@ } }, "bpmn-js": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/bpmn-js/-/bpmn-js-11.0.0.tgz", - "integrity": "sha512-XO08/hCpOm0X///Zk9VvQ/IwfwcVJHY3ztM0KhsbcfFtW8FR4VhJMu0EvGOpm2R0Z5ORiyyctft0SqrtewixPg==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/bpmn-js/-/bpmn-js-11.0.1.tgz", + "integrity": "sha512-ZY7XXfjuY1U3XOmiOrAVr4Whv0ENh1Z5tKyd11/gpgEWRr6r/Zo/zg8/s1TYXbw+iv5IdeBTZ64hH6b1sVMe8w==", "requires": { "bpmn-moddle": "^8.0.0", "css.escape": "^1.5.1", - "diagram-js": "^11.1.0", + "diagram-js": "^11.1.1", "diagram-js-direct-editing": "^2.0.0", "ids": "^1.0.0", "inherits-browser": "^0.1.0", @@ -11574,13 +11533,6 @@ "min-dom": "^4.0.2", "object-refs": "^0.3.0", "tiny-svg": "^3.0.0" - }, - "dependencies": { - "tiny-svg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tiny-svg/-/tiny-svg-3.0.0.tgz", - "integrity": "sha512-+u6VomQO7MbI7CQe5q1IwNePpbVKG/HVdUQBmaEpSCdP/QmeyjhrS6WKFsNetXlvf9LWu/f5woRqjMdxBMe/0w==" - } } }, "bpmn-js-executable-fix": { @@ -12221,9 +12173,9 @@ "dev": true }, "diagram-js": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/diagram-js/-/diagram-js-11.1.0.tgz", - "integrity": "sha512-LDfSPHpFaXiu2LkElf2iIBmM0M9iyN189Tt0xk/GsMqm468BUXsDKBSZEqwSUPGOPd5+afCtZIS4SRlr/vgZwg==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/diagram-js/-/diagram-js-11.1.1.tgz", + "integrity": "sha512-T2xzwQLeMfqT66Dup7cluozGbZ8R2Pmt3DXjLhaGAdjqpBoxg6huUTdpLEgP4uRUos3mhZqtTm8NOpAKSJg5pA==", "requires": { "@bpmn-io/diagram-js-ui": "^0.2.0", "clsx": "^1.2.1", @@ -12236,13 +12188,6 @@ "object-refs": "^0.3.0", "path-intersection": "^2.2.1", "tiny-svg": "^3.0.0" - }, - "dependencies": { - "tiny-svg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tiny-svg/-/tiny-svg-3.0.0.tgz", - "integrity": "sha512-+u6VomQO7MbI7CQe5q1IwNePpbVKG/HVdUQBmaEpSCdP/QmeyjhrS6WKFsNetXlvf9LWu/f5woRqjMdxBMe/0w==" - } } }, "diagram-js-direct-editing": { @@ -12263,21 +12208,14 @@ "min-dash": "^4.0.0", "min-dom": "^4.0.2", "tiny-svg": "^3.0.0" - }, - "dependencies": { - "tiny-svg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tiny-svg/-/tiny-svg-3.0.0.tgz", - "integrity": "sha512-+u6VomQO7MbI7CQe5q1IwNePpbVKG/HVdUQBmaEpSCdP/QmeyjhrS6WKFsNetXlvf9LWu/f5woRqjMdxBMe/0w==" - } } }, "diagram-js-origin": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/diagram-js-origin/-/diagram-js-origin-1.3.4.tgz", - "integrity": "sha512-0cSuf8iHG9IUC+rJpI2cGba0HEW17W5dv0JQWuGbFAG6Y12YUCDzFkMB44VhD+X/cvSmNRgyaDVxGw3U1LSpIQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/diagram-js-origin/-/diagram-js-origin-1.4.0.tgz", + "integrity": "sha512-qMX6jWsn26DJ4JMTXb+mIcuryosSZEsDaX2BrOk3roZ9peHLITOt4kg467/I6zVQZRWnQDq1PEuf1lIXCMDLkg==", "requires": { - "tiny-svg": "^2.0.0" + "tiny-svg": "^3.0.0" } }, "didi": { @@ -16770,9 +16708,9 @@ "dev": true }, "tiny-svg": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/tiny-svg/-/tiny-svg-2.2.4.tgz", - "integrity": "sha512-NOi39lBknf4UdDEahNkbEAJnzhu1ZcN2j75IS2vLRmIhsfxdZpTChfLKBcN1ShplVmPIXJAIafk6YY5/Aa80lQ==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tiny-svg/-/tiny-svg-3.0.0.tgz", + "integrity": "sha512-+u6VomQO7MbI7CQe5q1IwNePpbVKG/HVdUQBmaEpSCdP/QmeyjhrS6WKFsNetXlvf9LWu/f5woRqjMdxBMe/0w==" }, "tmp": { "version": "0.2.1", diff --git a/package.json b/package.json index 9bef8d27..5feb0eb5 100644 --- a/package.json +++ b/package.json @@ -47,13 +47,13 @@ "dependencies": { "@bpmn-io/align-to-origin": "^0.7.0", "@bpmn-io/element-templates-icons-renderer": "^0.3.0", - "bpmn-js": "^11.0.0", + "bpmn-js": "^11.0.1", "bpmn-js-executable-fix": "^0.2.0", "camunda-bpmn-js-behaviors": "^0.4.0", "camunda-bpmn-moddle": "^7.0.1", - "diagram-js": "^11.0.0", + "diagram-js": "^11.1.1", "diagram-js-minimap": "^3.0.0", - "diagram-js-origin": "^1.3.4", + "diagram-js-origin": "^1.4.0", "inherits": "^2.0.4", "min-dash": "^4.0.0", "zeebe-bpmn-moddle": "^0.17.0" From 831e48586b979bc6f70b23ec3ad6e77c51226e58 Mon Sep 17 00:00:00 2001 From: Beatriz Mendes Date: Mon, 28 Nov 2022 16:38:54 +0100 Subject: [PATCH 2/4] feat(cloud): templates as replace menu entries Closes #204 --- lib/camunda-cloud/Modeler.js | 4 +- .../ElementTemplatesReplaceProvider.js | 217 +++++ .../features/replace/ReplaceOptionsUtil.js | 7 + lib/camunda-cloud/features/replace/index.js | 6 + .../ElementTemplatesReplaceProvider.bpmn | 17 + ...atesReplaceProvider.element-templates.json | 760 ++++++++++++++++++ .../ElementTemplatesReplaceProviderSpec.js | 332 ++++++++ test/camunda-cloud/element-templates.json | 12 + 8 files changed, 1354 insertions(+), 1 deletion(-) create mode 100644 lib/camunda-cloud/features/replace/ElementTemplatesReplaceProvider.js create mode 100644 lib/camunda-cloud/features/replace/ReplaceOptionsUtil.js create mode 100644 lib/camunda-cloud/features/replace/index.js create mode 100644 test/camunda-cloud/ElementTemplatesReplaceProvider.bpmn create mode 100644 test/camunda-cloud/ElementTemplatesReplaceProvider.element-templates.json create mode 100644 test/camunda-cloud/ElementTemplatesReplaceProviderSpec.js diff --git a/lib/camunda-cloud/Modeler.js b/lib/camunda-cloud/Modeler.js index 5d7bd9ea..3922b90d 100644 --- a/lib/camunda-cloud/Modeler.js +++ b/lib/camunda-cloud/Modeler.js @@ -12,6 +12,7 @@ import { ZeebeDescriptionProvider } from 'bpmn-js-properties-panel'; +import replaceModule from './features/replace'; import { commonModdleExtensions, commonModules } from './util/commonModules'; @@ -44,7 +45,8 @@ Modeler.prototype._camundaCloudModules = [ behaviorsModule, rulesModule, zeebePropertiesProviderModule, - cloudElementTemplatesPropertiesProvider + cloudElementTemplatesPropertiesProvider, + replaceModule ]; Modeler.prototype._modules = [].concat( diff --git a/lib/camunda-cloud/features/replace/ElementTemplatesReplaceProvider.js b/lib/camunda-cloud/features/replace/ElementTemplatesReplaceProvider.js new file mode 100644 index 00000000..ceaca0a7 --- /dev/null +++ b/lib/camunda-cloud/features/replace/ElementTemplatesReplaceProvider.js @@ -0,0 +1,217 @@ +import { + isDifferentType +} from 'bpmn-js/lib/features/popup-menu/util/TypeUtil'; + +import { + getBusinessObject, + isAny +} from 'bpmn-js/lib/util/ModelUtil'; + +import { + getReplaceOptions +} from './ReplaceOptionsUtil'; + +/** + * A replace menu provider that allows to replace elements with + * element templates. + */ +export default function ElementTemplatesReplaceProvider(popupMenu, translate, elementTemplates) { + + this._popupMenu = popupMenu; + this._translate = translate; + this._elementTemplates = elementTemplates; + + this.register(); +} + +ElementTemplatesReplaceProvider.$inject = [ + 'popupMenu', + 'translate', + 'elementTemplates' +]; + +/** + * Register replace menu provider in the popup menu + */ +ElementTemplatesReplaceProvider.prototype.register = function() { + this._popupMenu.registerProvider('bpmn-replace', this); +}; + +/** + * Adds the element templates to the replace menu. + * @param {djs.model.Base} element + * + * @returns {Object} + */ +ElementTemplatesReplaceProvider.prototype.getPopupMenuEntries = function(element) { + + return (entries) => { + + // convert our entries into something sortable + let entrySet = Object.entries(entries); + + // add unlink template option + this._addPlainElementEntry(element, entrySet); + + // add template entries + entrySet = [ ...entrySet, ...this.getTemplateEntries(element) ]; + + // convert back to object + return entrySet.reduce((entries, [ key, value ]) => { + entries[key] = value; + + return entries; + }, {}); + }; +}; + +/** + * Adds the option to replace with plain element (unlink template). + * + * @param {djs.model.Base} element + * @param {Array} entries + */ +ElementTemplatesReplaceProvider.prototype._addPlainElementEntry = function(element, entries) { + + const replaceOption = this._getPlainEntry(element, entries); + + const [ + insertIndex, + entry + ] = replaceOption; + + // insert unlink entry + entries.splice(insertIndex, 0, [ entry.id, entry ]); +}; + +/** + * Returns the option to replace with plain element and the index where it should be inserted. + * + * @param {djs.model.Base} element + * @param {Array} entries + * + * @returns {Array} + */ +ElementTemplatesReplaceProvider.prototype._getPlainEntry = function(element, entries) { + + const options = getReplaceOptions(); + + const isSameType = (element, option) => option.target && !isDifferentType(element)(option); + + const optionIndex = options.findIndex(option => isSameType(element, option)); + + if (optionIndex === -1) { + return; + } + + const option = options[optionIndex]; + + const entry = { + id: option.actionName, + action: () => { + this._elementTemplates.applyTemplate(element, null); + }, + label: this._translate(option.label), + className: option.className + }; + + // insert after previous option, if it exists + const previousIndex = getOptionIndex(options, optionIndex - 1, entries); + + if (previousIndex) { + return [ + previousIndex + 1, + entry + ]; + } + + // insert before next option, if it exists + const nextIndex = getOptionIndex(options, optionIndex + 1, entries); + + if (nextIndex) { + return [ + nextIndex, + entry + ]; + } + + // fallback to insert at start + return [ + 0, + entry + ]; +}; + +/** + * Get all element templates that can be used to replace the given element. + * + * @param {djs.model.Base} element + * + * @return {Array} a list of element templates as menu entries + */ +ElementTemplatesReplaceProvider.prototype.getTemplateEntries = function(element) { + + const templates = this._getMatchingTemplates(element); + return templates.map(template => { + + const { + icon = {}, + category, + } = template; + + const entryId = `replace-with-template-${template.id}`; + + const defaultGroup = { + id: 'templates', + name: this._translate('Templates') + }; + + return [ entryId, { + name: template.name, + description: template.description, + documentationRef: template.documentationRef, + imageUrl: icon.contents, + group: category || defaultGroup, + action: () => { + this._elementTemplates.applyTemplate(element, template); + } + } ]; + }); +}; + +/** + * Returns the templates that can the element can be replaced with. + * + * @param {djs.model.Base} element + * + * @return {Array} + */ +ElementTemplatesReplaceProvider.prototype._getMatchingTemplates = function(element) { + return this._elementTemplates.getLatest().filter(template => { + return isAny(element, template.appliesTo) && !isTemplateApplied(element, template); + }); +}; + + +// helpers //////////// +export function isTemplateApplied(element, template) { + const businessObject = getBusinessObject(element); + + if (businessObject) { + return businessObject.get('zeebe:modelerTemplate') === template.id; + } + + return false; +} + +function getOptionIndex(options, index, entries) { + const option = options[index]; + + if (!option) { + return false; + } + + return entries.findIndex( + ([ key ]) => key === option.actionName + ); +} \ No newline at end of file diff --git a/lib/camunda-cloud/features/replace/ReplaceOptionsUtil.js b/lib/camunda-cloud/features/replace/ReplaceOptionsUtil.js new file mode 100644 index 00000000..9c9e2674 --- /dev/null +++ b/lib/camunda-cloud/features/replace/ReplaceOptionsUtil.js @@ -0,0 +1,7 @@ +import * as replaceOptions from 'bpmn-js/lib/features/replace/ReplaceOptions'; + +const ALL_OPTIONS = Object.values(replaceOptions).flat(); + +export function getReplaceOptions() { + return ALL_OPTIONS; +} \ No newline at end of file diff --git a/lib/camunda-cloud/features/replace/index.js b/lib/camunda-cloud/features/replace/index.js new file mode 100644 index 00000000..eefbf0e2 --- /dev/null +++ b/lib/camunda-cloud/features/replace/index.js @@ -0,0 +1,6 @@ +import ElementTemplatesReplaceProvider from './ElementTemplatesReplaceProvider'; + +export default { + __init__: [ 'elementTemplatesProvider' ], + elementTemplatesProvider: [ 'type', ElementTemplatesReplaceProvider ] +}; \ No newline at end of file diff --git a/test/camunda-cloud/ElementTemplatesReplaceProvider.bpmn b/test/camunda-cloud/ElementTemplatesReplaceProvider.bpmn new file mode 100644 index 00000000..95877e51 --- /dev/null +++ b/test/camunda-cloud/ElementTemplatesReplaceProvider.bpmn @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/test/camunda-cloud/ElementTemplatesReplaceProvider.element-templates.json b/test/camunda-cloud/ElementTemplatesReplaceProvider.element-templates.json new file mode 100644 index 00000000..f99dff91 --- /dev/null +++ b/test/camunda-cloud/ElementTemplatesReplaceProvider.element-templates.json @@ -0,0 +1,760 @@ +[ + { + "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "name": "REST Connector (No Auth)", + "id": "io.camunda.connectors.HttpJson.v1.noAuth", + "description": "Invoke REST API and retrieve the result", + "icon": { + "contents": "data:image/svg+xml;utf8,%3Csvg%20width%3D%2218%22%20height%3D%2218%22%20viewBox%3D%220%200%2018%2018%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%3Cpath%20d%3D%22M17.0335%208.99997C17.0335%2013.4475%2013.4281%2017.0529%208.98065%2017.0529C4.53316%2017.0529%200.927765%2013.4475%200.927765%208.99997C0.927765%204.55248%204.53316%200.947083%208.98065%200.947083C13.4281%200.947083%2017.0335%204.55248%2017.0335%208.99997Z%22%20fill%3D%22%23505562%22%2F%3E%0A%3Cpath%20d%3D%22M4.93126%2014.1571L6.78106%203.71471H10.1375C11.1917%203.71471%2011.9824%203.98323%2012.5095%204.52027C13.0465%205.04736%2013.315%205.73358%2013.315%206.57892C13.315%207.44414%2013.0714%208.15522%2012.5841%208.71215C12.1067%209.25913%2011.4553%209.63705%2010.6298%209.8459L12.0619%2014.1571H10.3315L9.03364%2010.0249H7.24351L6.51254%2014.1571H4.93126ZM7.49711%208.59281H9.24248C9.99832%208.59281%2010.5901%208.42374%2011.0177%208.08561C11.4553%207.73753%2011.6741%207.26513%2011.6741%206.66842C11.6741%206.19106%2011.5249%205.81811%2011.2265%205.54959C10.9282%205.27113%2010.4558%205.1319%209.80936%205.1319H8.10874L7.49711%208.59281Z%22%20fill%3D%22white%22%2F%3E%0A%3C%2Fsvg%3E%0A" + }, + "documentationRef": "https://docs.camunda.io/docs/components/modeler/web-modeler/connectors/available-connectors/rest/", + "appliesTo": [ + "bpmn:ServiceTask" + ], + "elementType": { + "value": "bpmn:ServiceTask" + }, + "groups": [ + { + "id": "endpoint", + "label": "HTTP Endpoint" + }, + { + "id": "input", + "label": "Payload" + }, + { + "id": "output", + "label": "Response Mapping" + } + ], + "properties": [ + { + "type": "Hidden", + "value": "io.camunda:http-json:1", + "binding": { + "type": "zeebe:taskDefinition:type" + } + }, + { + "label": "Method", + "group": "endpoint", + "type": "Dropdown", + "value": "get", + "choices": [ + { "name": "GET", "value": "get" }, + { "name": "POST", "value": "post" }, + { "name": "PATCH", "value": "patch" }, + { "name": "PUT", "value": "put" }, + { "name": "DELETE", "value": "delete" } + ], + "binding": { + "type": "zeebe:input", + "name": "method" + } + }, + { + "label": "URL", + "group": "endpoint", + "type": "String", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "url" + }, + "constraints": { + "notEmpty": true, + "pattern": { + "value": "^https?://.*", + "message": "Must be a http(s) URL." + } + } + }, + { + "label": "Query Parameters", + "description": "Map of query parameters to add to the request URL", + "group": "endpoint", + "type": "Text", + "feel": "required", + "binding": { + "type": "zeebe:input", + "name": "queryParameters" + }, + "optional": true + }, + { + "label": "HTTP Headers", + "description": "Map of HTTP headers to add to the request", + "group": "endpoint", + "type": "Text", + "feel": "required", + "binding": { + "type": "zeebe:input", + "name": "headers" + }, + "optional": true + }, + { + "label": "Request Body", + "description": "JSON payload to send with the request", + "group": "input", + "type": "Text", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "body" + }, + "optional": true + }, + { + "label": "Result Variable", + "description": "Name of variable to store the response in", + "group": "output", + "type": "String", + "binding": { + "type": "zeebe:taskHeader", + "key": "resultVariable" + } + }, + { + "label": "Result Expression", + "description": "Expression to map the response into process variables", + "group": "output", + "type": "Text", + "feel": "required", + "binding": { + "type": "zeebe:taskHeader", + "key": "resultExpression" + } + } + ] + }, + { + "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "name": "REST Connector (Basic Auth)", + "id": "io.camunda.connectors.HttpJson.v1.basicAuth", + "description": "Invoke REST API and retrieve the result secured by Basic Authentication", + "documentationRef": "https://docs.camunda.io/docs/components/modeler/web-modeler/connectors/available-connectors/rest/", + "icon": { + "contents": "data:image/svg+xml;utf8,%3Csvg%20width%3D%2218%22%20height%3D%2218%22%20viewBox%3D%220%200%2018%2018%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%3Cpath%20d%3D%22M17.0335%208.99997C17.0335%2013.4475%2013.4281%2017.0529%208.98065%2017.0529C4.53316%2017.0529%200.927765%2013.4475%200.927765%208.99997C0.927765%204.55248%204.53316%200.947083%208.98065%200.947083C13.4281%200.947083%2017.0335%204.55248%2017.0335%208.99997Z%22%20fill%3D%22%23505562%22%2F%3E%0A%3Cpath%20d%3D%22M4.93126%2014.1571L6.78106%203.71471H10.1375C11.1917%203.71471%2011.9824%203.98323%2012.5095%204.52027C13.0465%205.04736%2013.315%205.73358%2013.315%206.57892C13.315%207.44414%2013.0714%208.15522%2012.5841%208.71215C12.1067%209.25913%2011.4553%209.63705%2010.6298%209.8459L12.0619%2014.1571H10.3315L9.03364%2010.0249H7.24351L6.51254%2014.1571H4.93126ZM7.49711%208.59281H9.24248C9.99832%208.59281%2010.5901%208.42374%2011.0177%208.08561C11.4553%207.73753%2011.6741%207.26513%2011.6741%206.66842C11.6741%206.19106%2011.5249%205.81811%2011.2265%205.54959C10.9282%205.27113%2010.4558%205.1319%209.80936%205.1319H8.10874L7.49711%208.59281Z%22%20fill%3D%22white%22%2F%3E%0A%3C%2Fsvg%3E%0A" + }, + "category": { + "id": "connectors", + "name": "Connectors" + }, + "appliesTo": [ + "bpmn:Task" + ], + "elementType": { + "value": "bpmn:ServiceTask" + }, + "groups": [ + { + "id": "endpoint", + "label": "HTTP Endpoint" + }, + { + "id": "input", + "label": "Payload" + }, + { + "id": "authentication", + "label": "Authentication" + }, + { + "id": "output", + "label": "Response Mapping" + } + ], + "properties": [ + { + "type": "Hidden", + "value": "io.camunda:http-json:1", + "binding": { + "type": "zeebe:taskDefinition:type" + } + }, + { + "label": "Method", + "group": "endpoint", + "type": "Dropdown", + "value": "get", + "choices": [ + { "name": "GET", "value": "get" }, + { "name": "POST", "value": "post" }, + { "name": "PATCH", "value": "patch" }, + { "name": "PUT", "value": "put" }, + { "name": "DELETE", "value": "delete" } + ], + "binding": { + "type": "zeebe:input", + "name": "method" + } + }, + { + "label": "URL", + "group": "endpoint", + "type": "String", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "url" + }, + "constraints": { + "notEmpty": true, + "pattern": { + "value": "^https?://.*", + "message": "Must be a http(s) URL." + } + } + }, + { + "label": "Query Parameters", + "description": "Map of query parameters to add to the request URL", + "group": "endpoint", + "type": "Text", + "feel": "required", + "binding": { + "type": "zeebe:input", + "name": "queryParameters" + }, + "optional": true + }, + { + "label": "HTTP Headers", + "description": "Map of HTTP headers to add to the request", + "group": "endpoint", + "type": "Text", + "feel": "required", + "binding": { + "type": "zeebe:input", + "name": "headers" + }, + "optional": true + }, + { + "type": "Hidden", + "value": "basic", + "binding": { + "type": "zeebe:input", + "name": "authentication.type" + } + }, + { + "label": "Username", + "group": "authentication", + "type": "String", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "authentication.username" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Password", + "group": "authentication", + "type": "String", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "authentication.password" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Request Body", + "description": "JSON payload to send with the request", + "group": "input", + "type": "Text", + "feel": "required", + "binding": { + "type": "zeebe:input", + "name": "body" + }, + "optional": true + }, + { + "label": "Result Variable", + "description": "Name of variable to store the response in", + "group": "output", + "type": "String", + "binding": { + "type": "zeebe:taskHeader", + "key": "resultVariable" + } + }, + { + "label": "Result Expression", + "description": "Expression to map the response into process variables", + "group": "output", + "type": "Text", + "feel": "required", + "binding": { + "type": "zeebe:taskHeader", + "key": "resultExpression" + } + } + ] + }, + { + "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "name": "REST Connector (Bearer Token Auth)", + "id": "io.camunda.connectors.HttpJson.v1.bearerToken", + "description": "Invoke REST API and retrieve the result secured by Bearer Token Authentication", + "documentationRef": "https://docs.camunda.io/docs/components/modeler/web-modeler/connectors/available-connectors/rest/", + "icon": { + "contents": "data:image/svg+xml;utf8,%3Csvg%20width%3D%2218%22%20height%3D%2218%22%20viewBox%3D%220%200%2018%2018%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%3Cpath%20d%3D%22M17.0335%208.99997C17.0335%2013.4475%2013.4281%2017.0529%208.98065%2017.0529C4.53316%2017.0529%200.927765%2013.4475%200.927765%208.99997C0.927765%204.55248%204.53316%200.947083%208.98065%200.947083C13.4281%200.947083%2017.0335%204.55248%2017.0335%208.99997Z%22%20fill%3D%22%23505562%22%2F%3E%0A%3Cpath%20d%3D%22M4.93126%2014.1571L6.78106%203.71471H10.1375C11.1917%203.71471%2011.9824%203.98323%2012.5095%204.52027C13.0465%205.04736%2013.315%205.73358%2013.315%206.57892C13.315%207.44414%2013.0714%208.15522%2012.5841%208.71215C12.1067%209.25913%2011.4553%209.63705%2010.6298%209.8459L12.0619%2014.1571H10.3315L9.03364%2010.0249H7.24351L6.51254%2014.1571H4.93126ZM7.49711%208.59281H9.24248C9.99832%208.59281%2010.5901%208.42374%2011.0177%208.08561C11.4553%207.73753%2011.6741%207.26513%2011.6741%206.66842C11.6741%206.19106%2011.5249%205.81811%2011.2265%205.54959C10.9282%205.27113%2010.4558%205.1319%209.80936%205.1319H8.10874L7.49711%208.59281Z%22%20fill%3D%22white%22%2F%3E%0A%3C%2Fsvg%3E%0A" + }, + "appliesTo": [ + "bpmn:Task" + ], + "elementType": { + "value": "bpmn:ServiceTask" + }, + "groups": [ + { + "id": "endpoint", + "label": "HTTP Endpoint" + }, + { + "id": "input", + "label": "Payload" + }, + { + "id": "authentication", + "label": "Authentication" + }, + { + "id": "output", + "label": "Response Mapping" + } + ], + "properties": [ + { + "type": "Hidden", + "value": "io.camunda:http-json:1", + "binding": { + "type": "zeebe:taskDefinition:type" + } + }, + { + "label": "Method", + "group": "endpoint", + "type": "Dropdown", + "value": "get", + "choices": [ + { "name": "GET", "value": "get" }, + { "name": "POST", "value": "post" }, + { "name": "PATCH", "value": "patch" }, + { "name": "PUT", "value": "put" }, + { "name": "DELETE", "value": "delete" } + ], + "binding": { + "type": "zeebe:input", + "name": "method" + } + }, + { + "label": "URL", + "group": "endpoint", + "type": "String", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "url" + }, + "constraints": { + "notEmpty": true, + "pattern": { + "value": "^https?://.*", + "message": "Must be a http(s) URL." + } + } + }, + { + "label": "Query Parameters", + "description": "Map of query parameters to add to the request URL", + "group": "endpoint", + "type": "Text", + "feel": "required", + "binding": { + "type": "zeebe:input", + "name": "queryParameters" + }, + "optional": true + }, + { + "label": "HTTP Headers", + "description": "Map of HTTP headers to add to the request", + "group": "endpoint", + "type": "Text", + "feel": "required", + "binding": { + "type": "zeebe:input", + "name": "headers" + }, + "optional": true + }, + { + "type": "Hidden", + "value": "bearer", + "binding": { + "type": "zeebe:input", + "name": "authentication.type" + } + }, + { + "label": "Bearer Token", + "group": "authentication", + "type": "String", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "authentication.token" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Request Body", + "description": "JSON payload to send with the request", + "group": "input", + "type": "Text", + "feel": "required", + "binding": { + "type": "zeebe:input", + "name": "body" + }, + "optional": true + }, + { + "label": "Result Variable", + "description": "Name of variable to store the response in", + "group": "output", + "type": "String", + "binding": { + "type": "zeebe:taskHeader", + "key": "resultVariable" + } + }, + { + "label": "Result Expression", + "description": "Expression to map the response into process variables", + "group": "output", + "type": "Text", + "feel": "required", + "binding": { + "type": "zeebe:taskHeader", + "key": "resultExpression" + } + } + ] + }, + { + "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "name": "SendGrid Email Template Connector", + "id": "io.camunda.connectors.SendGrid.v1.template", + "description": "Send an Email via SendGrid Dynamic Template", + "documentationRef": "https://docs.camunda.io/docs/components/modeler/web-modeler/connectors/available-connectors/sendgrid/", + "icon": { + "contents": "data:image/svg+xml;utf8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%3Cpath%20d%3D%22M0.285706%205.40847H5.43837V10.5611H0.285706V5.40847Z%22%20fill%3D%22white%22%2F%3E%0A%3Cpath%20d%3D%22M0.285706%205.40847H5.43837V10.5611H0.285706V5.40847Z%22%20fill%3D%22%2399E1F4%22%2F%3E%0A%3Cpath%20d%3D%22M5.43837%2010.5611L10.5611%2010.5616V15.6844H5.43837V10.5611Z%22%20fill%3D%22white%22%2F%3E%0A%3Cpath%20d%3D%22M5.43837%2010.5611L10.5611%2010.5616V15.6844H5.43837V10.5611Z%22%20fill%3D%22%2399E1F4%22%2F%3E%0A%3Cpath%20d%3D%22M0.285706%2015.6846L5.43837%2015.6844V15.7143H0.285706V15.6846ZM0.285706%2010.5619H5.43837V15.6844L0.285706%2015.6846V10.5619Z%22%20fill%3D%22%231A82E2%22%2F%3E%0A%3Cpath%20d%3D%22M5.43837%200.285706H10.5611V5.40847H5.43837V0.285706ZM10.5616%205.43837H15.7143V10.5611H10.5616V5.43837Z%22%20fill%3D%22%2300B3E3%22%2F%3E%0A%3Cpath%20d%3D%22M5.43837%2010.5611L10.5611%2010.5616V5.40847H5.43837V10.5611Z%22%20fill%3D%22%23009DD9%22%2F%3E%0A%3Cpath%20d%3D%22M10.5611%200.285706H15.7143V5.40847H10.5611V0.285706Z%22%20fill%3D%22%231A82E2%22%2F%3E%0A%3Cpath%20d%3D%22M10.5611%205.40847H15.7143V5.43837H10.5616L10.5611%205.40847Z%22%20fill%3D%22%231A82E2%22%2F%3E%0A%3C%2Fsvg%3E" + }, + "appliesTo": [ + "bpmn:Task" + ], + "elementType": { + "value": "bpmn:ServiceTask" + }, + "groups": [ + { + "id": "sendgrid", + "label": "SendGrid API" + }, + { + "id": "sender", + "label": "Sender" + }, + { + "id": "receiver", + "label": "Receiver" + }, + { + "id": "template", + "label": "Dynamic Email Template" + } + ], + "properties": [ + { + "type": "Hidden", + "value": "io.camunda:sendgrid:1", + "binding": { + "type": "zeebe:taskDefinition:type" + } + }, + { + "label": "SendGrid API Key", + "group": "sendgrid", + "type": "String", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "apiKey" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Name", + "group": "sender", + "type": "String", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "from.name" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Email Address", + "group": "sender", + "type": "String", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "from.email" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Name", + "group": "receiver", + "type": "String", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "to.name" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Email Address", + "group": "receiver", + "type": "String", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "to.email" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Template ID", + "group": "template", + "type": "String", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "template.id" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Template Data", + "group": "template", + "type": "Text", + "feel": "required", + "binding": { + "type": "zeebe:input", + "name": "template.data" + }, + "constraints": { + "notEmpty": true + } + } + ] + }, + { + "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "name": "SendGrid Email Connector", + "id": "io.camunda.connectors.SendGrid.v1.content", + "description": "Send an Email via SendGrid", + "documentationRef": "https://docs.camunda.io/docs/components/modeler/web-modeler/connectors/available-connectors/sendgrid/", + "icon": { + "contents": "data:image/svg+xml;utf8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%3Cpath%20d%3D%22M0.285706%205.40847H5.43837V10.5611H0.285706V5.40847Z%22%20fill%3D%22white%22%2F%3E%0A%3Cpath%20d%3D%22M0.285706%205.40847H5.43837V10.5611H0.285706V5.40847Z%22%20fill%3D%22%2399E1F4%22%2F%3E%0A%3Cpath%20d%3D%22M5.43837%2010.5611L10.5611%2010.5616V15.6844H5.43837V10.5611Z%22%20fill%3D%22white%22%2F%3E%0A%3Cpath%20d%3D%22M5.43837%2010.5611L10.5611%2010.5616V15.6844H5.43837V10.5611Z%22%20fill%3D%22%2399E1F4%22%2F%3E%0A%3Cpath%20d%3D%22M0.285706%2015.6846L5.43837%2015.6844V15.7143H0.285706V15.6846ZM0.285706%2010.5619H5.43837V15.6844L0.285706%2015.6846V10.5619Z%22%20fill%3D%22%231A82E2%22%2F%3E%0A%3Cpath%20d%3D%22M5.43837%200.285706H10.5611V5.40847H5.43837V0.285706ZM10.5616%205.43837H15.7143V10.5611H10.5616V5.43837Z%22%20fill%3D%22%2300B3E3%22%2F%3E%0A%3Cpath%20d%3D%22M5.43837%2010.5611L10.5611%2010.5616V5.40847H5.43837V10.5611Z%22%20fill%3D%22%23009DD9%22%2F%3E%0A%3Cpath%20d%3D%22M10.5611%200.285706H15.7143V5.40847H10.5611V0.285706Z%22%20fill%3D%22%231A82E2%22%2F%3E%0A%3Cpath%20d%3D%22M10.5611%205.40847H15.7143V5.43837H10.5616L10.5611%205.40847Z%22%20fill%3D%22%231A82E2%22%2F%3E%0A%3C%2Fsvg%3E" + }, + "appliesTo": [ + "bpmn:Task" + ], + "elementType": { + "value": "bpmn:ServiceTask" + }, + "groups": [ + { + "id": "sendgrid", + "label": "SendGrid API" + }, + { + "id": "sender", + "label": "Sender" + }, + { + "id": "receiver", + "label": "Receiver" + }, + { + "id": "content", + "label": "Email Content" + } + ], + "properties": [ + { + "type": "Hidden", + "value": "io.camunda:sendgrid:1", + "binding": { + "type": "zeebe:taskDefinition:type" + } + }, + { + "label": "SendGrid API Key", + "group": "sendgrid", + "type": "String", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "apiKey" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Name", + "group": "sender", + "type": "String", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "from.name" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Email Address", + "group": "sender", + "type": "String", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "from.email" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Name", + "group": "receiver", + "type": "String", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "to.name" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Email Address", + "group": "receiver", + "type": "String", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "to.email" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Subject", + "group": "content", + "type": "String", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "content.subject" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Content Type", + "group": "content", + "type": "String", + "feel": "optional", + "value": "text/plain", + "binding": { + "type": "zeebe:input", + "name": "content.type" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Body", + "group": "content", + "type": "Text", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "content.value" + }, + "constraints": { + "notEmpty": true + } + } + ] + }, + { + "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "name": "AppliesToTask", + "id": "com.camunda.example.AwesomeTask", + "appliesTo": [ + "bpmn:Task" + ], + "properties": [ + { + "label": "Are you awesome?", + "type": "Boolean", + "value": true, + "binding": { + "type": "property", + "name": "customProperty" + } + } + ] + } +] \ No newline at end of file diff --git a/test/camunda-cloud/ElementTemplatesReplaceProviderSpec.js b/test/camunda-cloud/ElementTemplatesReplaceProviderSpec.js new file mode 100644 index 00000000..fd37d830 --- /dev/null +++ b/test/camunda-cloud/ElementTemplatesReplaceProviderSpec.js @@ -0,0 +1,332 @@ +import { + inject, + getBpmnJS, + bootstrapCamundaCloudModeler +} from 'test/TestHelper'; + +import { + query as domQuery +} from 'min-dom'; + +import { + isTemplateApplied +} from 'lib/camunda-cloud/features/replace/ElementTemplatesReplaceProvider'; + +import diagramXML from './ElementTemplatesReplaceProvider.bpmn'; +import templates from './ElementTemplatesReplaceProvider.element-templates.json'; + + +describe('', function() { + + beforeEach(bootstrapCamundaCloudModeler( + diagramXML + )); + + beforeEach(inject(function(elementTemplates) { + elementTemplates.set(templates); + })); + + + describe('display', function() { + + it('should display matching element templates', inject(function(elementRegistry, popupMenu) { + + // given + const task = elementRegistry.get('ServiceTask_1'); + + // when + openPopup(task); + + // then + const entries = getTemplateEntries(); + expect(entries).to.have.length(6); + })); + + + it('should not display element templates that do not apply', inject(function(elementRegistry) { + + // given + const task = elementRegistry.get('Task_1'); + + // when + openPopup(task); + + // then + const entries = getTemplateEntries(); + expect(entries).to.have.length(5); + })); + + + it('should not display applied element template', inject(function(elementRegistry, elementTemplates) { + + // given + const task = elementRegistry.get('ServiceTask_1'); + elementTemplates.applyTemplate(task, templates[0]); + + // when + openPopup(task); + + // then + const entries = getTemplateEntries(); + expect(entries).to.have.length(5); + })); + + + describe('display options to reset to plain element in correct order', function() { + + it('template service task -> service task', inject(function(elementRegistry, elementTemplates) { + + // given + const task = elementRegistry.get('ServiceTask_1'); + elementTemplates.applyTemplate(task, templates[templates[0]]); + + // when + openPopup(task); + + // then + const entries = Object.keys(getEntries()); + const entryIndex = entries.indexOf('replace-with-service-task'); + + expect(entryIndex).to.eql(6); + })); + + + it.skip('template task -> task', inject(function(elementRegistry, elementTemplates) { + + // given + const task = elementRegistry.get('Task_1'); + elementTemplates.applyTemplate(task, templates[templates[templates.length - 1]]); + + // when + openPopup(task); + + // then + const entries = Object.keys(getEntries()); + const entryIndex = entries.indexOf('replace-with-task'); + + expect(entryIndex).to.eql(0); + })); + + }); + + }); + + + describe('options', function() { + + beforeEach(inject(function(elementRegistry) { + + // given + const task = elementRegistry.get('ServiceTask_1'); + + // when + openPopup(task); + })); + + + it('should have title', inject(function() { + + // given + const template = templates[0]; + const entry = getEntry(`replace-with-template-${template.id}`); + + // then + expect(entry.name).to.eql(template.name); + })); + + + it('should have icon', inject(function() { + + // given + const template = templates[0]; + const entry = getEntry(`replace-with-template-${template.id}`); + + // then + expect(entry.imageUrl).to.eql(template.icon.contents); + })); + + + it('should have description', inject(function() { + + // given + const template = templates[0]; + const entry = getEntry(`replace-with-template-${template.id}`); + + // then + expect(entry.description).to.eql(template.description); + })); + + + it('should have documentation link', inject(function() { + + // given + const template = templates[0]; + const entry = getEntry(`replace-with-template-${template.id}`); + + // then + expect(entry.documentationRef).to.eql(template.documentationRef); + })); + + + it('should have group - default', inject(function() { + + // given + const templateEntryId = getTemplateEntries()[0]; + const entry = getEntry(templateEntryId); + + // then + expect(entry.group.id).to.eql('templates'); + expect(entry.group.name).to.eql('Templates'); + })); + + + it('should have group - category', inject(function(popupMenu) { + + // given + const templateEntryId = getTemplateEntries()[1]; + const entry = getEntry(templateEntryId); + + // then + expect(entry.group.id).to.eql('connectors'); + expect(entry.group.name).to.eql('Connectors'); + })); + + }); + + + describe('replace', function() { + + it('should apply template', inject(function(elementRegistry) { + + // given + const task = elementRegistry.get('ServiceTask_1'); + const template = templates[0]; + + // when + openPopup(task); + + triggerAction(`replace-with-template-${template.id}`); + + // then + expect(isTemplateApplied(task, template)).to.be.true; + + })); + + + it('should unbind template', inject(function(elementRegistry) { + + // given + const task = elementRegistry.get('ServiceTask_1'); + const template = templates[0]; + + openPopup(task); + triggerAction(`replace-with-template-${template.id}`); + + // when + openPopup(task); + triggerAction('replace-with-service-task'); + + // then + expect(isTemplateApplied(task, template)).to.be.false; + })); + + + it('should undo', inject(function(elementRegistry, commandStack) { + + // given + const task = elementRegistry.get('ServiceTask_1'); + const template = templates[0]; + + openPopup(task); + triggerAction(`replace-with-template-${template.id}`); + + // when + commandStack.undo(); + + // then + expect(isTemplateApplied(task, template)).to.be.false; + })); + + + it('should redo', inject(function(elementRegistry, commandStack) { + + // given + const task = elementRegistry.get('ServiceTask_1'); + const template = templates[0]; + + openPopup(task); + triggerAction(`replace-with-template-${template.id}`); + + // when + commandStack.undo(); + commandStack.redo(); + + // then + expect(isTemplateApplied(task, template)).to.be.true; + })); + + }); + +}); + + +// helpers //////////// + +function openPopup(element, offset) { + offset = offset || 100; + + getBpmnJS().invoke(function(popupMenu) { + popupMenu.open(element, 'bpmn-replace', { + x: element.x, y: element.y + }); + + }); +} + +function queryEntry(id) { + var container = getMenuContainer(); + + return domQuery('.djs-popup [data-id="' + id + '"]', container); +} + +function getMenuContainer() { + const popup = getBpmnJS().get('popupMenu'); + return popup._current.container; +} + +function triggerAction(id) { + const entry = queryEntry(id); + + if (!entry) { + throw new Error('entry "' + id + '" not found in replace menu'); + } + + const popupMenu = getBpmnJS().get('popupMenu'); + const eventBus = getBpmnJS().get('eventBus'); + + return popupMenu.trigger( + eventBus.createEvent({ + target: entry, + x: 0, + y: 0, + }) + ); +} + +function getEntries() { + const popupMenu = getBpmnJS().get('popupMenu'); + return popupMenu._current.entries; +} + +function getEntry(id) { + const popupMenu = getBpmnJS().get('popupMenu'); + + return popupMenu._getEntry(id); +} + +function getTemplateEntries() { + const entries = getEntries(); + const entryIds = Object.keys(entries); + + return entryIds.filter(entry => entry.startsWith('replace-with-template')); +} diff --git a/test/camunda-cloud/element-templates.json b/test/camunda-cloud/element-templates.json index be51a707..0accd1ad 100644 --- a/test/camunda-cloud/element-templates.json +++ b/test/camunda-cloud/element-templates.json @@ -11,6 +11,10 @@ "appliesTo": [ "bpmn:Task" ], + "category": { + "id": "rest", + "name": "REST Connector" + }, "elementType": { "value": "bpmn:ServiceTask" }, @@ -144,6 +148,10 @@ "elementType": { "value": "bpmn:ServiceTask" }, + "category": { + "id": "rest", + "name": "REST Connector" + }, "groups": [ { "id": "endpoint", @@ -312,6 +320,10 @@ "elementType": { "value": "bpmn:ServiceTask" }, + "category": { + "id": "rest", + "name": "REST Connector" + }, "groups": [ { "id": "endpoint", From dcaf8cadfa29e48d0319ef3bfa5fa11b162005f1 Mon Sep 17 00:00:00 2001 From: Nico Rehwaldt Date: Fri, 2 Dec 2022 10:44:31 +0100 Subject: [PATCH 3/4] test: be able to drop + open local diagrams Improves ability to integration test. --- package-lock.json | 32 +++++++++++++++++++++++++++++++ package.json | 2 ++ test/TestHelper.js | 48 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index c3b73ff0..b2e0492b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,9 +35,11 @@ "chai": "^4.3.7", "cross-env": "^7.0.3", "del-cli": "^5.0.0", + "downloadjs": "^1.4.7", "eslint": "^8.24.0", "eslint-plugin-bpmn-io": "^0.16.0", "execa": "^5.0.0", + "file-drops": "^0.5.0", "karma": "^6.4.1", "karma-chai": "^0.1.0", "karma-chrome-launcher": "^3.1.1", @@ -3220,6 +3222,12 @@ "resolved": "https://registry.npmjs.org/domify/-/domify-1.4.1.tgz", "integrity": "sha512-x18nuiDHMCZGXr4KJSRMf/TWYtiaRo6RX8KN9fEbW54mvbQ6pieUuerC2ahBg+kEp1wycFj8MPUI0WkIOw5E9w==" }, + "node_modules/downloadjs": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/downloadjs/-/downloadjs-1.4.7.tgz", + "integrity": "sha512-LN1gO7+u9xjU5oEScGFKvXhYf7Y/empUIIEAGBs1LzUq/rg5duiDrkuH5A2lQGd5jfMOb9X9usDa2oVXwJ0U/Q==", + "dev": true + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -3993,6 +4001,15 @@ "pend": "~1.2.0" } }, + "node_modules/file-drops": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/file-drops/-/file-drops-0.5.0.tgz", + "integrity": "sha512-ZaENKwVySae4RhEGjh1gEE1wMnIIPG6XqtOwHNQYSl7RNwUHoRGVVspe+BrW7cUFseHNIit3Oy9Z/HPIEU5XWA==", + "dev": true, + "dependencies": { + "min-dom": "^4.0.3" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -12272,6 +12289,12 @@ "resolved": "https://registry.npmjs.org/domify/-/domify-1.4.1.tgz", "integrity": "sha512-x18nuiDHMCZGXr4KJSRMf/TWYtiaRo6RX8KN9fEbW54mvbQ6pieUuerC2ahBg+kEp1wycFj8MPUI0WkIOw5E9w==" }, + "downloadjs": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/downloadjs/-/downloadjs-1.4.7.tgz", + "integrity": "sha512-LN1gO7+u9xjU5oEScGFKvXhYf7Y/empUIIEAGBs1LzUq/rg5duiDrkuH5A2lQGd5jfMOb9X9usDa2oVXwJ0U/Q==", + "dev": true + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -12862,6 +12885,15 @@ "pend": "~1.2.0" } }, + "file-drops": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/file-drops/-/file-drops-0.5.0.tgz", + "integrity": "sha512-ZaENKwVySae4RhEGjh1gEE1wMnIIPG6XqtOwHNQYSl7RNwUHoRGVVspe+BrW7cUFseHNIit3Oy9Z/HPIEU5XWA==", + "dev": true, + "requires": { + "min-dom": "^4.0.3" + } + }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", diff --git a/package.json b/package.json index 5feb0eb5..0a92233c 100644 --- a/package.json +++ b/package.json @@ -71,9 +71,11 @@ "chai": "^4.3.7", "cross-env": "^7.0.3", "del-cli": "^5.0.0", + "downloadjs": "^1.4.7", "eslint": "^8.24.0", "eslint-plugin-bpmn-io": "^0.16.0", "execa": "^5.0.0", + "file-drops": "^0.5.0", "karma": "^6.4.1", "karma-chai": "^0.1.0", "karma-chrome-launcher": "^3.1.1", diff --git a/test/TestHelper.js b/test/TestHelper.js index 45a026bc..03c0b5b5 100644 --- a/test/TestHelper.js +++ b/test/TestHelper.js @@ -4,14 +4,18 @@ import { attr as domAttr, query as domQuery, queryAll as domQueryAll - } from 'min-dom'; import { bootstrapBpmnJS, + getBpmnJS, insertCSS } from 'bpmn-js/test/helper'; +import fileDrop from 'file-drops'; + +import download from 'downloadjs'; + import CamundaCloudModeler from '../lib/camunda-cloud/Modeler'; import CamundaPlatformModeler from '../lib/camunda-platform/Modeler'; @@ -183,3 +187,45 @@ export function selectedByIndex(element) { return element.options[element.selectedIndex]; } + +// be able to load files into running bpmn-js test cases +document.documentElement.addEventListener('dragover', fileDrop('Drop a BPMN diagram to open it in the currently active test.', function(files) { + const bpmnJS = getBpmnJS(); + + if (bpmnJS && files.length === 1) { + bpmnJS.importXML(files[0].contents); + } +})); + +insertCSS('file-drops.css', ` + .drop-overlay .box { + background: orange; + border-radius: 3px; + display: inline-block; + font-family: sans-serif; + padding: 4px 10px; + position: fixed; + top: 30px; + left: 50%; + transform: translateX(-50%); + } +`); + +// be able to download diagrams using CTRL/CMD+S +document.addEventListener('keydown', function(event) { + const bpmnJS = getBpmnJS(); + + if (!bpmnJS) { + return; + } + + if (!(event.ctrlKey || event.metaKey) || event.code !== 'KeyS') { + return; + } + + event.preventDefault(); + + bpmnJS.saveXML({ format: true }).then(function(result) { + download(result.xml, 'test.bpmn', 'application/xml'); + }); +}); \ No newline at end of file From 96abacd15dd5ec476c3f8bde48439b3642fc5ffb Mon Sep 17 00:00:00 2001 From: Nico Rehwaldt Date: Fri, 2 Dec 2022 13:46:08 +0100 Subject: [PATCH 4/4] fix(cloud-templates): search replace option in local group --- .../ElementTemplatesReplaceProvider.js | 45 +++++++++++++++---- .../features/replace/ReplaceOptionsUtil.js | 4 +- .../ElementTemplatesReplaceProviderSpec.js | 2 +- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/lib/camunda-cloud/features/replace/ElementTemplatesReplaceProvider.js b/lib/camunda-cloud/features/replace/ElementTemplatesReplaceProvider.js index ceaca0a7..9cf1ddab 100644 --- a/lib/camunda-cloud/features/replace/ElementTemplatesReplaceProvider.js +++ b/lib/camunda-cloud/features/replace/ElementTemplatesReplaceProvider.js @@ -8,7 +8,7 @@ import { } from 'bpmn-js/lib/util/ModelUtil'; import { - getReplaceOptions + getReplaceOptionGroups } from './ReplaceOptionsUtil'; /** @@ -94,18 +94,16 @@ ElementTemplatesReplaceProvider.prototype._addPlainElementEntry = function(eleme */ ElementTemplatesReplaceProvider.prototype._getPlainEntry = function(element, entries) { - const options = getReplaceOptions(); + const { + options, + option, + optionIndex + } = findReplaceOptions(element) || { }; - const isSameType = (element, option) => option.target && !isDifferentType(element)(option); - - const optionIndex = options.findIndex(option => isSameType(element, option)); - - if (optionIndex === -1) { + if (!options) { return; } - const option = options[optionIndex]; - const entry = { id: option.actionName, action: () => { @@ -214,4 +212,33 @@ function getOptionIndex(options, index, entries) { return entries.findIndex( ([ key ]) => key === option.actionName ); +} + +/** + * @param {ModdleElement} element + * + * @return { { options: Array, option: any, optionIndex: number } | null } + */ +function findReplaceOptions(element) { + + const isSameType = (element, option) => option.target && !isDifferentType(element)(option); + + return getReplaceOptionGroups().reduce((result, options) => { + + if (result) { + return result; + } + + const optionIndex = options.findIndex(option => isSameType(element, option)); + + if (optionIndex === -1) { + return; + } + + return { + options, + option: options[optionIndex], + optionIndex + }; + }, null); } \ No newline at end of file diff --git a/lib/camunda-cloud/features/replace/ReplaceOptionsUtil.js b/lib/camunda-cloud/features/replace/ReplaceOptionsUtil.js index 9c9e2674..7b5942cc 100644 --- a/lib/camunda-cloud/features/replace/ReplaceOptionsUtil.js +++ b/lib/camunda-cloud/features/replace/ReplaceOptionsUtil.js @@ -1,7 +1,7 @@ import * as replaceOptions from 'bpmn-js/lib/features/replace/ReplaceOptions'; -const ALL_OPTIONS = Object.values(replaceOptions).flat(); +const ALL_OPTIONS = Object.values(replaceOptions); -export function getReplaceOptions() { +export function getReplaceOptionGroups() { return ALL_OPTIONS; } \ No newline at end of file diff --git a/test/camunda-cloud/ElementTemplatesReplaceProviderSpec.js b/test/camunda-cloud/ElementTemplatesReplaceProviderSpec.js index fd37d830..bc168a4f 100644 --- a/test/camunda-cloud/ElementTemplatesReplaceProviderSpec.js +++ b/test/camunda-cloud/ElementTemplatesReplaceProviderSpec.js @@ -91,7 +91,7 @@ describe('', function() { })); - it.skip('template task -> task', inject(function(elementRegistry, elementTemplates) { + it('template task -> task', inject(function(elementRegistry, elementTemplates) { // given const task = elementRegistry.get('Task_1');