Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(plugin): use ajv to validate data #1047

Merged
merged 6 commits into from
Dec 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
juzhiyuan marked this conversation as resolved.
Show resolved Hide resolved
"lodash": "^4.17.11",
"moment": "^2.25.3",
"nzh": "1.0.4",
Expand Down
67 changes: 51 additions & 16 deletions web/src/components/Plugin/PluginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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);
Expand All @@ -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>) => {
juzhiyuan marked this conversation as resolved.
Show resolved Hide resolved
// 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);
});
};
Expand Down Expand Up @@ -158,7 +193,7 @@ const PluginPage: React.FC<Props> = ({
if (isChecked) {
validateData(item.name, {
...initialData[item.name],
disable: false
disable: false,
});
} else {
onChange({
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/Plugin/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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] = (
Expand Down
22 changes: 21 additions & 1 deletion web/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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=
Expand Down Expand Up @@ -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"
Expand Down