Skip to content

Commit

Permalink
feat(FE): fetch category from manager-api (#1122)
Browse files Browse the repository at this point in the history
* feat: fetch category from manager-api

* feat: add category sort

* feat: use consumer_schema in consumer module

* feat: use local PluginOrchestration components

* feat: add  license

* feat: pluginOrchestration fetch type from manager-api

* feat: remove json-schema

* feat: add pluginOrchestration i18n

* feat: add showList sort

* feat: clean code

* feat: update name

Co-authored-by: juzhiyuan <juzhiyuan@apache.org>
  • Loading branch information
LiteSun and juzhiyuan authored Dec 29, 2020
1 parent f009244 commit 8f94c84
Show file tree
Hide file tree
Showing 22 changed files with 950 additions and 247 deletions.
6 changes: 4 additions & 2 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@
"@ant-design/icons": "^4.0.0",
"@ant-design/pro-layout": "^6.0.0",
"@ant-design/pro-table": "2.6.3",
"@api7-dashboard/pluginchart": "^1.0.14",
"@api7-dashboard/ui": "^1.0.3",
"@mrblenny/react-flow-chart": "^0.0.14",
"@rjsf/antd": "2.2.0",
"@rjsf/core": "2.2.0",
"@uiw/react-codemirror": "^3.0.1",
Expand All @@ -73,13 +73,15 @@
"react-dom": "^16.8.6",
"react-helmet-async": "^1.0.4",
"start-server-and-test": "^1.11.5",
"styled-components": "^5.2.1",
"umi": "^3.1.2",
"umi-request": "^1.0.8",
"use-merge-value": "^1.0.1",
"uuid": "7.0.3"
},
"devDependencies": {
"@ant-design/pro-cli": "^2.0.2",
"@types/base-64": "^0.1.3",
"@types/classnames": "^2.2.7",
"@types/express": "^4.17.0",
"@types/history": "^4.7.2",
Expand All @@ -91,8 +93,8 @@
"@types/react": "^16.9.17",
"@types/react-dom": "^16.8.4",
"@types/react-helmet": "^5.0.13",
"@types/styled-components": "^5.1.7",
"@types/uuid": "7.0.4",
"@types/base-64": "^0.1.3",
"@umijs/fabric": "^2.2.0",
"@umijs/plugin-blocks": "^2.0.5",
"@umijs/plugin-esbuild": "^1.0.0-beta.2",
Expand Down
211 changes: 106 additions & 105 deletions web/src/components/Plugin/PluginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
* limitations under the License.
*/
import React, { useEffect, useState } from 'react';
import { Anchor, Layout, Switch, Card, Tooltip, Button, notification, Avatar } from 'antd';
import { Anchor, Layout, Switch, Card, Tooltip, Button, notification } from 'antd';
import { SettingFilled } from '@ant-design/icons';
import { PanelSection } from '@api7-dashboard/ui';
import Ajv, { DefinedError } from 'ajv';

import { fetchSchema, getList } from './service';
import { fetchList } from './service';
import CodeMirrorDrawer from './CodeMirrorDrawer';

type Props = {
Expand Down Expand Up @@ -51,11 +51,23 @@ const PluginPage: React.FC<Props> = ({
schemaType = '',
onChange = () => {},
}) => {
const [pluginList, setPlugin] = useState<PluginComponent.Meta[][]>([]);
const [pluginList, setPluginList] = useState<PluginComponent.Meta[]>([]);
const [name, setName] = useState<string>(NEVER_EXIST_PLUGIN_FLAG);
const [typeList, setTypeList] = useState<string[]>([]);

const firstUpperCase = ([first, ...rest]: string) => first.toUpperCase() + rest.join('');
useEffect(() => {
getList().then(setPlugin);
fetchList().then((data) => {
setPluginList(data);

const categoryList: string[] = [];
data.forEach((item) => {
if (!categoryList.includes(firstUpperCase(item.type))) {
categoryList.push(firstUpperCase(item.type));
}
});
setTypeList(categoryList.sort());
});
}, []);

// NOTE: This function has side effect because it mutates the original schema data
Expand All @@ -73,48 +85,55 @@ const PluginPage: React.FC<Props> = ({
};

const validateData = (pluginName: string, value: PluginComponent.Data) => {
fetchSchema(pluginName, schemaType).then((schema) => {
if (schema.oneOf) {
(schema.oneOf || []).forEach((item: any) => {
injectDisableProperty(item);
});
} else {
injectDisableProperty(schema);
}
const plugin = pluginList.find((item) => item.name === pluginName);
let schema: any = {};

const validate = ajv.compile(schema);
if (schemaType === 'consumer' && plugin?.consumer_schema) {
schema = plugin.consumer_schema;
} else if (plugin?.schema) {
schema = plugin.schema;
}

if (validate(value)) {
setName(NEVER_EXIST_PLUGIN_FLAG);
onChange({ ...initialData, [pluginName]: value });
return;
}
if (schema.oneOf) {
(schema.oneOf || []).forEach((item: any) => {
injectDisableProperty(item);
});
} else {
injectDisableProperty(schema);
}

// 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,
});
const validate = ajv.compile(schema);

if (validate(value)) {
setName(NEVER_EXIST_PLUGIN_FLAG);
onChange({ ...initialData, [pluginName]: value });
return;
}

// 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}`;
}
setName(pluginName);
});
notification.error({
message: 'Invalid plugin data',
description,
});
}
setName(pluginName);
};

