Skip to content

Commit

Permalink
kie-issues#2466: Serverless Logic Web Tools: Runtime Tools settings d…
Browse files Browse the repository at this point in the history
…oesn't validate the data index url. (#2608)
  • Loading branch information
kumaradityaraj authored Oct 15, 2024
1 parent b905936 commit 5ebd85c
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 23 deletions.
19 changes: 19 additions & 0 deletions packages/runtime-tools-swf-gateway-api/src/gatewayApi/apis.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -729,3 +729,22 @@ export const saveFormContent = (formName: string, content: FormContent): Promise
.catch((error) => reject(error));
});
};

export async function verifyDataIndex(dataIndexUrl?: string): Promise<boolean> {
if (!dataIndexUrl) {
return false;
}

try {
const response = await fetch(dataIndexUrl, {
headers: {
"Content-Type": "application/json",
},
method: "POST",
body: '{"query":""}',
});
return response.status === 200;
} catch (e) {
return false;
}
}
6 changes: 6 additions & 0 deletions packages/serverless-logic-web-tools/src/i18n/AppI18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ interface AppDictionary extends ReferenceDictionary {
dependencyWarningTooltip: string;
};
};
RuntimeToolsSettings: {
configModal: {
validDataIndexURLError: string;
dataIndexConnectionError: string;
};
};
}

