Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QHAna plugin refactor #162

Merged
merged 5 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<>
<div>
<h3>QHAna endpoint configuration:</h3>
<table>
<tbody>
<tr className="spaceUnder">
<td align="right">List Plugins Endpoint</td>
<td align="left">
<input
className="qwm-input"
type="string"
name="listPluginsEndpoint"
value={listPluginsEndpoint}
onChange={(event) => setListPluginsEndpoint(event.target.value)}
/>
</td>
</tr>
<tr className="spaceUnder">
<td align="right">Get Plugin Endpoint</td>
<td align="right">Plugin Registry URL</td>
<td align="left">
<input
className="qwm-input"
type="string"
name="getPluginEndpoint"
value={getPluginEndpoint}
onChange={(event) => setGetPluginEndpoint(event.target.value)}
type="url"
name="qhanaPluginRegistryURL"
value={getPluginRegistryURL}
onChange={(event) => setPluginRegistryURL(event.target.value)}
/>
</td>
</tr>
</tbody>
</table>
</>
</div>
);
}

QHAnaConfigurationsTab.prototype.config = () => {
const modeler = getModeler();
modeler.config.qhanaListPluginsURL = configManager.getListPluginsURL();
modeler.config.qhanqGetPluginURL = configManager.getGetPluginsURL();
modeler.config.qhanaPluginRegistryURL = configManager.getPluginRegistryURL();
};
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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),
];
}

/**
Expand All @@ -88,6 +172,9 @@ export default class QHAnaConfigurationsEndpoint extends ConfigurationsEndpoint
* @return {*}
*/
getQHAnaServiceConfiguration(id) {
if (id === "CUSTOM") {
return CUSTOM_PLUGIN_CONFIG;
}
return this.getConfiguration(id);
}

Expand Down Expand Up @@ -208,7 +295,5 @@ export function createConfigurationForServiceData(serviceData) {
});
});

console.log("Created configuration for QHAna service");
console.log(configuration);
return configuration;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Loading
Loading