Skip to content

Commit b48d8ee

Browse files
authored
QHAna plugin refactor (#162)
* Refactor QHAna plugin config and loading plugin list * Always provide a QHAna service task option * Refactor QHAna transformation to use variables over topic names * Fix broken QHAna tests due to config changes * Fix linting issues
1 parent 1d3dd25 commit b48d8ee

File tree

9 files changed

+180
-149
lines changed

9 files changed

+180
-149
lines changed

components/bpmn-q/modeler-component/extensions/qhana/configTabs/QHAnaConfigTab.js

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,59 +10,41 @@ import { getModeler } from "../../../editor/ModelerHandler";
1010
* @constructor
1111
*/
1212
export default function QHAnaConfigurationsTab() {
13-
const [listPluginsEndpoint, setListPluginsEndpoint] = useState(
14-
configManager.getListPluginsURL()
15-
);
16-
const [getPluginEndpoint, setGetPluginEndpoint] = useState(
17-
configManager.getGetPluginsURL()
13+
const [getPluginRegistryURL, setPluginRegistryURL] = useState(
14+
configManager.getPluginRegistryURL()
1815
);
1916
const modeler = getModeler();
2017

2118
// save changed values on close
2219
QHAnaConfigurationsTab.prototype.onClose = () => {
23-
modeler.config.listPluginsEndpoint = listPluginsEndpoint;
24-
modeler.config.getGetPluginsURL = getPluginEndpoint;
25-
configManager.setListPluginsURL(listPluginsEndpoint);
26-
configManager.setGetPluginsURL(getPluginEndpoint);
20+
modeler.config.getPluginRegistryURL = getPluginRegistryURL;
21+
configManager.setPluginRegistryURL(setPluginRegistryURL);
2722
};
2823

2924
return (
30-
<>
25+
<div>
3126
<h3>QHAna endpoint configuration:</h3>
3227
<table>
3328
<tbody>
3429
<tr className="spaceUnder">
35-
<td align="right">List Plugins Endpoint</td>
36-
<td align="left">
37-
<input
38-
className="qwm-input"
39-
type="string"
40-
name="listPluginsEndpoint"
41-
value={listPluginsEndpoint}
42-
onChange={(event) => setListPluginsEndpoint(event.target.value)}
43-
/>
44-
</td>
45-
</tr>
46-
<tr className="spaceUnder">
47-
<td align="right">Get Plugin Endpoint</td>
30+
<td align="right">Plugin Registry URL</td>
4831
<td align="left">
4932
<input
5033
className="qwm-input"
51-
type="string"
52-
name="getPluginEndpoint"
53-
value={getPluginEndpoint}
54-
onChange={(event) => setGetPluginEndpoint(event.target.value)}
34+
type="url"
35+
name="qhanaPluginRegistryURL"
36+
value={getPluginRegistryURL}
37+
onChange={(event) => setPluginRegistryURL(event.target.value)}
5538
/>
5639
</td>
5740
</tr>
5841
</tbody>
5942
</table>
60-
</>
43+
</div>
6144
);
6245
}
6346

6447
QHAnaConfigurationsTab.prototype.config = () => {
6548
const modeler = getModeler();
66-
modeler.config.qhanaListPluginsURL = configManager.getListPluginsURL();
67-
modeler.config.qhanqGetPluginURL = configManager.getGetPluginsURL();
49+
modeler.config.qhanaPluginRegistryURL = configManager.getPluginRegistryURL();
6850
};

components/bpmn-q/modeler-component/extensions/qhana/configurations/QHAnaConfigurations.js

Lines changed: 143 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,56 @@ import ConfigurationsEndpoint from "../../../editor/configurations/Configuration
22
import * as configManager from "../framework-config/QHAnaConfigManager";
33
import * as consts from "../Constants";
44

5+
const CUSTOM_PLUGIN_CONFIG = {
6+
name: "CUSTOM",
7+
id: "CUSTOM",
8+
description: "",
9+
appliesTo: "qhana:QHAnaServiceTask",
10+
groupLabel: "Service Properties",
11+
attributes: [
12+
{
13+
name: "identifier",
14+
label: "Identifier",
15+
type: "string",
16+
value: "",
17+
editable: "true",
18+
bindTo: {
19+
name: "qhanaIdentifier",
20+
},
21+
},
22+
{
23+
name: "version",
24+
label: "Version",
25+
type: "string",
26+
value: "",
27+
editable: "true",
28+
bindTo: {
29+
name: "qhanaVersion",
30+
},
31+
},
32+
{
33+
name: "name",
34+
label: "Title",
35+
type: "string",
36+
value: "CUSTOM",
37+
editable: "true",
38+
bindTo: {
39+
name: "qhanaName",
40+
},
41+
},
42+
{
43+
name: "description",
44+
label: "Description",
45+
type: "string",
46+
value: "",
47+
editable: "true",
48+
bindTo: {
49+
name: "qhanaDescription",
50+
},
51+
},
52+
],
53+
};
54+
555
/**
656
* Custom ConfigurationsEndpoint for the QHAna Plugin. Extends the ConfigurationsEndpoint to fetch the configurations directly
757
* from the QHAna plugin registry.
@@ -14,71 +64,105 @@ export default class QHAnaConfigurationsEndpoint extends ConfigurationsEndpoint
1464
/**
1565
* Fetch all plugins from the QHAna plugin registry and transform them into configurations for QHAna service tasks.
1666
*/
17-
fetchConfigurations() {
18-
const self = this;
19-
20-
// fetch all QHAna services from the QHAna plugin registry
21-
fetch(configManager.getListPluginsURL())
22-
.then((response) => response.json())
23-
.then((data) => {
24-
try {
25-
const allServices = data.data.items;
26-
console.log("Received " + allServices.length + " QHAna services: ");
27-
28-
let serviceId;
29-
30-
// fetch details for each service and create configuration
31-
allServices.forEach(function (service) {
32-
serviceId = service.resourceKey.pluginId;
33-
34-
// fetch plugin details for serviceId
35-
fetch(configManager.getGetPluginsURL() + serviceId + "/")
36-
.then((response) => response.json())
37-
.then((data) => {
38-
const serviceData = data.data;
39-
console.log(
40-
"Received QHAna service details for service " + serviceId
41-
);
42-
console.log(serviceData);
43-
44-
// create configuration from serviceData
45-
self._configurations.push(
46-
createConfigurationForServiceData(serviceData)
47-
);
48-
})
49-
.catch((error) => {
50-
console.error(
51-
"Error fetching QHAna service with id " +
52-
serviceId +
53-
": \n" +
54-
error
55-
);
56-
});
57-
});
58-
} catch (error) {
59-
console.error(
60-
"Error while parsing QHAna services from " +
61-
configManager.getGetPluginsURL() +
62-
": \n" +
63-
error
64-
);
67+
async fetchConfigurations() {
68+
const newConfigurations = [];
69+
70+
const registryUrl = configManager.getPluginRegistryURL();
71+
72+
if (!registryUrl) {
73+
console.info(
74+
"Cannot fetch QHAna Plugins, Plugin Registry URL is not configured."
75+
);
76+
return; // nothing to fetch, registry is not configured
77+
}
78+
79+
let pluginsLink = null;
80+
try {
81+
const apiResult = await (await fetch(registryUrl)).json();
82+
pluginsLink = apiResult?.links?.find?.(
83+
(link) =>
84+
link.resourceType === "plugin" &&
85+
link.rel.some((r) => r === "collection")
86+
);
87+
} catch (error) {
88+
console.error(
89+
"Could not reach QHAna Plugin Registry to load available plugins!",
90+
error
91+
);
92+
}
93+
94+
if (!pluginsLink) {
95+
// no plugins found
96+
this.configurations = newConfigurations;
97+
return;
98+
}
99+
100+
async function loadPlugins(url, configurations, seen) {
101+
try {
102+
const pluginListResponse = await (await fetch(url)).json();
103+
104+
await Promise.allSettled(
105+
pluginListResponse.data.items.map(async (pluginLink) => {
106+
if (seen.has(pluginLink.href)) {
107+
return; // plugin already processed
108+
}
109+
seen.add(pluginLink.href);
110+
111+
let pluginResponse = pluginListResponse.embedded.find(
112+
(e) => e.data.self.href === pluginLink.href
113+
);
114+
115+
try {
116+
if (!pluginResponse) {
117+
pluginResponse = await (await fetch(pluginLink.href)).json();
118+
}
119+
120+
// create configuration from plugin data
121+
configurations.push(
122+
createConfigurationForServiceData(pluginResponse.data)
123+
);
124+
} catch (error) {
125+
console.error(
126+
`Failed to load plugin ${pluginLink.name} (${pluginLink.href})!`,
127+
error
128+
);
129+
}
130+
})
131+
);
132+
133+
const nextLink = pluginListResponse.links.find(
134+
(link) =>
135+
link.resourceType === "plugin" &&
136+
link.rel.some((r) => r === "page") &&
137+
link.rel.some((r) => r === "next")
138+
);
139+
if (nextLink && nextLink.href !== url) {
140+
await loadPlugins(nextLink.href, configurations, seen);
65141
}
66-
})
67-
.catch((error) => {
142+
} catch (error) {
68143
console.error(
69-
"Error fetching configurations from " +
70-
configManager.getListPluginsURL() +
71-
": \n" +
72-
error
144+
"Failed to fetch plugin page from QHAna Plugin Registry.",
145+
error
73146
);
74-
});
147+
}
148+
}
149+
150+
await loadPlugins(pluginsLink.href, newConfigurations, new Set());
151+
152+
console.info(`${newConfigurations.length} QHAna plugins loaded`);
153+
154+
this.configurations = newConfigurations;
155+
return;
75156
}
76157

77158
/**
78159
* Returns all Configurations for QHAna service tasks which are saved in this endpoint.
79160
*/
80161
getQHAnaServiceConfigurations() {
81-
return this.getConfigurations(consts.QHANA_SERVICE_TASK);
162+
return [
163+
CUSTOM_PLUGIN_CONFIG,
164+
...this.getConfigurations(consts.QHANA_SERVICE_TASK),
165+
];
82166
}
83167

84168
/**
@@ -88,6 +172,9 @@ export default class QHAnaConfigurationsEndpoint extends ConfigurationsEndpoint
88172
* @return {*}
89173
*/
90174
getQHAnaServiceConfiguration(id) {
175+
if (id === "CUSTOM") {
176+
return CUSTOM_PLUGIN_CONFIG;
177+
}
91178
return this.getConfiguration(id);
92179
}
93180

@@ -208,7 +295,5 @@ export function createConfigurationForServiceData(serviceData) {
208295
});
209296
});
210297

211-
console.log("Created configuration for QHAna service");
212-
console.log(configuration);
213298
return configuration;
214299
}