export interface AppI18n extends AppDictionary, CommonI18n {}
6 changes: 6 additions & 0 deletions packages/serverless-logic-web-tools/src/i18n/locales/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,10 @@ export const de: AppI18n = {
"Modelle in diesem Arbeitsbereich können von Bereitstellungen aus anderen Arbeitsbereichen abhängen.",
},
},
RuntimeToolsSettings: {
configModal: {
validDataIndexURLError: "Bitte geben Sie eine gültige Datenindex-URL ein.",
dataIndexConnectionError: "Verbindung abgelehnt. Bitte überprüfen Sie die bereitgestellten Informationen.",
},
},
};
6 changes: 6 additions & 0 deletions packages/serverless-logic-web-tools/src/i18n/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,10 @@ export const en: AppI18n = {
dependencyWarningTooltip: "Models in this workspace may depend on deployments from other workspaces.",
},
},
RuntimeToolsSettings: {
configModal: {
validDataIndexURLError: "Please enter a valid Data Index URL.",
dataIndexConnectionError: "Connection refused. Please check the information provided.",
},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
* under the License.
*/

import React from "react";
import React, { useEffect } from "react";
import { Button, ButtonVariant } from "@patternfly/react-core/dist/js/components/Button";
import { EmptyState, EmptyStateBody, EmptyStateIcon } from "@patternfly/react-core/dist/js/components/EmptyState";
import { ActionGroup, Form, FormGroup } from "@patternfly/react-core/dist/js/components/Form";
import { ActionGroup, Form, FormAlert, FormGroup } from "@patternfly/react-core/dist/js/components/Form";
import { InputGroup, InputGroupText } from "@patternfly/react-core/dist/js/components/InputGroup";
import { Modal, ModalVariant } from "@patternfly/react-core/dist/js/components/Modal";
import { PageSection } from "@patternfly/react-core/dist/js/components/Page";
Expand All @@ -43,14 +43,31 @@ import {
saveConfigCookie,
} from "./RuntimeToolsConfig";
import { removeTrailingSlashFromUrl } from "../../url";
import { isDataIndexUrlValid } from "../../url";
import { Alert } from "@patternfly/react-core/dist/js";
import { useAppI18n } from "../../i18n";
import { verifyDataIndex } from "@kie-tools/runtime-tools-swf-gateway-api/src/gatewayApi/apis";

const PAGE_TITLE = "Runtime Tools";

enum FormValiationOptions {
INITIAL = "INITIAL",
INVALID = "INVALID",
CONNECTION_ERROR = "CONNECTION_ERROR",
}

export function RuntimeToolsSettings(props: SettingsPageProps) {
const { i18n } = useAppI18n();
const settings = useSettings();
const settingsDispatch = useSettingsDispatch();
const [config, setConfig] = useState(settings.runtimeTools.config);
const [isModalOpen, setIsModalOpen] = useState(false);
const [isConfigValidated, setConfigValidated] = useState(FormValiationOptions.INITIAL);
const [dataIndexUrlAvailable, setDataIndexUrlAvailable] = useState<boolean>(false);

useEffect(() => {
setConfigValidated(FormValiationOptions.INITIAL);
}, [config]);

const handleModalToggle = useCallback(() => {
setIsModalOpen((prevIsModalOpen) => !prevIsModalOpen);
Expand All @@ -76,10 +93,23 @@ export function RuntimeToolsSettings(props: SettingsPageProps) {
resetConfigCookie();
}, [settingsDispatch.runtimeTools]);

const onApply = useCallback(() => {
const onApply = useCallback(async () => {
const newConfig: RuntimeToolsSettingsConfig = {
dataIndexUrl: removeTrailingSlashFromUrl(config.dataIndexUrl),
};
const isDataIndexUrlVerified = await verifyDataIndex(config.dataIndexUrl);
if (!isDataIndexUrlValid(config.dataIndexUrl)) {
setConfigValidated(FormValiationOptions.INVALID);
return;
} else {
if (isDataIndexUrlVerified == true) {
setDataIndexUrlAvailable(true);
} else {
setConfigValidated(FormValiationOptions.CONNECTION_ERROR);
return;
}
}

setConfig(newConfig);
settingsDispatch.runtimeTools.setConfig(newConfig);
saveConfigCookie(newConfig);
Expand Down Expand Up @@ -144,6 +174,28 @@ export function RuntimeToolsSettings(props: SettingsPageProps) {
appendTo={props.pageContainerRef.current || document.body}
>
<Form>
{isConfigValidated === FormValiationOptions.INVALID && (
<FormAlert>
<Alert
variant="danger"
title={i18n.RuntimeToolsSettings.configModal.validDataIndexURLError}
aria-live="polite"
isInline
data-testid="alert-data-index-url-invalid"
/>
</FormAlert>
)}
{isConfigValidated === FormValiationOptions.CONNECTION_ERROR && (
<FormAlert>
<Alert
variant="danger"
title={i18n.RuntimeToolsSettings.configModal.dataIndexConnectionError}
aria-live="polite"
isInline
data-testid="alert-data-index-url-connection-error"
/>
</FormAlert>
)}
<FormGroup
label={"Data Index URL"}
labelIcon={
Expand Down
9 changes: 9 additions & 0 deletions packages/serverless-logic-web-tools/src/url/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,12 @@
export function removeTrailingSlashFromUrl(url: string): string {
return url.replace(/\/$/, "");
}

export function isDataIndexUrlValid(url: string): boolean {
try {
const parsedUrl = new URL(url);
return parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:";
} catch (_) {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { isDataIndexUrlValid } from "../../src/url";

describe("isDataIndexUrlValid", () => {
it.each([
["http://example.com", true],
["http://example.com/", true],
["https://example.com/", true],
["ftps://example.com/", false],
])("the data index URL %s validation should be %s", (inputUrl, isValidUrl) => {
expect(isDataIndexUrlValid(inputUrl)).toBe(isValidUrl);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@

import React, { PropsWithChildren, useEffect, useMemo, useState } from "react";
import { DEFAULT_APPDATA_VALUES } from "../AppConstants";
import { AppData, verifyDataIndex } from "../data";
import { AppData } from "../data";
import { useAppDataPromise } from "../hooks/useAppDataPromise";
import { AppContext } from "./AppContext";
import { verifyDataIndex } from "@kie-tools/runtime-tools-swf-gateway-api/src/gatewayApi/apis";

export function AppContextProvider(props: PropsWithChildren<{}>) {
const appDataPromise = useAppDataPromise();
Expand Down
19 changes: 0 additions & 19 deletions packages/sonataflow-deployment-webapp/src/data/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,3 @@ export async function fetchAppData(): Promise<AppData> {
const response = await fetch(routes.dataJson.path({}));
return (await response.json()) as AppData;
}

export async function verifyDataIndex(dataIndexUrl?: string): Promise<boolean> {
if (!dataIndexUrl) {
return false;
}

try {
const response = await fetch(dataIndexUrl, {
headers: {
"Content-Type": "application/json",
},
method: "POST",
body: '{"query":""}',
});
return response.status === 200;
} catch (e) {
return false;
}
}

0 comments on commit 5ebd85c

Please sign in to comment.