This is an automated email from the ASF dual-hosted git repository.

fantonangeli pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-tools.git


The following commit(s) were added to refs/heads/main by this push:
     new 5ebd85c3518 kie-issues#2466: Serverless Logic Web Tools: Runtime Tools 
settings doesn't validate the data index url. (#2608)
5ebd85c3518 is described below

commit 5ebd85c351864ab86fb16714b34b5e996b3e7829
Author: Kumar Aditya Raj <[email protected]>
AuthorDate: Tue Oct 15 15:28:00 2024 +0530

    kie-issues#2466: Serverless Logic Web Tools: Runtime Tools settings doesn't 
validate the data index url. (#2608)
---
 .../src/gatewayApi/apis.tsx                        | 19 +++++++
 .../serverless-logic-web-tools/src/i18n/AppI18n.ts |  6 +++
 .../src/i18n/locales/de.ts                         |  6 +++
 .../src/i18n/locales/en.ts                         |  6 +++
 .../settings/runtimeTools/RuntimeToolsSettings.tsx | 58 ++++++++++++++++++++--
 .../serverless-logic-web-tools/src/url/index.ts    |  9 ++++
 .../url/dataIndexValidUrl.test.ts}                 | 21 ++++----
 .../src/context/AppContextProvider.tsx             |  3 +-
 .../sonataflow-deployment-webapp/src/data/index.ts | 19 -------
 9 files changed, 115 insertions(+), 32 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<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;
+  }
+}
diff --git a/packages/serverless-logic-web-tools/src/i18n/AppI18n.ts 
b/packages/serverless-logic-web-tools/src/i18n/AppI18n.ts
index 9824c624c08..3b1178924cc 100644
--- a/packages/serverless-logic-web-tools/src/i18n/AppI18n.ts
+++ b/packages/serverless-logic-web-tools/src/i18n/AppI18n.ts
@@ -105,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 f795fce6748..2c102f9781b 100644
--- a/packages/serverless-logic-web-tools/src/i18n/locales/de.ts
+++ b/packages/serverless-logic-web-tools/src/i18n/locales/de.ts
@@ -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.",
+    },
+  },
 };
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..cb2359064e1 100644
--- a/packages/serverless-logic-web-tools/src/i18n/locales/en.ts
+++ b/packages/serverless-logic-web-tools/src/i18n/locales/en.ts
@@ -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.",
+    },
+  },
 };
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..79b253567e4 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,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);
@@ -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);
@@ -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={
diff --git a/packages/serverless-logic-web-tools/src/url/index.ts 
b/packages/serverless-logic-web-tools/src/url/index.ts
index be6c6a1b06e..a9a65557128 100644
--- a/packages/serverless-logic-web-tools/src/url/index.ts
+++ b/packages/serverless-logic-web-tools/src/url/index.ts
@@ -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;
+  }
+}
diff --git a/packages/serverless-logic-web-tools/src/url/index.ts 
b/packages/serverless-logic-web-tools/tests/url/dataIndexValidUrl.test.ts
similarity index 66%
copy from packages/serverless-logic-web-tools/src/url/index.ts
copy to packages/serverless-logic-web-tools/tests/url/dataIndexValidUrl.test.ts
index be6c6a1b06e..2b67d22ef20 100644
--- a/packages/serverless-logic-web-tools/src/url/index.ts
+++ b/packages/serverless-logic-web-tools/tests/url/dataIndexValidUrl.test.ts
@@ -17,12 +17,15 @@
  * under the License.
  */
 
-/**
- * Remove the trailing slash from an URL if it exists.
- *
- * @param url -
- * @returns
- */
-export function removeTrailingSlashFromUrl(url: string): string {
-  return url.replace(/\/$/, "");
-}
+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);
+  });
+});
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<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;
-  }
-}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to