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);
+ });
+});