return (
Expand All @@ -133,78 +152,60 @@ const PluginPage: React.FC<Props> = ({
<Layout>
<Sider theme="light">
<Anchor offsetTop={150}>
{pluginList.map((plugins) => {
const { category } = plugins[0];
return (
<Anchor.Link
href={`#plugin-category-${category}`}
title={category}
key={category}
/>
);
{typeList.map((type) => {
return <Anchor.Link href={`#plugin-category-${type}`} title={type} key={type} />;
})}
</Anchor>
</Sider>
<Content style={{ padding: '0 10px', backgroundColor: '#fff', minHeight: 1400 }}>
{pluginList.map((plugins) => {
const { category } = plugins[0];
{typeList.map((type) => {
return (
<PanelSection
title={category}
key={category}
title={type}
key={type}
style={PanelSectionStyle}
id={`plugin-category-${category}`}
id={`plugin-category-${type}`}
>
{plugins.map((item) => (
<Card
key={item.name}
title={[
item.avatar && (
<Avatar
key={1}
icon={item.avatar}
className="plugin-avatar"
style={{
marginRight: 5,
}}
/>
),
<span key={2}>{item.name}</span>,
]}
style={{ height: 66 }}
extra={[
<Tooltip title="Setting" key={`plugin-card-${item.name}-extra-tooltip-2`}>
<Button
shape="circle"
icon={<SettingFilled />}
style={{ marginRight: 10, marginLeft: 10 }}
size="middle"
onClick={() => {
setName(item.name);
{pluginList
.filter((item) => item.type === type.toLowerCase())
.map((item) => (
<Card
key={item.name}
title={[<span key={2}>{item.name}</span>]}
style={{ height: 66 }}
extra={[
<Tooltip title="Setting" key={`plugin-card-${item.name}-extra-tooltip-2`}>
<Button
shape="circle"
icon={<SettingFilled />}
style={{ marginRight: 10, marginLeft: 10 }}
size="middle"
onClick={() => {
setName(item.name);
}}
/>
</Tooltip>,
<Switch
defaultChecked={initialData[item.name] && !initialData[item.name].disable}
disabled={readonly}
onChange={(isChecked) => {
if (isChecked) {
validateData(item.name, {
...initialData[item.name],
disable: false,
});
} else {
onChange({
...initialData,
[item.name]: { ...initialData[item.name], disable: true },
});
}
}}
/>
</Tooltip>,
<Switch
defaultChecked={initialData[item.name] && !initialData[item.name].disable}
disabled={readonly}
onChange={(isChecked) => {
if (isChecked) {
validateData(item.name, {
...initialData[item.name],
disable: false,
});
} else {
onChange({
...initialData,
[item.name]: { ...initialData[item.name], disable: true },
});
}
}}
key={Math.random().toString(36).substring(7)}
/>,
]}
/>
))}
key={Math.random().toString(36).substring(7)}
/>,
]}
/>
))}
</PanelSection>
);
})}
Expand Down
53 changes: 4 additions & 49 deletions web/src/components/Plugin/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,55 +17,10 @@
import { omit } from 'lodash';
import { request } from 'umi';

import { PLUGIN_MAPPER_SOURCE } from './data';

enum Category {
'Limit traffic',
'Observability',
'Security',
'Authentication',
'Log',
'Other',
}

export const fetchList = () => request<Res<string[]>>('/plugins');

let cachedPluginNameList: string[] = [];
export const getList = async () => {
if (!cachedPluginNameList.length) {
cachedPluginNameList = (await fetchList()).data;
}
const names = cachedPluginNameList;
const data: Record<string, PluginComponent.Meta[]> = {};

names.forEach((name) => {
const plugin = PLUGIN_MAPPER_SOURCE[name] || {};
const { category = 'Other', hidden = false } = plugin;

// NOTE: assign it to Authentication plugin
if (name.includes('auth')) {
plugin.category = 'Authentication';
}

if (!data[category]) {
data[category] = [];
}

if (!hidden) {
data[category] = data[category].concat({
...plugin,
name,
});
}
});

return Object.keys(data)
.sort((a, b) => Category[a] - Category[b])
.map((category) => {
return data[category].sort((a, b) => {
return (a.priority || 9999) - (b.priority || 9999);
});
});
export const fetchList = () => {
return request<Res<PluginComponent.Meta[]>>('/plugins?all=true').then(data => {
return data.data;
})
};

/**
Expand Down
18 changes: 5 additions & 13 deletions web/src/components/Plugin/typing.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,12 @@ declare namespace PluginComponent {

type Schema = '' | 'route' | 'consumer' | 'service';

type Category =
| 'Security'
| 'Limit traffic'
| 'Log'
| 'Observability'
| 'Other'
| 'Authentication';

type Meta = {
name: string;
category: Category;
hidden?: boolean;
// Note: Plugins are sorted by priority under the same category in the frontend, the smaller the number, the higher the priority. The default value is 9999.
priority?: number;
avatar?: React.ReactNode;
priority: number;
schema: object;
type: string;
version: number;
consumer_schema?: object;
};
}
Loading

0 comments on commit 8f94c84

Please sign in to comment.