From fc27f22fa01303a39132ae27494b43a41bc7101f Mon Sep 17 00:00:00 2001 From: kumaradityaraj Date: Fri, 20 Sep 2024 17:22:11 +0530 Subject: [PATCH 1/3] Added validation for data index url and its tests --- .../src/i18n/AppI18n.ts | 1 + .../src/i18n/locales/de.ts | 1 + .../src/i18n/locales/en.ts | 1 + .../runtimeTools/RuntimeToolsSettings.tsx | 34 +++++++++++++++++-- .../src/url/index.ts | 9 +++++ .../tests/url/dataIndexValidUrl.test.ts | 31 +++++++++++++++++ 6 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 packages/serverless-logic-web-tools/tests/url/dataIndexValidUrl.test.ts diff --git a/packages/serverless-logic-web-tools/src/i18n/AppI18n.ts b/packages/serverless-logic-web-tools/src/i18n/AppI18n.ts index 9824c624c08..dd19298ac2b 100644 --- a/packages/serverless-logic-web-tools/src/i18n/AppI18n.ts +++ b/packages/serverless-logic-web-tools/src/i18n/AppI18n.ts @@ -71,6 +71,7 @@ interface AppDictionary extends ReferenceDictionary { validationError: string; connectionError: string; configExpiredWarning: string; + validDataIndexURLError: string; }; confirmModal: { title: string; diff --git a/packages/serverless-logic-web-tools/src/i18n/locales/de.ts b/packages/serverless-logic-web-tools/src/i18n/locales/de.ts index f795fce6748..fa0e1c330af 100644 --- a/packages/serverless-logic-web-tools/src/i18n/locales/de.ts +++ b/packages/serverless-logic-web-tools/src/i18n/locales/de.ts @@ -73,6 +73,7 @@ export const de: AppI18n = { validationError: "Sie müssen alle erforderlichen Felder ausfüllen, bevor Sie fortfahren können.", connectionError: "Verbindung abgelehnt. Bitte überprüfen Sie die angegebenen Informationen.", configExpiredWarning: "Token oder Konto ist abgelaufen. Bitte aktualisieren Sie Ihre Konfiguration.", + validDataIndexURLError: "Bitte geben Sie eine gültige Datenindex-URL ein.", }, confirmModal: { title: "Bereitstellen", diff --git a/packages/serverless-logic-web-tools/src/i18n/locales/en.ts b/packages/serverless-logic-web-tools/src/i18n/locales/en.ts index 800745e2565..d896ed0bf76 100644 --- a/packages/serverless-logic-web-tools/src/i18n/locales/en.ts +++ b/packages/serverless-logic-web-tools/src/i18n/locales/en.ts @@ -71,6 +71,7 @@ export const en: AppI18n = { validationError: "You must fill out all required fields before you can proceed.", connectionError: "Connection refused. Please check the information provided.", configExpiredWarning: "Token or account expired. Please update your configuration.", + validDataIndexURLError: "Please enter a valid Data Index URL.", }, confirmModal: { title: "Deploy", diff --git a/packages/serverless-logic-web-tools/src/settings/runtimeTools/RuntimeToolsSettings.tsx b/packages/serverless-logic-web-tools/src/settings/runtimeTools/RuntimeToolsSettings.tsx index ccbdd84723c..b11f4d32fd2 100644 --- a/packages/serverless-logic-web-tools/src/settings/runtimeTools/RuntimeToolsSettings.tsx +++ b/packages/serverless-logic-web-tools/src/settings/runtimeTools/RuntimeToolsSettings.tsx @@ -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"; @@ -43,14 +43,28 @@ import { saveConfigCookie, } from "./RuntimeToolsConfig"; import { removeTrailingSlashFromUrl } from "../../url"; +import { validDataIndexUrl } from "../../url"; +import { Alert } from "@patternfly/react-core/dist/js"; +import { useAppI18n } from "../../i18n"; const PAGE_TITLE = "Runtime Tools"; +enum DataIndexValidation { + INITIAL = "INITIAL", + INVALID = "INVALID", +} + 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 [isDataIndexUrlValidated, setDataIndexUrlValidated] = useState(DataIndexValidation.INVALID); + + useEffect(() => { + setDataIndexUrlValidated(DataIndexValidation.INITIAL); + }, [config]); const handleModalToggle = useCallback(() => { setIsModalOpen((prevIsModalOpen) => !prevIsModalOpen); @@ -80,6 +94,11 @@ export function RuntimeToolsSettings(props: SettingsPageProps) { const newConfig: RuntimeToolsSettingsConfig = { dataIndexUrl: removeTrailingSlashFromUrl(config.dataIndexUrl), }; + if (!validDataIndexUrl(config.dataIndexUrl)) { + setDataIndexUrlValidated(DataIndexValidation.INVALID); + return; + } + setConfig(newConfig); settingsDispatch.runtimeTools.setConfig(newConfig); saveConfigCookie(newConfig); @@ -144,6 +163,17 @@ export function RuntimeToolsSettings(props: SettingsPageProps) { appendTo={props.pageContainerRef.current || document.body} >
+ {isDataIndexUrlValidated === DataIndexValidation.INVALID && ( + + + + )} { + it.each([ + ["http://example.com/", true], + ["https://example.com/", true], + ["loremIpsum", false], + ["google.com", false], + ])("should validate the data index URL", (inputUrl, isValidUrl) => { + expect(validDataIndexUrl(inputUrl)).toBe(isValidUrl); + }); +}); From 2709ce4349c467ffba41e2e8f9cbe78c10502333 Mon Sep 17 00:00:00 2001 From: kumaradityaraj Date: Mon, 23 Sep 2024 14:59:49 +0530 Subject: [PATCH 2/3] Resolved suggested changes --- .../src/gatewayApi/apis.tsx | 19 +++++++++ .../src/i18n/AppI18n.ts | 7 +++- .../src/i18n/locales/de.ts | 7 +++- .../src/i18n/locales/en.ts | 7 +++- .../runtimeTools/RuntimeToolsSettings.tsx | 41 +++++++++++++++---- .../src/url/index.ts | 6 +-- .../tests/url/dataIndexValidUrl.test.ts | 12 +++--- .../src/context/AppContextProvider.tsx | 3 +- .../src/data/index.ts | 19 --------- 9 files changed, 80 insertions(+), 41 deletions(-) diff --git a/packages/runtime-tools-swf-gateway-api/src/gatewayApi/apis.tsx b/packages/runtime-tools-swf-gateway-api/src/gatewayApi/apis.tsx index 619ebe387c0..3d391590079 100644 --- a/packages/runtime-tools-swf-gateway-api/src/gatewayApi/apis.tsx +++ b/packages/runtime-tools-swf-gateway-api/src/gatewayApi/apis.tsx @@ -729,3 +729,22 @@ export const saveFormContent = (formName: string, content: FormContent): Promise .catch((error) => reject(error)); }); }; + +export async function verifyDataIndex(dataIndexUrl?: string): Promise { + 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; + } +} diff --git a/packages/serverless-logic-web-tools/src/i18n/AppI18n.ts b/packages/serverless-logic-web-tools/src/i18n/AppI18n.ts index dd19298ac2b..3b1178924cc 100644 --- a/packages/serverless-logic-web-tools/src/i18n/AppI18n.ts +++ b/packages/serverless-logic-web-tools/src/i18n/AppI18n.ts @@ -71,7 +71,6 @@ interface AppDictionary extends ReferenceDictionary { validationError: string; connectionError: string; configExpiredWarning: string; - validDataIndexURLError: string; }; confirmModal: { title: string; @@ -106,6 +105,12 @@ interface AppDictionary extends ReferenceDictionary { dependencyWarningTooltip: string; }; }; + RuntimeToolsSettings: { + configModal: { + validDataIndexURLError: string; + dataIndexConnectionError: string; + }; + }; } export interface AppI18n extends AppDictionary, CommonI18n {} diff --git a/packages/serverless-logic-web-tools/src/i18n/locales/de.ts b/packages/serverless-logic-web-tools/src/i18n/locales/de.ts index fa0e1c330af..2c102f9781b 100644 --- a/packages/serverless-logic-web-tools/src/i18n/locales/de.ts +++ b/packages/serverless-logic-web-tools/src/i18n/locales/de.ts @@ -73,7 +73,6 @@ export const de: AppI18n = { validationError: "Sie müssen alle erforderlichen Felder ausfüllen, bevor Sie fortfahren können.", connectionError: "Verbindung abgelehnt. Bitte überprüfen Sie die angegebenen Informationen.", configExpiredWarning: "Token oder Konto ist abgelaufen. Bitte aktualisieren Sie Ihre Konfiguration.", - validDataIndexURLError: "Bitte geben Sie eine gültige Datenindex-URL ein.", }, confirmModal: { title: "Bereitstellen", @@ -113,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.", + }, + }, }; diff --git a/packages/serverless-logic-web-tools/src/i18n/locales/en.ts b/packages/serverless-logic-web-tools/src/i18n/locales/en.ts index d896ed0bf76..cb2359064e1 100644 --- a/packages/serverless-logic-web-tools/src/i18n/locales/en.ts +++ b/packages/serverless-logic-web-tools/src/i18n/locales/en.ts @@ -71,7 +71,6 @@ export const en: AppI18n = { validationError: "You must fill out all required fields before you can proceed.", connectionError: "Connection refused. Please check the information provided.", configExpiredWarning: "Token or account expired. Please update your configuration.", - validDataIndexURLError: "Please enter a valid Data Index URL.", }, confirmModal: { title: "Deploy", @@ -109,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.", + }, + }, }; diff --git a/packages/serverless-logic-web-tools/src/settings/runtimeTools/RuntimeToolsSettings.tsx b/packages/serverless-logic-web-tools/src/settings/runtimeTools/RuntimeToolsSettings.tsx index b11f4d32fd2..5549fd8a664 100644 --- a/packages/serverless-logic-web-tools/src/settings/runtimeTools/RuntimeToolsSettings.tsx +++ b/packages/serverless-logic-web-tools/src/settings/runtimeTools/RuntimeToolsSettings.tsx @@ -43,15 +43,17 @@ import { saveConfigCookie, } from "./RuntimeToolsConfig"; import { removeTrailingSlashFromUrl } from "../../url"; -import { validDataIndexUrl } 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 DataIndexValidation { +enum FormValiationOptions { INITIAL = "INITIAL", INVALID = "INVALID", + CONNECTION_ERROR = "CONNECTION_ERROR", } export function RuntimeToolsSettings(props: SettingsPageProps) { @@ -60,10 +62,12 @@ export function RuntimeToolsSettings(props: SettingsPageProps) { const settingsDispatch = useSettingsDispatch(); const [config, setConfig] = useState(settings.runtimeTools.config); const [isModalOpen, setIsModalOpen] = useState(false); - const [isDataIndexUrlValidated, setDataIndexUrlValidated] = useState(DataIndexValidation.INVALID); + const [isDataIndexUrlValidated, setDataIndexUrlValidated] = useState(FormValiationOptions.INITIAL); + const [isDataIndexUrlVerified, setisDataIndexUrlVerified] = useState(FormValiationOptions.INITIAL); + const [dataIndexUrlAvailable, setDataIndexUrlAvailable] = useState(false); useEffect(() => { - setDataIndexUrlValidated(DataIndexValidation.INITIAL); + setDataIndexUrlValidated(FormValiationOptions.INITIAL); }, [config]); const handleModalToggle = useCallback(() => { @@ -90,13 +94,21 @@ export function RuntimeToolsSettings(props: SettingsPageProps) { resetConfigCookie(); }, [settingsDispatch.runtimeTools]); - const onApply = useCallback(() => { + const onApply = useCallback(async () => { const newConfig: RuntimeToolsSettingsConfig = { dataIndexUrl: removeTrailingSlashFromUrl(config.dataIndexUrl), }; - if (!validDataIndexUrl(config.dataIndexUrl)) { - setDataIndexUrlValidated(DataIndexValidation.INVALID); + const isDataIndexUrlVerified = await verifyDataIndex(config.dataIndexUrl); + if (!isDataIndexUrlValid(config.dataIndexUrl)) { + setDataIndexUrlValidated(FormValiationOptions.INVALID); return; + } else { + if (isDataIndexUrlVerified == true) { + setDataIndexUrlAvailable(true); + } else { + setisDataIndexUrlVerified(FormValiationOptions.CONNECTION_ERROR); + return; + } } setConfig(newConfig); @@ -163,17 +175,28 @@ export function RuntimeToolsSettings(props: SettingsPageProps) { appendTo={props.pageContainerRef.current || document.body} > - {isDataIndexUrlValidated === DataIndexValidation.INVALID && ( + {isDataIndexUrlValidated === FormValiationOptions.INVALID && ( )} + {isDataIndexUrlVerified === FormValiationOptions.CONNECTION_ERROR && ( + + + + )} { +describe("isDataIndexUrlValid", () => { it.each([ + ["http://example.com", true], ["http://example.com/", true], ["https://example.com/", true], - ["loremIpsum", false], - ["google.com", false], - ])("should validate the data index URL", (inputUrl, isValidUrl) => { - expect(validDataIndexUrl(inputUrl)).toBe(isValidUrl); + ["ftps://example.com/", false], + ])("the data index URL %s validation should be %s", (inputUrl, isValidUrl) => { + expect(isDataIndexUrlValid(inputUrl)).toBe(isValidUrl); }); }); diff --git a/packages/sonataflow-deployment-webapp/src/context/AppContextProvider.tsx b/packages/sonataflow-deployment-webapp/src/context/AppContextProvider.tsx index d9e645f007c..f8f8822fc28 100644 --- a/packages/sonataflow-deployment-webapp/src/context/AppContextProvider.tsx +++ b/packages/sonataflow-deployment-webapp/src/context/AppContextProvider.tsx @@ -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(); diff --git a/packages/sonataflow-deployment-webapp/src/data/index.ts b/packages/sonataflow-deployment-webapp/src/data/index.ts index 37aa2884108..505d6362ea9 100644 --- a/packages/sonataflow-deployment-webapp/src/data/index.ts +++ b/packages/sonataflow-deployment-webapp/src/data/index.ts @@ -29,22 +29,3 @@ export async function fetchAppData(): Promise { const response = await fetch(routes.dataJson.path({})); return (await response.json()) as AppData; } - -export async function verifyDataIndex(dataIndexUrl?: string): Promise { - 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; - } -} From d91c8bda8011c9098f6c4f149a6c6b00d5098eec Mon Sep 17 00:00:00 2001 From: kumaradityaraj Date: Mon, 23 Sep 2024 18:19:22 +0530 Subject: [PATCH 3/3] Resolved suggested changes in states --- .../settings/runtimeTools/RuntimeToolsSettings.tsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/serverless-logic-web-tools/src/settings/runtimeTools/RuntimeToolsSettings.tsx b/packages/serverless-logic-web-tools/src/settings/runtimeTools/RuntimeToolsSettings.tsx index 5549fd8a664..79b253567e4 100644 --- a/packages/serverless-logic-web-tools/src/settings/runtimeTools/RuntimeToolsSettings.tsx +++ b/packages/serverless-logic-web-tools/src/settings/runtimeTools/RuntimeToolsSettings.tsx @@ -62,12 +62,11 @@ export function RuntimeToolsSettings(props: SettingsPageProps) { const settingsDispatch = useSettingsDispatch(); const [config, setConfig] = useState(settings.runtimeTools.config); const [isModalOpen, setIsModalOpen] = useState(false); - const [isDataIndexUrlValidated, setDataIndexUrlValidated] = useState(FormValiationOptions.INITIAL); - const [isDataIndexUrlVerified, setisDataIndexUrlVerified] = useState(FormValiationOptions.INITIAL); + const [isConfigValidated, setConfigValidated] = useState(FormValiationOptions.INITIAL); const [dataIndexUrlAvailable, setDataIndexUrlAvailable] = useState(false); useEffect(() => { - setDataIndexUrlValidated(FormValiationOptions.INITIAL); + setConfigValidated(FormValiationOptions.INITIAL); }, [config]); const handleModalToggle = useCallback(() => { @@ -100,13 +99,13 @@ export function RuntimeToolsSettings(props: SettingsPageProps) { }; const isDataIndexUrlVerified = await verifyDataIndex(config.dataIndexUrl); if (!isDataIndexUrlValid(config.dataIndexUrl)) { - setDataIndexUrlValidated(FormValiationOptions.INVALID); + setConfigValidated(FormValiationOptions.INVALID); return; } else { if (isDataIndexUrlVerified == true) { setDataIndexUrlAvailable(true); } else { - setisDataIndexUrlVerified(FormValiationOptions.CONNECTION_ERROR); + setConfigValidated(FormValiationOptions.CONNECTION_ERROR); return; } } @@ -175,7 +174,7 @@ export function RuntimeToolsSettings(props: SettingsPageProps) { appendTo={props.pageContainerRef.current || document.body} > - {isDataIndexUrlValidated === FormValiationOptions.INVALID && ( + {isConfigValidated === FormValiationOptions.INVALID && ( )} - {isDataIndexUrlVerified === FormValiationOptions.CONNECTION_ERROR && ( + {isConfigValidated === FormValiationOptions.CONNECTION_ERROR && (