diff --git a/components/bpmn-q/modeler-component/QuantumWorkflowModeler.js b/components/bpmn-q/modeler-component/QuantumWorkflowModeler.js
index 7960f62d5..35728c0d7 100644
--- a/components/bpmn-q/modeler-component/QuantumWorkflowModeler.js
+++ b/components/bpmn-q/modeler-component/QuantumWorkflowModeler.js
@@ -12,7 +12,7 @@ import './modeler.css';
import React from 'react';
import { createRoot } from 'react-dom/client';
import ButtonToolbar from "./editor/ui/ButtonToolbar";
-import { createNewDiagram, loadDiagram } from "./editor/util/IoUtilities";
+import { createNewDiagram, loadDiagram, setAutoSaveInterval } from "./editor/util/IoUtilities";
import NotificationHandler from "./editor/ui/notifications/NotificationHandler";
import { createModeler, getModeler } from "./editor/ModelerHandler";
import { getPluginButtons, getTransformationButtons } from "./editor/plugin/PluginHandler";
@@ -336,6 +336,7 @@ export class QuantumWorkflowModeler extends HTMLElement {
// restart modeler to apply plugin config when shadow dom is rendered
requestAnimationFrame(() => {
this.startModeler();
+ setAutoSaveInterval();
});
}
}
diff --git a/components/bpmn-q/modeler-component/editor/EditorConstants.js b/components/bpmn-q/modeler-component/editor/EditorConstants.js
index 3ede37e5a..288375b25 100644
--- a/components/bpmn-q/modeler-component/editor/EditorConstants.js
+++ b/components/bpmn-q/modeler-component/editor/EditorConstants.js
@@ -10,13 +10,18 @@ export const workflowEventTypes = {
LOADED: 'quantum-workflow-loaded', // New Workflow loaded in modeler
SAVED: 'quantum-workflow-saved', // Workflow saved
TRANSFORMED: 'quantum-workflow-transformed', // Workflow transformed
- DEPLOYED: 'quantum-workflow-deployed' // Workflow deployed to workflow engine
+ DEPLOYED: 'quantum-workflow-deployed', // Workflow deployed to workflow engine
};
+export const autoSaveFile = {
+ INTERVAL: 'Interval',
+ ON_ACTION: 'On Action'
+}
+
// supported save file options
export const saveFileFormats = {
ALL: 'all',
BPMN: '.bpmn',
PNG: '.png',
SVG: '.svg'
-};
\ No newline at end of file
+};
diff --git a/components/bpmn-q/modeler-component/editor/ModelerHandler.js b/components/bpmn-q/modeler-component/editor/ModelerHandler.js
index ebcb98fcb..57c55399b 100644
--- a/components/bpmn-q/modeler-component/editor/ModelerHandler.js
+++ b/components/bpmn-q/modeler-component/editor/ModelerHandler.js
@@ -85,27 +85,38 @@ export function createTempModeler() {
}
/**
- * Create a Modeler with only Camunda native extensions and no additional modules
+ * Creates a modeler with all additional modules and extension moddles from all active plugins which is not
+ * saved in as the current modeler instance and load the given xml into it.
+ *
+ * @param xml the xml representing the BPMN diagram to load
*
- * @returns the created bpmn-js modeler
+ * @returns the created modeler
*/
-export function createLightweightModeler() {
- return new BpmnModeler({
- moddleExtensions: getExtensions(),
- });
+export async function createTempModelerFromXml(xml) {
+ // create new modeler with the custom QuantME extensions
+ const bpmnModeler = createTempModeler();
+
+ // import the xml containing the definitions
+ try {
+ await bpmnModeler.importXML(xml);
+ return bpmnModeler;
+ } catch (err) {
+ console.error(err);
+ }
+ return undefined;
}
/**
- * Creates a modeler with all additional modules and extension moddles from all active plugins which is not
- * saved in as the current modeler instance and load the given xml into it.
+ * Creates a modeler with all additional modules and extension moddles from all active plugins which is
+ * saved as the current modeler instance and load the given xml into it.
*
* @param xml the xml representing the BPMN diagram to load
*
* @returns the created modeler
*/
-export async function createTempModelerFromXml(xml) {
+export async function createModelerFromXml(xml) {
// create new modeler with the custom QuantME extensions
- const bpmnModeler = createTempModeler();
+ const bpmnModeler = createModeler();
// import the xml containing the definitions
try {
diff --git a/components/bpmn-q/modeler-component/editor/config/EditorConfigManager.js b/components/bpmn-q/modeler-component/editor/config/EditorConfigManager.js
index 1339fedd1..43b7baf2c 100644
--- a/components/bpmn-q/modeler-component/editor/config/EditorConfigManager.js
+++ b/components/bpmn-q/modeler-component/editor/config/EditorConfigManager.js
@@ -1,12 +1,14 @@
import { getPluginConfig } from '../plugin/PluginConfigHandler';
-import { saveFileFormats, transformedWorkflowHandlers } from '../EditorConstants';
+import { saveFileFormats, transformedWorkflowHandlers, autoSaveFile } from '../EditorConstants';
// default configurations of the editor
const defaultConfig = {
camundaEndpoint: process.env.CAMUNDA_ENDPOINT,
fileName: process.env.DOWNLOAD_FILE_NAME,
transformedWorkflowHandler: transformedWorkflowHandlers.NEW_TAB,
- fileFormat: saveFileFormats.BPMN
+ autoSaveFileOption: autoSaveFile.INTERVAL,
+ fileFormat: saveFileFormats.BPMN,
+ autoSaveIntervalSize: process.env.AUTOSAVE_INTERVAL
};
let config = {};
@@ -90,6 +92,33 @@ export function setTransformedWorkflowHandler(transformedWorkflowHandler) {
}
/**
+ * Get the id of the handler to handle auto save of files.
+ *
+ * @return {string} the currently specified handler id
+ */
+export function getAutoSaveFileOption() {
+ if (config.autoSaveFileOption === undefined) {
+ const autoSaveFileOption = autoSaveFile[getPluginConfig('editor').autoSaveFileOption];
+ setAutoSaveFileOption(autoSaveFileOption || defaultConfig.autoSaveFileOption);
+ }
+ return config.autoSaveFileOption;
+}
+
+/**
+ * Set the id of the handler to handle auto save of files
+ *
+ * @param autoSaveFileOption the id of the transformed workflow handler
+ */
+export function setAutoSaveFileOption(autoSaveFileOption) {
+ if (autoSaveFileOption !== null && autoSaveFileOption !== undefined
+ // check that the new value is a valid handler id
+ && Object.values(autoSaveFile).includes(autoSaveFileOption)) {
+
+ config.autoSaveFileOption = autoSaveFileOption;
+ }
+}
+
+/**
* Get the file format
*
* @return {string} the currently specified handler id
@@ -116,6 +145,29 @@ export function setFileFormat(fileFormat) {
}
}
+/**
+ * Get the autosave interval size
+ *
+ * @return {string} the current interval size
+ */
+export function getAutoSaveIntervalSize() {
+ if (config.autoSaveIntervalSize === undefined) {
+ setAutoSaveIntervalSize(getPluginConfig('editor').autoSaveIntervalSize || defaultConfig.autoSaveIntervalSize);
+ }
+ return config.autoSaveIntervalSize;
+}
+
+/**
+ * Set the interval size of the autosave function
+ *
+ * @param intervalSize the interval size
+ */
+export function setAutoSaveIntervalSize(intervalSize) {
+ if (intervalSize !== null && intervalSize !== undefined) {
+ config.autoSaveIntervalSize = intervalSize;
+ }
+}
+
/**
* Resets the current editor configs
*/
diff --git a/components/bpmn-q/modeler-component/editor/config/EditorTab.js b/components/bpmn-q/modeler-component/editor/config/GeneralTab.js
similarity index 71%
rename from components/bpmn-q/modeler-component/editor/config/EditorTab.js
rename to components/bpmn-q/modeler-component/editor/config/GeneralTab.js
index 38ed8bc67..4f6f822a9 100644
--- a/components/bpmn-q/modeler-component/editor/config/EditorTab.js
+++ b/components/bpmn-q/modeler-component/editor/config/GeneralTab.js
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { getModeler } from "../ModelerHandler";
import * as editorConfig from "./EditorConfigManager";
-import { transformedWorkflowHandlers, saveFileFormats } from '../EditorConstants';
+import { autoSaveFile, saveFileFormats, transformedWorkflowHandlers } from '../EditorConstants';
/**
* Tab for the ConfigModal. Used to allow the configurations of the editor configs, namely the camunda endpoint and the
@@ -14,8 +14,11 @@ export default function EditorTab() {
const [camundaEndpoint, setCamundaEndpoint] = useState(editorConfig.getCamundaEndpoint());
const [workflowHandler, setWorkflowHandler] = useState(editorConfig.getTransformedWorkflowHandler());
+ const [autoSaveFileOption, setAutoSaveFileOption] = useState(editorConfig.getAutoSaveFileOption());
const [fileName, setFileName] = useState(editorConfig.getFileName());
const [fileFormat, setFileFormat] = useState(editorConfig.getFileFormat());
+ const [autoSaveIntervalSize, setAutoSaveIntervalSize] = useState(editorConfig.getAutoSaveIntervalSize());
+
const modeler = getModeler();
@@ -45,8 +48,11 @@ export default function EditorTab() {
modeler.config.fileName = fileName;
editorConfig.setCamundaEndpoint(camundaEndpoint);
editorConfig.setTransformedWorkflowHandler(workflowHandler);
+ editorConfig.setAutoSaveFileOption(autoSaveFileOption);
+ modeler.get('eventBus').fire('autoSaveOptionChanged', { autoSaveFileOption });
editorConfig.setFileName(fileName);
editorConfig.setFileFormat(fileFormat);
+ editorConfig.setAutoSaveIntervalSize(autoSaveIntervalSize);
};
// return tab which contains entries to change the camunda endpoint and the workflow handler
@@ -118,6 +124,38 @@ export default function EditorTab() {
+
Auto save file:
+
>);
}
diff --git a/components/bpmn-q/modeler-component/editor/configurations/ConfigurationEndpoint.js b/components/bpmn-q/modeler-component/editor/configurations/ConfigurationEndpoint.js
index 17e56ecb9..7cfc8c4c9 100644
--- a/components/bpmn-q/modeler-component/editor/configurations/ConfigurationEndpoint.js
+++ b/components/bpmn-q/modeler-component/editor/configurations/ConfigurationEndpoint.js
@@ -17,13 +17,11 @@ export default class ConfigurationsEndpoint {
* Fetch the configured endpoint and store the result in this._configurations
*/
fetchConfigurations() {
-
fetch(this._endpointUrl)
- .then(response => response.json())
+ .then(response => response.headers.get('content-type') === 'text/plain; charset=utf-8' ? response.text() : response.json())
.then(data => {
- this._configurations = data;
- console.log('Received ' + data.length + ' configurations: ');
- console.log(data);
+ this._configurations = typeof data === "string" ? JSON.parse(data) : data;
+ console.log(this._configurations);
})
.catch(error => {
console.error('Error fetching configurations from ' + this._endpointUrl + ': \n' + error);
diff --git a/components/bpmn-q/modeler-component/editor/plugin/PluginHandler.js b/components/bpmn-q/modeler-component/editor/plugin/PluginHandler.js
index d60722905..efc963b5f 100644
--- a/components/bpmn-q/modeler-component/editor/plugin/PluginHandler.js
+++ b/components/bpmn-q/modeler-component/editor/plugin/PluginHandler.js
@@ -1,10 +1,11 @@
-import PlanQKPlugin from "../../extensions/planqk/PlanQKPlugin";
-import QuantMEPlugin from "../../extensions/quantme/QuantMEPlugin";
+import PlanQKPlugin from '../../extensions/planqk/PlanQKPlugin';
+import QuantMEPlugin from '../../extensions/quantme/QuantMEPlugin';
import OpenToscaPlugin from "../../extensions/opentosca/OpenTOSCAPlugin";
import DataFlowPlugin from '../../extensions/data-extension/DataFlowPlugin';
import QHAnaPlugin from '../../extensions/qhana/QHAnaPlugin';
-import {getAllConfigs} from "./PluginConfigHandler";
-import EditorTab from "../config/EditorTab";
+import { getAllConfigs } from './PluginConfigHandler';
+import GeneralTab from '../config/GeneralTab';
+import GitHubTab from '../../extensions/quantme/configTabs/GitHubTab';
/**
* Handler for plugins of the modeler. Controls active plugins and the properties they define. Central access point to
@@ -12,51 +13,69 @@ import EditorTab from "../config/EditorTab";
*/
// list of plugins integrated in the modeler, register new plugins here
+// dependencies can be specified by the name of the corresponding plugins
const PLUGINS = [
- DataFlowPlugin,
- QHAnaPlugin,
- PlanQKPlugin,
- QuantMEPlugin,
- OpenToscaPlugin
+ {
+ plugin: QuantMEPlugin,
+ dependencies: ['DataFlowPlugin']
+ },
+ {
+ plugin: DataFlowPlugin,
+ dependencies: []
+ },
+ {
+ plugin: QHAnaPlugin,
+ dependencies: []
+ },
+ {
+ plugin: PlanQKPlugin,
+ dependencies: []
+ },
+ {
+ plugin: OpenToscaPlugin,
+ dependencies: []
+ }
];
// list of currently active plugins in the current running instance of the modeler, defined based on the plugin configuration
let activePlugins = [];
-/**
- * Returns these plugins of PLUGINS which have an entry in the current plugin configuration of the modeler.
- *
- * @returns {*[]} Array of active plugins.
- */
export function getActivePlugins() {
-
- // return saved active plugins array
if (activePlugins.length > 0) {
return activePlugins;
-
- // determine active plugins
} else {
-
activePlugins = [];
-
- let plugin;
-
- // add all plugins of PLUGINS to active plugins which have a config entry for them
- for (let pluginConfig of getAllConfigs()) {
-
- plugin = PLUGINS.find(plugin => plugin.name === pluginConfig.name && checkEnabledStatus(plugin.name));
+ const loadPlugin = (plugin) => {
+ if (!activePlugins.includes(plugin.plugin)) {
+ for (const dependency of plugin.dependencies) {
+ const dependencyPlugin = PLUGINS.find((p) => p.plugin.name === dependency);
+ if (dependencyPlugin && !activePlugins.includes(dependencyPlugin.plugin)) {
+ activePlugins.push(dependencyPlugin.plugin);
+ loadPlugin(dependencyPlugin);
+ }
+ }
+ activePlugins.push(plugin.plugin);
+ }
+ };
+ for (const pluginConfig of getAllConfigs()) {
+ const plugin = PLUGINS.find(
+ (p) => p.plugin.name === pluginConfig.name && checkEnabledStatus(p.plugin.name)
+ );
if (plugin) {
- activePlugins.push(plugin);
+ loadPlugin(plugin);
}
}
+
return activePlugins;
}
}
+
+
export function checkEnabledStatus(pluginName) {
- switch(pluginName) {
+ switch (pluginName) {
case 'dataflow':
return process.env.ENABLE_DATA_FLOW_PLUGIN !== "false";
case 'planqk':
@@ -183,13 +202,17 @@ export function getConfigTabs() {
// add default editor tab to configure editor configs
let configTabs = [{
tabId: 'EditorTab',
- tabTitle: 'Editor',
- configTab: EditorTab,
+ tabTitle: 'General',
+ configTab: GeneralTab,
+ }, {
+ tabId: 'GitHubTab',
+ tabTitle: 'GitHub',
+ configTab: GitHubTab,
}];
// load the config tabs of the active plugins into one array
for (let plugin of getActivePlugins()) {
- if (plugin.configTabs) {
+ if (plugin.configTabs && checkEnabledStatus(plugin.name)) {
configTabs = configTabs.concat(plugin.configTabs);
}
}
diff --git a/components/bpmn-q/modeler-component/editor/util/IoUtilities.js b/components/bpmn-q/modeler-component/editor/util/IoUtilities.js
index db34386b3..425f3a361 100644
--- a/components/bpmn-q/modeler-component/editor/util/IoUtilities.js
+++ b/components/bpmn-q/modeler-component/editor/util/IoUtilities.js
@@ -1,11 +1,11 @@
-import { file } from 'jszip';
-import { transformedWorkflowHandlers, workflowEventTypes, saveFileFormats } from '../EditorConstants';
-import { dispatchWorkflowEvent } from '../events/EditorEventHandler';
+import { autoSaveFile, saveFileFormats, transformedWorkflowHandlers, workflowEventTypes } from "../EditorConstants";
+import { getModeler } from "../ModelerHandler";
+import { dispatchWorkflowEvent } from "../events/EditorEventHandler";
+import fetch from "node-fetch";
const editorConfig = require('../config/EditorConfigManager');
let FormData = require('form-data');
-import fetch from 'node-fetch';
// workflow with a start event to use as template for new workflows
const NEW_DIAGRAM_XML = '\n' +
@@ -47,18 +47,19 @@ export async function saveXmlAsLocalFile(xml, fileName = editorConfig.getFileNam
* @param fileName The name of the file.
* @returns {Promise}
*/
-export async function saveModelerAsLocalFile(modeler, fileName = editorConfig.getFileName(), fileFormat = editorConfig.getFileFormat()) {
+export async function saveModelerAsLocalFile(modeler, fileName = editorConfig.getFileName(), fileFormat = editorConfig.getFileFormat(), openWindow = true) {
const xml = await getXml(modeler);
-
if (fileFormat === saveFileFormats.BPMN || fileFormat === saveFileFormats.ALL) {
- await openFileDialog(xml, fileName, saveFileFormats.BPMN);
+ if (openWindow) {
+ await openFileDialog(xml, fileName, saveFileFormats.BPMN);
+ } else {
+ await saveXmlAsLocalFile(xml, fileName);
+ }
}
if (fileFormat === saveFileFormats.ALL || fileFormat === saveFileFormats.SVG || fileFormat === saveFileFormats.PNG) {
await saveWorkflowAsSVG(modeler, fileName, fileFormat);
}
-
- return;
}
/**
@@ -231,6 +232,36 @@ export function openInNewTab(workflowXml, fileName) {
};
}
+export function setAutoSaveInterval(autoSaveFileOption = editorConfig.getAutoSaveFileOption()) {
+ if (autoSaveFileOption === autoSaveFile.INTERVAL) {
+ getModeler().autosaveIntervalId = setInterval(() => { saveFile(); }, editorConfig.getAutoSaveIntervalSize());
+ } else {
+ saveFile();
+ }
+}
+
+export function saveFile() {
+ // extract the xml and save it to a file
+ getModeler().saveXML({ format: true }, function (err, xml) {
+ if (!err) {
+ let oldXml = getModeler().oldXml;
+ if (oldXml !== xml && oldXml !== undefined) {
+ // Save the XML
+ console.log('Autosaved:', xml);
+ getModeler().oldXml = xml;
+ const timestamp = getTimestamp();
+ const filename = `${editorConfig.getFileName().replace('.bpmn','')}_autosave_${timestamp}`;
+ saveXmlAsLocalFile(xml, filename);
+ }
+ }
+ });
+}
+
+function getTimestamp() {
+ const date = new Date();
+ return date.toISOString().replace(/:/g, '-');
+}
+
export async function saveWorkflowAsSVG(modeler, fileName, fileFormat) {
modeler.saveSVG({ format: true }, function (error, svg) {
if (error) {
@@ -269,8 +300,8 @@ function downloadPng(pngDataUrl, fileName, fileFormat) {
async function openFileDialog(content, fileName, fileFormat) {
let suggestedName = fileName;
if (suggestedName.includes('.bpmn')) {
- suggestedName = fileName.split('.bpmn')[0];
- }
+ suggestedName = fileName.split('.bpmn')[0];
+ }
let fileHandle = await window.showSaveFilePicker({
startIn: 'downloads', suggestedName: suggestedName + fileFormat, types: [
{
@@ -289,8 +320,8 @@ async function openFileDialog(content, fileName, fileFormat) {
async function openFileUrlDialog(content, fileName, fileFormat) {
let suggestedName = fileName;
if (suggestedName.includes('.bpmn')) {
- suggestedName = fileName.split('.bpmn')[0];
- }
+ suggestedName = fileName.split('.bpmn')[0];
+ }
let fileHandle = await window.showSaveFilePicker({
startIn: 'downloads', suggestedName: suggestedName + fileFormat, types: [
{
diff --git a/components/bpmn-q/modeler-component/editor/util/TransformationUtilities.js b/components/bpmn-q/modeler-component/editor/util/TransformationUtilities.js
index f283945c2..000a5b8ed 100644
--- a/components/bpmn-q/modeler-component/editor/util/TransformationUtilities.js
+++ b/components/bpmn-q/modeler-component/editor/util/TransformationUtilities.js
@@ -1,5 +1,5 @@
-import {isFlowLikeElement} from './ModellingUtilities';
-import {getDi, is} from 'bpmn-js/lib/util/ModelUtil';
+import { isFlowLikeElement } from './ModellingUtilities';
+import { getDi, is } from 'bpmn-js/lib/util/ModelUtil';
/**
* Insert the given element and all child elements into the diagram
@@ -30,18 +30,18 @@ export function insertShape(definitions, parent, newElement, idMap, replace, mod
if (replace) {
// replace old element to retain attached sequence flow, associations, data objects, ...
- element = bpmnReplace.replaceElement(elementRegistry.get(oldElement.id), {type: newElement.$type});
+ element = bpmnReplace.replaceElement(elementRegistry.get(oldElement.id), { type: newElement.$type });
} else {
// create new shape for this element
- element = modeling.createShape({type: newElement.$type}, {x: 50, y: 50}, parent, {});
+ element = modeling.createShape({ type: newElement.$type }, { x: 50, y: 50 }, parent, {});
}
} else {
// create connection between two previously created elements
let sourceElement = elementRegistry.get(idMap[newElement.sourceRef.id]);
let targetElement = elementRegistry.get(idMap[newElement.targetRef.id]);
- element = modeling.connect(sourceElement, targetElement, {type: newElement.$type});
+ element = modeling.connect(sourceElement, targetElement, { type: newElement.$type });
}
// store id to create sequence flows
@@ -52,13 +52,11 @@ export function insertShape(definitions, parent, newElement, idMap, replace, mod
// get the shape element related to the subprocess
let shape = getDi(element);
- shape.isExpanded = true;
- // TODO: fix the following if, as the access to the DI of the new element is not possible with the current BPMN-JS version
- /*if (shape && shape.isExpanded) {
- // expand the new element
- elementRegistry.get(element.id).businessObject.di.isExpanded = true;
- }*/
+ // expand the replacement subprocess if the detector subprocess was expanded
+ if (shape && (newElement.isExpanded === 'true')) {
+ shape.isExpanded = true;
+ }
// preserve messages defined in ReceiveTasks
} else if (newElement.$type === 'bpmn:ReceiveTask' && newElement.messageRef) {
@@ -71,21 +69,21 @@ export function insertShape(definitions, parent, newElement, idMap, replace, mod
let message = bpmnFactory.create('bpmn:Message');
message.name = oldMessage.name;
definitions.rootElements.push(message);
- modeling.updateProperties(element, {'messageRef': message});
+ modeling.updateProperties(element, { 'messageRef': message });
// store id if other receive tasks reference the same message
idMap[oldMessage.id] = message.id;
} else {
// reuse already created message and add it to receive task
- modeling.updateProperties(element, {'messageRef': idMap[oldMessage.id]});
+ modeling.updateProperties(element, { 'messageRef': idMap[oldMessage.id] });
}
}
// add element to which a boundary event is attached
if (newElement.$type === 'bpmn:BoundaryEvent') {
let hostElement = elementRegistry.get(idMap[newElement.attachedToRef.id]);
- modeling.updateProperties(element, {'attachedToRef': hostElement.businessObject});
+ modeling.updateProperties(element, { 'attachedToRef': hostElement.businessObject });
element.host = hostElement;
}
@@ -109,7 +107,7 @@ export function insertShape(definitions, parent, newElement, idMap, replace, mod
}
// return success flag and idMap with id mappings of this element and all children
- return {success: success, idMap: idMap, element: element};
+ return { success: success, idMap: idMap, element: element };
}
/**
@@ -162,7 +160,7 @@ export function insertChildElements(definitions, parent, newElement, idMap, mode
}
}
- return {success: success, idMap: idMap, element: parent};
+ return { success: success, idMap: idMap, element: parent };
}
/**
@@ -229,7 +227,7 @@ export function getAllElementsInProcess(processBo, elementRegistry, elementType)
for (let i = 0; i < flowElementBos.length; i++) {
let flowElementBo = flowElementBos[i];
if (flowElementBo.$type && flowElementBo.$type === elementType) {
- elements.push({element: flowElementBo, parent: processElement});
+ elements.push({ element: flowElementBo, parent: processElement });
}
// recursively retrieve service tasks if subprocess is found
@@ -258,7 +256,7 @@ export function getAllElementsForProcess(processBo, elementRegistry, elementType
for (let i = 0; i < flowElements.length; i++) {
let flowElement = flowElements[i];
if (is(flowElement, elementType)) {
- elements.push({element: flowElement, parent: processElement});
+ elements.push({ element: flowElement, parent: processElement });
}
}
return elements;
diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/DataFlowPlugin.js b/components/bpmn-q/modeler-component/extensions/data-extension/DataFlowPlugin.js
index 0f4581342..d4a31260e 100644
--- a/components/bpmn-q/modeler-component/extensions/data-extension/DataFlowPlugin.js
+++ b/components/bpmn-q/modeler-component/extensions/data-extension/DataFlowPlugin.js
@@ -25,7 +25,7 @@ export default {
configTabs: [
{
tabId: 'DataEndpointsTab',
- tabTitle: 'Data Endpoints',
+ tabTitle: 'Data Flow Plugin',
configTab: TransformationTaskConfigurationsTab,
},
],
diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/rules/DataFlowRulesProvider.js b/components/bpmn-q/modeler-component/extensions/data-extension/rules/DataFlowRulesProvider.js
index 63cb439ad..55037fc3c 100644
--- a/components/bpmn-q/modeler-component/extensions/data-extension/rules/DataFlowRulesProvider.js
+++ b/components/bpmn-q/modeler-component/extensions/data-extension/rules/DataFlowRulesProvider.js
@@ -5,8 +5,11 @@ import {
} from 'bpmn-js/lib/features/modeling/util/ModelingUtil';
import * as consts from '../Constants';
import { isConnectedWith } from '../../../editor/util/ModellingUtilities';
+import { saveFile, setAutoSaveInterval } from '../../../editor/util/IoUtilities';
import { getModeler } from '../../../editor/ModelerHandler';
import ace from 'ace-builds';
+import * as editorConfig from "../../../editor/config/EditorConfigManager";
+import { autoSaveFile } from '../../../editor/EditorConstants';
/**
* Custom rules provider for the DataFlow elements. Extends the BpmnRules.
@@ -20,8 +23,7 @@ export default class CustomRulesProvider extends BpmnRules {
const canConnect = this.canConnect.bind(this);
const canCreate = this.canCreate.bind(this);
- // persist into local storage whenever
- // copy took place
+ // persist into local storage whenever copy took place
eventBus.on('copyPaste.elementsCopied', event => {
const { tree } = event;
@@ -68,21 +70,41 @@ export default class CustomRulesProvider extends BpmnRules {
);
});
+ // save every change when the autosave option is on action
+ eventBus.on("commandStack.changed", function () {
+ if (editorConfig.getAutoSaveFileOption() === autoSaveFile.ON_ACTION) {
+ saveFile();
+ }
+ });
+
+ // remove interval when autosave option is on action
+ eventBus.on("autoSaveOptionChanged", function (context) {
+ if (context.autoSaveFileOption === autoSaveFile.ON_ACTION) {
+ clearInterval(getModeler().autosaveIntervalId);
+ } else {
+ setAutoSaveInterval();
+ }
+ });
+
// update xml viewer on diagram change
eventBus.on("commandStack.changed", function () {
let editor = document.getElementById('editor');
let aceEditor = ace.edit(editor);
let modeler = getModeler();
if (modeler) {
+ if (modeler.xml !== undefined) {
+ modeler.oldXml = getModeler().xml;
+ if (getModeler().xml.xml !== undefined)
+ modeler.oldXml = getModeler().xml.xml;
+ }
modeler.saveXML({ format: true }).then(function (result) {
- if (result.xml != undefined) {
+ if (result.xml !== undefined) {
result = result.xml;
}
aceEditor.setValue(result);
- })
+ });
}
});
-
}
/**
diff --git a/components/bpmn-q/modeler-component/extensions/qhana/QHAnaPlugin.js b/components/bpmn-q/modeler-component/extensions/qhana/QHAnaPlugin.js
index 4219772fa..63a86d72f 100644
--- a/components/bpmn-q/modeler-component/extensions/qhana/QHAnaPlugin.js
+++ b/components/bpmn-q/modeler-component/extensions/qhana/QHAnaPlugin.js
@@ -23,7 +23,7 @@ export default {
configTabs: [
{
tabId: 'QHAnaEndpointsTab',
- tabTitle: 'QHAna Endpoints',
+ tabTitle: 'QHAna Plugin',
configTab: QHAnaConfigurationsTab,
},
],
diff --git a/components/bpmn-q/modeler-component/extensions/quantme/QuantMEPlugin.js b/components/bpmn-q/modeler-component/extensions/quantme/QuantMEPlugin.js
index efb6b9322..673889435 100644
--- a/components/bpmn-q/modeler-component/extensions/quantme/QuantMEPlugin.js
+++ b/components/bpmn-q/modeler-component/extensions/quantme/QuantMEPlugin.js
@@ -1,20 +1,13 @@
-import React from "react";
-
-import QuantMEExtensionModule from "./modeling";
-import BPMNConfigTab from "./configTabs/BPMNConfigTab";
-import NisqAnalyzerTab from "./configTabs/NisqAnalyzerTab";
-import QrmDataTab from "./configTabs/QrmDataTab";
-import HybridRuntimeTab from "./configTabs/HybridRuntimeTab";
-import UploadTab from "./configTabs/UploadTab";
-import {getQRMs} from "./qrm-manager";
-import {startQuantmeReplacementProcess} from "./replacement/QuantMETransformator";
-import * as camundaConfig from "../../editor/config/EditorConfigManager";
-import * as config from "./framework-config/config-manager";
-import TransformationButton from "../../editor/ui/TransformationButton";
-import DataObjectConfigurationsTab from './configurations/DataObjectConfigurationsTab';
+import React from 'react';
+import QuantMEExtensionModule from './modeling';
+import {getQRMs} from './qrm-manager';
+import {startQuantmeReplacementProcess} from './replacement/QuantMETransformator';
+import * as camundaConfig from '../../editor/config/EditorConfigManager';
+import * as config from './framework-config/config-manager';
+import TransformationButton from '../../editor/ui/TransformationButton';
import quantMEStyles from './styling/quantme.css';
-import QuantMEPluginButton from "./ui/QuantMEPluginButton";
+import QuantMEPluginButton from './ui/QuantMEPluginButton';
let quantMEModdleExtension = require('./resources/quantum4bpmn.json');
@@ -22,37 +15,12 @@ let quantMEModdleExtension = require('./resources/quantum4bpmn.json');
* Plugin Object of the QuantME extension. Used to register the plugin in the plugin handler of the modeler.
*/
export default {
- buttons: [ ],
+ buttons: [ ],
configTabs: [
- {
- tabId: 'DataConfigurationEndpointTab',
- tabTitle: 'QuantME Data',
- configTab: DataObjectConfigurationsTab,
- },
{
tabId: 'BPMNTab',
- tabTitle: 'Workflow',
- configTab: BPMNConfigTab,
- },
- {
- tabId: 'NISQAnalyzerEndpointTab',
- tabTitle: 'NISQ Analyzer',
- configTab: NisqAnalyzerTab,
- },
- {
- tabId: 'QRMDataTab',
- tabTitle: 'QRM Data',
- configTab: QrmDataTab,
- },
- {
- tabId: 'HybridRuntimesTab',
- tabTitle: 'Hybrid Runtimes',
- configTab: HybridRuntimeTab,
- },
- {
- tabId: 'UploadTab',
- tabTitle: 'Upload data',
- configTab: UploadTab,
+ tabTitle: 'QuantME Plugin',
+ configTab: QuantMETab,
}
],
name: 'quantme',
@@ -71,5 +39,5 @@ export default {
}
);
}
- }/>,
+ }/>
};
\ No newline at end of file
diff --git a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/BPMNConfigTab.js b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/BPMNConfigTab.js
deleted file mode 100644
index ac63d075c..000000000
--- a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/BPMNConfigTab.js
+++ /dev/null
@@ -1,107 +0,0 @@
-import React, {useState} from 'react';
-import {getModeler} from "../../../editor/ModelerHandler";
-import * as config from "../framework-config/config-manager";
-
-/**
- * React component specifying a tab for the configuration dialog of the modeler. The tab allows the user to change workflow
- * related configuration entries of the QuantME configs.
- *
- * @return {JSX.Element} The tab as a React component
- * @constructor
- */
-export default function BPMNConfigTab() {
-
- const [transformationFrameworkEndpoint, setTransformationFrameworkEndpoint] = useState(config.getTransformationFrameworkEndpoint());
- const [scriptSplitterEndpoint, setScriptSplitterEndpoint] = useState(config.getScriptSplitterEndpoint());
- const [scriptSplitterThreshold, setScriptSplitterThreshold] = useState(config.getScriptSplitterThreshold());
-
- const modeler = getModeler();
-
- const editorActions = modeler.get('editorActions');
- const eventBus = modeler.get('eventBus');
-
- // register editor action listener for changes in config entries
- if (!editorActions._actions.hasOwnProperty('transformationFrameworkEndpointChanged')) {
- editorActions.register({
- transformationFrameworkEndpointChanged: function (transformationFrameworkEndpoint) {
- modeler.config.transformationFrameworkEndpoint = transformationFrameworkEndpoint;
- }
- });
- }
- if (!editorActions._actions.hasOwnProperty('scriptSplitterEndpointChanged')) {
- editorActions.register({
- scriptSplitterEndpointChanged: function (scriptSplitterEndpoint) {
- modeler.config.scriptSplitterEndpoint = scriptSplitterEndpoint;
- eventBus.fire('config.updated', self.modeler.config);
- }
- });
- }
- if (!editorActions._actions.hasOwnProperty('scriptSplitterThresholdChanged')) {
- editorActions.register({
- scriptSplitterThresholdChanged: function (scriptSplitterEndpoint) {
- modeler.config.scriptSplitterThreshold = scriptSplitterEndpoint;
- }
- });
- }
-
- // save changed config entries on close
- BPMNConfigTab.prototype.onClose = () => {
- modeler.config.transformationFrameworkEndpoint = transformationFrameworkEndpoint;
- modeler.config.scriptSplitterEndpoint = scriptSplitterEndpoint;
- modeler.config.scriptSplitterThreshold = scriptSplitterThreshold;
- config.setTransformationFrameworkEndpoint(transformationFrameworkEndpoint);
- config.setScriptSplitterEndpoint(scriptSplitterEndpoint);
- config.setScriptSplitterThreshold(scriptSplitterThreshold);
- };
-
- return <>
- BPMN related configurations:
-
- Workflow generation:
-
- >;
-}
-
-BPMNConfigTab.prototype.config = () => {
- const modeler = getModeler();
-
- modeler.config.transformationFrameworkEndpoint = config.getTransformationFrameworkEndpoint();
- modeler.config.scriptSplitterEndpoint = config.getScriptSplitterEndpoint();
- modeler.config.scriptSplitterThreshold = config.getScriptSplitterThreshold();
-};
\ No newline at end of file
diff --git a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/UploadTab.js b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/GitHubTab.js
similarity index 53%
rename from components/bpmn-q/modeler-component/extensions/quantme/configTabs/UploadTab.js
rename to components/bpmn-q/modeler-component/extensions/quantme/configTabs/GitHubTab.js
index 66624d03f..e99c95f85 100644
--- a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/UploadTab.js
+++ b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/GitHubTab.js
@@ -9,8 +9,12 @@ import * as config from "../framework-config/config-manager";
* @return {JSX.Element} The tab as a React component
* @constructor
*/
-export default function UploadTab() {
+export default function GitHubTab() {
+ const [githubRepositoryName, setGithubRepositoryName] = useState(config.getQRMRepositoryName());
+ const [githubUsername, setGithubUsername] = useState(config.getQRMRepositoryUserName());
+ const [githubRepositoryPath, setGithubRepositoryPath] = useState(config.getQRMRepositoryPath());
+ const [githubToken, setGitHubToken] = useState(config.getGitHubToken());
const [uploadGithubRepositoryName, setUploadGithubRepositoryName] = useState(config.getUploadGithubRepositoryName());
const [uploadGithubOwner, setUploadGithubOwner] = useState(config.getUploadGithubRepositoryOwner());
const [uploadFileName, setUploadFileName] = useState(config.getUploadFileName());
@@ -20,6 +24,35 @@ export default function UploadTab() {
const editorActions = modeler.get('editorActions');
// register editor action listener for changes in config entries
+ if (!editorActions._actions.hasOwnProperty('qrmRepoNameChanged')) {
+ editorActions.register({
+ qrmRepoNameChanged: function (qrmRepoName) {
+ self.modeler.config.githubRepositoryName = qrmRepoName;
+ }
+ });
+ }
+ if (!editorActions._actions.hasOwnProperty('qrmUserNameChanged')) {
+ editorActions.register({
+ qrmUserNameChanged: function (qrmUserName) {
+ self.modeler.config.githubUsername = qrmUserName;
+ }
+ });
+ }
+ if (!editorActions._actions.hasOwnProperty('qrmRepoPathChanged')) {
+ editorActions.register({
+ qrmRepoPathChanged: function (qrmRepoPath) {
+ self.modeler.config.githubRepositoryPath = qrmRepoPath;
+ }
+ });
+ }
+ if (!editorActions._actions.hasOwnProperty('githubTokenChanged')) {
+ editorActions.register({
+ githubTokenChanged: function (githubToken) {
+ self.modeler.config.githubToken = githubToken;
+ }
+ });
+ }
+
if (!editorActions._actions.hasOwnProperty('uploadGithubRepositoryNameChanged')) {
editorActions.register({
uploadGithubRepositoryNameChanged: function (uploadGithubRepositoryName) {
@@ -51,7 +84,11 @@ export default function UploadTab() {
}
// save changed config entries on close
- UploadTab.prototype.onClose = () => {
+ GitHubTab.prototype.onClose = () => {
+ modeler.config.githubRepositoryName = githubRepositoryName;
+ modeler.config.githubUsername = githubUsername;
+ modeler.config.githubRepositoryPath = githubRepositoryPath;
+ modeler.config.githubToken = githubToken;
modeler.config.uploadGithubRepositoryName = uploadGithubRepositoryName;
modeler.config.uploadGithubRepositoryOwner = uploadGithubOwner;
modeler.config.uploadFileName = uploadFileName;
@@ -61,10 +98,63 @@ export default function UploadTab() {
config.setUploadGithubRepositoryOwner(uploadGithubOwner);
config.setUploadFileName(uploadFileName);
config.setUploadBranchName(uploadBranchName);
-
+ config.setQRMRepositoryName(githubRepositoryName);
+ config.setQRMUserName(githubUsername);
+ config.setQRMRepositoryPath(githubRepositoryPath);
+ config.setGitHubToken(githubToken);
};
return <>
+ QRM Data
+
+ GitHub Authentication
+
Upload Data
@@ -113,9 +203,14 @@ export default function UploadTab() {
>;
}
-UploadTab.prototype.config = () => {
+GitHubTab.prototype.config = () => {
const modeler = getModeler();
+ modeler.config.githubRepositoryName = config.getQRMRepositoryName();
+ modeler.config.githubUsername = config.getQRMRepositoryUserName();
+ modeler.config.githubRepositoryPath = config.getQRMRepositoryPath();
+ modeler.config.githubToken = config.getGitHubToken();
+
modeler.config.uploadGithubRepositoryName = config.getUploadGithubRepositoryName();
modeler.config.uploadGithubRepositoryOwner = config.getUploadGithubRepositoryOwner();
modeler.config.uploadFileName = config.getUploadFileName();
diff --git a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/HybridRuntimeTab.js b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/HybridRuntimeTab.js
deleted file mode 100644
index 7a4b370db..000000000
--- a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/HybridRuntimeTab.js
+++ /dev/null
@@ -1,114 +0,0 @@
-import React, {useState} from 'react';
-import {getModeler} from "../../../editor/ModelerHandler";
-import * as config from "../framework-config/config-manager";
-
-/**
- * React component specifying a tab for the configuration dialog of the modeler. The tab allows the user to change the
- * hybrid runtime configs.
- *
- * @return {JSX.Element} The tab as a React component
- * @constructor
- */
-export default function HybridRuntimeTab() {
-
- const [qiskitRuntimeHandlerEndpoint, setQiskitRuntimeHandlerEndpoint] = useState(config.getQiskitRuntimeHandlerEndpoint());
- const [hybridRuntimeProvenance, setHybridRuntimeProvenance] = useState(config.getHybridRuntimeProvenance());
- const [awsRuntimeHandlerEndpoint, setAWSRuntimeHandlerEndpoint] = useState(config.getAWSRuntimeHandlerEndpoint());
-
- let hybridRuntimeProvenanceBoolean = hybridRuntimeProvenance;
-
- const modeler = getModeler();
-
- const editorActions = modeler.get('editorActions');
- const eventBus = modeler.get('eventBus');
-
- // register editor action listener for changes in config entries
- if (!editorActions._actions.hasOwnProperty('qiskitRuntimeHandlerEndpointChanged')) {
- editorActions.register({
- qiskitRuntimeHandlerEndpointChanged: function (qiskitRuntimeHandlerEndpoint) {
- self.modeler.config.qiskitRuntimeHandlerEndpoint = qiskitRuntimeHandlerEndpoint;
- eventBus.fire('config.updated', self.modeler.config);
- }
- });
- }
- if (!editorActions._actions.hasOwnProperty('awsRuntimeHandlerEndpointChanged')) {
- editorActions.register({
- awsRuntimeHandlerEndpointChanged: function (awsRuntimeHandlerEndpoint) {
- self.modeler.config.awsRuntimeHandlerEndpoint = awsRuntimeHandlerEndpoint;
- eventBus.fire('config.updated', self.modeler.config);
- }
- });
- }
- if (!editorActions._actions.hasOwnProperty('hybridRuntimeProvenanceChanged')) {
- editorActions.register({
- hybridRuntimeProvenanceChanged: function (hybridRuntimeProvenance) {
- self.modeler.config.hybridRuntimeProvenance = hybridRuntimeProvenance;
- eventBus.fire('config.updated', self.modeler.config);
- }
- });
- }
-
- // save changed config entries on close
- HybridRuntimeTab.prototype.onClose = () => {
- modeler.config.qiskitRuntimeHandlerEndpoint = qiskitRuntimeHandlerEndpoint;
- modeler.config.hybridRuntimeProvenance = hybridRuntimeProvenance;
- modeler.config.awsRuntimeHandlerEndpoint = awsRuntimeHandlerEndpoint;
- config.setQiskitRuntimeHandlerEndpoint(qiskitRuntimeHandlerEndpoint);
- config.setAWSRuntimeHandlerEndpoint(awsRuntimeHandlerEndpoint);
- config.setHybridRuntimeProvenance(hybridRuntimeProvenance);
- };
-
- return (<>
- Hybrid Runtime Handler Endpoints
-
- Provenance Collection for Hybrid Runtime
-
- >);
-}
-
-HybridRuntimeTab.prototype.config = () => {
- const modeler = getModeler();
-
- modeler.config.qiskitRuntimeHandlerEndpoint = config.getQiskitRuntimeHandlerEndpoint();
- modeler.config.hybridRuntimeProvenance = config.getHybridRuntimeProvenance();
- modeler.config.awsRuntimeHandlerEndpoint = config.getAWSRuntimeHandlerEndpoint();
-};
\ No newline at end of file
diff --git a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/NisqAnalyzerTab.js b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/NisqAnalyzerTab.js
deleted file mode 100644
index 45bcdcd75..000000000
--- a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/NisqAnalyzerTab.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import React, {useState} from 'react';
-import {getModeler} from "../../../editor/ModelerHandler";
-import * as config from "../framework-config/config-manager";
-
-/**
- * React component specifying a tab for the configuration dialog of the modeler. The tab allows the user to change the
- * NISQ analyzer endpoint.
- *
- * @return {JSX.Element} The tab as a React component
- * @constructor
- */
-export default function NisqAnalyzerTab() {
-
- const [nisqAnalyzerEndpoint, setNisqAnalyzerEndpoint] = useState(config.getNisqAnalyzerEndpoint());
-
- const modeler = getModeler();
-
- const editorActions = modeler.get('editorActions');
-
- // register editor action listener for changes in config entries
- if (!editorActions._actions.hasOwnProperty('nisqAnalyzerEndpointChanged')) {
- editorActions.register({
- nisqAnalyzerEndpointChanged: function (nisqAnalyzerEndpoint) {
- self.modeler.config.nisqAnalyzerEndpoint = nisqAnalyzerEndpoint;
- }
- });
- }
-
- // save changed config entries on close
- NisqAnalyzerTab.prototype.onClose = () => {
- modeler.config.nisqAnalyzerEndpoint = nisqAnalyzerEndpoint;
- config.setNisqAnalyzerEndpoint(nisqAnalyzerEndpoint);
- };
-
- return <>
- NISQ Analyzer
-
- >;
-}
-
-NisqAnalyzerTab.prototype.config = () => {
- const modeler = getModeler();
-
- modeler.config.nisqAnalyzerEndpoint = config.getNisqAnalyzerEndpoint();
-};
\ No newline at end of file
diff --git a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/QrmDataTab.js b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/QrmDataTab.js
deleted file mode 100644
index 6a2b70688..000000000
--- a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/QrmDataTab.js
+++ /dev/null
@@ -1,126 +0,0 @@
-import React, {useState} from 'react';
-import {getModeler} from "../../../editor/ModelerHandler";
-import * as config from "../framework-config/config-manager";
-
-/**
- * React component specifying a tab for the configuration dialog of the modeler. The tab allows the user to change the
- * QRM data.
- *
- * @return {JSX.Element} The tab as a React component
- * @constructor
- */
-export default function QrmDataTab() {
-
- const [githubRepositoryName, setGithubRepositoryName] = useState(config.getQRMRepositoryName());
- const [githubUsername, setGithubUsername] = useState(config.getQRMRepositoryUserName());
- const [githubRepositoryPath, setGithubRepositoryPath] = useState(config.getQRMRepositoryPath());
- const [githubToken, setGitHubToken] = useState(config.getGitHubToken());
- const modeler = getModeler();
-
- const editorActions = modeler.get('editorActions');
-
- // register editor action listener for changes in config entries
- if (!editorActions._actions.hasOwnProperty('qrmRepoNameChanged')) {
- editorActions.register({
- qrmRepoNameChanged: function (qrmRepoName) {
- self.modeler.config.githubRepositoryName = qrmRepoName;
- }
- });
- }
- if (!editorActions._actions.hasOwnProperty('qrmUserNameChanged')) {
- editorActions.register({
- qrmUserNameChanged: function (qrmUserName) {
- self.modeler.config.githubUsername = qrmUserName;
- }
- });
- }
- if (!editorActions._actions.hasOwnProperty('qrmRepoPathChanged')) {
- editorActions.register({
- qrmRepoPathChanged: function (qrmRepoPath) {
- self.modeler.config.githubRepositoryPath = qrmRepoPath;
- }
- });
- }
- if (!editorActions._actions.hasOwnProperty('githubTokenChanged')) {
- editorActions.register({
- githubTokenChanged: function (githubToken) {
- self.modeler.config.githubToken = githubToken;
- }
- });
- }
-
- // save changed config entries on close
- QrmDataTab.prototype.onClose = () => {
- modeler.config.githubRepositoryName = githubRepositoryName;
- modeler.config.githubUsername = githubUsername;
- modeler.config.githubRepositoryPath = githubRepositoryPath;
- modeler.config.githubToken = githubToken;
- config.setQRMRepositoryName(githubRepositoryName);
- config.setQRMUserName(githubUsername);
- config.setQRMRepositoryPath(githubRepositoryPath);
- config.setGitHubToken(githubToken);
- };
-
- return <>
- QRM Data
-
- GitHub Authentication
-
- >;
-}
-
-QrmDataTab.prototype.config = () => {
- const modeler = getModeler();
-
- modeler.config.githubRepositoryName = config.getQRMRepositoryName();
- modeler.config.githubUsername = config.getQRMRepositoryUserName();
- modeler.config.githubRepositoryPath = config.getQRMRepositoryPath();
- modeler.config.githubToken = config.getGitHubToken();
-
-};
\ No newline at end of file
diff --git a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/QuantMETab.js b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/QuantMETab.js
new file mode 100644
index 000000000..dbd5cc07a
--- /dev/null
+++ b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/QuantMETab.js
@@ -0,0 +1,273 @@
+import React, { useState } from 'react';
+import { getModeler } from '../../../editor/ModelerHandler';
+import * as config from '../framework-config/config-manager';
+
+/**
+ * React component specifying a tab for the configuration dialog of the modeler. The tab allows the user to change workflow
+ * related configuration entries of the QuantME configs.
+ *
+ * @return {JSX.Element} The tab as a React component
+ * @constructor
+ */
+export default function BPMNConfigTab() {
+ const [dataConfigurationsEndpoint, setDataConfigurationsEndpoint] = useState(config.getQuantMEDataConfigurationsEndpoint());
+
+ const [opentoscaEndpoint, setOpentoscaEndpoint] = useState(config.getOpenTOSCAEndpoint());
+ const [wineryEndpoint, setWineryEndpoint] = useState(config.getWineryEndpoint());
+ const [nisqAnalyzerEndpoint, setNisqAnalyzerEndpoint] = useState(config.getNisqAnalyzerEndpoint());
+ const [qiskitRuntimeHandlerEndpoint, setQiskitRuntimeHandlerEndpoint] = useState(config.getQiskitRuntimeHandlerEndpoint());
+ const [hybridRuntimeProvenance, setHybridRuntimeProvenance] = useState(config.getHybridRuntimeProvenance());
+ const [awsRuntimeHandlerEndpoint, setAWSRuntimeHandlerEndpoint] = useState(config.getAWSRuntimeHandlerEndpoint());
+ const [transformationFrameworkEndpoint, setTransformationFrameworkEndpoint] = useState(config.getTransformationFrameworkEndpoint());
+ const [scriptSplitterEndpoint, setScriptSplitterEndpoint] = useState(config.getScriptSplitterEndpoint());
+ const [scriptSplitterThreshold, setScriptSplitterThreshold] = useState(config.getScriptSplitterThreshold());
+ let hybridRuntimeProvenanceBoolean = hybridRuntimeProvenance;
+
+ const modeler = getModeler();
+
+ const editorActions = modeler.get('editorActions');
+ const eventBus = modeler.get('eventBus');
+
+ // register editor action listener for changes in config entries
+ if (!editorActions._actions.hasOwnProperty('qiskitRuntimeHandlerEndpointChanged')) {
+ editorActions.register({
+ qiskitRuntimeHandlerEndpointChanged: function (qiskitRuntimeHandlerEndpoint) {
+ self.modeler.config.qiskitRuntimeHandlerEndpoint = qiskitRuntimeHandlerEndpoint;
+ eventBus.fire('config.updated', self.modeler.config);
+ }
+ });
+ }
+ if (!editorActions._actions.hasOwnProperty('awsRuntimeHandlerEndpointChanged')) {
+ editorActions.register({
+ awsRuntimeHandlerEndpointChanged: function (awsRuntimeHandlerEndpoint) {
+ self.modeler.config.awsRuntimeHandlerEndpoint = awsRuntimeHandlerEndpoint;
+ eventBus.fire('config.updated', self.modeler.config);
+ }
+ });
+ }
+ if (!editorActions._actions.hasOwnProperty('hybridRuntimeProvenanceChanged')) {
+ editorActions.register({
+ hybridRuntimeProvenanceChanged: function (hybridRuntimeProvenance) {
+ self.modeler.config.hybridRuntimeProvenance = hybridRuntimeProvenance;
+ eventBus.fire('config.updated', self.modeler.config);
+ }
+ });
+ }
+ if (!editorActions._actions.hasOwnProperty('opentoscaEndpointChanged')) {
+ editorActions.register({
+ opentoscaEndpointChanged: function (opentoscaEndpoint) {
+ self.modeler.config.opentoscaEndpoint = opentoscaEndpoint;
+ }
+ });
+ }
+ if (!editorActions._actions.hasOwnProperty('wineryEndpointChanged')) {
+ editorActions.register({
+ wineryEndpointChanged: function (wineryEndpoint) {
+ self.modeler.config.wineryEndpoint = wineryEndpoint;
+ eventBus.fire('config.updated', self.modeler.config);
+ }
+ });
+ }
+ if (!editorActions._actions.hasOwnProperty('nisqAnalyzerEndpointChanged')) {
+ editorActions.register({
+ nisqAnalyzerEndpointChanged: function (nisqAnalyzerEndpoint) {
+ self.modeler.config.nisqAnalyzerEndpoint = nisqAnalyzerEndpoint;
+ }
+ });
+ }
+ if (!editorActions._actions.hasOwnProperty('transformationFrameworkEndpointChanged')) {
+ editorActions.register({
+ transformationFrameworkEndpointChanged: function (transformationFrameworkEndpoint) {
+ modeler.config.transformationFrameworkEndpoint = transformationFrameworkEndpoint;
+ }
+ });
+ }
+ if (!editorActions._actions.hasOwnProperty('scriptSplitterEndpointChanged')) {
+ editorActions.register({
+ scriptSplitterEndpointChanged: function (scriptSplitterEndpoint) {
+ modeler.config.scriptSplitterEndpoint = scriptSplitterEndpoint;
+ eventBus.fire('config.updated', self.modeler.config);
+ }
+ });
+ }
+ if (!editorActions._actions.hasOwnProperty('scriptSplitterThresholdChanged')) {
+ editorActions.register({
+ scriptSplitterThresholdChanged: function (scriptSplitterEndpoint) {
+ modeler.config.scriptSplitterThreshold = scriptSplitterEndpoint;
+ }
+ });
+ }
+
+ // save changed config entries on close
+ BPMNConfigTab.prototype.onClose = () => {
+ modeler.config.dataConfigurationsEndpoint = dataConfigurationsEndpoint;
+ modeler.config.opentoscaEndpoint = opentoscaEndpoint;
+ modeler.config.wineryEndpoint = wineryEndpoint;
+ modeler.config.nisqAnalyzerEndpoint = nisqAnalyzerEndpoint;
+ modeler.config.transformationFrameworkEndpoint = transformationFrameworkEndpoint;
+ modeler.config.scriptSplitterEndpoint = scriptSplitterEndpoint;
+ modeler.config.scriptSplitterThreshold = scriptSplitterThreshold;
+ modeler.config.qiskitRuntimeHandlerEndpoint = qiskitRuntimeHandlerEndpoint;
+ modeler.config.hybridRuntimeProvenance = hybridRuntimeProvenance;
+ modeler.config.awsRuntimeHandlerEndpoint = awsRuntimeHandlerEndpoint;
+ config.setQuantMEDataConfigurationsEndpoint(dataConfigurationsEndpoint);
+ config.setOpenTOSCAEndpoint(opentoscaEndpoint);
+ config.setWineryEndpoint(wineryEndpoint);
+ config.setNisqAnalyzerEndpoint(nisqAnalyzerEndpoint);
+ config.setTransformationFrameworkEndpoint(transformationFrameworkEndpoint);
+ config.setScriptSplitterEndpoint(scriptSplitterEndpoint);
+ config.setScriptSplitterThreshold(scriptSplitterThreshold);
+ config.setQiskitRuntimeHandlerEndpoint(qiskitRuntimeHandlerEndpoint);
+ config.setAWSRuntimeHandlerEndpoint(awsRuntimeHandlerEndpoint);
+ config.setHybridRuntimeProvenance(hybridRuntimeProvenance);
+ };
+
+ return <>
+ QuantME data configuration endpoint:
+
+ OpenTOSCA
+
+ BPMN related configurations:
+
+ NISQ Analyzer
+
+ Workflow generation:
+
+ Hybrid Runtime Handler Endpoints
+
+ Provenance Collection for Hybrid Runtime
+
+ >;
+}
+
+BPMNConfigTab.prototype.config = () => {
+ const modeler = getModeler();
+
+ modeler.config.transformationFrameworkEndpoint = config.getTransformationFrameworkEndpoint();
+ modeler.config.scriptSplitterEndpoint = config.getScriptSplitterEndpoint();
+ modeler.config.scriptSplitterThreshold = config.getScriptSplitterThreshold();
+};
\ No newline at end of file
diff --git a/components/bpmn-q/modeler-component/extensions/quantme/configurations/quantmeDataObjects.json b/components/bpmn-q/modeler-component/extensions/quantme/configurations/quantmeDataObjects.json
new file mode 100644
index 000000000..aacfa956f
--- /dev/null
+++ b/components/bpmn-q/modeler-component/extensions/quantme/configurations/quantmeDataObjects.json
@@ -0,0 +1,123 @@
+[
+ {
+ "name": "Quantum Circuit Object",
+ "id": "Quantum-Circuit-Object",
+ "description": "data object for storing and transferring all relevant data about a quantum circuit",
+ "appliesTo": "dataflow:DataMapObject",
+ "groupLabel": "Quantum Circuit Info",
+ "icon": {
+ "transform": "matrix(0.14, 0, 0, 0.14, 5, 5)",
+ "svg": " "
+ },
+ "attributes": [
+ {
+ "name": "quantum-circuit",
+ "label": "Quantum Circuit",
+ "type": "string",
+ "value": "",
+ "bindTo": {
+ "name": "content",
+ "type": "KeyValueMap"
+ }
+ },
+ {
+ "name": "programming-language",
+ "label": "Programming Language",
+ "type": "string",
+ "value": "",
+ "bindTo": {
+ "name": "content",
+ "type": "KeyValueMap"
+ }
+ }
+ ]
+ },
+ {
+ "name": "Result Object",
+ "id": "Result-Object",
+ "description": "data object to transfer the results of quantum computations",
+ "appliesTo": "dataflow:DataMapObject",
+ "groupLabel": "Result",
+ "icon": {
+ "transform": "matrix(0.24, 0, 0, 0.24, 3, 3)",
+ "svg": " "
+ },
+ "attributes": [
+ {
+ "name": "Execution-Result",
+ "label": "Execution Result",
+ "type": "string",
+ "bindTo": {
+ "name": "content",
+ "type": "KeyValueMap"
+ }
+ }
+ ]
+ },
+ {
+ "name": "Evaluation Result Object",
+ "id": "Evaluation-Result-Object",
+ "description": "data object to transfer the evaluated results of quantum computations",
+ "appliesTo": "dataflow:DataMapObject",
+ "groupLabel": "Evaluation Result",
+ "icon": {
+ "transform": "matrix(0.09, 0, 0, 0.09, 3, 3)",
+ "svg": " "
+ },
+ "attributes": [
+ {
+ "name": "Evaluation Result",
+ "label": "Evaluation Result",
+ "type": "string",
+ "bindTo": {
+ "name": "content",
+ "type": "KeyValueMap"
+ }
+ }
+ ]
+ },
+ {
+ "name": "Parameterization Object",
+ "id": "Parameterization-Object",
+ "description": "data object to transfer optimization parameters, e.g., in variational quantum algorithms",
+ "appliesTo": "dataflow:DataMapObject",
+ "groupLabel": "Parameterization",
+ "icon": {
+ "transform": "matrix(0.12, 0, 0, 0.12, 6, 4)",
+ "svg": "𝜶 𝜷 "
+ },
+ "attributes": [
+ {
+ "name": "Parameterization",
+ "label": "Parameterization",
+ "type": "string",
+ "bindTo": {
+ "name": "content",
+ "type": "KeyValueMap"
+ }
+ }
+ ]
+ },
+ {
+ "name": "Initial State Object",
+ "id": "Initial-State-Object",
+ "description": "data object to transfer the initial state use d to warm-start a quantum computations",
+ "appliesTo": "dataflow:DataMapObject",
+ "groupLabel": "Initial State",
+ "icon": {
+ "transform": "matrix(0.16, 0, 0, 0.16, 6, 3)",
+ "svg": "0 | ⟩ 1 | ⟩ "
+ },
+ "attributes": [
+ {
+ "name": "Initial State",
+ "label": "Initial State",
+ "type": "string",
+ "bindTo": {
+ "name": "content",
+ "type": "KeyValueMap"
+ }
+ }
+ ]
+ }
+]
diff --git a/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config.js b/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config.js
index 402cdd1fc..c732a5231 100644
--- a/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config.js
+++ b/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config.js
@@ -9,9 +9,13 @@
* SPDX-License-Identifier: Apache-2.0
*/
-// takes either the environment variables or the default values definded in webpack.config
+// takes either the environment variables or the default values defined in webpack.config
+// TODO: On change ALWAYS UPDATE corresponding doc: doc/quantum-workflow-modeler/modeler-configuration.md
const defaultConfig = {
quantmeDataConfigurationsEndpoint: process.env.DATA_CONFIG,
+ opentoscaEndpoint: process.env.OPENTOSCA_ENDPOINT,
+ wineryEndpoint: process.env.WINERY_ENDPOINT,
+ camundaEndpoint: process.env.CAMUNDA_ENDPOINT,
nisqAnalyzerEndpoint: process.env.NISQ_ANALYZER_ENDPOINT,
githubToken: process.env.GITHUB_TOKEN,
transformationFrameworkEndpoint: process.env.TRANSFORMATION_FRAMEWORK_ENDPOINT,
diff --git a/components/bpmn-q/modeler-component/extensions/quantme/replacement/QuantMETransformator.js b/components/bpmn-q/modeler-component/extensions/quantme/replacement/QuantMETransformator.js
index 751b477e5..db464ffaa 100644
--- a/components/bpmn-q/modeler-component/extensions/quantme/replacement/QuantMETransformator.js
+++ b/components/bpmn-q/modeler-component/extensions/quantme/replacement/QuantMETransformator.js
@@ -9,22 +9,24 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import {layout} from './layouter/Layouter';
-import {matchesQRM} from './QuantMEMatcher';
-import {addQuantMEInputParameters} from './InputOutputHandler';
+import { layout } from './layouter/Layouter';
+import { matchesQRM } from './QuantMEMatcher';
+import { addQuantMEInputParameters } from './InputOutputHandler';
import * as Constants from '../Constants';
-import {replaceHardwareSelectionSubprocess} from './hardware-selection/QuantMEHardwareSelectionHandler';
-import {replaceCuttingSubprocess} from './circuit-cutting/QuantMECuttingHandler';
-import {insertShape} from '../../../editor/util/TransformationUtilities';
-import {createTempModelerFromXml} from '../../../editor/ModelerHandler';
+import { replaceHardwareSelectionSubprocess } from './hardware-selection/QuantMEHardwareSelectionHandler';
+import { replaceCuttingSubprocess } from './circuit-cutting/QuantMECuttingHandler';
+import { insertShape } from '../../../editor/util/TransformationUtilities';
+import { createModelerFromXml } from '../../../editor/ModelerHandler';
import {
getCamundaInputOutput,
getDefinitionsFromXml,
getRootProcess,
getSingleFlowElement
} from '../../../editor/util/ModellingUtilities';
-import {getXml} from '../../../editor/util/IoUtilities';
+import { getXml } from '../../../editor/util/IoUtilities';
+const { DOMParser } = require('xmldom');
+const xmlParser = require('xml-js');
/**
* Initiate the replacement process for the QuantME tasks that are contained in the current process model
*
@@ -33,24 +35,24 @@ import {getXml} from '../../../editor/util/IoUtilities';
* @param endpointConfig endpoints of the services required for the dynamic hardware selection
*/
export async function startQuantmeReplacementProcess(xml, currentQRMs, endpointConfig) {
- let modeler = await createTempModelerFromXml(xml);
+ let modeler = await createModelerFromXml(xml);
let modeling = modeler.get('modeling');
let elementRegistry = modeler.get('elementRegistry');
// get root element of the current diagram
- const definitions = modeler.getDefinitions();
- const rootElement = getRootProcess(definitions);
+ let definitions = modeler.getDefinitions();
+ let rootElement = getRootProcess(definitions);
console.log(rootElement);
if (typeof rootElement === 'undefined') {
console.log('Unable to retrieve root process element from definitions!');
- return {status: 'failed', cause: 'Unable to retrieve root process element from definitions!'};
+ return { status: 'failed', cause: 'Unable to retrieve root process element from definitions!' };
}
// get all QuantME modeling constructs from the process
let replacementConstructs = getQuantMETasks(rootElement, elementRegistry);
console.log('Process contains ' + replacementConstructs.length + ' QuantME modeling constructs to replace...');
if (!replacementConstructs || !replacementConstructs.length) {
- return {status: 'transformed', xml: xml};
+ return { status: 'transformed', xml: xml };
}
// check for available replacement models for all QuantME modeling constructs
@@ -111,10 +113,96 @@ export async function startQuantmeReplacementProcess(xml, currentQRMs, endpointC
}
// layout diagram after successful transformation
- layout(modeling, elementRegistry, rootElement);
+ //layout(modeling, elementRegistry, rootElement);
let updated_xml = await getXml(modeler);
- console.log(updated_xml);
- return {status: 'transformed', xml: updated_xml};
+
+ // Parse the XML string into a JavaScript object
+ let xmlDoc = xmlParser.xml2js(updated_xml, { compact: true });
+ const bpmnNamespace = 'http://www.omg.org/spec/BPMN/20100524/MODEL';
+ const diagramNamespace = 'http://www.omg.org/spec/BPMN/20100524/DI';
+ const quantmeNamespace = 'https://github.com/UST-QuAntiL/QuantME-Quantum4BPMN';
+
+ // retrieve the namespace prefixes from the rootElement
+ let prefixes = Object.entries(rootElement.$parent.$attrs);
+ const foundBpmnPair = prefixes.find(pair => pair[1] === bpmnNamespace);
+ const foundDiagramPair = prefixes.find(pair => pair[1] === diagramNamespace);
+ const foundQuantMEPair = prefixes.find(pair => pair[1] === quantmeNamespace);
+ let modifiedXmlString = updated_xml;
+ if (foundBpmnPair && foundDiagramPair && foundQuantMEPair) {
+
+ // Remove xmlns: prefix from the key
+ const bpmnPrefix = foundBpmnPair[0].replace(/^xmlns:/, '');
+ const diagramPrefix = foundDiagramPair[0].replace(/^xmlns:/, '');
+ const quantmePrefix = foundQuantMEPair[0].replace(/^xmlns:/, '');
+
+ // Get all BPMNDiagram elements
+ const definitionsElement = xmlDoc[bpmnPrefix + ':definitions'];
+ let process = definitionsElement[bpmnPrefix + ':process'];
+
+ let subprocesses = process[bpmnPrefix + ':subProcess'];
+ let quantmeCuttingSubprocess = process[quantmePrefix + 'circuitCuttingSubprocess'];
+ let quantmeHardwareSelectionSubprocess = process[quantmePrefix + 'quantumHardwareSelectionSubprocess'];
+ let bpmnDiagrams = definitionsElement[diagramPrefix + ':BPMNDiagram'];
+
+ let subprocessBpmnElement = [];
+
+ // remove additional planes & extract bpmnElement
+ for (let i = 0; i < bpmnDiagrams.length; i++) {
+ let bpmnPlane = JSON.parse(JSON.stringify(bpmnDiagrams[i]));
+ subprocessBpmnElement.push(bpmnPlane['bpmndi:BPMNPlane']['_attributes']['bpmnElement']);
+ }
+
+ let shapes = JSON.parse(JSON.stringify(bpmnDiagrams[0]['bpmndi:BPMNPlane']))['bpmndi:BPMNShape'];
+ for (let i = 0; i < shapes.length; i++) {
+ let shape = shapes[i];
+ let bpmnShape = shape._attributes.bpmnElement;
+ let height = bpmnDiagrams[0]['bpmndi:BPMNPlane']['bpmndi:BPMNShape'][i]['dc:Bounds']['_attributes'].height;
+ if (height === 10 || height === '10') {
+ bpmnDiagrams[0]['bpmndi:BPMNPlane']['bpmndi:BPMNShape'][i]['dc:Bounds']['_attributes'].height = 80;
+ bpmnDiagrams[0]['bpmndi:BPMNPlane']['bpmndi:BPMNShape'][i]['dc:Bounds']['_attributes'].width = 100;
+ }
+ }
+
+ // Remove all bpmndi:BPMNDiagram elements which do not contain bpmn shapes
+ if (Array.isArray(bpmnDiagrams)) {
+ if (bpmnDiagrams.length > 1) {
+
+ // extract the diagrams with shapes
+ let diagram = [];
+ for (let i = 0; i < bpmnDiagrams.length; i++) {
+ if (bpmnDiagrams[i]['bpmndi:BPMNPlane']['bpmndi:BPMNShape'] !== undefined) {
+ diagram.push(bpmnDiagrams[i]);
+ }
+ }
+ xmlDoc[bpmnPrefix + ':definitions'][diagramPrefix + ':BPMNDiagram'] = diagram;
+ }
+ }
+
+ if (subprocesses !== undefined) {
+ process[bpmnPrefix + ':subProcess'] = removeIsExpandedAttribute(subprocesses, bpmnPrefix, quantmePrefix);
+ }
+ if (quantmeHardwareSelectionSubprocess !== undefined) {
+ process[quantmePrefix + ':quantumHardwareSelectionSubprocess'] = removeIsExpandedAttribute(quantmeHardwareSelectionSubprocess, bpmnPrefix, quantmePrefix);
+ }
+ if (quantmeCuttingSubprocess) {
+ process[quantmePrefix + ':circuitCuttingSubprocess'] = removeIsExpandedAttribute(quantmeCuttingSubprocess, bpmnPrefix, quantmePrefix);
+ }
+
+ // Serialize the modified JavaScript object back to XML string
+ modifiedXmlString = xmlParser.js2xml(xmlDoc, { compact: true });
+ }
+ modeler = await createModelerFromXml(modifiedXmlString);
+ modeling = modeler.get('modeling');
+ elementRegistry = modeler.get('elementRegistry');
+
+ // get root element of the current diagram
+ definitions = modeler.getDefinitions();
+ rootElement = getRootProcess(definitions);
+ layout(modeling, elementRegistry, rootElement);
+ let updated_xml2 = await getXml(modeler);
+
+
+ return { status: 'transformed', xml: updated_xml2 };
}
/**
@@ -130,7 +218,7 @@ export function getQuantMETasks(process, elementRegistry) {
for (let i = 0; i < flowElements.length; i++) {
let flowElement = flowElements[i];
if (flowElement.$type && flowElement.$type.startsWith('quantme:')) {
- quantmeTasks.push({task: flowElement, parent: processBo});
+ quantmeTasks.push({ task: flowElement, parent: processBo });
}
// recursively retrieve QuantME tasks if subprocess is found
@@ -160,7 +248,6 @@ async function getMatchingQRM(task, currentQRMs) {
*/
async function replaceByFragment(definitions, task, parent, replacement, modeler) {
let bpmnFactory = modeler.get('bpmnFactory');
-
if (!replacement) {
console.log('Replacement fragment is undefined. Aborting replacement!');
return false;
@@ -175,6 +262,37 @@ async function replaceByFragment(definitions, task, parent, replacement, modeler
}
console.log('Replacement element: ', replacementElement);
+
+ if (['bpmn:SubProcess', 'quantme:QuantumHardwareSelectionSubprocess', 'quantme:CircuitCuttingSubprocess'].includes(replacementElement.$type)) {
+
+ // Create a DOM parser
+ const parser = new DOMParser();
+
+ // Parse the XML string
+ const xmlDoc = parser.parseFromString(replacement, 'text/xml');
+
+ const bpmndiNamespace = 'http://www.omg.org/spec/BPMN/20100524/DI';
+ const bpmndiShapes = xmlDoc.getElementsByTagNameNS(bpmndiNamespace, 'BPMNShape');
+
+ let isExpanded = null;
+ for (let i = 0; i < bpmndiShapes.length; i++) {
+ const bpmnElement = bpmndiShapes[i].getAttribute('bpmnElement');
+ if (bpmnElement === replacementElement.id) {
+ isExpanded = bpmndiShapes[i].getAttribute('isExpanded');
+ replacementElement.isExpanded = isExpanded;
+
+ // check the children of each replacementElement
+ for (let j = 0; j < replacementElement.flowElements.length; j++) {
+ if (['bpmn:SubProcess', 'quantme:QuantumHardwareSelectionSubprocess', 'quantme:CircuitCuttingSubprocess'].includes(replacementElement.flowElements[j].$type)) {
+ isChildExpanded(replacementElement.flowElements[j], bpmndiShapes);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+
let result = insertShape(definitions, parent, replacementElement, {}, true, modeler, task);
// add all attributes of the replaced QuantME task to the input parameters of the replacement fragment
@@ -183,3 +301,83 @@ async function replaceByFragment(definitions, task, parent, replacement, modeler
return result['success'];
}
+
+/**
+ * Recursively checks the children of an element and updates the isExpanded attribute.
+ * @param {*} element
+ * @param {*} bpmndiShapes the diagram elements
+ */
+function isChildExpanded(element, bpmndiShapes) {
+ for (let i = 0; i < bpmndiShapes.length; i++) {
+ const bpmnElement = bpmndiShapes[i].getAttribute('bpmnElement');
+
+ if (bpmnElement === element.id && ['bpmn:SubProcess', 'quantme:QuantumHardwareSelectionSubprocess', 'quantme:CircuitCuttingSubprocess'].includes(element.$type)) {
+ let isExpanded = bpmndiShapes[i].getAttribute('isExpanded');
+ if (isExpanded) {
+ element.isExpanded = isExpanded;
+ }
+ }
+ }
+
+ if (element.flowElements !== undefined) {
+ for (let i = 0; i < element.flowElements.length; i++) {
+ const child = element.flowElements[i];
+ if (['bpmn:SubProcess', 'quantme:QuantumHardwareSelectionSubprocess', 'quantme:CircuitCuttingSubprocess'].includes(child.$type)) {
+ if (isChildExpanded(child, bpmndiShapes)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Recursively removes the isExpanded attribute from the bpmn shapes.
+ *
+ * @param subprocessElements
+ * @param bpmnPrefix
+ * @param quantmePrefix
+ * @returns the modified subprocess elements
+ */
+function removeIsExpandedAttribute(subprocessElements, bpmnPrefix, quantmePrefix) {
+
+ // remove top-level isExpanded attribute from shape
+ if (subprocessElements['_attributes'] !== undefined) {
+ let parentAttributes = subprocessElements['_attributes'];
+ delete parentAttributes.isExpanded;
+ }
+
+ if (Array.isArray(subprocessElements)) {
+ for (let i = 0; i < subprocessElements.length; i++) {
+ let subprocess = subprocessElements[i];
+ deleteAttribute(subprocess, bpmnPrefix, quantmePrefix);
+ let subprocesses = subprocess[bpmnPrefix + ':subProcess'];
+ deleteAttribute(subprocesses, bpmnPrefix, quantmePrefix);
+ let quantmeCuttingSubprocess = subprocess[quantmePrefix + ':circuitCuttingSubprocess'];
+ deleteAttribute(quantmeCuttingSubprocess, bpmnPrefix, quantmePrefix);
+ let quantmeHardwareSelectionSubprocess = subprocess[quantmePrefix + ':quantumHardwareSelectionSubprocess'];
+ deleteAttribute(quantmeHardwareSelectionSubprocess, bpmnPrefix, quantmePrefix);
+ }
+ return subprocessElements;
+ } else {
+ let subprocesses = subprocessElements[bpmnPrefix + ':subProcess'];
+ deleteAttribute(subprocesses, bpmnPrefix, quantmePrefix);
+ let quantmeCuttingSubprocess = subprocessElements[quantmePrefix + ':circuitCuttingSubprocess'];
+ deleteAttribute(quantmeCuttingSubprocess, bpmnPrefix, quantmePrefix);
+ let quantmeHardwareSelectionSubprocess = subprocessElements[quantmePrefix + ':quantumHardwareSelectionSubprocess'];
+ deleteAttribute(quantmeHardwareSelectionSubprocess, bpmnPrefix, quantmePrefix);
+ return subprocessElements;
+ }
+}
+
+function deleteAttribute(element, bpmnPrefix, quantmePrefix) {
+ if (element !== undefined) {
+ let attributes = element['_attributes'];
+ if (attributes !== undefined) {
+ delete attributes.isExpanded;
+ }
+ removeIsExpandedAttribute(element, bpmnPrefix, quantmePrefix);
+ }
+}
diff --git a/components/bpmn-q/modeler-component/extensions/quantme/replacement/hardware-selection/QuantMEHardwareSelectionHandler.js b/components/bpmn-q/modeler-component/extensions/quantme/replacement/hardware-selection/QuantMEHardwareSelectionHandler.js
index 3590d67ba..4cde893a0 100644
--- a/components/bpmn-q/modeler-component/extensions/quantme/replacement/hardware-selection/QuantMEHardwareSelectionHandler.js
+++ b/components/bpmn-q/modeler-component/extensions/quantme/replacement/hardware-selection/QuantMEHardwareSelectionHandler.js
@@ -9,7 +9,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import {getQuantMETasks} from '../QuantMETransformator';
+import { getQuantMETasks } from '../QuantMETransformator';
import {
INVOKE_NISQ_ANALYZER_SCRIPT,
INVOKE_TRANSFORMATION_SCRIPT, POLL_FOR_TRANSFORMATION_SCRIPT,
@@ -18,11 +18,11 @@ import {
SELECT_ON_QUEUE_SIZE_SCRIPT
} from './HardwareSelectionScripts';
import * as consts from '../../Constants';
-import {addExtensionElements} from '../../../../editor/util/camunda-utils/ExtensionElementsUtil';
-import {createTempModelerFromXml, createPlainModeler} from '../../../../editor/ModelerHandler';
-import {getPropertiesToCopy, insertShape} from '../../../../editor/util/TransformationUtilities';
-import {getCamundaInputOutput, getExtensionElements, getRootProcess} from '../../../../editor/util/ModellingUtilities';
-import {getXml} from '../../../../editor/util/IoUtilities';
+import { addExtensionElements } from '../../../../editor/util/camunda-utils/ExtensionElementsUtil';
+import { createTempModelerFromXml, createModeler } from '../../../../editor/ModelerHandler';
+import { getPropertiesToCopy, insertShape } from '../../../../editor/util/TransformationUtilities';
+import { getCamundaInputOutput, getExtensionElements, getRootProcess } from '../../../../editor/util/ModellingUtilities';
+import { getXml } from '../../../../editor/util/IoUtilities';
/**
* Replace the given QuantumHardwareSelectionSubprocess by a native subprocess orchestrating the hardware selection
@@ -36,7 +36,7 @@ export async function replaceHardwareSelectionSubprocess(subprocess, parent, mod
let moddle = modeler.get('moddle');
// replace QuantumHardwareSelectionSubprocess with traditional subprocess
- let element = bpmnReplace.replaceElement(elementRegistry.get(subprocess.id), {type: 'bpmn:SubProcess'});
+ let element = bpmnReplace.replaceElement(elementRegistry.get(subprocess.id), { type: 'bpmn:SubProcess' });
// update the properties of the new element
modeling.updateProperties(element, getPropertiesToCopy(subprocess));
@@ -48,7 +48,6 @@ export async function replaceHardwareSelectionSubprocess(subprocess, parent, mod
// retrieve business object of the new element
let bo = elementRegistry.get(element.id).businessObject;
- bo.di.isExpanded = true;
// extract workflow fragment within the QuantumHardwareSelectionSubprocess
let hardwareSelectionFragment = await getHardwareSelectionFragment(bo);
@@ -57,20 +56,20 @@ export async function replaceHardwareSelectionSubprocess(subprocess, parent, mod
bo.flowElements = [];
// add start event for the new subprocess
- let startEvent = modeling.createShape({type: 'bpmn:StartEvent'}, {x: 50, y: 50}, element, {});
+ let startEvent = modeling.createShape({ type: 'bpmn:StartEvent' }, { x: 50, y: 50 }, element, {});
let startEventBo = elementRegistry.get(startEvent.id).businessObject;
startEventBo.name = 'Start Hardware Selection Subprocess';
// add gateway to avoid multiple hardware selections for the same circuit
- let splittingGateway = modeling.createShape({type: 'bpmn:ExclusiveGateway'}, {x: 50, y: 50}, element, {});
+ let splittingGateway = modeling.createShape({ type: 'bpmn:ExclusiveGateway' }, { x: 50, y: 50 }, element, {});
let splittingGatewayBo = elementRegistry.get(splittingGateway.id).businessObject;
splittingGatewayBo.name = 'Hardware already selected?';
// connect start event and gateway
- modeling.connect(startEvent, splittingGateway, {type: 'bpmn:SequenceFlow'});
+ modeling.connect(startEvent, splittingGateway, { type: 'bpmn:SequenceFlow' });
// add task to invoke the NISQ Analyzer and connect it
- let invokeHardwareSelection = modeling.createShape({type: 'bpmn:ScriptTask'}, {x: 50, y: 50}, element, {});
+ let invokeHardwareSelection = modeling.createShape({ type: 'bpmn:ScriptTask' }, { x: 50, y: 50 }, element, {});
let invokeHardwareSelectionBo = elementRegistry.get(invokeHardwareSelection.id).businessObject;
invokeHardwareSelectionBo.name = 'Invoke NISQ Analyzer';
invokeHardwareSelectionBo.scriptFormat = 'groovy';
@@ -106,7 +105,7 @@ export async function replaceHardwareSelectionSubprocess(subprocess, parent, mod
);
// connect gateway with selection path and add condition
- let selectionFlow = modeling.connect(splittingGateway, invokeHardwareSelection, {type: 'bpmn:SequenceFlow'});
+ let selectionFlow = modeling.connect(splittingGateway, invokeHardwareSelection, { type: 'bpmn:SequenceFlow' });
let selectionFlowBo = elementRegistry.get(selectionFlow.id).businessObject;
selectionFlowBo.name = 'no';
let selectionFlowCondition = bpmnFactory.create('bpmn:FormalExpression');
@@ -120,26 +119,26 @@ export async function replaceHardwareSelectionSubprocess(subprocess, parent, mod
}
let selectionTaskBo = elementRegistry.get(selectionTask.id).businessObject;
selectionTaskBo.asyncBefore = true;
- modeling.connect(invokeHardwareSelection, selectionTask, {type: 'bpmn:SequenceFlow'});
+ modeling.connect(invokeHardwareSelection, selectionTask, { type: 'bpmn:SequenceFlow' });
// add task implementing the transformation of the QuantME modeling constructs within the QuantumHardwareSelectionSubprocess
console.log('Adding extracted workflow fragment XML: ', hardwareSelectionFragment);
- let retrieveFragment = modeling.createShape({type: 'bpmn:ScriptTask'}, {x: 50, y: 50}, element, {});
+ let retrieveFragment = modeling.createShape({ type: 'bpmn:ScriptTask' }, { x: 50, y: 50 }, element, {});
let retrieveFragmentBo = elementRegistry.get(retrieveFragment.id).businessObject;
retrieveFragmentBo.name = 'Retrieve Fragment to Transform';
retrieveFragmentBo.scriptFormat = 'groovy';
retrieveFragmentBo.script = RETRIEVE_FRAGMENT_SCRIPT_PREFIX + hardwareSelectionFragment + RETRIEVE_FRAGMENT_SCRIPT_SUFFIX;
retrieveFragmentBo.asyncBefore = true;
- modeling.connect(selectionTask, retrieveFragment, {type: 'bpmn:SequenceFlow'});
+ modeling.connect(selectionTask, retrieveFragment, { type: 'bpmn:SequenceFlow' });
// add task implementing the transformation of the QuantME modeling constructs within the QuantumHardwareSelectionSubprocess
- let invokeTransformation = modeling.createShape({type: 'bpmn:ScriptTask'}, {x: 50, y: 50}, element, {});
+ let invokeTransformation = modeling.createShape({ type: 'bpmn:ScriptTask' }, { x: 50, y: 50 }, element, {});
let invokeTransformationBo = elementRegistry.get(invokeTransformation.id).businessObject;
invokeTransformationBo.name = 'Invoke Transformation Framework';
invokeTransformationBo.scriptFormat = 'groovy';
invokeTransformationBo.script = INVOKE_TRANSFORMATION_SCRIPT;
invokeTransformationBo.asyncBefore = true;
- modeling.connect(retrieveFragment, invokeTransformation, {type: 'bpmn:SequenceFlow'});
+ modeling.connect(retrieveFragment, invokeTransformation, { type: 'bpmn:SequenceFlow' });
// add Transformation Framework endpoint as input parameter
let invokeTransformationInOut = getCamundaInputOutput(invokeTransformationBo, bpmnFactory);
@@ -157,20 +156,20 @@ export async function replaceHardwareSelectionSubprocess(subprocess, parent, mod
);
// add task to poll for the results of the transformation and deployment
- let pollForTransformation = modeling.createShape({type: 'bpmn:ScriptTask'}, {x: 50, y: 50}, element, {});
+ let pollForTransformation = modeling.createShape({ type: 'bpmn:ScriptTask' }, { x: 50, y: 50 }, element, {});
let pollForTransformationBo = elementRegistry.get(pollForTransformation.id).businessObject;
pollForTransformationBo.name = 'Poll for Transformation and Deployment';
pollForTransformationBo.scriptFormat = 'groovy';
pollForTransformationBo.script = POLL_FOR_TRANSFORMATION_SCRIPT;
pollForTransformationBo.asyncBefore = true;
- modeling.connect(invokeTransformation, pollForTransformation, {type: 'bpmn:SequenceFlow'});
+ modeling.connect(invokeTransformation, pollForTransformation, { type: 'bpmn:SequenceFlow' });
// join control flow
- let joiningGateway = modeling.createShape({type: 'bpmn:ExclusiveGateway'}, {x: 50, y: 50}, element, {});
- modeling.connect(pollForTransformation, joiningGateway, {type: 'bpmn:SequenceFlow'});
+ let joiningGateway = modeling.createShape({ type: 'bpmn:ExclusiveGateway' }, { x: 50, y: 50 }, element, {});
+ modeling.connect(pollForTransformation, joiningGateway, { type: 'bpmn:SequenceFlow' });
// add connection from splitting to joining gateway and add condition
- let alreadySelectedFlow = modeling.connect(splittingGateway, joiningGateway, {type: 'bpmn:SequenceFlow'});
+ let alreadySelectedFlow = modeling.connect(splittingGateway, joiningGateway, { type: 'bpmn:SequenceFlow' });
let alreadySelectedFlowBo = elementRegistry.get(alreadySelectedFlow.id).businessObject;
alreadySelectedFlowBo.name = 'yes';
let alreadySelectedFlowCondition = bpmnFactory.create('bpmn:FormalExpression');
@@ -178,16 +177,16 @@ export async function replaceHardwareSelectionSubprocess(subprocess, parent, mod
alreadySelectedFlowBo.conditionExpression = alreadySelectedFlowCondition;
// add call activity invoking the dynamically transformed and deployed workflow fragment
- let invokeTransformedFragment = modeling.createShape({type: 'bpmn:CallActivity'}, {x: 50, y: 50}, element, {});
+ let invokeTransformedFragment = modeling.createShape({ type: 'bpmn:CallActivity' }, { x: 50, y: 50 }, element, {});
let invokeTransformedFragmentBo = elementRegistry.get(invokeTransformedFragment.id).businessObject;
invokeTransformedFragmentBo.name = 'Invoke Transformed Fragment';
invokeTransformedFragmentBo.calledElement = '${fragment_endpoint}';
invokeTransformedFragmentBo.calledElementBinding = 'latest';
invokeTransformedFragmentBo.asyncBefore = true;
- modeling.connect(joiningGateway, invokeTransformedFragment, {type: 'bpmn:SequenceFlow'});
+ modeling.connect(joiningGateway, invokeTransformedFragment, { type: 'bpmn:SequenceFlow' });
// pass all variables between the caller and callee workflow
- addExtensionElements(invokeTransformedFragmentBo, invokeTransformedFragmentBo, bpmnFactory.create('camunda:In'), bpmnFactory, commandStack);
+ addExtensionElements(invokeTransformedFragment, invokeTransformedFragmentBo, bpmnFactory.create('camunda:In'), bpmnFactory, commandStack);
let extensionElements = getExtensionElements(invokeTransformedFragmentBo, moddle);
let invokeTransformedFragmentIn = extensionElements.values[0];
let invokeTransformedFragmentOut = bpmnFactory.create('camunda:Out');
@@ -197,10 +196,10 @@ export async function replaceHardwareSelectionSubprocess(subprocess, parent, mod
invokeTransformedFragmentBo.extensionElements = extensionElements;
// add end event for the new subprocess
- let endEvent = modeling.createShape({type: 'bpmn:EndEvent'}, {x: 50, y: 50}, element, {});
+ let endEvent = modeling.createShape({ type: 'bpmn:EndEvent' }, { x: 50, y: 50 }, element, {});
let endEventBo = elementRegistry.get(endEvent.id).businessObject;
endEventBo.name = 'Terminate Hardware Selection Subprocess';
- modeling.connect(invokeTransformedFragment, endEvent, {type: 'bpmn:SequenceFlow'});
+ modeling.connect(invokeTransformedFragment, endEvent, { type: 'bpmn:SequenceFlow' });
return true;
}
@@ -221,7 +220,7 @@ export async function configureBasedOnHardwareSelection(xml, provider, qpu, circ
const rootElement = getRootProcess(modeler.getDefinitions());
if (typeof rootElement === 'undefined') {
console.log('Unable to retrieve root process element from definitions!');
- return {status: 'failed', cause: 'Unable to retrieve root process element from definitions!'};
+ return { status: 'failed', cause: 'Unable to retrieve root process element from definitions!' };
}
rootElement.isExecutable = true;
@@ -244,7 +243,7 @@ export async function configureBasedOnHardwareSelection(xml, provider, qpu, circ
}
}
- return {status: 'success', xml: await getXml(modeler)};
+ return { status: 'success', xml: await getXml(modeler) };
}
/**
@@ -271,7 +270,7 @@ function addSelectionStrategyTask(selectionStrategy, parent, elementRegistry, mo
* Add a task implementing the Shortest-Queue selection strategy
*/
function addShortestQueueSelectionStrategy(parent, elementRegistry, modeling) {
- let task = modeling.createShape({type: 'bpmn:ScriptTask'}, {x: 50, y: 50}, parent, {});
+ let task = modeling.createShape({ type: 'bpmn:ScriptTask' }, { x: 50, y: 50 }, parent, {});
let taskBo = elementRegistry.get(task.id).businessObject;
taskBo.name = 'Selecting based on Queue Size';
taskBo.scriptFormat = 'groovy';
@@ -283,7 +282,7 @@ async function getHardwareSelectionFragment(subprocess) {
console.log('Extracting workflow fragment from subprocess: ', subprocess);
// create new modeler to extract the XML of the workflow fragment
- let modeler = createPlainModeler();
+ let modeler = createModeler();
let elementRegistry = modeler.get('elementRegistry');
let bpmnReplace = modeler.get('bpmnReplace');
let modeling = modeler.get('modeling');
@@ -305,13 +304,13 @@ async function getHardwareSelectionFragment(subprocess) {
let rootElementBo = elementRegistry.get(rootElement.id);
// add start and end event to the new process
- let startEvent = bpmnReplace.replaceElement(elementRegistry.get(rootElement.flowElements[0].id), {type: 'bpmn:StartEvent'});
- let endEvent = modeling.createShape({type: 'bpmn:EndEvent'}, {x: 50, y: 50}, rootElementBo, {});
+ let startEvent = bpmnReplace.replaceElement(elementRegistry.get(rootElement.flowElements[0].id), { type: 'bpmn:StartEvent' });
+ let endEvent = modeling.createShape({ type: 'bpmn:EndEvent' }, { x: 50, y: 50 }, rootElementBo, {});
// insert given subprocess and connect to start and end event
let insertedSubprocess = insertShape(definitions, rootElementBo, subprocess, {}, false, modeler).element;
- modeling.connect(startEvent, insertedSubprocess, {type: 'bpmn:SequenceFlow'});
- modeling.connect(insertedSubprocess, endEvent, {type: 'bpmn:SequenceFlow'});
+ modeling.connect(startEvent, insertedSubprocess, { type: 'bpmn:SequenceFlow' });
+ modeling.connect(insertedSubprocess, endEvent, { type: 'bpmn:SequenceFlow' });
// export xml and remove line breaks
let xml = await getXml(modeler);
diff --git a/components/bpmn-q/modeler-component/extensions/quantme/replacement/layouter/Layouter.js b/components/bpmn-q/modeler-component/extensions/quantme/replacement/layouter/Layouter.js
index ef1035d5b..27f70df1e 100644
--- a/components/bpmn-q/modeler-component/extensions/quantme/replacement/layouter/Layouter.js
+++ b/components/bpmn-q/modeler-component/extensions/quantme/replacement/layouter/Layouter.js
@@ -9,8 +9,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import {getDi, is} from 'bpmn-js/lib/util/ModelUtil';
-import {isFlowLikeElement} from '../../../../editor/util/ModellingUtilities';
+import { getDi, is } from 'bpmn-js/lib/util/ModelUtil';
+import { isFlowLikeElement } from '../../../../editor/util/ModellingUtilities';
// space between multiple boundary events of a task/subprocess
let BOUNDARY_EVENT_MARGIN = '10';
@@ -56,15 +56,6 @@ function layoutProcess(modeling, elementRegistry, process) {
// layout elements in subprocess
if (['bpmn:SubProcess', 'quantme:QuantumHardwareSelectionSubprocess', 'quantme:CircuitCuttingSubprocess'].includes(flowElements[i].$type)) {
console.log('Flow element is subprocess. Layouting contained elements...');
- const flowElement = elementRegistry.get(flowElements[i].id);
- let oldBounds = getDi(flowElement).bounds;
- modeling.resizeShape(elementRegistry.get(flowElements[i].id), {
- x: oldBounds.x,
- y: oldBounds.y,
- height: 10,
- width: 10
- });
-
layoutProcess(modeling, elementRegistry, elementRegistry.get(flowElements[i].id).businessObject);
}
@@ -97,7 +88,7 @@ function layoutProcess(modeling, elementRegistry, process) {
nodes.push(elementRegistry.get(artifact.id));
if (artifact.$type === 'bpmn:Association') {
- edges.push({id: artifact.id, sourceId: artifact.sourceRef.id, targetId: artifact.targetRef.id});
+ edges.push({ id: artifact.id, sourceId: artifact.sourceRef.id, targetId: artifact.targetRef.id });
}
}
}
@@ -140,7 +131,7 @@ function layoutBoundaryEvents(modeling, elementRegistry) {
let offset = (attachedToElementBoundaries.length + 1) * (parseInt(boundaryEventBounds.width) + parseInt(BOUNDARY_EVENT_MARGIN));
let to_move_x = bottomOfAttached - offset;
let to_move_y = attachedToBounds.y - boundaryEventBounds.y + attachedToBounds.height - boundaryEventBounds.height / 2;
- modeling.moveShape(boundaryEventShape, {x: to_move_x, y: to_move_y});
+ modeling.moveShape(boundaryEventShape, { x: to_move_x, y: to_move_y });
// update list for the next boundary event
attachedToElementBoundaries.push(boundaryEventShape.id);
@@ -155,7 +146,7 @@ function layoutBoundaryEvents(modeling, elementRegistry) {
let sourceX = boundaryEventBounds.x + boundaryEventBounds.width / 2;
let sourceY = boundaryEventBounds.y + boundaryEventBounds.height;
waypoints.shift();
- waypoints.unshift({x: sourceX, y: sourceY});
+ waypoints.unshift({ x: sourceX, y: sourceY });
// update diagram
modeling.updateWaypoints(connectionShape, waypoints);
@@ -234,7 +225,7 @@ function adaptLabels(modeling, connection) {
// place the first label of the given connection
let firstLabel = connection.labels[0];
let middle = getMiddleOfLocation(connection, firstLabel);
- modeling.moveElements([firstLabel], {x: middle.x - firstLabel.x, y: middle.y - firstLabel.y});
+ modeling.moveElements([firstLabel], { x: middle.x - firstLabel.x, y: middle.y - firstLabel.y });
}
// TODO: handle cases with multiple labels defined for the connection
@@ -255,11 +246,11 @@ function getMiddleOfLocation(connection, label) {
let middlePoint2 = waypoints[middleWaypointIndex];
if (middlePoint1.x === middlePoint2.x) {
- return {x: middlePoint1.x - LABEL_MARGIN - parseInt(label.width), y: (middlePoint1.y + middlePoint2.y) / 2};
+ return { x: middlePoint1.x - LABEL_MARGIN - parseInt(label.width), y: (middlePoint1.y + middlePoint2.y) / 2 };
}
if (middlePoint1.y === middlePoint2.y) {
- return {x: (middlePoint1.x + middlePoint2.x) / 2, y: middlePoint1.y - LABEL_MARGIN - parseInt(label.height)};
+ return { x: (middlePoint1.x + middlePoint2.x) / 2, y: middlePoint1.y - LABEL_MARGIN - parseInt(label.height) };
}
return {
@@ -356,7 +347,7 @@ function getEdgeFromFlowElement(elementRegistry, flowElement) {
sourceElement = sourceElement.attachedToRef;
}
- return {id: flowElement.id, sourceId: sourceElement.id, targetId: flowElement.targetRef.id};
+ return { id: flowElement.id, sourceId: sourceElement.id, targetId: flowElement.targetRef.id };
}
/**
@@ -372,14 +363,14 @@ function layoutWithDagre(modeling, elementRegistry, dagre, tasks, flows, options
console.log('Adding %i tasks to the graph for layouting: ', tasks.length);
for (let i = 0; i < tasks.length; i++) {
let task = tasks[i];
- g.setNode(task.id, {label: task.id, width: task.width, height: task.height});
+ g.setNode(task.id, { label: task.id, width: task.width, height: task.height });
}
// add flows as edges to the graph
console.log('Adding %i flows to the graph for layouting: ', flows.length);
for (let i = 0; i < flows.length; i++) {
let flow = flows[i];
- g.setEdge(flow['sourceId'], flow['targetId'], {label: flow['id']});
+ g.setEdge(flow['sourceId'], flow['targetId'], { label: flow['id'] });
}
// layout the graph
@@ -393,7 +384,7 @@ function layoutWithDagre(modeling, elementRegistry, dagre, tasks, flows, options
// determine new position of task and move it there
let to_move_x = node.x - element.x - element.width / 2;
let to_move_y = node.y - element.y - element.height / 2;
- let delta_string = {x: to_move_x, y: to_move_y};
+ let delta_string = { x: to_move_x, y: to_move_y };
modeling.moveElements([element], delta_string);
});
@@ -402,18 +393,21 @@ function layoutWithDagre(modeling, elementRegistry, dagre, tasks, flows, options
let edge = g.edge(e);
let points = edge.points;
let element = elementRegistry.get(edge.label);
- let waypoints = element.waypoints;
+ if (element !== undefined) {
+ let waypoints = element.waypoints;
- while (waypoints.length > 0) {
- waypoints.pop();
- }
+ while (waypoints.length > 0) {
+ waypoints.pop();
+ }
- for (let pointsIndex = 0; pointsIndex < points.length; pointsIndex++) {
- let point;
- point = {x: points[pointsIndex].x, y: points[pointsIndex].y};
- waypoints.push(point);
- }
+ for (let pointsIndex = 0; pointsIndex < points.length; pointsIndex++) {
+ let point;
+ point = { x: points[pointsIndex].x, y: points[pointsIndex].y };
+ waypoints.push(point);
+ }
- element.waypoints = waypoints;
+ element.waypoints = waypoints;
+ }
});
+
}
diff --git a/components/bpmn-q/package-lock.json b/components/bpmn-q/package-lock.json
index 71709e287..558d9eeed 100644
--- a/components/bpmn-q/package-lock.json
+++ b/components/bpmn-q/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@planqk/quantum-workflow-modeler",
- "version": "0.1.8",
+ "version": "0.1.9",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@planqk/quantum-workflow-modeler",
- "version": "0.1.8",
+ "version": "0.1.9",
"license": "Apache-2.0",
"dependencies": {
"@bpmn-io/properties-panel": "^1.3.1",
@@ -39,7 +39,9 @@
"timers": "^0.1.1",
"tiny-svg": "^3.0.0",
"webpack": "^5.75.0",
- "whatwg-fetch": "^3.6.2"
+ "whatwg-fetch": "^3.6.2",
+ "xml-js": "^1.6.11",
+ "xpath": "^0.0.32"
},
"devDependencies": {
"@babel/core": "^7.20.12",
@@ -4112,9 +4114,9 @@
"link": true
},
"node_modules/bpmnlint-utils": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/bpmnlint-utils/-/bpmnlint-utils-1.0.2.tgz",
- "integrity": "sha512-+ti0VICOpgYpQgzpF0mwXqDX4NhrQCc2YKy28VjPu7v9sXpdpWEylP+iBw6WlheJFRcXoZh2q/QRLb5DMB3naQ==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/bpmnlint-utils/-/bpmnlint-utils-1.1.1.tgz",
+ "integrity": "sha512-Afdb77FmwNB3INyUfbzXW40yY+mc0qYU3SgDFeI4zTtduiVomOlfqoXiEaUIGI8Hyh7aVYpmf3O97P2w7x0DYQ==",
"dev": true
},
"node_modules/bpmnlint/node_modules/bpmn-moddle": {
@@ -13457,9 +13459,7 @@
"node_modules/sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
- "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
- "dev": true,
- "optional": true
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"node_modules/saxen": {
"version": "8.1.2",
@@ -16082,6 +16082,17 @@
"node": ">=0.10.0"
}
},
+ "node_modules/xml-js": {
+ "version": "1.6.11",
+ "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
+ "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
+ "dependencies": {
+ "sax": "^1.2.4"
+ },
+ "bin": {
+ "xml-js": "bin/cli.js"
+ }
+ },
"node_modules/xmlbuilder": {
"version": "15.1.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
@@ -16101,6 +16112,14 @@
"node": ">=0.1"
}
},
+ "node_modules/xpath": {
+ "version": "0.0.32",
+ "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz",
+ "integrity": "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==",
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
@@ -19347,9 +19366,9 @@
}
},
"bpmnlint-utils": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/bpmnlint-utils/-/bpmnlint-utils-1.0.2.tgz",
- "integrity": "sha512-+ti0VICOpgYpQgzpF0mwXqDX4NhrQCc2YKy28VjPu7v9sXpdpWEylP+iBw6WlheJFRcXoZh2q/QRLb5DMB3naQ==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/bpmnlint-utils/-/bpmnlint-utils-1.1.1.tgz",
+ "integrity": "sha512-Afdb77FmwNB3INyUfbzXW40yY+mc0qYU3SgDFeI4zTtduiVomOlfqoXiEaUIGI8Hyh7aVYpmf3O97P2w7x0DYQ==",
"dev": true
},
"brace-expansion": {
@@ -28658,6 +28677,14 @@
"integrity": "sha512-dTaaRwm4ccF8UF15/PLT3pNNlZP04qko/FUcr0QBppYLk8+J7xA9gg2vI2X4Kr1PcJAVxwI9NdADex29FX2QVQ==",
"dev": true
},
+ "xml-js": {
+ "version": "1.6.11",
+ "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
+ "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
+ "requires": {
+ "sax": "^1.2.4"
+ }
+ },
"xmlbuilder": {
"version": "15.1.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
@@ -28670,6 +28697,11 @@
"integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==",
"dev": true
},
+ "xpath": {
+ "version": "0.0.32",
+ "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz",
+ "integrity": "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw=="
+ },
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
diff --git a/components/bpmn-q/package.json b/components/bpmn-q/package.json
index 621f11e41..6e6cf745b 100644
--- a/components/bpmn-q/package.json
+++ b/components/bpmn-q/package.json
@@ -1,6 +1,6 @@
{
"name": "@planqk/quantum-workflow-modeler",
- "version": "0.1.8",
+ "version": "0.1.9",
"description": "",
"package name": "@PlanQK/workflow-modeler",
"main": "index.js",
@@ -53,7 +53,9 @@
"timers": "^0.1.1",
"tiny-svg": "^3.0.0",
"webpack": "^5.75.0",
- "whatwg-fetch": "^3.6.2"
+ "whatwg-fetch": "^3.6.2",
+ "xml-js": "^1.6.11",
+ "xpath": "^0.0.32"
},
"devDependencies": {
"@babel/core": "^7.20.12",
diff --git a/components/bpmn-q/test/test-setup/QuantMEDataConfigurationsServer.js b/components/bpmn-q/test/test-setup/QuantMEDataConfigurationsServer.js
index ea40e0538..4e2b16204 100644
--- a/components/bpmn-q/test/test-setup/QuantMEDataConfigurationsServer.js
+++ b/components/bpmn-q/test/test-setup/QuantMEDataConfigurationsServer.js
@@ -1,3 +1,7 @@
+/*
+THIS CLASS IS DEPRECATED AND SHALL ONLY BE USED FOR TESTING NEW SVG ICON SIZES
+ */
+
const http = require('http');
const server = http.createServer((req, res) => {
@@ -25,6 +29,7 @@ const server = http.createServer((req, res) => {
});
server.listen(8100, () => {
+ // console.log(JSON.stringify(quantmeDataObjects));
console.log('Server listening on http://localhost:8100/');
});
@@ -36,8 +41,8 @@ const quantmeDataObjects = [
appliesTo: "dataflow:DataMapObject",
groupLabel: 'Quantum Circuit Info',
icon: {
- transform: 'matrix(0.13, 0, 0, 0.13, 5, 5)',
- svg: ' ',
+ transform: 'matrix(0.14, 0, 0, 0.14, 5, 5)',
+ svg: ' ',
},
attributes: [
{
@@ -69,7 +74,7 @@ const quantmeDataObjects = [
appliesTo: "dataflow:DataMapObject",
groupLabel: 'Result',
icon: {
- transform: 'matrix(0.22, 0, 0, 0.22, 3, 3)',
+ transform: 'matrix(0.24, 0, 0, 0.24, 3, 3)',
svg: ' ',
},
attributes: [
@@ -84,4 +89,70 @@ const quantmeDataObjects = [
},
]
},
+ {
+ name: 'Evaluation Result Object',
+ id: 'Evaluation-Result-Object',
+ description: "data object to transfer the evaluated results of quantum computations",
+ appliesTo: "dataflow:DataMapObject",
+ groupLabel: 'Evaluation Result',
+ icon: {
+ transform: 'matrix(0.09, 0, 0, 0.09, 3, 3)',
+ svg: ' ',
+ },
+ attributes: [
+ {
+ name: 'Evaluation Result',
+ label: 'Evaluation Result',
+ type: 'string',
+ bindTo: {
+ name: 'content',
+ type: 'KeyValueMap',
+ },
+ },
+ ]
+ },
+ {
+ name: 'Parameterization Object',
+ id: 'Parameterization-Object',
+ description: "data object to transfer optimization parameters, e.g., in variational quantum algorithms",
+ appliesTo: "dataflow:DataMapObject",
+ groupLabel: 'Parameterization',
+ icon: {
+ transform: 'matrix(0.12, 0, 0, 0.12, 6, 4)',
+ svg: '𝜶 𝜷 ',
+ },
+ attributes: [
+ {
+ name: 'Parameterization',
+ label: 'Parameterization',
+ type: 'string',
+ bindTo: {
+ name: 'content',
+ type: 'KeyValueMap',
+ },
+ },
+ ]
+ },
+ {
+ name: 'Initial State Object',
+ id: 'Initial-State-Object',
+ description: "data object to transfer the initial state used to warm-start a quantum computations",
+ appliesTo: "dataflow:DataMapObject",
+ groupLabel: 'Initial State',
+ icon: {
+ transform: 'matrix(0.16, 0, 0, 0.16, 6, 3)',
+ svg: '0 | ⟩ 1 | ⟩ ',
+ },
+ attributes: [
+ {
+ name: 'Initial State',
+ label: 'Initial State',
+ type: 'string',
+ bindTo: {
+ name: 'content',
+ type: 'KeyValueMap',
+ },
+ },
+ ]
+ },
];
\ No newline at end of file
diff --git a/components/bpmn-q/test/tests/editor/plugin.spec.js b/components/bpmn-q/test/tests/editor/plugin.spec.js
index ca1ccfc7a..b8ec76cab 100644
--- a/components/bpmn-q/test/tests/editor/plugin.spec.js
+++ b/components/bpmn-q/test/tests/editor/plugin.spec.js
@@ -56,7 +56,7 @@ describe('Test plugins', function () {
expect(extensions['planqk']).to.not.be.undefined;
expect(transfButtons.length).to.equal(3);
expect(buttons.length).to.equal(3);
- expect(tabs.length).to.equal(9);
+ expect(tabs.length).to.equal(4);
expect(styles.length).to.equal(4);
});
});
diff --git a/components/bpmn-q/test/tests/helpers/DiagramHelper.js b/components/bpmn-q/test/tests/helpers/DiagramHelper.js
index 410ff88a3..58f6a00bd 100644
--- a/components/bpmn-q/test/tests/helpers/DiagramHelper.js
+++ b/components/bpmn-q/test/tests/helpers/DiagramHelper.js
@@ -286,4 +286,5 @@ export const validQuantMEDiagram = ' ]*>/g;
+ const matches = xmlString.match(regexPattern);
+
+ let expanded = [];
+
+ // Regular expression pattern to extract bpmndi:BPMNShape elements
+ const shapeRegexPattern = /]*>/g;
+
+ // Regular expression pattern to extract isExpanded attribute value
+ const isExpandedRegexPattern = /isExpanded="([^"]+)"/;
+
+ // Extract the bpmndi:BPMNShape elements using the regular expression
+ const shapeMatches = xmlString.match(shapeRegexPattern);
+
+ // Loop through the shape matches and extract the isExpanded attribute
+ for (let i = 0; i < shapeMatches.length; i++) {
+ const shapeMatch = shapeMatches[i];
+
+ // Extract the bpmnElement attribute value
+ const bpmnElementMatch = shapeMatch.match(/bpmnElement="([^"]+)"/);
+ if (bpmnElementMatch && bpmnElementMatch.length > 1) {
+ const bpmnElement = bpmnElementMatch[1];
+
+ // Extract the isExpanded attribute value
+ const isExpandedMatch = shapeMatch.match(isExpandedRegexPattern);
+ let positionCircuitCutting = xmlString.search('circuitCuttingSubprocess id="' + bpmnElement + '"');
+ let positionSubProcess = xmlString.search('subProcess id="' + bpmnElement + '"');
+
+ if (positionCircuitCutting > -1 || positionSubProcess > -1) {
+ if (isExpandedMatch && isExpandedMatch.length > 1) {
+ const isExpanded = isExpandedMatch[1];
+
+ if (isExpanded !== undefined) {
+ expanded.push(isExpanded);
+ }
+ } else {
+ expanded.push('false');
+ }
+ }
+ }
+ }
+ return expanded;
+}
\ No newline at end of file
diff --git a/components/bpmn-q/webpack.config.js b/components/bpmn-q/webpack.config.js
index 2d8869012..932447456 100644
--- a/components/bpmn-q/webpack.config.js
+++ b/components/bpmn-q/webpack.config.js
@@ -69,22 +69,23 @@ module.exports = {
}),
// use the default values if environment variable does not exist
new webpack.EnvironmentPlugin({
+ AUTOSAVE_INTERVAL: 300000,
AWS_RUNTIME_HANDLER_ENDPOINT: 'http://localhost:8890',
- CAMUNDA_ENDPOINT: 'http://localhost:8080/engine-rest',
- DATA_CONFIG: 'http://localhost:8100/data-objects',
- PROVENANCE_COLLECTION: false,
+ CAMUNDA_ENDPOINT: 'http://localhost:8090/engine-rest',
+ DATA_CONFIG: 'https://raw.githubusercontent.com/PlanQK/workflow-modeler/master/components/bpmn-q/modeler-component/extensions/quantme/configurations/quantmeDataObjects.json',
DOWNLOAD_FILE_NAME: 'quantum-workflow-model',
ENABLE_DATA_FLOW_PLUGIN: true,
ENABLE_PLANQK_PLUGIN: true,
ENABLE_QHANA_PLUGIN: true,
ENABLE_QUANTME_PLUGIN: true,
ENABLE_OPENTOSCA_PLUGIN: true,
- OPENTOSCA_ENDPOINT: 'http://localhost:1337/csars',
GITHUB_TOKEN: '',
+ OPENTOSCA_ENDPOINT: 'http://localhost:1337/csars',
NISQ_ANALYZER_ENDPOINT: 'http://localhost:8098/nisq-analyzer',
- QISKIT_RUNTIME_HANDLER_ENDPOINT: 'http://localhost:8889',
+ PROVENANCE_COLLECTION: false,
QHANA_GET_PLUGIN_URL: 'http://localhost:5006/api/plugins/',
QHANA_LIST_PLUGINS_URL: 'http://localhost:5006/api/plugins/?item-count=100',
+ QISKIT_RUNTIME_HANDLER_ENDPOINT: 'http://localhost:8889',
QRM_USERNAME: '',
QRM_REPONAME: '',
QRM_REPOPATH: '',
diff --git a/doc/quantum-workflow-modeler/extensions/plugins.md b/doc/quantum-workflow-modeler/extensions/plugins.md
new file mode 100644
index 000000000..bc9462c54
--- /dev/null
+++ b/doc/quantum-workflow-modeler/extensions/plugins.md
@@ -0,0 +1,57 @@
+# Plugin Dependencies
+Plugin dependencies are a way to establish relationships between different plugins.
+A plugin dependency indicates that one plugin relies on another plugin to function properly. By defining plugin dependencies, you ensure that the required plugins are loaded and available before using a particular plugin.
+
+To add plugin dependencies, you typically need to follow these steps:
+
+1. Identify the plugins: Determine which plugins in your system have dependencies. Identify the plugins that require other plugins to be present and functional.
+
+2. Define & update the dependencies: For each plugin with dependencies, specify the required plugins. This can be done by associating the dependent plugin with the required plugins, i.e., by adding the plugin id to the dependency attribute.
+
+The following code snippet demonstrates how to define plugin dependencies using an array of plugins.
+Exemplarily, we specify that the `QuantMEPlugin` depends on the `QHAanaPlugin`.
+
+```javascript
+const PLUGINS = [
+ {
+ plugin: DataFlowPlugin,
+ dependencies: []
+ },
+ {
+ plugin: QHAnaPlugin,
+ dependencies: []
+ },
+ {
+ plugin: PlanQKPlugin,
+ dependencies: []
+ },
+ {
+ plugin: QuantMEPlugin,
+ dependencies: ['QHAnaPlugin']
+ }
+];
+```
+
+## Dependency resolution
+The provided [code](../../../components/bpmn-q/modeler-component/editor/plugin/PluginHandler.js#L38) handles transitive dependencies by using a recursive approach to load plugins and their dependencies.
+Here is a breakdown of how the code handles transitive dependencies:
+
+1. Check if active plugins have already been determined: The function first checks if the `activePlugins` array has already been populated. If it contains plugins, indicating that the active plugins have already been determined, the function simply returns the `activePlugins` array.
+
+2. Determine active plugins and their dependencies: If the `activePlugins` array is empty, the function proceeds to determine the active plugins and their dependencies.
+
+3. Recursive loading of plugins and dependencies: The `loadPlugin` function is defined as a recursive function that takes a plugin as an argument. It checks if the plugin is already included in the `activePlugins` array. If it is not, it iterates over the plugin's `dependencies` array.
+
+4. Recursive dependency resolution: For each dependency, the function finds the corresponding plugin object from the `PLUGINS` array based on the dependency's name. If the dependency plugin is found, the `loadPlugin` function is recursively called with the dependency plugin as the argument. This allows the function to resolve dependencies at multiple levels, handling transitive dependencies.
+
+5. Add plugin to activePlugins: After resolving all dependencies, the plugin object is added to the `activePlugins` array.
+
+6. Iterate over plugin configurations: The function then iterates over the plugin configurations obtained from `getAllConfigs()`.
+
+7. Find enabled plugins: For each plugin configuration, the function finds the corresponding plugin object from the `PLUGINS` array based on the plugin's name. It also checks if the plugin is enabled by calling the `checkEnabledStatus` function.
+
+8. Load plugins and dependencies: If a plugin object is found, and it is enabled, the `loadPlugin` function is called with the plugin as the argument. This initiates the loading of the plugin and its dependencies.
+
+9. Return active plugins: Finally, the function returns the `activePlugins` array, which contains all the active plugins and their resolved dependencies.
+
+By recursively loading plugins and their dependencies, the code handles transitive dependencies, ensuring that all required plugins are loaded and added to the `activePlugins` array in the correct order.
\ No newline at end of file
diff --git a/doc/quantum-workflow-modeler/modeler-configuration.md b/doc/quantum-workflow-modeler/modeler-configuration.md
new file mode 100644
index 000000000..958eab5c1
--- /dev/null
+++ b/doc/quantum-workflow-modeler/modeler-configuration.md
@@ -0,0 +1,53 @@
+# Environment Variables
+
+In the following, all environment variables that can be used to customize the workflow modeler are summarized.
+
+### Overview
+
+* ```AWS_RUNTIME_HANDLER_ENDPOINT``` (default 'http://localhost:8890'): Defines the endpoint of the [Amazon Braket Hybrid Jobs Handler](https://github.com/UST-QuAntiL/amazon-braket-hybrid-jobs-handler) which enables the automatic generation of hybrid programs from Amazon Braket programs.
+
+* ```CAMUNDA_ENDPOINT``` (default: 'http://localhost:8080/engine-rest'): Defines the endpoint of the Camunda engine to deploy workflows to.
+
+* ```DATA_CONFIG``` (default: 'http://localhost:8100/data-objects'): Defines the configuration of data objects.
+
+* ```ENABLE_DATA_FLOW_PLUGIN``` (default: 'true'): Defines if the Data Flow plugin is enabled.
+
+* ```ENABLE_PLANQK_PLUGIN``` (default: 'true'): Defines if the PlanQK plugin is enabled.
+
+* ```ENABLE_QHANA_PLUGIN``` (default: 'true'): Defines if the QHAna plugin is enabled.
+
+* ```ENABLE_QUANTME_PLUGIN``` (default: 'true'): Defines if the QuantME plugin is enabled.
+
+* ```GITHUB_TOKEN``` (default: ''): Defines the GitHub Token which can be used to make authorized requests. For more information take a look at [GitHub Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token).
+
+* ```AUTOSAVE_INTERVAL``` (default ms: '300000'): Defines the interval of the auto save feature. If changes are applied to the workflow then it get saved after `5` minutes.
+
+* ```NISQ_ANALYZER_ENDPOINT``` (default: 'http://localhost:8098/nisq-analyzer'): Defines the endpoint of the [NISQ Analyzer](https://github.com/UST-QuAntiL/nisq-analyzer) to enable an automated hardware selection.
+
+* ```OPENTOSCA_ENDPOINT``` (default: 'http://localhost:1337/csars'): Defines the endpoint of the OpenTOSCA container to deploy services with.
+
+* ```QISKIT_RUNTIME_HANDLER_ENDPOINT``` (default: 'http://localhost:8889'): Defines the endpoint of the [Qiskit Runtime Handler](https://github.com/UST-QuAntiL/qiskit-runtime-handler) which enables the automatic generation of hybrid programs from Qiskit programs.
+
+* ```QHANA_GET_PLUGIN_URL``` (default: 'http://localhost:5006/api/plugins/'): Defines the plugin url for QHAna.
+
+* ```QHANA_LIST_PLUGINS_URL``` (default: 'http://localhost:5006/api/plugins/?item-count=100'): Defines the plugin list url for QHAna.
+
+* ```QRM_USERNAME``` (default: ' '): Defines the GitHub username to access the [QRM-Repository](../QRM-Repository)
+
+* ```QRM_REPONAME``` (default: ' '): Defines the GitHub repository name to access the [QRM-Repository](../QRM-Repository)
+
+* ```QRM_REPOPATH``` (default: ' '): Defines the local path in the GitHub repository to the folder containing the [QRM-Repository](../QRM-Repository). This parameter is optional and if it is not set, the root folder of the repository is used.
+
+* ```SERVICE_DATA_CONFIG``` (default: 'http://localhost:8000/service-task'): Defines the configuration for the service task.
+
+* ```SCRIPT_SPLITTER_EDNPOINT``` (default: 'http://localhost:8891'): Defines the endpoint of the Script Splitter.
+
+* ```SCRIPT_SPLITTER_THRESHOLD``` (default: '5'): Defines the splitting threshold for the Script Splitter.
+
+* ```TRANSFORMATION_FRAMEWORK_ENDPOINT``` (default: 'http://localhost:8888'): Defines the endpoint of the QuantME Transformation Framework to use for the automated hardware selection.
+
+* ```WINERY_ENDPOINT``` (default: 'http://localhost:8081/winery'): Defines the endpoint of the Winery to retrieve deployment models for services from.
+
+* ```PROVENANCE_COLLECTION``` (default: 'false'): Defines if the intermediate results of the workflow executed should be collected.
+
+The value of an environment variable is accessed using `process.env.ENV_NAME`. If you want to add a new environment variable, add it to the [webpack.config](../../../../../components/bpmn-q/webpack.config.js) file and restart the application.
\ No newline at end of file