diff --git a/components/bpmn-q/modeler-component/extensions/qhana/configTabs/QHAnaConfigTab.js b/components/bpmn-q/modeler-component/extensions/qhana/configTabs/QHAnaConfigTab.js
index 5919b36c..58f81f8a 100644
--- a/components/bpmn-q/modeler-component/extensions/qhana/configTabs/QHAnaConfigTab.js
+++ b/components/bpmn-q/modeler-component/extensions/qhana/configTabs/QHAnaConfigTab.js
@@ -10,59 +10,41 @@ import { getModeler } from "../../../editor/ModelerHandler";
* @constructor
*/
export default function QHAnaConfigurationsTab() {
- const [listPluginsEndpoint, setListPluginsEndpoint] = useState(
- configManager.getListPluginsURL()
- );
- const [getPluginEndpoint, setGetPluginEndpoint] = useState(
- configManager.getGetPluginsURL()
+ const [getPluginRegistryURL, setPluginRegistryURL] = useState(
+ configManager.getPluginRegistryURL()
);
const modeler = getModeler();
// save changed values on close
QHAnaConfigurationsTab.prototype.onClose = () => {
- modeler.config.listPluginsEndpoint = listPluginsEndpoint;
- modeler.config.getGetPluginsURL = getPluginEndpoint;
- configManager.setListPluginsURL(listPluginsEndpoint);
- configManager.setGetPluginsURL(getPluginEndpoint);
+ modeler.config.getPluginRegistryURL = getPluginRegistryURL;
+ configManager.setPluginRegistryURL(setPluginRegistryURL);
};
return (
- <>
+
QHAna endpoint configuration:
- >
+
);
}
QHAnaConfigurationsTab.prototype.config = () => {
const modeler = getModeler();
- modeler.config.qhanaListPluginsURL = configManager.getListPluginsURL();
- modeler.config.qhanqGetPluginURL = configManager.getGetPluginsURL();
+ modeler.config.qhanaPluginRegistryURL = configManager.getPluginRegistryURL();
};
diff --git a/components/bpmn-q/modeler-component/extensions/qhana/configurations/QHAnaConfigurations.js b/components/bpmn-q/modeler-component/extensions/qhana/configurations/QHAnaConfigurations.js
index fd4a4d8c..4f1b27ec 100644
--- a/components/bpmn-q/modeler-component/extensions/qhana/configurations/QHAnaConfigurations.js
+++ b/components/bpmn-q/modeler-component/extensions/qhana/configurations/QHAnaConfigurations.js
@@ -2,6 +2,56 @@ import ConfigurationsEndpoint from "../../../editor/configurations/Configuration
import * as configManager from "../framework-config/QHAnaConfigManager";
import * as consts from "../Constants";
+const CUSTOM_PLUGIN_CONFIG = {
+ name: "CUSTOM",
+ id: "CUSTOM",
+ description: "",
+ appliesTo: "qhana:QHAnaServiceTask",
+ groupLabel: "Service Properties",
+ attributes: [
+ {
+ name: "identifier",
+ label: "Identifier",
+ type: "string",
+ value: "",
+ editable: "true",
+ bindTo: {
+ name: "qhanaIdentifier",
+ },
+ },
+ {
+ name: "version",
+ label: "Version",
+ type: "string",
+ value: "",
+ editable: "true",
+ bindTo: {
+ name: "qhanaVersion",
+ },
+ },
+ {
+ name: "name",
+ label: "Title",
+ type: "string",
+ value: "CUSTOM",
+ editable: "true",
+ bindTo: {
+ name: "qhanaName",
+ },
+ },
+ {
+ name: "description",
+ label: "Description",
+ type: "string",
+ value: "",
+ editable: "true",
+ bindTo: {
+ name: "qhanaDescription",
+ },
+ },
+ ],
+};
+
/**
* Custom ConfigurationsEndpoint for the QHAna Plugin. Extends the ConfigurationsEndpoint to fetch the configurations directly
* from the QHAna plugin registry.
@@ -14,71 +64,105 @@ export default class QHAnaConfigurationsEndpoint extends ConfigurationsEndpoint
/**
* Fetch all plugins from the QHAna plugin registry and transform them into configurations for QHAna service tasks.
*/
- fetchConfigurations() {
- const self = this;
-
- // fetch all QHAna services from the QHAna plugin registry
- fetch(configManager.getListPluginsURL())
- .then((response) => response.json())
- .then((data) => {
- try {
- const allServices = data.data.items;
- console.log("Received " + allServices.length + " QHAna services: ");
-
- let serviceId;
-
- // fetch details for each service and create configuration
- allServices.forEach(function (service) {
- serviceId = service.resourceKey.pluginId;
-
- // fetch plugin details for serviceId
- fetch(configManager.getGetPluginsURL() + serviceId + "/")
- .then((response) => response.json())
- .then((data) => {
- const serviceData = data.data;
- console.log(
- "Received QHAna service details for service " + serviceId
- );
- console.log(serviceData);
-
- // create configuration from serviceData
- self._configurations.push(
- createConfigurationForServiceData(serviceData)
- );
- })
- .catch((error) => {
- console.error(
- "Error fetching QHAna service with id " +
- serviceId +
- ": \n" +
- error
- );
- });
- });
- } catch (error) {
- console.error(
- "Error while parsing QHAna services from " +
- configManager.getGetPluginsURL() +
- ": \n" +
- error
- );
+ async fetchConfigurations() {
+ const newConfigurations = [];
+
+ const registryUrl = configManager.getPluginRegistryURL();
+
+ if (!registryUrl) {
+ console.info(
+ "Cannot fetch QHAna Plugins, Plugin Registry URL is not configured."
+ );
+ return; // nothing to fetch, registry is not configured
+ }
+
+ let pluginsLink = null;
+ try {
+ const apiResult = await (await fetch(registryUrl)).json();
+ pluginsLink = apiResult?.links?.find?.(
+ (link) =>
+ link.resourceType === "plugin" &&
+ link.rel.some((r) => r === "collection")
+ );
+ } catch (error) {
+ console.error(
+ "Could not reach QHAna Plugin Registry to load available plugins!",
+ error
+ );
+ }
+
+ if (!pluginsLink) {
+ // no plugins found
+ this.configurations = newConfigurations;
+ return;
+ }
+
+ async function loadPlugins(url, configurations, seen) {
+ try {
+ const pluginListResponse = await (await fetch(url)).json();
+
+ await Promise.allSettled(
+ pluginListResponse.data.items.map(async (pluginLink) => {
+ if (seen.has(pluginLink.href)) {
+ return; // plugin already processed
+ }
+ seen.add(pluginLink.href);
+
+ let pluginResponse = pluginListResponse.embedded.find(
+ (e) => e.data.self.href === pluginLink.href
+ );
+
+ try {
+ if (!pluginResponse) {
+ pluginResponse = await (await fetch(pluginLink.href)).json();
+ }
+
+ // create configuration from plugin data
+ configurations.push(
+ createConfigurationForServiceData(pluginResponse.data)
+ );
+ } catch (error) {
+ console.error(
+ `Failed to load plugin ${pluginLink.name} (${pluginLink.href})!`,
+ error
+ );
+ }
+ })
+ );
+
+ const nextLink = pluginListResponse.links.find(
+ (link) =>
+ link.resourceType === "plugin" &&
+ link.rel.some((r) => r === "page") &&
+ link.rel.some((r) => r === "next")
+ );
+ if (nextLink && nextLink.href !== url) {
+ await loadPlugins(nextLink.href, configurations, seen);
}
- })
- .catch((error) => {
+ } catch (error) {
console.error(
- "Error fetching configurations from " +
- configManager.getListPluginsURL() +
- ": \n" +
- error
+ "Failed to fetch plugin page from QHAna Plugin Registry.",
+ error
);
- });
+ }
+ }
+
+ await loadPlugins(pluginsLink.href, newConfigurations, new Set());
+
+ console.info(`${newConfigurations.length} QHAna plugins loaded`);
+
+ this.configurations = newConfigurations;
+ return;
}
/**
* Returns all Configurations for QHAna service tasks which are saved in this endpoint.
*/
getQHAnaServiceConfigurations() {
- return this.getConfigurations(consts.QHANA_SERVICE_TASK);
+ return [
+ CUSTOM_PLUGIN_CONFIG,
+ ...this.getConfigurations(consts.QHANA_SERVICE_TASK),
+ ];
}
/**
@@ -88,6 +172,9 @@ export default class QHAnaConfigurationsEndpoint extends ConfigurationsEndpoint
* @return {*}
*/
getQHAnaServiceConfiguration(id) {
+ if (id === "CUSTOM") {
+ return CUSTOM_PLUGIN_CONFIG;
+ }
return this.getConfiguration(id);
}
@@ -208,7 +295,5 @@ export function createConfigurationForServiceData(serviceData) {
});
});
- console.log("Created configuration for QHAna service");
- console.log(configuration);
return configuration;
}
diff --git a/components/bpmn-q/modeler-component/extensions/qhana/framework-config/QHAnaConfigManager.js b/components/bpmn-q/modeler-component/extensions/qhana/framework-config/QHAnaConfigManager.js
index b8c3a5bf..e90abca3 100644
--- a/components/bpmn-q/modeler-component/extensions/qhana/framework-config/QHAnaConfigManager.js
+++ b/components/bpmn-q/modeler-component/extensions/qhana/framework-config/QHAnaConfigManager.js
@@ -2,62 +2,33 @@ import { getPluginConfig } from "../../../editor/plugin/PluginConfigHandler";
// default config entries used if no value is specified in the initial plugin config
const defaultConfig = {
- qhanaListPluginsURL: process.env.QHANA_LIST_PLUGINS_URL,
- qhanqGetPluginURL: process.env.QHANA_GET_PLUGIN_URL,
+ qhanaPluginRegistryURL: process.env.QHANA_PLUGIN_REGISTRY_URL ?? "",
};
const config = {};
/**
- * Get the url to list all plugins of the QHAna plugin registry
+ * Get the url of the QHAna plugin registry
*
* @return {string} the url
*/
-export function getListPluginsURL() {
+export function getPluginRegistryURL() {
if (config.qhanaListPluginsURL === undefined) {
- setListPluginsURL(
- getPluginConfig("qhana").qhanaListPluginsURL ||
- defaultConfig.qhanaListPluginsURL
+ setPluginRegistryURL(
+ getPluginConfig("qhana").qhanaPluginRegistryURL ||
+ defaultConfig.qhanaPluginRegistryURL
);
}
- return config.qhanaListPluginsURL;
+ return config.qhanaPluginRegistryURL;
}
/**
- * Set the url to list all plugins of the QHAna plugin registry
+ * Set the url of the QHAna plugin registry
*
* @return {string} the url
*/
-export function setListPluginsURL(url) {
+export function setPluginRegistryURL(url) {
if (url !== null && url !== undefined) {
- // remove trailing slashes
- config.qhanaListPluginsURL = url.replace(/\/$/, "");
- }
-}
-
-/**
- * Get the url to get a specific plugin from the QHAna plugin registry
- *
- * @return {string} the url
- */
-export function getGetPluginsURL() {
- if (config.qhanqGetPluginURL === undefined) {
- setGetPluginsURL(
- getPluginConfig("qhana").qhanqGetPluginURL ||
- defaultConfig.qhanqGetPluginURL
- );
- }
- return config.qhanqGetPluginURL;
-}
-
-/**
- * Set the url to get a specific plugin from the QHAna plugin registry
- *
- * @return {string} the url
- */
-export function setGetPluginsURL(url) {
- if (url !== null && url !== undefined) {
- // remove trailing slashes
- config.qhanqGetPluginURL = url;
+ config.qhanaPluginRegistryURL = url;
}
}
diff --git a/components/bpmn-q/modeler-component/extensions/qhana/replacement/QHAnaTransformationHandler.js b/components/bpmn-q/modeler-component/extensions/qhana/replacement/QHAnaTransformationHandler.js
index 3b4627a8..a5eed359 100644
--- a/components/bpmn-q/modeler-component/extensions/qhana/replacement/QHAnaTransformationHandler.js
+++ b/components/bpmn-q/modeler-component/extensions/qhana/replacement/QHAnaTransformationHandler.js
@@ -155,7 +155,7 @@ async function replaceQHAnaServiceTaskByServiceTask(
const bpmnFactory = modeler.get("bpmnFactory");
// create a BPMN service task with implementation external
- const topic = "qhana-plugin." + qhanaServiceTask.get(consts.IDENTIFIER);
+ const topic = "qhana-task";
const newServiceTask = bpmnFactory.create("bpmn:ServiceTask", {
type: "external",
topic: topic,
@@ -176,13 +176,13 @@ async function replaceQHAnaServiceTaskByServiceTask(
const newElement = result.element;
addCamundaInputParameter(
newElement.businessObject,
- "qhanaIdentifier",
+ "qhanaPlugin",
qhanaServiceTask.qhanaIdentifier,
bpmnFactory
);
addCamundaInputParameter(
newElement.businessObject,
- "qhanaVersion",
+ "qhanaPluginVersion",
qhanaServiceTask.qhanaVersion,
bpmnFactory
);
@@ -221,7 +221,7 @@ async function replaceQHAnaServiceStepTaskByServiceTask(
const bpmnFactory = modeler.get("bpmnFactory");
// create a BPMN service task with implementation external and the topic defined in the next step attribute
- const topic = "plugin-step." + consts.NEXT_STEP;
+ const topic = "qhana-task";
const newServiceTask = bpmnFactory.create("bpmn:ServiceTask", {
type: "external",
topic: topic,
@@ -242,7 +242,7 @@ async function replaceQHAnaServiceStepTaskByServiceTask(
const newElement = result.element;
addCamundaInputParameter(
newElement.businessObject,
- "qhanaNextStep",
+ "qhanaPluginStep",
qhanaServiceTask.qhanaNextStep,
bpmnFactory
);
diff --git a/components/bpmn-q/public/env.js.template b/components/bpmn-q/public/env.js.template
index 3002bf3b..6fa0ff91 100644
--- a/components/bpmn-q/public/env.js.template
+++ b/components/bpmn-q/public/env.js.template
@@ -20,8 +20,7 @@ window.env = {
"PATTERN_ATLAS_UI_ENDPOINT": "${PATTERN_ATLAS_UI_ENDPOINT}" !== "" ? "${PATTERN_ATLAS_UI_ENDPOINT}" : undefined,
"PROVENANCE_COLLECTION": "${PROVENANCE_COLLECTION}" !== "" ? "${PROVENANCE_COLLECTION}" : undefined,
"QC_ATLAS_ENDPOINT": "${QC_ATLAS_ENDPOINT}" !== "" ? "${QC_ATLAS_ENDPOINT}" : undefined,
- "QHANA_GET_PLUGIN_URL": "${QHANA_GET_PLUGIN_URL}" !== "" ? "${QHANA_GET_PLUGIN_URL}" : undefined,
- "QHANA_LIST_PLUGINS_URL": "${QHANA_LIST_PLUGINS_URL}" !== "" ? "${QHANA_LIST_PLUGINS_URL}" : undefined,
+ "QHANA_PLUGIN_REGISTRY_URL": "${QHANA_PLUGIN_REGISTRY_URL}" !== "" ? "${QHANA_PLUGIN_REGISTRY_URL}" : undefined,
"QISKIT_RUNTIME_HANDLER_ENDPOINT": "${QISKIT_RUNTIME_HANDLER_ENDPOINT}" !== "" ? "${QISKIT_RUNTIME_HANDLER_ENDPOINT}" : undefined,
"QPROV_ENDPOINT": "${QPROV_ENDPOINT}" !== "" ? "${QPROV_ENDPOINT}" : undefined,
"QRM_USERNAME": "${QRM_USERNAME}" !== "" ? "${QRM_USERNAME}" : undefined,
diff --git a/components/bpmn-q/test/tests/qhana/qhana-plugin-config.spec.js b/components/bpmn-q/test/tests/qhana/qhana-plugin-config.spec.js
index a83d02ff..13340ac8 100644
--- a/components/bpmn-q/test/tests/qhana/qhana-plugin-config.spec.js
+++ b/components/bpmn-q/test/tests/qhana/qhana-plugin-config.spec.js
@@ -9,17 +9,13 @@ describe("Test QHAna plugin config", function () {
{
name: "qhana",
config: {
- qhanaListPluginsURL: "http://test:5006/api/plugins/?item-count=100",
- qhanqGetPluginURL: "http://test:5006/api/plugins/",
+ qhanaPluginRegistryURL: "http://test:5006/api/",
},
},
]);
- expect(qhanaConfig.getListPluginsURL()).to.equal(
- "http://test:5006/api/plugins/?item-count=100"
- );
- expect(qhanaConfig.getGetPluginsURL()).to.equal(
- "http://test:5006/api/plugins/"
+ expect(qhanaConfig.getPluginRegistryURL()).to.equal(
+ "http://test:5006/api/"
);
});
});
diff --git a/components/bpmn-q/test/tests/qhana/qhana-service-configs.spec.js b/components/bpmn-q/test/tests/qhana/qhana-service-configs.spec.js
index a3c4c205..68025d73 100644
--- a/components/bpmn-q/test/tests/qhana/qhana-service-configs.spec.js
+++ b/components/bpmn-q/test/tests/qhana/qhana-service-configs.spec.js
@@ -39,9 +39,10 @@ describe("Test QHAnaConfigurations", function () {
sinon.assert.calledOnce(fetchStub);
- expect(configurations.length).to.equal(2);
- expect(configurations[0].name).to.equal("Aggregators");
- expect(configurations[1].name).to.equal("Ultimate Aggregators");
+ expect(configurations.length).to.equal(3);
+ expect(configurations[0].name).to.equal("CUSTOM");
+ expect(configurations[1].name).to.equal("Aggregators");
+ expect(configurations[2].name).to.equal("Ultimate Aggregators");
});
});
diff --git a/components/bpmn-q/webpack.config.js b/components/bpmn-q/webpack.config.js
index 8f9daa04..c73ff93e 100644
--- a/components/bpmn-q/webpack.config.js
+++ b/components/bpmn-q/webpack.config.js
@@ -25,8 +25,7 @@ let defaultConfig = {
PATTERN_ATLAS_UI_ENDPOINT: "http://localhost:1978",
PROVENANCE_COLLECTION: "false",
QC_ATLAS_ENDPOINT: "http://localhost:6626",
- QHANA_GET_PLUGIN_URL: "http://localhost:5006/api/plugins/",
- QHANA_LIST_PLUGINS_URL: "http://localhost:5006/api/plugins/?item-count=100",
+ QHANA_PLUGIN_REGISTRY_URL: "http://localhost:5006/api/",
QISKIT_RUNTIME_HANDLER_ENDPOINT: "http://localhost:8889",
QPROV_ENDPOINT: "http://localhost:8099/qprov",
QRM_USERNAME: "",
diff --git a/doc/quantum-workflow-modeler/modeler-configuration.md b/doc/quantum-workflow-modeler/modeler-configuration.md
index 996afbef..b6071915 100644
--- a/doc/quantum-workflow-modeler/modeler-configuration.md
+++ b/doc/quantum-workflow-modeler/modeler-configuration.md
@@ -38,9 +38,7 @@ In the following, all environment variables that can be used to customize the wo
* ```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.
+* ```QHANA_PLUGIN_REGISTRY_URL``` (default: 'http://localhost:5006/api/'): Defines the url of the plugin registry api for QHAna.
* ```QProv_ENDPOINT``` (default: 'http://localhost:8099/qprov'): Defines the endpoint of [QProv](https://github.com/UST-QuAntiL/qprov) to store and retrieve provenance data.