components/bpmn-q/modeler-component/extensions/qhana/framework-config/QHAnaConfigManager.js

Lines changed: 10 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,62 +2,33 @@ import { getPluginConfig } from "../../../editor/plugin/PluginConfigHandler";
22

33
// default config entries used if no value is specified in the initial plugin config
44
const defaultConfig = {
5-
qhanaListPluginsURL: process.env.QHANA_LIST_PLUGINS_URL,
6-
qhanqGetPluginURL: process.env.QHANA_GET_PLUGIN_URL,
5+
qhanaPluginRegistryURL: process.env.QHANA_PLUGIN_REGISTRY_URL ?? "",
76
};
87

98
const config = {};
109

1110
/**
12-
* Get the url to list all plugins of the QHAna plugin registry
11+
* Get the url of the QHAna plugin registry
1312
*
1413
* @return {string} the url
1514
*/
16-
export function getListPluginsURL() {
15+
export function getPluginRegistryURL() {
1716
if (config.qhanaListPluginsURL === undefined) {
18-
setListPluginsURL(
19-
getPluginConfig("qhana").qhanaListPluginsURL ||
20-
defaultConfig.qhanaListPluginsURL
17+
setPluginRegistryURL(
18+
getPluginConfig("qhana").qhanaPluginRegistryURL ||
19+
defaultConfig.qhanaPluginRegistryURL
2120
);
2221
}
23-
return config.qhanaListPluginsURL;
22+
return config.qhanaPluginRegistryURL;
2423
}
2524

2625
/**
27-
* Set the url to list all plugins of the QHAna plugin registry
26+
* Set the url of the QHAna plugin registry
2827
*
2928
* @return {string} the url
3029
*/
31-
export function setListPluginsURL(url) {
30+
export function setPluginRegistryURL(url) {
3231
if (url !== null && url !== undefined) {
33-
// remove trailing slashes
34-
config.qhanaListPluginsURL = url.replace(/\/$/, "");
35-
}
36-
}
37-
38-
/**
39-
* Get the url to get a specific plugin from the QHAna plugin registry
40-
*
41-
* @return {string} the url
42-
*/
43-
export function getGetPluginsURL() {
44-
if (config.qhanqGetPluginURL === undefined) {
45-
setGetPluginsURL(
46-
getPluginConfig("qhana").qhanqGetPluginURL ||
47-
defaultConfig.qhanqGetPluginURL
48-
);
49-
}
50-
return config.qhanqGetPluginURL;
51-
}
52-
53-
/**
54-
* Set the url to get a specific plugin from the QHAna plugin registry
55-
*
56-
* @return {string} the url
57-
*/
58-
export function setGetPluginsURL(url) {
59-
if (url !== null && url !== undefined) {
60-
// remove trailing slashes
61-
config.qhanqGetPluginURL = url;
32+
config.qhanaPluginRegistryURL = url;
6233
}
6334
}

0 commit comments

Comments
 (0)