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

dengliming pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu-dashboard.git


The following commit(s) were added to refs/heads/master by this push:
     new 9a1ae4cb fix: invoke JSON validator callback once (#667)
9a1ae4cb is described below

commit 9a1ae4cbc0dbec73f28e79a147a0b3e5b5210e3b
Author: vansh <[email protected]>
AuthorDate: Sun Sep 27 10:57:41 2026 +0530

    fix: invoke JSON validator callback once (#667)
    
    * fix: invoke JSON validator callback once
    
    Signed-off-by: vansh-nagar <[email protected]>
    
    * fix: reject untouched JSON rule handles
    
    ---------
    
    Signed-off-by: vansh-nagar <[email protected]>
---
 src/routes/Plugin/Common/CommonRuleHandle.js      | 54 +++++++------
 src/routes/Plugin/Common/CommonRuleHandle.test.js | 94 +++++++++++++++++++++++
 2 files changed, 126 insertions(+), 22 deletions(-)

diff --git a/src/routes/Plugin/Common/CommonRuleHandle.js 
b/src/routes/Plugin/Common/CommonRuleHandle.js
index 33da4d12..272d0ad1 100644
--- a/src/routes/Plugin/Common/CommonRuleHandle.js
+++ b/src/routes/Plugin/Common/CommonRuleHandle.js
@@ -33,6 +33,32 @@ const formItemLayout = {
   },
 };
 
+export const isValidHandleJSON = (handleType, value) => {
+  if (handleType !== "2") {
+    return true;
+  }
+
+  if (typeof value !== "string") {
+    return false;
+  }
+
+  try {
+    const parsed = JSON.parse(value);
+    return parsed !== null && parsed.constructor === Object;
+  } catch (e) {
+    return false;
+  }
+};
+
+export const validateHandleJSON = (
+  handleType,
+  value,
+  callback,
+  invalidMessage,
+) => {
+  callback(isValidHandleJSON(handleType, value) ? undefined : invalidMessage);
+};
+
 export default class CommonRuleHandle extends Component {
   render() {
     const labelWidth = 160;
@@ -241,28 +267,12 @@ export default class CommonRuleHandle extends Component {
             rules: [
               {
                 validator(rule, value, callback) {
-                  if (
-                    getFieldValue("handleType") === "1" ||
-                    typeof value !== "string"
-                  ) {
-                    callback();
-                  }
-                  if (getFieldValue("handleType") === "2") {
-                    try {
-                      const obj = JSON.parse(value);
-                      if (obj.constructor === Object) {
-                        callback();
-                      } else {
-                        callback(
-                          getIntlContent("SHENYU.PLUGIN.RULE.JSON.INVALID"),
-                        );
-                      }
-                    } catch (e) {
-                      callback(
-                        getIntlContent("SHENYU.PLUGIN.RULE.JSON.INVALID"),
-                      );
-                    }
-                  }
+                  validateHandleJSON(
+                    getFieldValue("handleType"),
+                    value,
+                    callback,
+                    getIntlContent("SHENYU.PLUGIN.RULE.JSON.INVALID"),
+                  );
                 },
               },
             ],
diff --git a/src/routes/Plugin/Common/CommonRuleHandle.test.js 
b/src/routes/Plugin/Common/CommonRuleHandle.test.js
new file mode 100644
index 00000000..2a99caf0
--- /dev/null
+++ b/src/routes/Plugin/Common/CommonRuleHandle.test.js
@@ -0,0 +1,94 @@
+/*
+ * 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 React from "react";
+import { Form } from "antd";
+import { act, render } from "@testing-library/react";
+import CommonRuleHandle, {
+  isValidHandleJSON,
+  validateHandleJSON,
+} from "./CommonRuleHandle";
+import { initIntl } from "../../../utils/IntlUtils";
+
+beforeAll(() => {
+  initIntl("en-US");
+});
+
+describe("isValidHandleJSON", () => {
+  it("accepts values while the structured form is active", () => {
+    expect(isValidHandleJSON("1", undefined)).toBe(true);
+  });
+
+  it("accepts JSON objects", () => {
+    expect(isValidHandleJSON("2", '{"enabled":true}')).toBe(true);
+  });
+
+  it.each([undefined, null, "", "null", "[]", "true", "not-json"])(
+    "rejects non-object JSON input %p",
+    (value) => {
+      expect(isValidHandleJSON("2", value)).toBe(false);
+    },
+  );
+});
+
+describe("validateHandleJSON", () => {
+  it.each([
+    ["1", undefined, undefined],
+    ["2", '{"enabled":true}', undefined],
+    ["2", "[]", "Invalid JSON object"],
+  ])("calls back once for handle type %s", (handleType, value, error) => {
+    const callback = jest.fn();
+
+    validateHandleJSON(handleType, value, callback, "Invalid JSON object");
+
+    expect(callback).toHaveBeenCalledTimes(1);
+    expect(callback).toHaveBeenCalledWith(error);
+  });
+
+  it("rejects an untouched JSON textarea in the actual form", async () => {
+    let form;
+    const Harness = Form.create()((props) => {
+      form = props.form;
+      return (
+        <Form>
+          <CommonRuleHandle
+            form={props.form}
+            pluginHandleList={[]}
+            multiRuleHandle={false}
+          />
+        </Form>
+      );
+    });
+
+    render(<Harness />);
+
+    let errors;
+    await act(
+      () =>
+        new Promise((resolve) => {
+          form.validateFields((validationErrors) => {
+            errors = validationErrors;
+            resolve();
+          });
+        }),
+    );
+
+    expect(form.getFieldValue("handleType")).toBe("2");
+    expect(form.getFieldValue("handleJSON")).toBeUndefined();
+    expect(errors.handleJSON.errors).toHaveLength(1);
+  });
+});

Reply via email to