Skip to content

Commit

Permalink
Pattern-based Generation and Adaptation of Quantum Workflows (#145)
Browse files Browse the repository at this point in the history
* allow manual adaptation of plugins

* add patterns to extension

* add visualization of behavioral patterns

* add svg of error pattern

* add other patterns to renderer

* add all svgs

* fix naming

* fix rendering of patterns

* add rules

* fix create rule

* fix replace menu

* center patterns

* add transformator

* fix clip and img ids to prevent svg id overlaps

* remove attached pattern after transformation

* fix wrong naming

* add warm start task

* add cutting pattern, update palette icon, restriction in pattern modal

* add transformation of patterns

* begin error correction transformation

* start rewriting transformation

* fix double warm start tasks & transformation

* fix cutting transformation

* add augmentation pattern icons to replace menu

* fix links

* fix cancel, add labels

* update pattern handling

reset checkbox on second click to disable selection
add empty subprocess if solution does not exists
wrap solution into subprocess
attach patterns to subprocess then detect suitable tasks

* fix cutting transformation

* update pattern handlers

* fix checkbox

* update modal

* add pattern config test

* filter out mitigation patterns

* add transformation test

* fix cancel of pattern modal

* remove general pattern

* fix id of di elements

* delete gate error mitigation pattern during transformation

* fix missing form fields

* fix missing businessobject for tasks

* fix checkbox

* fix delete row, check result

* fix id of form fields, add mapping task

* update matching, rename event

* fix exception

* add replacement for pre deployed execution

* check attached behavioral patterns to subprocess

* refine pattern attachment

* apply plugin structure

* fix test

* fix lint

* fix bugs

* add warm starting patterns

* add validation rule

* refine replacement

* update rewrite

* update link

* extend candidate detection

* session, hybrid program generation with patterns

* update attachment for warm starting patterns

* update diagram

* refine transformation logic

* update docs

* update linter

* fix warm starting pattern attribute

* fix layout of behavioral pattern

* update model extension

* remove warm start

* update manual attachment of pattern

* update pattern position, transformation, detection

* add error handling, reduce subprocess width

* add qprovUrl for deployed tasks to startevent

* fix service binding

* fix svg for candidate

* fix service binding

* fix dedicatedHosting policy condition

* rewrite pattern attachment to fix innner outer

* fix replacement of behavioral

* update subprocess height

* refactoring

* update position for new layout

* only bind max 1 running instance

* update header

* add check if optimization candidate has changed

* solve issue when no suitable instances are available

* update deploymentmodelurl binding for always on deployment

* fix bugs for deployment

* adjust hiding for cutting task

* linting

---------

Co-authored-by: LaviniaStiliadou <livia_16@live.de>
Co-authored-by: mbeisel <beiselmn@gmail.com>
  • Loading branch information
3 people authored Apr 24, 2024
1 parent a93f2de commit 35bf203
Show file tree
Hide file tree
Showing 89 changed files with 8,404 additions and 244 deletions.
1 change: 1 addition & 0 deletions components/bpmn-q/.bpmnlintrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
],
"rules": {
"custom/quantme-tasks": "warn",
"custom/pre-deployed-pattern-on-demand-policy": "error",
"custom/subprocess-connected-end-event": "warn",
"custom/subprocess-required-start-event": "warn",
"camunda/no-collapsed-sub-processes": "off"
Expand Down
1 change: 1 addition & 0 deletions components/bpmn-q/bpmnlint-plugin-custom/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module.exports = {
all: {
rules: {
"quantme-tasks": "warn",
"pre-deployed-pattern-on-demand-policy": "error",
"subprocess-required-start-event": "warn",
"subprocess-connected-end-event": "warn",
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
const { is } = require("bpmnlint-utils");

/**
* A rule that checks that no on-demand policy is inside the subprocess to which a pre-deployed pattern is attached.
*/
module.exports = function () {
function check(node, reporter) {
if (!is(node, "pattern:PredeployedExecution")) {
return;
}

function checkForOnDemandPolicyInSubprocesses(subprocess) {
let containsOnDemandPolicy = false;
const flowElements = subprocess.flowElements || [];
flowElements.forEach(function (flowElement) {
if (is(flowElement, "opentosca:OnDemandPolicy")) {
policies.push(flowElement);
containsOnDemandPolicy = true;
}
if (is(flowElement, "bpmn:SubProcess")) {
containsOnDemandPolicy =
checkForOnDemandPolicyInSubprocesses(flowElement) ||
containsOnDemandPolicy;
}
});
return containsOnDemandPolicy;
}

let attachedSubprocess = node.attachedToRef.id;
let parent;
let policies = [];
const flowElements = node.$parent.flowElements || [];
flowElements.forEach(function (flowElement) {
if (flowElement.id === attachedSubprocess) {
parent = flowElement;
}
});

let containsOnDemandPolicy = checkForOnDemandPolicyInSubprocesses(parent);

if (containsOnDemandPolicy) {
reporter.report(
node.id,
"Pre-deployed Pattern and on-demand policy cannot be used together",
["eventDefinitions"]
);
for (let i = 0; i < policies.length; i++) {
reporter.report(
policies[i].id,
"Pre-deployed Pattern and on-demand policy cannot be used together",
["eventDefinitions"]
);
}
}
}

return {
check,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ module.exports = function () {
}

function check(node, reporter) {
console.log(node);
if (!isAny(node, ["bpmn:SubProcess"])) {
return;
}
Expand Down
2 changes: 2 additions & 0 deletions components/bpmn-q/karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ module.exports = function (config) {
"test/tests/dataflow/data-flow-configurations-endpoint.spec.js",
"test/tests/dataflow/data-flow-palette.spec.js",
"test/tests/dataflow/data-flow-replace-menu.spec.js",
"test/tests/pattern/pattern-config.spec.js",
"test/tests/pattern/pattern-transformation.spec.js",
],

// list of files / patterns to exclude
Expand Down
16 changes: 16 additions & 0 deletions components/bpmn-q/modeler-component/editor/EditorConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,19 @@ export const saveFileFormats = {
ZIP: ".zip",
CSAR: ".csar",
};

// workflow with a start event to use as template for new workflows
export const INITIAL_DIAGRAM_XML =
'<?xml version="1.0" encoding="UTF-8"?>\n' +
'<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" id="sample-diagram" targetNamespace="http://bpmn.io/schema/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">\n' +
' <bpmn2:process id="Process_1" isExecutable="true">\n' +
' <bpmn2:startEvent id="StartEvent_1" />\n' +
" </bpmn2:process>\n" +
' <bpmndi:BPMNDiagram id="BPMNDiagram_1">\n' +
' <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">\n' +
' <bpmndi:BPMNShape id="BPMNShape_StartEvent_1" bpmnElement="StartEvent_1">\n' +
' <dc:Bounds x="412" y="240" width="36" height="36" />\n' +
" </bpmndi:BPMNShape>\n" +
" </bpmndi:BPMNPlane>\n" +
" </bpmndi:BPMNDiagram>\n" +
"</bpmn2:definitions>";
24 changes: 22 additions & 2 deletions components/bpmn-q/modeler-component/editor/util/HttpUtilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@
* Retrieves the Json data from the given endpoint.
*
* @param endpoint the endpoint to retrieve the data form
* @param method
* @returns
*/
export async function fetchDataFromEndpoint(endpoint) {
export async function fetchDataFromEndpoint(endpoint, method = "GET") {
try {
const response = await fetch(endpoint);
const response = await fetch(endpoint, {
method: method,
headers: {
Accept: ["application/json", "application/hal+json"],
},
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
Expand All @@ -27,3 +33,17 @@ export async function fetchDataFromEndpoint(endpoint) {
return {};
}
}

export async function fetchSolutionFromEndpoint(endpoint) {
try {
const response = await fetch(endpoint);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.text();
return data;
} catch (error) {
console.error("Error fetching data:", error);
return {};
}
}
19 changes: 2 additions & 17 deletions components/bpmn-q/modeler-component/editor/util/IoUtilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
saveFileFormats,
transformedWorkflowHandlers,
workflowEventTypes,
INITIAL_DIAGRAM_XML,
} from "../EditorConstants";
import { getModeler } from "../ModelerHandler";
import { dispatchWorkflowEvent } from "../events/EditorEventHandler";
Expand All @@ -22,22 +23,6 @@ const quantmeConfig = require("../../extensions/quantme/framework-config/config-

let FormData = require("form-data");

// workflow with a start event to use as template for new workflows
const NEW_DIAGRAM_XML =
'<?xml version="1.0" encoding="UTF-8"?>\n' +
'<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" id="sample-diagram" targetNamespace="http://bpmn.io/schema/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">\n' +
' <bpmn2:process id="Process_1" isExecutable="true">\n' +
' <bpmn2:startEvent id="StartEvent_1" />\n' +
" </bpmn2:process>\n" +
' <bpmndi:BPMNDiagram id="BPMNDiagram_1">\n' +
' <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">\n' +
' <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">\n' +
' <dc:Bounds x="412" y="240" width="36" height="36" />\n' +
" </bpmndi:BPMNShape>\n" +
" </bpmndi:BPMNPlane>\n" +
" </bpmndi:BPMNDiagram>\n" +
"</bpmn2:definitions>";

/**
* Saves a given bpmn diagram as a bpmn file to the locale storage of the user.
*
Expand Down Expand Up @@ -241,7 +226,7 @@ export async function loadDiagram(xml, modeler, dispatchEvent = true) {
* @param modeler the given modeler to open the new bpmn diagram in.
*/
export function createNewDiagram(modeler) {
loadDiagram(NEW_DIAGRAM_XML, modeler).then();
loadDiagram(INITIAL_DIAGRAM_XML, modeler).then();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,7 @@ export default class CustomRulesProvider extends BpmnRules {
* Fired when a new shape for an element is created
*/
this.addRule("shape.create", 200, function (context) {
return canCreate(
context.shape,
context.target,
context.source,
context.position
);
return canCreate(context.shape, context.target);
});
}

Expand Down Expand Up @@ -213,7 +208,7 @@ export default class CustomRulesProvider extends BpmnRules {
* @param position The position where the shape should be created
* @returns {boolean|*|boolean}
*/
canCreate(shape, target, source, position) {
canCreate(shape, target) {
console.log("##### can create");

// do not allow insertion of DataMapObjects
Expand All @@ -222,8 +217,6 @@ export default class CustomRulesProvider extends BpmnRules {
return false;
}
}

return super.canCreate(shape, target, source, position);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
*/
import * as config from "../framework-config/config-manager";
import { fetchDataFromEndpoint } from "../../../editor/util/HttpUtilities";
import { getRootProcess } from "../../../editor/util/ModellingUtilities";

const QUANTME_NAMESPACE_PULL_ENCODED = encodeURIComponent(
encodeURIComponent("http://quantil.org/quantme/pull")
Expand Down Expand Up @@ -110,11 +111,18 @@ export function bindUsingPull(csar, serviceTaskId, elementRegistry, modeling) {
* @param elementRegistry the element registry of the modeler to find workflow elements
* @return {{success: boolean}} true if binding is successful, false otherwise
*/
export async function bindUsingPush(csar, serviceTaskId, elementRegistry) {
export async function bindUsingPush(
csar,
serviceTaskId,
elementRegistry,
modeler
) {
console.log("binding using push");
console.log(csar);
let url = await extractSelfserviceApplicationUrl(csar.buildPlanUrl);
console.log(url);
let selfServiceApplicationUrl = await extractSelfserviceApplicationUrl(
csar.properties
);
console.log(selfServiceApplicationUrl);
let success = false;

if (
Expand Down Expand Up @@ -162,37 +170,71 @@ export async function bindUsingPush(csar, serviceTaskId, elementRegistry) {
connectorElement[0].inputOutput.inputParameters.filter(
(x) => x.name === "url"
)[0].value;
if (url.slice(-1) === "/") {
url = url.substring(url.length - 1);
if (
selfServiceApplicationUrl.charAt(
selfServiceApplicationUrl.length - 1
) === "/"
) {
selfServiceApplicationUrl = selfServiceApplicationUrl.substring(
selfServiceApplicationUrl.length - 1
);
}
if (connectorUrl.slice(0) === "/") {
if (connectorUrl.charAt(0) === "/") {
connectorUrl = connectorUrl.substring(1, connectorUrl.length);
}
inputParameter.value = url + "/" + connectorUrl;
inputParameter.value =
selfServiceApplicationUrl + "/" + connectorUrl;
success = true;
}
}
}
}
}

const qprovEndpoint = await extractQProvEndpoint(csar.properties);
let moddle = modeler.get("moddle");
const rootElement = getRootProcess(modeler.getDefinitions());
let rootStartEvent = rootElement.flowElements.filter(
(flowElement) => flowElement.$type === "bpmn:StartEvent"
);
let formFields = rootStartEvent[0]?.extensionElements?.values.filter(
(x) => x.$type === "camunda:FormData"
)[0].fields;
const formFieldQProvEndpoint = moddle.create("camunda:FormField", {
defaultValue: qprovEndpoint,
id: serviceTaskId + "_qProvUrl",
label: "QProv Endpoint for corresponding Task ID",
type: "string",
});
formFields.push(formFieldQProvEndpoint);

return { success: success };
}

async function extractQProvEndpoint(propertiesUrl) {
let propertiesResponse = await fetchDataFromEndpoint(propertiesUrl);
console.log(propertiesResponse);
const qprovEndpoint = propertiesResponse.qProvUrl;
if (qprovEndpoint === undefined) {
console.error("Unable to fetch qprov endpoint from: " + propertiesUrl);
return undefined;
}
console.log(qprovEndpoint);
return qprovEndpoint;
}

async function extractSelfserviceApplicationUrl(propertiesUrl) {
let buildPlanResponse = await fetchDataFromEndpoint(propertiesUrl);
console.log(buildPlanResponse);
const selfServiceApplicationUrl = buildPlanResponse.outputs.filter(
(x) => x.name.toLowerCase() === "selfserviceapplicationurl"
);
if (
selfServiceApplicationUrl === undefined ||
selfServiceApplicationUrl.length < 1
) {
let propertiesResponse = await fetchDataFromEndpoint(propertiesUrl);
console.log(propertiesResponse);
const selfServiceApplicationUrl =
propertiesResponse.selfServiceApplicationUrl;
if (selfServiceApplicationUrl === undefined) {
console.error(
"Unable to fetch selfServiceApplicationUrl from: " + propertiesUrl
"Unable to fetch selfServiceApplicationUrl endpoint from: " +
propertiesUrl
);
return undefined;
}
console.log(selfServiceApplicationUrl);
return selfServiceApplicationUrl[0].value;
return selfServiceApplicationUrl;
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ export function isDeployableServiceTask(element) {
element.$type &&
element.$type === "bpmn:ServiceTask" &&
element.deploymentModelUrl &&
getBindingType(element) !== undefined
getBindingType(element) !== undefined &&
!element.$attrs["opentosca:isDeployedAndBound"]
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ export default class OpenTOSCARenderer extends BpmnRenderer {
execute: ({ showDeploymentModel }) => {
const elementsWithDeploymentModel = this.elementRegistry.filter(
(element) =>
element.businessObject.get("opentosca:deploymentModelUrl")
element.businessObject.get("opentosca:deploymentModelUrl") !==
undefined
);
const changed = [];
for (const element of elementsWithDeploymentModel) {
Expand Down Expand Up @@ -274,7 +275,12 @@ export default class OpenTOSCARenderer extends BpmnRenderer {
let deploymentModelUrl = element.businessObject.get(
"opentosca:deploymentModelUrl"
);
if (!deploymentModelUrl) return;
console.log(deploymentModelUrl);
console.log(element);
console.log(deploymentModelUrl === undefined);
console.log(!deploymentModelUrl);
if (!deploymentModelUrl || deploymentModelUrl === undefined) return;
console.log("render button");

const button = drawTaskSVG(
parentGfx,
Expand Down
Loading

0 comments on commit 35bf203

Please sign in to comment.