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

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


The following commit(s) were added to refs/heads/master by this push:
     new 8b07c43  feat(plugin): use ajv to validate data (#1047)
8b07c43 is described below

commit 8b07c437f93f3be1fbf0320d021ce9115a7c4ce0
Author: 琚致远 <juzhiy...@apache.org>
AuthorDate: Fri Dec 18 22:05:53 2020 +0800

    feat(plugin): use ajv to validate data (#1047)
    
    * feat: use ajv to validate data
    
    * style: format codes
    
    * style: format codes
    
    * style: remove extra ;
    
    * style: format codes
---
 web/package.json                         |  2 +-
 web/src/components/Plugin/PluginPage.tsx | 67 ++++++++++++++++++++++++--------
 web/src/components/Plugin/service.ts     |  4 +-
 web/yarn.lock                            | 22 ++++++++++-
 4 files changed, 75 insertions(+), 20 deletions(-)

diff --git a/web/package.json b/web/package.json
index e66e9de..8a6382a 100644
--- a/web/package.json
+++ b/web/package.json
@@ -54,11 +54,11 @@
     "@rjsf/antd": "2.2.0",
     "@rjsf/core": "2.2.0",
     "@uiw/react-codemirror": "^3.0.1",
+    "ajv": "^7.0.0-rc.2",
     "antd": "^4.4.0",
     "classnames": "^2.2.6",
     "dayjs": "1.8.28",
     "js-beautify": "^1.13.0",
-    "json-schema": "0.2.5",
     "lodash": "^4.17.11",
     "moment": "^2.25.3",
     "nzh": "1.0.4",
diff --git a/web/src/components/Plugin/PluginPage.tsx 
b/web/src/components/Plugin/PluginPage.tsx
index 4224af2..e9b10af 100644
--- a/web/src/components/Plugin/PluginPage.tsx
+++ b/web/src/components/Plugin/PluginPage.tsx
@@ -18,7 +18,7 @@ import React, { useEffect, useState } from 'react';
 import { Anchor, Layout, Switch, Card, Tooltip, Button, notification, Avatar } 
from 'antd';
 import { SettingFilled } from '@ant-design/icons';
 import { PanelSection } from '@api7-dashboard/ui';
-import { validate } from 'json-schema';
+import Ajv, { DefinedError } from 'ajv';
 
 import { fetchSchema, getList } from './service';
 import CodeMirrorDrawer from './CodeMirrorDrawer';
@@ -43,11 +43,13 @@ const { Sider, Content } = Layout;
 // NOTE: use this flag as plugin's name to hide drawer
 const NEVER_EXIST_PLUGIN_FLAG = 'NEVER_EXIST_PLUGIN_FLAG';
 
+const ajv = new Ajv();
+
 const PluginPage: React.FC<Props> = ({
   readonly = false,
   initialData = {},
   schemaType = '',
-  onChange = () => {},
+  onChange = () => { },
 }) => {
   const [pluginList, setPlugin] = useState<PluginComponent.Meta[][]>([]);
   const [name, setName] = useState<string>(NEVER_EXIST_PLUGIN_FLAG);
@@ -56,30 +58,63 @@ const PluginPage: React.FC<Props> = ({
     getList().then(setPlugin);
   }, []);
 
+  // NOTE: This function has side effect because it mutates the original 
schema data
+  const injectDisableProperty = (schema: Record<string, any>) => {
+    // NOTE: The frontend will inject the disable property into schema just 
like the manager-api does
+    if (!schema.properties) {
+      // eslint-disable-next-line
+      schema.properties = {};
+    }
+    // eslint-disable-next-line
+    (schema.properties as any).disable = {
+      type: 'boolean',
+    };
+    return schema;
+  };
+
   const validateData = (pluginName: string, value: PluginComponent.Data) => {
     fetchSchema(pluginName, schemaType).then((schema) => {
-      // NOTE: The frontend will inject the disable property into schema just 
like the manager-api does
-      if (!schema.properties) {
-        // eslint-disable-next-line
-        schema.properties = {}
-      }
-      // eslint-disable-next-line
-      ;(schema.properties as any).disable = {
-        type: "boolean"
+      if (schema.oneOf) {
+        (schema.oneOf || []).forEach((item: any) => {
+          injectDisableProperty(item);
+        });
+      } else {
+        injectDisableProperty(schema);
       }
 
-      const { valid, errors } = validate(value, schema);
-      if (valid) {
+      const validate = ajv.compile(schema);
+
+      if (validate(value)) {
         setName(NEVER_EXIST_PLUGIN_FLAG);
         onChange({ ...initialData, [pluginName]: value });
         return;
       }
-      errors?.forEach((item) => {
+
+      // eslint-disable-next-line
+      for (const err of validate.errors as DefinedError[]) {
+        let description = '';
+        switch (err.keyword) {
+          case 'enum':
+            description = `${err.dataPath} ${err.message}: 
${err.params.allowedValues.join(
+              ', ',
+            )}`;
+            break;
+          case 'minItems':
+          case 'type':
+            description = `${err.dataPath} ${err.message}`;
+            break;
+          case 'oneOf':
+          case 'required':
+            description = err.message || '';
+            break;
+          default:
+            description = `${err.schemaPath} ${err.message}`;
+        }
         notification.error({
           message: 'Invalid plugin data',
-          description: item.message,
+          description,
         });
-      });
+      }
       setName(pluginName);
     });
   };
@@ -158,7 +193,7 @@ const PluginPage: React.FC<Props> = ({
                           if (isChecked) {
                             validateData(item.name, {
                               ...initialData[item.name],
-                              disable: false
+                              disable: false,
                             });
                           } else {
                             onChange({
diff --git a/web/src/components/Plugin/service.ts 
b/web/src/components/Plugin/service.ts
index 8bbcc2c..1f94ab6 100644
--- a/web/src/components/Plugin/service.ts
+++ b/web/src/components/Plugin/service.ts
@@ -14,9 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { JSONSchema7 } from 'json-schema';
 import { omit } from 'lodash';
 import { request } from 'umi';
+
 import { PLUGIN_MAPPER_SOURCE } from './data';
 
 enum Category {
@@ -80,7 +80,7 @@ const cachedPluginSchema: Record<string, object> = {
 export const fetchSchema = async (
   name: string,
   schemaType: PluginComponent.Schema,
-): Promise<JSONSchema7> => {
+): Promise<any> => {
   if (!cachedPluginSchema[schemaType][name]) {
     const queryString = schemaType !== 'route' ? `?schema_type=${schemaType}` 
: '';
     cachedPluginSchema[schemaType][name] = (
diff --git a/web/yarn.lock b/web/yarn.lock
index f9c7796..eacd092 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -3923,6 +3923,16 @@ ajv@^6.12.5:
     json-schema-traverse "^0.4.1"
     uri-js "^4.2.2"
 
+ajv@^7.0.0-rc.2:
+  version "7.0.0-rc.2"
+  resolved 
"https://registry.yarnpkg.com/ajv/-/ajv-7.0.0-rc.2.tgz#9c237b95072c1ee8c38e2df76422f37bacc9ae5e";
+  integrity 
sha512-D2iqHvbT3lszv5KSsTvJL9PSPf/2/s45i68vLXJmT124cxK/JOoOFyo/QnrgMKa2FHlVaMIsp1ZN1P4EH3bCKw==
+  dependencies:
+    fast-deep-equal "^3.1.1"
+    json-schema-traverse "^1.0.0"
+    require-from-string "^2.0.2"
+    uri-js "^4.2.2"
+
 alphanum-sort@^1.0.0:
   version "1.0.2"
   resolved 
"https://registry.npm.taobao.org/alphanum-sort/download/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3";
@@ -10654,12 +10664,17 @@ json-schema-traverse@^0.4.1:
   resolved 
"https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660";
   integrity sha1-afaofZUTq4u4/mO9sJecRI5oRmA=
 
+json-schema-traverse@^1.0.0:
+  version "1.0.0"
+  resolved 
"https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2";
+  integrity 
sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
+
 json-schema@0.2.3:
   version "0.2.3"
   resolved 
"https://registry.npm.taobao.org/json-schema/download/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13";
   integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
 
-json-schema@0.2.5, json-schema@^0.2.5:
+json-schema@^0.2.5:
   version "0.2.5"
   resolved 
"https://registry.npm.taobao.org/json-schema/download/json-schema-0.2.5.tgz#97997f50972dd0500214e208c407efa4b5d7063b";
   integrity sha1-l5l/UJct0FACFOIIxAfvpLXXBjs=
@@ -15485,6 +15500,11 @@ require-directory@^2.1.1:
   resolved 
"https://registry.npm.taobao.org/require-directory/download/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42";
   integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
 
+require-from-string@^2.0.2:
+  version "2.0.2"
+  resolved 
"https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909";
+  integrity 
sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
+
 require-main-filename@^2.0.0:
   version "2.0.0"
   resolved 
"https://registry.npm.taobao.org/require-main-filename/download/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b";

Reply via email to