From 860887129c48d0058731d56f333b3600f1d363ec Mon Sep 17 00:00:00 2001 From: guoqqqi <979918879@qq.com> Date: Thu, 25 Feb 2021 16:44:20 +0800 Subject: [PATCH 01/31] fix: update code --- web/yarn.lock | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/web/yarn.lock b/web/yarn.lock index c956847dc7..e9f189d94f 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -2824,6 +2824,13 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== +"@types/react-copy-to-clipboard@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.0.tgz#38b035ca0c28334d3e0efaf3f319b81eea9690cd" + integrity sha512-faUg6Kx3Dfv0MBIcs+xzIptlRtjEVSaNjqyC14YAp4UwSiTHghnKtBOt9ERRTZZJfoJgnw10tomVaqG86GzdAw== + dependencies: + "@types/react" "*" + "@types/react-dom@^16.8.4", "@types/react-dom@^16.9.8": version "16.9.10" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.10.tgz#4485b0bec3d41f856181b717f45fd7831101156f" From 32c4216db615a12aabb229bfd5e4b8e43e27a3c6 Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Mon, 1 Mar 2021 15:26:20 +0800 Subject: [PATCH 02/31] feat: add plugin template config button --- web/src/pages/Route/List.tsx | 4 ++++ web/src/pages/Route/locales/en-US.ts | 1 + web/src/pages/Route/locales/zh-CN.ts | 1 + 3 files changed, 6 insertions(+) diff --git a/web/src/pages/Route/List.tsx b/web/src/pages/Route/List.tsx index 1abf05f946..0c3d98e17a 100644 --- a/web/src/pages/Route/List.tsx +++ b/web/src/pages/Route/List.tsx @@ -426,6 +426,10 @@ const Page: React.FC = () => { resetText: formatMessage({ id: 'component.global.reset' }), }} toolBarRender={() => [ + , + + { + remove(record.id!).then(() => { + handleTableActionSuccessResponse( + `${formatMessage({ id: 'component.global.delete' })} ${formatMessage({ + id: 'menu.pluginTemplate', + })} ${formatMessage({ id: 'component.status.success' })}`, + ); + }); + }} + okText={formatMessage({ id: 'component.global.confirm' })} + cancelText={formatMessage({ id: 'component.global.cancel' })} + > + + + + + ), + }, + ]; + + return ( + + + actionRef={ref} + rowKey="id" + search={false} + columns={columns} + request={fetchList} + toolBarRender={() => [ + , + ]} + /> + + ); +}; + +export default Page; diff --git a/web/src/pages/PluginTemplate/service.ts b/web/src/pages/PluginTemplate/service.ts new file mode 100644 index 0000000000..b4e2133828 --- /dev/null +++ b/web/src/pages/PluginTemplate/service.ts @@ -0,0 +1,33 @@ +/* + * 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 { request } from 'umi'; + +export const fetchList = ({ current = 1, pageSize = 10, ...res }) => + request('/plugin_configs', { + params: { + name: res.name, + page: current, + page_size: pageSize, + }, + }).then(({ data }) => { + return { + data: data.rows, + total: data.total_size, + } + }); + +export const remove = (rid: string) => request(`/plugin_configs/${rid}`, { method: 'DELETE' }); diff --git a/web/src/pages/Route/List.tsx b/web/src/pages/Route/List.tsx index 0c3d98e17a..cba989fbd6 100644 --- a/web/src/pages/Route/List.tsx +++ b/web/src/pages/Route/List.tsx @@ -426,7 +426,7 @@ const Page: React.FC = () => { resetText: formatMessage({ id: 'component.global.reset' }), }} toolBarRender={() => [ - , From 7bb2675fcf50e1ed95fbcfdfdddd2680bb494c2a Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Tue, 2 Mar 2021 17:27:01 +0800 Subject: [PATCH 04/31] feat: update plugin template list --- web/src/pages/PluginTemplate/List.tsx | 23 ++++++++++++++++++++--- web/src/pages/PluginTemplate/typing.d.ts | 24 ++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 web/src/pages/PluginTemplate/typing.d.ts diff --git a/web/src/pages/PluginTemplate/List.tsx b/web/src/pages/PluginTemplate/List.tsx index 1728e5934a..f9419e5504 100644 --- a/web/src/pages/PluginTemplate/List.tsx +++ b/web/src/pages/PluginTemplate/List.tsx @@ -19,7 +19,7 @@ import { PageHeaderWrapper } from '@ant-design/pro-layout'; import { history, useIntl } from 'umi'; import ProTable from '@ant-design/pro-table'; import type { ActionType, ProColumns } from '@ant-design/pro-table'; -import { Button, notification, Popconfirm, Space } from 'antd'; +import { Button, notification, Popconfirm, Space, Tag } from 'antd'; import { PlusOutlined } from '@ant-design/icons'; import { fetchList, remove } from './service'; @@ -36,11 +36,28 @@ const Page: React.FC = () => { ref.current?.reload(); }; - const columns: ProColumns[] = [ + const columns: ProColumns[] = [ + { + title: 'ID', + dataIndex: 'id', + hideInSearch: true, + }, { title: formatMessage({ id: 'component.global.description' }), dataIndex: 'desc', }, + { + title: formatMessage({ id: 'component.global.labels' }), + dataIndex: 'labels', + render: (_, record) => { + return Object.keys(record.labels || {}) + .map((item) => ( + + {item}:{record.labels[item]} + + )); + } + }, { title: formatMessage({ id: 'component.global.operation' }), valueType: 'option', @@ -83,7 +100,7 @@ const Page: React.FC = () => { return ( - + actionRef={ref} rowKey="id" search={false} diff --git a/web/src/pages/PluginTemplate/typing.d.ts b/web/src/pages/PluginTemplate/typing.d.ts new file mode 100644 index 0000000000..2631d6995b --- /dev/null +++ b/web/src/pages/PluginTemplate/typing.d.ts @@ -0,0 +1,24 @@ +/* + * 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. + */ + +declare namespace PluginTemplateModule { + type PluginTemplate = { + id: string; + desc: string; + labels: Record; + }; +} From b670a585cfd981bba11e9d3a73a72798185cbcac Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Tue, 2 Mar 2021 18:11:42 +0800 Subject: [PATCH 05/31] feat: add create plugTemplate page --- web/config/routes.ts | 2 +- web/src/pages/PluginTemplate/Create.tsx | 87 ++++++++++++++++++- web/src/pages/PluginTemplate/List.tsx | 4 +- .../PluginTemplate/components/Preview.tsx | 37 ++++++++ .../pages/PluginTemplate/components/Step1.tsx | 52 +++++++++++ web/src/pages/PluginTemplate/service.ts | 15 ++++ web/src/pages/PluginTemplate/typing.d.ts | 9 +- 7 files changed, 199 insertions(+), 7 deletions(-) create mode 100644 web/src/pages/PluginTemplate/components/Preview.tsx create mode 100644 web/src/pages/PluginTemplate/components/Step1.tsx diff --git a/web/config/routes.ts b/web/config/routes.ts index 609314d43f..05b78e25ff 100644 --- a/web/config/routes.ts +++ b/web/config/routes.ts @@ -108,7 +108,7 @@ const routes = [ component: './pluginTemplate/Create', }, { - path: '/pluginTemplate/:pluginTemplateId/edit', + path: '/pluginTemplate/:id/edit', component: './pluginTemplate/Create', }, { diff --git a/web/src/pages/PluginTemplate/Create.tsx b/web/src/pages/PluginTemplate/Create.tsx index 25e839bc58..f1455dca8d 100644 --- a/web/src/pages/PluginTemplate/Create.tsx +++ b/web/src/pages/PluginTemplate/Create.tsx @@ -14,13 +14,96 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React from 'react'; +import React, { useState, useEffect } from 'react'; +import { PageContainer } from '@ant-design/pro-layout'; +import { Card, Steps, notification, Form } from 'antd'; +import { history, useIntl } from 'umi'; + +import ActionBar from '@/components/ActionBar'; +import PluginPage from '@/components/Plugin'; + +import Step1 from './components/Step1'; +import Preview from './components/Preview'; +import { fetchItem, create, update, } from './service'; const Page: React.FC = (props) => { + const [step, setStep] = useState(1); + const [plugins, setPlugins] = useState({}); + const [form1] = Form.useForm(); + const { formatMessage } = useIntl(); + + useEffect(() => { + const { id } = (props as any).match.params; + if (id) { + fetchItem(id).then(({ data }) => { + const { desc, ...rest } = data; + form1.setFieldsValue({ id, desc }); + setPlugins(rest.plugins); + }); + } + }, []); + + const onSubmit = () => { + const data = { ...form1.getFieldsValue(), plugins } as PluginTemplateModule.Entity; + const { id } = (props as any).match.params; + (id ? update(id, data) : create(data)) + .then(() => { + notification.success({ + message: `${id + ? formatMessage({ id: 'component.global.edit' }) + : formatMessage({ id: 'component.global.create' }) + } ${formatMessage({ id: 'menu.pluginTemplate' })} ${formatMessage({ + id: 'component.status.success', + })}`, + }); + history.push('/pluginTemplate/list'); + }) + .catch(() => { + setStep(3); + }); + }; + + const onStepChange = (nextStep: number) => { + if (step === 1) { + form1.validateFields().then(() => { + setStep(nextStep); + }); + } else if (nextStep === 3) { + setStep(3); + } else if (nextStep === 4) { + onSubmit(); + } else { + setStep(nextStep); + } + }; return ( <> - hello world! + + + + + + + + + {step === 1 && } + {step === 2 && ( + + )} + {step === 3 && } + + + ); }; diff --git a/web/src/pages/PluginTemplate/List.tsx b/web/src/pages/PluginTemplate/List.tsx index f9419e5504..95bb0e188e 100644 --- a/web/src/pages/PluginTemplate/List.tsx +++ b/web/src/pages/PluginTemplate/List.tsx @@ -36,7 +36,7 @@ const Page: React.FC = () => { ref.current?.reload(); }; - const columns: ProColumns[] = [ + const columns: ProColumns[] = [ { title: 'ID', dataIndex: 'id', @@ -100,7 +100,7 @@ const Page: React.FC = () => { return ( - + actionRef={ref} rowKey="id" search={false} diff --git a/web/src/pages/PluginTemplate/components/Preview.tsx b/web/src/pages/PluginTemplate/components/Preview.tsx new file mode 100644 index 0000000000..4e140ad3a9 --- /dev/null +++ b/web/src/pages/PluginTemplate/components/Preview.tsx @@ -0,0 +1,37 @@ +/* + * 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 type { FormInstance } from 'antd/lib/form'; + +import PluginPage from '@/components/Plugin'; +import Step1 from './Step1'; + +type Props = { + form1: FormInstance; + plugins: PluginComponent.Data; +}; + +const Page: React.FC = ({ form1, plugins }) => { + return ( + <> + + + + ); +}; + +export default Page; diff --git a/web/src/pages/PluginTemplate/components/Step1.tsx b/web/src/pages/PluginTemplate/components/Step1.tsx new file mode 100644 index 0000000000..66d238781f --- /dev/null +++ b/web/src/pages/PluginTemplate/components/Step1.tsx @@ -0,0 +1,52 @@ +/* + * 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, Input } from 'antd'; +import type { FormInstance } from 'antd/lib/form'; +import { useIntl } from 'umi'; + +const FORM_LAYOUT = { + labelCol: { + span: 2, + }, + wrapperCol: { + span: 8, + }, +}; + +type Props = { + form: FormInstance; + disabled?: boolean; +}; + +const Step1: React.FC = ({ form, disabled }) => { + const { formatMessage } = useIntl(); + return ( +
+ + + +
+ ); +}; + +export default Step1; diff --git a/web/src/pages/PluginTemplate/service.ts b/web/src/pages/PluginTemplate/service.ts index b4e2133828..a1ca222bfd 100644 --- a/web/src/pages/PluginTemplate/service.ts +++ b/web/src/pages/PluginTemplate/service.ts @@ -31,3 +31,18 @@ export const fetchList = ({ current = 1, pageSize = 10, ...res }) => }); export const remove = (rid: string) => request(`/plugin_configs/${rid}`, { method: 'DELETE' }); + +export const fetchItem = (id: string) => + request<{ data: PluginTemplateModule.ResEntity }>(`/plugin_configs/${id}`); + +export const create = (data: PluginTemplateModule.Entity) => + request('/plugin_configs', { + method: 'POST', + data, + }); + +export const update = (id: string, data: PluginTemplateModule.Entity) => + request(`/plugin_configs/${id}`, { + method: 'PATCH', + data, + }); diff --git a/web/src/pages/PluginTemplate/typing.d.ts b/web/src/pages/PluginTemplate/typing.d.ts index 2631d6995b..4e2ee88655 100644 --- a/web/src/pages/PluginTemplate/typing.d.ts +++ b/web/src/pages/PluginTemplate/typing.d.ts @@ -16,9 +16,14 @@ */ declare namespace PluginTemplateModule { - type PluginTemplate = { - id: string; + type Entity = { desc: string; labels: Record; + plugins: Record; + }; + + type ResEntity = Entity & { + id: string; + update_time: string; }; } From 2afac227bbca65df5be394bead9f3193a096e19b Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Tue, 2 Mar 2021 19:29:08 +0800 Subject: [PATCH 06/31] feat: add pluginTemplate selector --- web/src/components/Plugin/PluginPage.tsx | 52 ++++++++++++++++++++-- web/src/components/Plugin/locales/en-US.ts | 2 + web/src/components/Plugin/locales/zh-CN.ts | 2 + web/src/components/Plugin/service.ts | 6 +++ 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/web/src/components/Plugin/PluginPage.tsx b/web/src/components/Plugin/PluginPage.tsx index a8a811733a..b38bd66870 100644 --- a/web/src/components/Plugin/PluginPage.tsx +++ b/web/src/components/Plugin/PluginPage.tsx @@ -15,13 +15,13 @@ * limitations under the License. */ import React, { useEffect, useState } from 'react'; -import { Anchor, Layout, Card, Button } from 'antd'; +import { Anchor, Layout, Card, Button, Form, Select } from 'antd'; import { PanelSection } from '@api7-dashboard/ui'; import { omit, orderBy } from 'lodash'; import { useIntl } from 'umi'; import PluginDetail from './PluginDetail'; -import { fetchList } from './service'; +import { fetchList, fetchPluginTemplateList } from './service'; import { PLUGIN_ICON_LIST, PLUGIN_FILTER_LIST } from './data'; import defaultPluginImg from '../../../public/static/default-plugin.png'; @@ -31,6 +31,7 @@ type Props = { initialData?: PluginComponent.Data; schemaType?: PluginComponent.Schema; referPage?: PluginComponent.ReferPage; + showSelector: boolean, onChange?: (data: PluginComponent.Data) => void; }; @@ -53,11 +54,13 @@ const PluginPage: React.FC = ({ schemaType = 'route', referPage = '', type = 'scoped', - onChange = () => {}, + showSelector = true, + onChange = () => { }, }) => { const { formatMessage } = useIntl(); - + const [form] = Form.useForm(); const [pluginList, setPluginList] = useState([]); + const [pluginTemplateList, setPluginTemplateList] = useState([]); const [name, setName] = useState(NEVER_EXIST_PLUGIN_FLAG); const [typeList, setTypeList] = useState([]); @@ -79,6 +82,9 @@ const PluginPage: React.FC = ({ }); setTypeList(categoryList.sort()); }); + fetchPluginTemplateList().then((data) => { + setPluginTemplateList(data); + }) }, []); const PluginList = () => ( @@ -104,6 +110,44 @@ const PluginPage: React.FC = ({ +
+ {showSelector && ( + { + if (prev.plugin_config_id !== next.plugin_config_id) { + const id = next.plugin_config_id; + if (id) { + form.setFieldsValue({ + plugin_config_id: id, + }); + } + } + return prev.plugin_config_id !== next.plugin_config_id; + }} + > + + + )} +
{typeList.map((typeItem) => { return ( { + return request>>('/plugin_configs').then((data) => { + return data.data.rows; + }); +}; From 7c7e5e854bc7dd5439aa60c4001241eeafbea3f3 Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Wed, 3 Mar 2021 11:04:58 +0800 Subject: [PATCH 07/31] =?UTF-8?q?feat=EF=BC=9A=20add=20tips?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/components/Plugin/PluginPage.tsx | 82 ++++++++++--------- web/src/components/Plugin/locales/en-US.ts | 4 +- web/src/components/Plugin/locales/zh-CN.ts | 4 +- .../pages/Route/components/Step3/index.tsx | 1 + 4 files changed, 51 insertions(+), 40 deletions(-) diff --git a/web/src/components/Plugin/PluginPage.tsx b/web/src/components/Plugin/PluginPage.tsx index b38bd66870..8872acb5d1 100644 --- a/web/src/components/Plugin/PluginPage.tsx +++ b/web/src/components/Plugin/PluginPage.tsx @@ -15,7 +15,7 @@ * limitations under the License. */ import React, { useEffect, useState } from 'react'; -import { Anchor, Layout, Card, Button, Form, Select } from 'antd'; +import { Anchor, Layout, Card, Button, Form, Select, Alert } from 'antd'; import { PanelSection } from '@api7-dashboard/ui'; import { omit, orderBy } from 'lodash'; import { useIntl } from 'umi'; @@ -31,7 +31,7 @@ type Props = { initialData?: PluginComponent.Data; schemaType?: PluginComponent.Schema; referPage?: PluginComponent.ReferPage; - showSelector: boolean, + showSelector?: boolean, onChange?: (data: PluginComponent.Data) => void; }; @@ -54,7 +54,7 @@ const PluginPage: React.FC = ({ schemaType = 'route', referPage = '', type = 'scoped', - showSelector = true, + showSelector = false, onChange = () => { }, }) => { const { formatMessage } = useIntl(); @@ -110,44 +110,50 @@ const PluginPage: React.FC = ({ -
- {showSelector && ( - { - if (prev.plugin_config_id !== next.plugin_config_id) { - const id = next.plugin_config_id; - if (id) { - form.setFieldsValue({ - plugin_config_id: id, - }); + {showSelector && ( + <> + + { + if (prev.plugin_config_id !== next.plugin_config_id) { + const id = next.plugin_config_id; + if (id) { + form.setFieldsValue({ + plugin_config_id: id, + }); + } } - } - return prev.plugin_config_id !== next.plugin_config_id; - }} - > - - - )} - + +
+ + +

{formatMessage({id:'component.plugin.pluginTemplate.tips1'})}

+

{formatMessage({id:'component.plugin.pluginTemplate.tips2'})}

+ } type="info" /> + + )} {typeList.map((typeItem) => { return ( = ({ data, onChange, readonly = false, isForceHttps initialData={plugins} schemaType="route" referPage="route" + showSelector onChange={(pluginsData) => onChange({ plugins: pluginsData, script: {} })} /> )} From 6e219cc3dfa51f468693b64c26a133d3f87c013e Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Wed, 3 Mar 2021 11:11:30 +0800 Subject: [PATCH 08/31] feat: update --- web/src/components/Plugin/PluginPage.tsx | 6 +++--- web/src/components/Plugin/locales/zh-CN.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/web/src/components/Plugin/PluginPage.tsx b/web/src/components/Plugin/PluginPage.tsx index 8872acb5d1..ce96dbd9af 100644 --- a/web/src/components/Plugin/PluginPage.tsx +++ b/web/src/components/Plugin/PluginPage.tsx @@ -136,13 +136,13 @@ const PluginPage: React.FC = ({ > {[ { - name: formatMessage({ id: 'component.step.select.pluginTemplate.select.option' }), - id: '', + id:'', + desc: formatMessage({ id: 'component.step.select.pluginTemplate.select.option' }), }, ...pluginTemplateList, ].map((item) => ( - {item.id} + {item.desc} ))} diff --git a/web/src/components/Plugin/locales/zh-CN.ts b/web/src/components/Plugin/locales/zh-CN.ts index dca7851037..425d211145 100644 --- a/web/src/components/Plugin/locales/zh-CN.ts +++ b/web/src/components/Plugin/locales/zh-CN.ts @@ -19,6 +19,6 @@ export default { 'component.plugin.tip2': '如何更新?', "component.select.pluginTemplate": '选择插件模板', 'component.step.select.pluginTemplate.select.option': '手动配置', - 'component.plugin.pluginTemplate.tips1': '1.如果这个路由已经配置了 插件,那么 插件模板 里面的插件配置会合并进去。', + 'component.plugin.pluginTemplate.tips1': '1.如果这个路由已经配置了插件,那么插件模板里面的插件配置会合并进去。', 'component.plugin.pluginTemplate.tips2': '2.插件模板相同的插件会覆盖掉原有的插件。' }; From 88ebe373aca2dca572a355e391ea504d2d85cb9d Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Wed, 3 Mar 2021 12:20:11 +0800 Subject: [PATCH 09/31] feat: update plugin page --- web/src/components/Plugin/PluginPage.tsx | 24 +++++++++++++------ web/src/pages/Route/Create.tsx | 13 +++++----- .../pages/Route/components/Step3/index.tsx | 10 +++++--- web/src/pages/Route/constants.ts | 1 + web/src/pages/Route/typing.d.ts | 1 + 5 files changed, 32 insertions(+), 17 deletions(-) diff --git a/web/src/components/Plugin/PluginPage.tsx b/web/src/components/Plugin/PluginPage.tsx index ce96dbd9af..ced72c7d80 100644 --- a/web/src/components/Plugin/PluginPage.tsx +++ b/web/src/components/Plugin/PluginPage.tsx @@ -28,11 +28,12 @@ import defaultPluginImg from '../../../public/static/default-plugin.png'; type Props = { readonly?: boolean; type?: 'global' | 'scoped'; - initialData?: PluginComponent.Data; + initialData?: PluginComponent.Data, + plugin_config_id?: string, schemaType?: PluginComponent.Schema; referPage?: PluginComponent.ReferPage; showSelector?: boolean, - onChange?: (data: PluginComponent.Data) => void; + onChange?: (plugins: PluginComponent.Data, plugin_config_id?: string) => void; }; const PanelSectionStyle = { @@ -51,6 +52,7 @@ const NEVER_EXIST_PLUGIN_FLAG = 'NEVER_EXIST_PLUGIN_FLAG'; const PluginPage: React.FC = ({ readonly = false, initialData = {}, + plugin_config_id = "", schemaType = 'route', referPage = '', type = 'scoped', @@ -63,9 +65,11 @@ const PluginPage: React.FC = ({ const [pluginTemplateList, setPluginTemplateList] = useState([]); const [name, setName] = useState(NEVER_EXIST_PLUGIN_FLAG); const [typeList, setTypeList] = useState([]); + const [plugins, setPlugins] = useState({}); const firstUpperCase = ([first, ...rest]: string) => first.toUpperCase() + rest.join(''); useEffect(() => { + setPlugins(initialData); fetchList().then((data) => { const filteredData = data.filter( (item) => @@ -84,6 +88,7 @@ const PluginPage: React.FC = ({ }); fetchPluginTemplateList().then((data) => { setPluginTemplateList(data); + form.setFieldsValue({ plugin_config_id }) }) }, []); @@ -112,7 +117,7 @@ const PluginPage: React.FC = ({ {showSelector && ( <> -
+ = ({ data-cy="pluginTemplateSelector" disabled={readonly} onChange={(plugin_config_id) => { + form.setFieldsValue({ + plugin_config_id, + }); + onChange(plugins, plugin_config_id as string); }} > {[ { - id:'', + id: '', desc: formatMessage({ id: 'component.step.select.pluginTemplate.select.option' }), }, ...pluginTemplateList, @@ -149,8 +158,8 @@ const PluginPage: React.FC = ({
-

{formatMessage({id:'component.plugin.pluginTemplate.tips1'})}

-

{formatMessage({id:'component.plugin.pluginTemplate.tips2'})}

+

{formatMessage({ id: 'component.plugin.pluginTemplate.tips1' })}

+

{formatMessage({ id: 'component.plugin.pluginTemplate.tips2' })}

} type="info" /> )} @@ -244,7 +253,8 @@ const PluginPage: React.FC = ({ if (shouldDelete === true) { plugins = omit(plugins, name); } - onChange(plugins); + onChange(plugins, form.getFieldValue('plugin_config_id')); + setPlugins(plugins); setName(NEVER_EXIST_PLUGIN_FLAG); }} /> diff --git a/web/src/pages/Route/Create.tsx b/web/src/pages/Route/Create.tsx index 0de1d737ce..f6ac4dd2ac 100644 --- a/web/src/pages/Route/Create.tsx +++ b/web/src/pages/Route/Create.tsx @@ -149,8 +149,8 @@ const Page: React.FC = (props) => { { - setStep3Data({ plugins, script }); + onChange={({ plugins, script = INIT_CHART, plugin_config_id }) => { + setStep3Data({ plugins, script, plugin_config_id }); setChart(script); }} /> @@ -262,11 +262,10 @@ const Page: React.FC = (props) => { return ( <> diff --git a/web/src/pages/Route/components/Step3/index.tsx b/web/src/pages/Route/components/Step3/index.tsx index 7aeba5dc70..c2407d42e1 100644 --- a/web/src/pages/Route/components/Step3/index.tsx +++ b/web/src/pages/Route/components/Step3/index.tsx @@ -26,8 +26,9 @@ type Props = { data: { plugins: PluginComponent.Data; script: Record; + plugin_config_id?: string; }; - onChange: (data: { plugins: PluginComponent.Data; script: any }) => void; + onChange: (data: { plugins: PluginComponent.Data; script: any, plugin_config_id?: string; }) => void; readonly?: boolean; isForceHttps: boolean; }; @@ -35,7 +36,7 @@ type Props = { type Mode = 'NORMAL' | 'DRAW'; const Page: React.FC = ({ data, onChange, readonly = false, isForceHttps }) => { - const { plugins = {}, script = {} } = data; + const { plugins = {}, script = {}, plugin_config_id = '' } = data; // NOTE: Currently only compatible with chrome const disableDraw = !isChrome || isForceHttps; @@ -84,10 +85,13 @@ const Page: React.FC = ({ data, onChange, readonly = false, isForceHttps {Boolean(mode === 'NORMAL') && ( onChange({ plugins: pluginsData, script: {} })} + onChange={(pluginsData, plugin_config_id) => { + onChange({ plugins: pluginsData, script: {}, plugin_config_id }) + }} /> )} {Boolean(mode === 'DRAW') && ( diff --git a/web/src/pages/Route/constants.ts b/web/src/pages/Route/constants.ts index 1b7a4c4dd3..b5812e4b1c 100644 --- a/web/src/pages/Route/constants.ts +++ b/web/src/pages/Route/constants.ts @@ -60,6 +60,7 @@ export const DEFAULT_STEP_1_DATA: RouteModule.Form1Data = { export const DEFAULT_STEP_3_DATA: RouteModule.Step3Data = { plugins: {}, script: {}, + plugin_config_id: "" }; export const INIT_CHART = { diff --git a/web/src/pages/Route/typing.d.ts b/web/src/pages/Route/typing.d.ts index d56228b761..0fd041ffbb 100644 --- a/web/src/pages/Route/typing.d.ts +++ b/web/src/pages/Route/typing.d.ts @@ -34,6 +34,7 @@ declare namespace RouteModule { plugins: PluginPage.PluginData; // TEMP script: any; + plugin_config_id?: string }; type UpstreamHost = { From b5467fd27f37232dd570abc655947b4283b8753a Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Wed, 3 Mar 2021 14:20:00 +0800 Subject: [PATCH 10/31] feat: update --- web/src/pages/Route/transform.ts | 3 ++- web/src/pages/Route/typing.d.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/web/src/pages/Route/transform.ts b/web/src/pages/Route/transform.ts index 3b9fa4f09f..6534c4cae1 100644 --- a/web/src/pages/Route/transform.ts +++ b/web/src/pages/Route/transform.ts @@ -222,11 +222,12 @@ export const transformRouteData = (data: RouteModule.Body) => { const form2Data: RouteModule.Form2Data = upstream || { upstream_id }; - const { plugins, script } = data; + const { plugins, script, plugin_config_id = '' } = data; const step3Data: RouteModule.Step3Data = { plugins, script, + plugin_config_id, }; return { diff --git a/web/src/pages/Route/typing.d.ts b/web/src/pages/Route/typing.d.ts index 0fd041ffbb..d2c9260b2c 100644 --- a/web/src/pages/Route/typing.d.ts +++ b/web/src/pages/Route/typing.d.ts @@ -91,6 +91,7 @@ declare namespace RouteModule { }; upstream_id?: string; plugins: Record; + plugin_config_id?: string; script: Record; url?: string; enable_websocket?: boolean; From eac99c56cba6ffba1f09856cb5eccaf431a0301c Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Wed, 3 Mar 2021 15:01:05 +0800 Subject: [PATCH 11/31] feat: update label search --- web/src/pages/PluginTemplate/List.tsx | 47 ++++++++++++++++++++++--- web/src/pages/PluginTemplate/service.ts | 16 +++++++-- web/src/typings.d.ts | 4 +++ 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/web/src/pages/PluginTemplate/List.tsx b/web/src/pages/PluginTemplate/List.tsx index 95bb0e188e..68bce3da5b 100644 --- a/web/src/pages/PluginTemplate/List.tsx +++ b/web/src/pages/PluginTemplate/List.tsx @@ -14,20 +14,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, { useRef } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { PageHeaderWrapper } from '@ant-design/pro-layout'; import { history, useIntl } from 'umi'; import ProTable from '@ant-design/pro-table'; import type { ActionType, ProColumns } from '@ant-design/pro-table'; -import { Button, notification, Popconfirm, Space, Tag } from 'antd'; +import { Button, notification, Popconfirm, Select, Space, Tag } from 'antd'; import { PlusOutlined } from '@ant-design/icons'; -import { fetchList, remove } from './service'; +import { fetchList, remove, fetchLabelList } from './service'; const Page: React.FC = () => { const ref = useRef(); + const [labelList, setLabelList] = useState({}); const { formatMessage } = useIntl(); + useEffect(() => { + fetchLabelList().then(setLabelList); + }, []); + const handleTableActionSuccessResponse = (msgTip: string) => { notification.success({ message: msgTip, @@ -56,7 +61,40 @@ const Page: React.FC = () => { {item}:{record.labels[item]} )); - } + }, renderFormItem: (_, { type }) => { + if (type === 'form') { + return null; + } + + return ( + + ); + }, }, { title: formatMessage({ id: 'component.global.operation' }), @@ -103,7 +141,6 @@ const Page: React.FC = () => { actionRef={ref} rowKey="id" - search={false} columns={columns} request={fetchList} toolBarRender={() => [ diff --git a/web/src/pages/PluginTemplate/service.ts b/web/src/pages/PluginTemplate/service.ts index a1ca222bfd..8f204accc1 100644 --- a/web/src/pages/PluginTemplate/service.ts +++ b/web/src/pages/PluginTemplate/service.ts @@ -15,11 +15,15 @@ * limitations under the License. */ import { request } from 'umi'; +import { transformLabelList } from '../Route/transform'; -export const fetchList = ({ current = 1, pageSize = 10, ...res }) => - request('/plugin_configs', { +export const fetchList = ({ current = 1, pageSize = 10, ...res }) => { + const { labels = [] } = res; + + return request('/plugin_configs', { params: { - name: res.name, + desc: res.desc, + label: labels.join(','), page: current, page_size: pageSize, }, @@ -29,6 +33,7 @@ export const fetchList = ({ current = 1, pageSize = 10, ...res }) => total: data.total_size, } }); +} export const remove = (rid: string) => request(`/plugin_configs/${rid}`, { method: 'DELETE' }); @@ -46,3 +51,8 @@ export const update = (id: string, data: PluginTemplateModule.Entity) => method: 'PATCH', data, }); + +export const fetchLabelList = () => + request('/labels/plugin_config').then( + ({ data }) => transformLabelList(data.rows) as LabelList, + ); diff --git a/web/src/typings.d.ts b/web/src/typings.d.ts index fa45e4d3e9..dc17a7c88f 100644 --- a/web/src/typings.d.ts +++ b/web/src/typings.d.ts @@ -69,3 +69,7 @@ type ResListData = { }; type HttpMethod = 'GET' | 'POST' | 'DELETE' | 'PUT' | 'OPTIONS' | 'HEAD' | 'PATCH'; + +type ResponseLabelList = Record[]; + +type LabelList = Record; From ffbff25c0a2b5abb82d481aacd464cebc1bb1776 Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Wed, 3 Mar 2021 15:48:24 +0800 Subject: [PATCH 12/31] feat: Extracting labelsDrawer as a public component --- web/mock/pluginTemplate.ts | 33 +++++++++++++++++++ .../LabelsfDrawer}/LabelsDrawer.tsx | 17 ++++++---- web/src/components/LabelsfDrawer/index.ts | 17 ++++++++++ web/src/helpers.tsx | 30 +++++++++++++++++ web/src/pages/PluginTemplate/service.ts | 3 +- .../pages/Route/components/Step1/MetaView.tsx | 4 ++- web/src/pages/Route/service.ts | 2 +- web/src/pages/Route/transform.ts | 32 ++---------------- web/src/pages/Route/typing.d.ts | 10 ------ 9 files changed, 98 insertions(+), 50 deletions(-) create mode 100644 web/mock/pluginTemplate.ts rename web/src/{pages/Route/components/Step1 => components/LabelsfDrawer}/LabelsDrawer.tsx (93%) create mode 100644 web/src/components/LabelsfDrawer/index.ts diff --git a/web/mock/pluginTemplate.ts b/web/mock/pluginTemplate.ts new file mode 100644 index 0000000000..1fb759c662 --- /dev/null +++ b/web/mock/pluginTemplate.ts @@ -0,0 +1,33 @@ +/* + * 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 { Request, Response } from 'express'; + +export default { + 'GET /apisix/admin/plugin_configs': (req: Request, res: Response) => { + res.json({ + code: 0, + data: { + rows: [{ id: "1", desc: 'pluginCongfig' }], + total_size: 1 + }, + message: "" + }) + }, + 'GET /apisix/admin/labels/plugin_config': (req: Request, res: Response) => { + res.json({ "code": 0, "message": "", "data": { "rows": [{ "API_VERSION": "1.0" }, { "aaa": "aaa" }, { "ddd": "ddd" }], "total_size": 3 }, "request_id": "6a1658dd-b7dc-40af-af67-aed7e6b21461" }) + }, +}; diff --git a/web/src/pages/Route/components/Step1/LabelsDrawer.tsx b/web/src/components/LabelsfDrawer/LabelsDrawer.tsx similarity index 93% rename from web/src/pages/Route/components/Step1/LabelsDrawer.tsx rename to web/src/components/LabelsfDrawer/LabelsDrawer.tsx index 5a176827a5..046f8cb221 100644 --- a/web/src/pages/Route/components/Step1/LabelsDrawer.tsx +++ b/web/src/components/LabelsfDrawer/LabelsDrawer.tsx @@ -19,22 +19,23 @@ import { AutoComplete, Button, Col, Drawer, Form, notification, Row } from 'antd import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons'; import { useIntl } from 'umi'; -import { transformLableValueToKeyValue } from '../../transform'; -import { fetchLabelList } from '../../service'; +import { transformLableValueToKeyValue } from '../../helpers'; type Props = { title?: string; actionName: string; dataSource: string[]; + filterList?: string[], + fetchLabelList: any, disabled: boolean; onClose: () => void; } & Pick; -const LabelList = (disabled: boolean, labelList: RouteModule.LabelList) => { +const LabelList = (disabled: boolean, labelList: LabelList, filterList: string[]) => { const { formatMessage } = useIntl(); const keyOptions = Object.keys(labelList || {}) - .filter((item) => item !== 'API_VERSION') + .filter((item) => !filterList.includes(item)) .map((item) => ({ value: item })); return ( @@ -116,14 +117,16 @@ const LabelsDrawer: React.FC = ({ actionName = '', disabled = false, dataSource = [], + filterList = [], + fetchLabelList, onClose, - onChange = () => {}, + onChange = () => { }, }) => { const transformLabel = transformLableValueToKeyValue(dataSource); const { formatMessage } = useIntl(); const [form] = Form.useForm(); - const [labelList, setLabelList] = useState({}); + const [labelList, setLabelList] = useState({}); form.setFieldsValue({ labels: transformLabel }); useEffect(() => { @@ -172,7 +175,7 @@ const LabelsDrawer: React.FC = ({ } >
- {LabelList(disabled, labelList || {})} + {LabelList(disabled, labelList || {}, filterList)}
); diff --git a/web/src/components/LabelsfDrawer/index.ts b/web/src/components/LabelsfDrawer/index.ts new file mode 100644 index 0000000000..7f31877860 --- /dev/null +++ b/web/src/components/LabelsfDrawer/index.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ +export { default } from './LabelsDrawer'; \ No newline at end of file diff --git a/web/src/helpers.tsx b/web/src/helpers.tsx index de5bc4a85b..5ee05b009b 100644 --- a/web/src/helpers.tsx +++ b/web/src/helpers.tsx @@ -130,3 +130,33 @@ export const timestampToLocaleString = (timestamp: number) => { return moment.unix(timestamp).format('YYYY-MM-DD HH:mm:ss'); }; + +export const transformLableValueToKeyValue = (data: string[]) => { + return (data || []).map((item) => { + const index = item.indexOf(':'); + const labelKey = item.substring(0, index); + const labelValue = item.substring(index + 1); + return { labelKey, labelValue, key: Math.random().toString(36).slice(2) }; + }); +}; + +export const transformLabelList = (data: ResponseLabelList) => { + if (!data) { + return {}; + } + const transformData = {}; + data.forEach((item) => { + const key = Object.keys(item)[0]; + const value = item[key]; + if (!transformData[key]) { + transformData[key] = []; + transformData[key].push(value); + return; + } + + if (transformData[key] && !transformData[key][value]) { + transformData[key].push(value); + } + }); + return transformData; +}; diff --git a/web/src/pages/PluginTemplate/service.ts b/web/src/pages/PluginTemplate/service.ts index 8f204accc1..123b79b3a5 100644 --- a/web/src/pages/PluginTemplate/service.ts +++ b/web/src/pages/PluginTemplate/service.ts @@ -15,7 +15,8 @@ * limitations under the License. */ import { request } from 'umi'; -import { transformLabelList } from '../Route/transform'; + +import { transformLabelList } from '@/helpers'; export const fetchList = ({ current = 1, pageSize = 10, ...res }) => { const { labels = [] } = res; diff --git a/web/src/pages/Route/components/Step1/MetaView.tsx b/web/src/pages/Route/components/Step1/MetaView.tsx index e0728dc881..aec27f27fb 100644 --- a/web/src/pages/Route/components/Step1/MetaView.tsx +++ b/web/src/pages/Route/components/Step1/MetaView.tsx @@ -21,7 +21,7 @@ import { useIntl } from 'umi'; import { PanelSection } from '@api7-dashboard/ui'; import { FORM_ITEM_WITHOUT_LABEL } from '@/pages/Route/constants'; -import LabelsDrawer from './LabelsDrawer'; +import LabelsDrawer from '@/components/LabelsfDrawer'; import { fetchLabelList } from '../../service'; const MetaView: React.FC = ({ disabled, form, isEdit, onChange }) => { @@ -75,6 +75,8 @@ const MetaView: React.FC = ({ disabled, form, isEdit disabled={disabled || false} onChange={onChange} onClose={() => setVisible(false)} + filterList={["API_VERSION"]} + fetchLabelList={fetchLabelList} /> ); }} diff --git a/web/src/pages/Route/service.ts b/web/src/pages/Route/service.ts index 06c9660255..5f40589412 100644 --- a/web/src/pages/Route/service.ts +++ b/web/src/pages/Route/service.ts @@ -21,8 +21,8 @@ import { transformStepData, transformRouteData, transformUpstreamNodes, - transformLabelList, } from './transform'; +import { transformLabelList } from '@/helpers'; export const create = (data: RouteModule.RequestData) => request(`/routes`, { diff --git a/web/src/pages/Route/transform.ts b/web/src/pages/Route/transform.ts index 6534c4cae1..68c0ccf5bc 100644 --- a/web/src/pages/Route/transform.ts +++ b/web/src/pages/Route/transform.ts @@ -16,14 +16,7 @@ */ import { omit, pick, cloneDeep } from 'lodash'; -export const transformLableValueToKeyValue = (data: string[]) => { - return (data || []).map((item) => { - const index = item.indexOf(':'); - const labelKey = item.substring(0, index); - const labelValue = item.substring(index + 1); - return { labelKey, labelValue, key: Math.random().toString(36).slice(2) }; - }); -}; +import { transformLableValueToKeyValue } from '@/helpers'; // Transform Route data then sent to API export const transformStepData = ({ @@ -128,10 +121,10 @@ export const transformStepData = ({ 'redirect', 'vars', 'plugins', + 'labels', service_id.length !== 0 ? 'service_id' : '', form1Data.hosts.filter(Boolean).length !== 0 ? 'hosts' : '', data.remote_addrs?.filter(Boolean).length !== 0 ? 'remote_addrs' : '', - form1Data.custom_version_label.length !== 0 ? 'labels' : '', ]); }; @@ -237,24 +230,3 @@ export const transformRouteData = (data: RouteModule.Body) => { advancedMatchingRules, }; }; - -export const transformLabelList = (data: RouteModule.ResponseLabelList) => { - if (!data) { - return {}; - } - const transformData = {}; - data.forEach((item) => { - const key = Object.keys(item)[0]; - const value = item[key]; - if (!transformData[key]) { - transformData[key] = []; - transformData[key].push(value); - return; - } - - if (transformData[key] && !transformData[key][value]) { - transformData[key].push(value); - } - }); - return transformData; -}; diff --git a/web/src/pages/Route/typing.d.ts b/web/src/pages/Route/typing.d.ts index d2c9260b2c..1c2c6c00f3 100644 --- a/web/src/pages/Route/typing.d.ts +++ b/web/src/pages/Route/typing.d.ts @@ -106,16 +106,6 @@ declare namespace RouteModule { key: string; }; - type ResponseLabelList = Record[]; - - type LabelList = Record; - - type LabelTableProps = { - labelKey: string; - labelValue: string; - key: string; - }; - type Step1PassProps = { form: FormInstance; advancedMatchingRules: MatchingRule[]; From 2fc4805423e3985cb4209256862f5fc1d195d960 Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Wed, 3 Mar 2021 17:48:36 +0800 Subject: [PATCH 13/31] feat: update labels --- web/mock/pluginTemplate.ts | 33 ---------- web/src/pages/PluginTemplate/Create.tsx | 16 ++++- .../pages/PluginTemplate/components/Step1.tsx | 63 ++++++++++++++++++- web/src/pages/Route/List.tsx | 2 +- .../pages/Route/components/Step1/MetaView.tsx | 2 +- web/src/pages/Route/service.ts | 2 +- 6 files changed, 77 insertions(+), 41 deletions(-) delete mode 100644 web/mock/pluginTemplate.ts diff --git a/web/mock/pluginTemplate.ts b/web/mock/pluginTemplate.ts deleted file mode 100644 index 1fb759c662..0000000000 --- a/web/mock/pluginTemplate.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 { Request, Response } from 'express'; - -export default { - 'GET /apisix/admin/plugin_configs': (req: Request, res: Response) => { - res.json({ - code: 0, - data: { - rows: [{ id: "1", desc: 'pluginCongfig' }], - total_size: 1 - }, - message: "" - }) - }, - 'GET /apisix/admin/labels/plugin_config': (req: Request, res: Response) => { - res.json({ "code": 0, "message": "", "data": { "rows": [{ "API_VERSION": "1.0" }, { "aaa": "aaa" }, { "ddd": "ddd" }], "total_size": 3 }, "request_id": "6a1658dd-b7dc-40af-af67-aed7e6b21461" }) - }, -}; diff --git a/web/src/pages/PluginTemplate/Create.tsx b/web/src/pages/PluginTemplate/Create.tsx index f1455dca8d..1eb307c76f 100644 --- a/web/src/pages/PluginTemplate/Create.tsx +++ b/web/src/pages/PluginTemplate/Create.tsx @@ -25,6 +25,7 @@ import PluginPage from '@/components/Plugin'; import Step1 from './components/Step1'; import Preview from './components/Preview'; import { fetchItem, create, update, } from './service'; +import { transformLableValueToKeyValue } from '@/helpers'; const Page: React.FC = (props) => { const [step, setStep] = useState(1); @@ -36,15 +37,24 @@ const Page: React.FC = (props) => { const { id } = (props as any).match.params; if (id) { fetchItem(id).then(({ data }) => { - const { desc, ...rest } = data; - form1.setFieldsValue({ id, desc }); + const { desc, labels, ...rest } = data; + form1.setFieldsValue({ + id, desc, custom_normal_labels: Object.keys(labels) + .map((key) => `${key}:${labels[key]}`) + }); setPlugins(rest.plugins); }); } }, []); const onSubmit = () => { - const data = { ...form1.getFieldsValue(), plugins } as PluginTemplateModule.Entity; + const { desc, custom_normal_labels } = form1.getFieldsValue(); + const labels: Record = {}; + transformLableValueToKeyValue(custom_normal_labels || []).forEach(({ labelKey, labelValue }) => { + labels[labelKey] = labelValue; + }); + const data = { desc, labels, plugins } as PluginTemplateModule.Entity; + const { id } = (props as any).match.params; (id ? update(id, data) : create(data)) .then(() => { diff --git a/web/src/pages/PluginTemplate/components/Step1.tsx b/web/src/pages/PluginTemplate/components/Step1.tsx index 66d238781f..8b0af7317e 100644 --- a/web/src/pages/PluginTemplate/components/Step1.tsx +++ b/web/src/pages/PluginTemplate/components/Step1.tsx @@ -14,10 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React from 'react'; -import { Form, Input } from 'antd'; +import React, { useState } from 'react'; +import { Button, Form, Input, Select, Tag } from 'antd'; import type { FormInstance } from 'antd/lib/form'; import { useIntl } from 'umi'; +import LabelsDrawer from '@/components/LabelsfDrawer'; +import { fetchLabelList } from '../service'; const FORM_LAYOUT = { labelCol: { @@ -34,7 +36,63 @@ type Props = { }; const Step1: React.FC = ({ form, disabled }) => { + const [visible, setVisible] = useState(false); const { formatMessage } = useIntl(); + + const NormalLabelComponent = () => { + const field = 'custom_normal_labels'; + const title = 'Label Manager'; + return ( + + + { + onChange={(id) => { form.setFieldsValue({ - plugin_config_id, + plugin_config_id: id, }); - onChange(plugins, plugin_config_id as string); + onChange(plugins, id as string); }} > {[ @@ -246,15 +246,15 @@ const PluginPage: React.FC = ({ setName(NEVER_EXIST_PLUGIN_FLAG); }} onChange={({ codemirrorData, formData, shouldDelete }) => { - let plugins = { + let newPlugins = { ...initialData, [name]: { ...codemirrorData, disable: !formData.disable }, }; if (shouldDelete === true) { - plugins = omit(plugins, name); + newPlugins = omit(newPlugins, name); } - onChange(plugins, form.getFieldValue('plugin_config_id')); - setPlugins(plugins); + onChange(newPlugins, form.getFieldValue('plugin_config_id')); + setPlugins(newPlugins); setName(NEVER_EXIST_PLUGIN_FLAG); }} /> diff --git a/web/src/pages/PluginTemplate/components/Step1.tsx b/web/src/pages/PluginTemplate/components/Step1.tsx index 28700f682d..d84d22edde 100644 --- a/web/src/pages/PluginTemplate/components/Step1.tsx +++ b/web/src/pages/PluginTemplate/components/Step1.tsx @@ -78,8 +78,8 @@ const Step1: React.FC = ({ form, disabled }) => { dataSource={labels} disabled={disabled || false} onChange={({ data }) => { - const labels = [...new Set([...(form.getFieldValue('custom_normal_labels') || []), ...data])] - form.setFieldsValue({ ...form.getFieldsValue(), custom_normal_labels: labels }) + const handledLabels = [...new Set([...(form.getFieldValue('custom_normal_labels') || []), ...data])]; + form.setFieldsValue({ ...form.getFieldsValue(), custom_normal_labels: handledLabels }); }} onClose={() => setVisible(false)} filterList={[]} diff --git a/web/src/pages/Route/components/Step3/index.tsx b/web/src/pages/Route/components/Step3/index.tsx index c2407d42e1..d610817c5a 100644 --- a/web/src/pages/Route/components/Step3/index.tsx +++ b/web/src/pages/Route/components/Step3/index.tsx @@ -89,8 +89,8 @@ const Page: React.FC = ({ data, onChange, readonly = false, isForceHttps schemaType="route" referPage="route" showSelector - onChange={(pluginsData, plugin_config_id) => { - onChange({ plugins: pluginsData, script: {}, plugin_config_id }) + onChange={(pluginsData, id) => { + onChange({ plugins: pluginsData, script: {}, plugin_config_id: id }) }} /> )} From a81f67fbdd8c59a64f100a60573df1cb3db81a70 Mon Sep 17 00:00:00 2001 From: guoqqqi <979918879@qq.com> Date: Thu, 4 Mar 2021 01:53:40 +0800 Subject: [PATCH 19/31] test: added test for Plugn template --- ...create-edit-delete-plugin-template.spec.js | 79 ++++++++++++++ .../create-plugin-template-with-route.spec.js | 102 ++++++++++++++++++ web/src/pages/PluginTemplate/Create.tsx | 2 +- web/src/pages/PluginTemplate/List.tsx | 4 + 4 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js create mode 100644 web/cypress/integration/pluginTemplate/create-plugin-template-with-route.spec.js diff --git a/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js b/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js new file mode 100644 index 0000000000..00b865da41 --- /dev/null +++ b/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js @@ -0,0 +1,79 @@ +/* + * 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. + */ +/* eslint-disable no-undef */ + +context('Create Edit and Delete PluginTemplate', () => { + beforeEach(() => { + cy.login(); + + cy.fixture('selector.json').as('domSelector'); + cy.fixture('data.json').as('data'); + }); + + it('should create pluginTemplate', function () { + cy.visit('/'); + cy.contains('Route').click(); + cy.contains('Plugin Template Config').click(); + cy.contains('Create').click(); + + cy.get('#desc').type('test_plugin_template1'); + cy.contains('Next').click(); + cy.contains('Enable').click({ force: true }); + cy.focused(this.domSelector.drawer).should('exist'); + cy.get(this.domSelector.drawer, { timeout }).within(() => { + cy.get("#disable").click({ + force: true, + }); + }); + cy.contains('Submit').click(); + cy.contains('Next').click(); + cy.contains('Submit').click(); + cy.get(this.domSelector.notification).should('contain', 'Create Plugin Template Successfully'); + }); + + it('should edit the pluginTemplate', function () { + cy.visit('pluginTemplate/list'); + + cy.get(this.domSelector.refresh).click(); + cy.get('[title=Description]').type('test_plugin_template1'); + cy.contains('button', 'Search').click(); + cy.contains('test_plugin_template1').siblings().contains('Edit').click(); + + cy.get('#desc').clear().type('test_plugin_template2'); + cy.contains('Next').click(); + cy.contains('Next').click(); + cy.contains('Submit').click(); + + cy.get(this.domSelector.notification).should('contain', 'Edit Plugin Template Successfully'); + }); + + it('should delete pluginTemplate', function () { + cy.visit('pluginTemplate/list'); + + cy.get(this.domSelector.refresh).click(); + cy.get('[title=Description]').type('test_plugin_template1'); + cy.contains('button', 'Search').click(); + cy.get('.ant-empty').should('exist'); + + cy.contains('button', 'Reset').click(); + cy.get('[title=Description]').type('test_plugin_template2'); + cy.contains('button', 'Search').click(); + cy.contains('test_plugin_template2').siblings().contains('Delete').click(); + cy.contains('button', 'Confirm').click(); + cy.get(this.domSelector.notification).should('contain', 'Delete Plugin Template Successfully'); + }); +}); diff --git a/web/cypress/integration/pluginTemplate/create-plugin-template-with-route.spec.js b/web/cypress/integration/pluginTemplate/create-plugin-template-with-route.spec.js new file mode 100644 index 0000000000..d56a6a7e65 --- /dev/null +++ b/web/cypress/integration/pluginTemplate/create-plugin-template-with-route.spec.js @@ -0,0 +1,102 @@ +/* + * 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. + */ +/* eslint-disable no-undef */ + +context('Create PluginTemplate Binding To Route', () => { + beforeEach(() => { + cy.login(); + + cy.fixture('selector.json').as('domSelector'); + cy.fixture('data.json').as('data'); + }); + + it('should create test pluginTemplate', function () { + cy.visit('/'); + cy.contains('Route').click(); + cy.contains('Plugin Template Config').click(); + cy.contains('Create').click(); + cy.get('#desc').type('test_plugin_template1'); + cy.contains('Next').click(); + cy.contains('Next').click(); + cy.contains('Submit').click(); + cy.get(this.domSelector.notification).should('contain', 'Create Plugin Template Successfully'); + + cy.visit('routes/list'); + cy.contains('Create').click(); + cy.get('#name').type('test_route'); + cy.contains('Next').click(); + cy.get('#nodes_0_host').type('127.0.0.1'); + cy.contains('Next').click(); + cy.get('[title=Custom]').click(); + cy.contains('test_plugin_template1').click(); + + cy.contains('Next').click(); + cy.contains('Submit').click(); + cy.contains(this.data.submitSuccess); + cy.contains('Goto List').click(); + cy.url().should('contains', 'routes/list'); + }); + + it('should delete the pluginTemplate failure', function () { + cy.visit('pluginTemplate/list'); + cy.get(this.domSelector.refresh).click(); + + cy.get('[title=Description]').type('test_plugin_template1'); + cy.contains('button', 'Search').click(); + cy.contains('test_plugin_template1').siblings().contains('Delete').click(); + cy.contains('button', 'Confirm').click(); + cy.get(this.domSelector.notification).should('contain', 'Request Error Code: 10000'); + cy.get('.anticon-close').should('be.visible').click(); + }); + + it('should edit the route with pluginTemplate', function () { + cy.visit('routes/list'); + + cy.get(this.domSelector.nameSelector).type('test_route'); + cy.contains('Search').click(); + cy.contains('test_route').siblings().contains('Edit').click(); + + cy.get('#redirectOption').click(); + cy.contains('Custom').click(); + cy.get('#redirectURI').clear().type('123'); + cy.get('#ret_code').click(); + cy.contains('301(Permanent Redirect)').click(); + cy.contains('Next').click(); + cy.contains('Submit').click(); + cy.contains(this.data.submitSuccess); + cy.contains('Goto List').click(); + cy.url().should('contains', 'routes/list'); + }); + + it('should delete the pluginTemplate successfully', function () { + cy.visit('pluginTemplate/list'); + + cy.get(this.domSelector.refresh).click(); + cy.get('[title=Description]').type('test_plugin_template1'); + cy.contains('button', 'Search').click(); + cy.contains('test_plugin_template1').siblings().contains('Delete').click(); + cy.contains('button', 'Confirm').click(); + cy.get(this.domSelector.notification).should('contain', 'Delete Plugin Template Successfully'); + + cy.visit('/routes/list'); + cy.get(this.domSelector.nameSelector).type('test_route'); + cy.contains('Search').click(); + cy.contains('test_route').siblings().contains('Delete').click(); + cy.contains('button', 'Confirm').click(); + cy.get(this.domSelector.notification).should('contain', this.data.deleteRouteSuccess); + }); +}); diff --git a/web/src/pages/PluginTemplate/Create.tsx b/web/src/pages/PluginTemplate/Create.tsx index 1eb307c76f..9ebc25af3f 100644 --- a/web/src/pages/PluginTemplate/Create.tsx +++ b/web/src/pages/PluginTemplate/Create.tsx @@ -37,7 +37,7 @@ const Page: React.FC = (props) => { const { id } = (props as any).match.params; if (id) { fetchItem(id).then(({ data }) => { - const { desc, labels, ...rest } = data; + const { desc, labels = {}, ...rest } = data; form1.setFieldsValue({ id, desc, custom_normal_labels: Object.keys(labels) .map((key) => `${key}:${labels[key]}`) diff --git a/web/src/pages/PluginTemplate/List.tsx b/web/src/pages/PluginTemplate/List.tsx index 68bce3da5b..2860a1a207 100644 --- a/web/src/pages/PluginTemplate/List.tsx +++ b/web/src/pages/PluginTemplate/List.tsx @@ -143,6 +143,10 @@ const Page: React.FC = () => { rowKey="id" columns={columns} request={fetchList} + search={{ + searchText: formatMessage({ id: 'component.global.search' }), + resetText: formatMessage({ id: 'component.global.reset' }), + }} toolBarRender={() => [ , From 67613bb988d9a4ecddb835686ade07d278e9e5c0 Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Thu, 4 Mar 2021 09:52:16 +0800 Subject: [PATCH 21/31] feat: ignore frontend e2e test output --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 04f148e317..1997f52ba1 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,6 @@ api/build-tools/apisix api/coverage.txt api/dag-to-lua/ +# frontend e2e test output +web/.nyc_output +web/coverage From 07000e2376027bd4fc97de28d11bce361709b868 Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Thu, 4 Mar 2021 09:52:42 +0800 Subject: [PATCH 22/31] feat: update pluginTemplate e2e testcase --- .../create-edit-delete-plugin-template.spec.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js b/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js index 00b865da41..cd093806a4 100644 --- a/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js +++ b/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js @@ -17,6 +17,7 @@ /* eslint-disable no-undef */ context('Create Edit and Delete PluginTemplate', () => { + const timeout = 5000; beforeEach(() => { cy.login(); @@ -32,9 +33,13 @@ context('Create Edit and Delete PluginTemplate', () => { cy.get('#desc').type('test_plugin_template1'); cy.contains('Next').click(); - cy.contains('Enable').click({ force: true }); + cy.contains('Enable').click({ + force: true + }); cy.focused(this.domSelector.drawer).should('exist'); - cy.get(this.domSelector.drawer, { timeout }).within(() => { + cy.get(this.domSelector.drawer, { + timeout + }).within(() => { cy.get("#disable").click({ force: true, }); From 642b1f915c9c4987daaff8eacdf102bbc564795b Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Thu, 4 Mar 2021 10:32:56 +0800 Subject: [PATCH 23/31] feat: upddate pluginTemplate testcase --- .../pluginTemplate/create-plugin-template-with-route.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/cypress/integration/pluginTemplate/create-plugin-template-with-route.spec.js b/web/cypress/integration/pluginTemplate/create-plugin-template-with-route.spec.js index d56a6a7e65..6c72d4f10d 100644 --- a/web/cypress/integration/pluginTemplate/create-plugin-template-with-route.spec.js +++ b/web/cypress/integration/pluginTemplate/create-plugin-template-with-route.spec.js @@ -70,7 +70,7 @@ context('Create PluginTemplate Binding To Route', () => { cy.contains('Search').click(); cy.contains('test_route').siblings().contains('Edit').click(); - cy.get('#redirectOption').click(); + cy.contains('Forbidden').click(); cy.contains('Custom').click(); cy.get('#redirectURI').clear().type('123'); cy.get('#ret_code').click(); From 131e7c77f18fd7c77bc68ce7b5e3210ecff8d276 Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Thu, 4 Mar 2021 11:04:30 +0800 Subject: [PATCH 24/31] feat: update --- web/src/pages/PluginTemplate/List.tsx | 3 +-- web/src/pages/PluginTemplate/components/Step1.tsx | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/pages/PluginTemplate/List.tsx b/web/src/pages/PluginTemplate/List.tsx index 95c387e08d..aec568c092 100644 --- a/web/src/pages/PluginTemplate/List.tsx +++ b/web/src/pages/PluginTemplate/List.tsx @@ -85,8 +85,7 @@ const Page: React.FC = () => { {(labelList[key] || []).map((value: string) => ( - {' '} - {value}{' '} + {value} ))} diff --git a/web/src/pages/PluginTemplate/components/Step1.tsx b/web/src/pages/PluginTemplate/components/Step1.tsx index d84d22edde..dbeea66c96 100644 --- a/web/src/pages/PluginTemplate/components/Step1.tsx +++ b/web/src/pages/PluginTemplate/components/Step1.tsx @@ -18,6 +18,7 @@ import React, { useState } from 'react'; import { Button, Form, Input, Select, Tag } from 'antd'; import type { FormInstance } from 'antd/lib/form'; import { useIntl } from 'umi'; + import LabelsDrawer from '@/components/LabelsfDrawer'; import { fetchLabelList } from '../service'; @@ -62,7 +63,7 @@ const Step1: React.FC = ({ form, disabled }) => { }} /> - + From 17c6ba4edc75a80d5bbd0518b98a62f2ad849948 Mon Sep 17 00:00:00 2001 From: guoqqqi <979918879@qq.com> Date: Thu, 4 Mar 2021 14:08:46 +0800 Subject: [PATCH 25/31] test: move selector and data to public file --- web/cypress/fixtures/data.json | 8 +++- web/cypress/fixtures/selector.json | 7 +++- ...create-edit-delete-plugin-template.spec.js | 24 ++++++------ .../create-plugin-template-with-route.spec.js | 38 +++++++++---------- 4 files changed, 44 insertions(+), 33 deletions(-) diff --git a/web/cypress/fixtures/data.json b/web/cypress/fixtures/data.json index 536a71b7e1..94be061069 100644 --- a/web/cypress/fixtures/data.json +++ b/web/cypress/fixtures/data.json @@ -27,5 +27,11 @@ "updateSuccessfully": "Update Configuration Successfully", "deleteSSLSuccess": "Remove target SSL successfully", "sslErrorAlert": "key and cert don't match", - "pluginErrorAlert": "Invalid plugin data" + "pluginErrorAlert": "Invalid plugin data", + "pluginTemplateName": "test_plugin_template1", + "pluginTemplateName2": "test_plugin_template2", + "createPluginTemplateSuccess": "Create Plugin Template Successfully", + "editPluginTemplateSuccess": "Edit Plugin Template Successfully", + "deletePluginTemplateSuccess": "Delete Plugin Template Successfully", + "pluginTemplateErrorAlert": "Request Error Code: 10000" } diff --git a/web/cypress/fixtures/selector.json b/web/cypress/fixtures/selector.json index ffb125a609..e7b2abb165 100644 --- a/web/cypress/fixtures/selector.json +++ b/web/cypress/fixtures/selector.json @@ -64,5 +64,10 @@ "passwordInput": "#control-ref_password", "drawer": ".ant-drawer-content", "codemirrorScroll": ".CodeMirror-scroll", - "drawerClose": ".ant-drawer-close" + "drawerClose": ".ant-drawer-close", + "descriptionSelector": "[title=Description]", + "customSelector": "[title=Custom]", + "errorAlertClose": ".anticon-close", + "redirectURIInput": "#redirectURI", + "redirectCodeSelector": "#ret_code" } diff --git a/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js b/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js index cd093806a4..4d869e8c7f 100644 --- a/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js +++ b/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js @@ -31,7 +31,7 @@ context('Create Edit and Delete PluginTemplate', () => { cy.contains('Plugin Template Config').click(); cy.contains('Create').click(); - cy.get('#desc').type('test_plugin_template1'); + cy.get(this.domSelector.description).type(this.data.pluginTemplateName); cy.contains('Next').click(); cy.contains('Enable').click({ force: true @@ -40,45 +40,45 @@ context('Create Edit and Delete PluginTemplate', () => { cy.get(this.domSelector.drawer, { timeout }).within(() => { - cy.get("#disable").click({ + cy.get(this.domSelector.disabledSwitcher).click({ force: true, }); }); cy.contains('Submit').click(); cy.contains('Next').click(); cy.contains('Submit').click(); - cy.get(this.domSelector.notification).should('contain', 'Create Plugin Template Successfully'); + cy.get(this.domSelector.notification).should('contain', this.data.createPluginTemplateSuccess); }); it('should edit the pluginTemplate', function () { cy.visit('pluginTemplate/list'); cy.get(this.domSelector.refresh).click(); - cy.get('[title=Description]').type('test_plugin_template1'); + cy.get(this.domSelector.descriptionSelector).type(this.data.pluginTemplateName); cy.contains('button', 'Search').click(); - cy.contains('test_plugin_template1').siblings().contains('Edit').click(); + cy.contains(this.data.pluginTemplateName).siblings().contains('Edit').click(); - cy.get('#desc').clear().type('test_plugin_template2'); + cy.get(this.domSelector.description).clear().type(this.data.pluginTemplateName2); cy.contains('Next').click(); cy.contains('Next').click(); cy.contains('Submit').click(); - cy.get(this.domSelector.notification).should('contain', 'Edit Plugin Template Successfully'); + cy.get(this.domSelector.notification).should('contain', this.data.editPluginTemplateSuccess); }); it('should delete pluginTemplate', function () { cy.visit('pluginTemplate/list'); cy.get(this.domSelector.refresh).click(); - cy.get('[title=Description]').type('test_plugin_template1'); + cy.get(this.domSelector.descriptionSelector).type(this.data.pluginTemplateName); cy.contains('button', 'Search').click(); - cy.get('.ant-empty').should('exist'); + cy.get(this.domSelector.empty).should('exist'); cy.contains('button', 'Reset').click(); - cy.get('[title=Description]').type('test_plugin_template2'); + cy.get(this.domSelector.descriptionSelector).type(this.data.pluginTemplateName2); cy.contains('button', 'Search').click(); - cy.contains('test_plugin_template2').siblings().contains('Delete').click(); + cy.contains(this.data.pluginTemplateName2).siblings().contains('Delete').click(); cy.contains('button', 'Confirm').click(); - cy.get(this.domSelector.notification).should('contain', 'Delete Plugin Template Successfully'); + cy.get(this.domSelector.notification).should('contain', this.data.deletePluginTemplateSuccess); }); }); diff --git a/web/cypress/integration/pluginTemplate/create-plugin-template-with-route.spec.js b/web/cypress/integration/pluginTemplate/create-plugin-template-with-route.spec.js index 6c72d4f10d..058ca01614 100644 --- a/web/cypress/integration/pluginTemplate/create-plugin-template-with-route.spec.js +++ b/web/cypress/integration/pluginTemplate/create-plugin-template-with-route.spec.js @@ -29,20 +29,20 @@ context('Create PluginTemplate Binding To Route', () => { cy.contains('Route').click(); cy.contains('Plugin Template Config').click(); cy.contains('Create').click(); - cy.get('#desc').type('test_plugin_template1'); + cy.get(this.domSelector.description).type(this.data.pluginTemplateName); cy.contains('Next').click(); cy.contains('Next').click(); cy.contains('Submit').click(); - cy.get(this.domSelector.notification).should('contain', 'Create Plugin Template Successfully'); + cy.get(this.domSelector.notification).should('contain', this.data.createPluginTemplateSuccess); cy.visit('routes/list'); cy.contains('Create').click(); - cy.get('#name').type('test_route'); + cy.get(this.domSelector.name).type(this.data.routeName); cy.contains('Next').click(); - cy.get('#nodes_0_host').type('127.0.0.1'); + cy.get(this.domSelector.nodes_0_host).type(this.data.ip1); cy.contains('Next').click(); - cy.get('[title=Custom]').click(); - cy.contains('test_plugin_template1').click(); + cy.get(this.domSelector.customSelector).click(); + cy.contains(this.data.pluginTemplateName).click(); cy.contains('Next').click(); cy.contains('Submit').click(); @@ -55,25 +55,25 @@ context('Create PluginTemplate Binding To Route', () => { cy.visit('pluginTemplate/list'); cy.get(this.domSelector.refresh).click(); - cy.get('[title=Description]').type('test_plugin_template1'); + cy.get(this.domSelector.descriptionSelector).type(this.data.pluginTemplateName); cy.contains('button', 'Search').click(); - cy.contains('test_plugin_template1').siblings().contains('Delete').click(); + cy.contains(this.data.pluginTemplateName).siblings().contains('Delete').click(); cy.contains('button', 'Confirm').click(); - cy.get(this.domSelector.notification).should('contain', 'Request Error Code: 10000'); - cy.get('.anticon-close').should('be.visible').click(); + cy.get(this.domSelector.notification).should('contain', this.data.pluginTemplateErrorAlert); + cy.get(this.domSelector.errorAlertClose).should('be.visible').click(); }); it('should edit the route with pluginTemplate', function () { cy.visit('routes/list'); - cy.get(this.domSelector.nameSelector).type('test_route'); + cy.get(this.domSelector.nameSelector).type(this.data.routeName); cy.contains('Search').click(); - cy.contains('test_route').siblings().contains('Edit').click(); + cy.contains(this.data.routeName).siblings().contains('Edit').click(); cy.contains('Forbidden').click(); cy.contains('Custom').click(); - cy.get('#redirectURI').clear().type('123'); - cy.get('#ret_code').click(); + cy.get(this.domSelector.redirectURIInput).clear().type('123'); + cy.get(this.domSelector.redirectCodeSelector).click(); cy.contains('301(Permanent Redirect)').click(); cy.contains('Next').click(); cy.contains('Submit').click(); @@ -86,16 +86,16 @@ context('Create PluginTemplate Binding To Route', () => { cy.visit('pluginTemplate/list'); cy.get(this.domSelector.refresh).click(); - cy.get('[title=Description]').type('test_plugin_template1'); + cy.get(this.domSelector.descriptionSelector).type(this.data.pluginTemplateName); cy.contains('button', 'Search').click(); - cy.contains('test_plugin_template1').siblings().contains('Delete').click(); + cy.contains(this.data.pluginTemplateName).siblings().contains('Delete').click(); cy.contains('button', 'Confirm').click(); - cy.get(this.domSelector.notification).should('contain', 'Delete Plugin Template Successfully'); + cy.get(this.domSelector.notification).should('contain', this.data.deletePluginTemplateSuccess); cy.visit('/routes/list'); - cy.get(this.domSelector.nameSelector).type('test_route'); + cy.get(this.domSelector.nameSelector).type(this.data.routeName); cy.contains('Search').click(); - cy.contains('test_route').siblings().contains('Delete').click(); + cy.contains(this.data.routeName).siblings().contains('Delete').click(); cy.contains('button', 'Confirm').click(); cy.get(this.domSelector.notification).should('contain', this.data.deleteRouteSuccess); }); From f5dc8bd657d9b0b8b7dc9e55d84051ac03471aac Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Thu, 4 Mar 2021 14:40:19 +0800 Subject: [PATCH 26/31] fix: typo --- web/config/routes.ts | 10 +++------- .../create-edit-delete-plugin-template.spec.js | 4 ++-- .../create-plugin-template-with-route.spec.js | 4 ++-- web/src/components/LabelsfDrawer/LabelsDrawer.tsx | 2 +- web/src/components/Plugin/PluginPage.tsx | 4 ++-- web/src/components/Plugin/locales/en-US.ts | 6 +++--- web/src/components/Plugin/locales/zh-CN.ts | 4 ++-- web/src/pages/PluginTemplate/Create.tsx | 4 ++-- web/src/pages/PluginTemplate/List.tsx | 4 ++-- web/src/pages/Route/List.tsx | 2 +- 10 files changed, 20 insertions(+), 24 deletions(-) diff --git a/web/config/routes.ts b/web/config/routes.ts index 99b85cdf94..b47c7cdc5a 100644 --- a/web/config/routes.ts +++ b/web/config/routes.ts @@ -100,15 +100,15 @@ const routes = [ component: './Setting', }, { - path: '/pluginTemplate/list', + path: '/plugin-template/list', component: './PluginTemplate/List', }, { - path: '/pluginTemplate/create', + path: 'plugin-template/create', component: './PluginTemplate/Create', }, { - path: '/pluginTemplate/:id/edit', + path: '/plugin-template/:id/edit', component: './PluginTemplate/Create', }, { @@ -121,10 +121,6 @@ const routes = [ component: './User/Logout', layout: false, }, - { - path: '/user/logout', - component: './User/Logout', - }, { component: './404', }, diff --git a/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js b/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js index 4d869e8c7f..1294c6c28a 100644 --- a/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js +++ b/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js @@ -51,7 +51,7 @@ context('Create Edit and Delete PluginTemplate', () => { }); it('should edit the pluginTemplate', function () { - cy.visit('pluginTemplate/list'); + cy.visit('plugin-template/list'); cy.get(this.domSelector.refresh).click(); cy.get(this.domSelector.descriptionSelector).type(this.data.pluginTemplateName); @@ -67,7 +67,7 @@ context('Create Edit and Delete PluginTemplate', () => { }); it('should delete pluginTemplate', function () { - cy.visit('pluginTemplate/list'); + cy.visit('plugin-template/list'); cy.get(this.domSelector.refresh).click(); cy.get(this.domSelector.descriptionSelector).type(this.data.pluginTemplateName); diff --git a/web/cypress/integration/pluginTemplate/create-plugin-template-with-route.spec.js b/web/cypress/integration/pluginTemplate/create-plugin-template-with-route.spec.js index 058ca01614..67bd664a40 100644 --- a/web/cypress/integration/pluginTemplate/create-plugin-template-with-route.spec.js +++ b/web/cypress/integration/pluginTemplate/create-plugin-template-with-route.spec.js @@ -52,7 +52,7 @@ context('Create PluginTemplate Binding To Route', () => { }); it('should delete the pluginTemplate failure', function () { - cy.visit('pluginTemplate/list'); + cy.visit('plugin-template/list'); cy.get(this.domSelector.refresh).click(); cy.get(this.domSelector.descriptionSelector).type(this.data.pluginTemplateName); @@ -83,7 +83,7 @@ context('Create PluginTemplate Binding To Route', () => { }); it('should delete the pluginTemplate successfully', function () { - cy.visit('pluginTemplate/list'); + cy.visit('plugin-template/list'); cy.get(this.domSelector.refresh).click(); cy.get(this.domSelector.descriptionSelector).type(this.data.pluginTemplateName); diff --git a/web/src/components/LabelsfDrawer/LabelsDrawer.tsx b/web/src/components/LabelsfDrawer/LabelsDrawer.tsx index 046f8cb221..33f58805fc 100644 --- a/web/src/components/LabelsfDrawer/LabelsDrawer.tsx +++ b/web/src/components/LabelsfDrawer/LabelsDrawer.tsx @@ -31,7 +31,7 @@ type Props = { onClose: () => void; } & Pick; -const LabelList = (disabled: boolean, labelList: LabelList, filterList: string[]) => { +const LabelList = (disabled: boolean, labelList: LabelList, filterList: string[] = []) => { const { formatMessage } = useIntl(); const keyOptions = Object.keys(labelList || {}) diff --git a/web/src/components/Plugin/PluginPage.tsx b/web/src/components/Plugin/PluginPage.tsx index 58c64314d9..6bfd3ef7b3 100644 --- a/web/src/components/Plugin/PluginPage.tsx +++ b/web/src/components/Plugin/PluginPage.tsx @@ -158,8 +158,8 @@ const PluginPage: React.FC = ({ -

{formatMessage({ id: 'component.plugin.pluginTemplate.tips1' })}

-

{formatMessage({ id: 'component.plugin.pluginTemplate.tips2' })}

+

{formatMessage({ id: 'component.plugin.pluginTemplate.tip1' })}

+

{formatMessage({ id: 'component.plugin.pluginTemplate.tip2' })}

} type="info" /> )} diff --git a/web/src/components/Plugin/locales/en-US.ts b/web/src/components/Plugin/locales/en-US.ts index 56fbd480c0..f70e668ca8 100644 --- a/web/src/components/Plugin/locales/en-US.ts +++ b/web/src/components/Plugin/locales/en-US.ts @@ -17,8 +17,8 @@ export default { 'component.plugin.tip1': 'NOTE: After customizing the plugin, you need to update schema.json.', 'component.plugin.tip2': 'How to update?', - 'component.select.pluginTemplate': 'Select PluginTemplate', + 'component.select.pluginTemplate': 'Select a plugin template', 'component.step.select.pluginTemplate.select.option': 'Custom', - 'component.plugin.pluginTemplate.tips1': '1.When a route already have plugins field configured, the plugins in the plugin template will be merged into it.', - 'component.plugin.pluginTemplate.tips2': '2.The same plugin in the plugin template will override one in the plugins' + 'component.plugin.pluginTemplate.tip1': '1. When a route already have plugins field configured, the plugins in the plugin template will be merged into it.', + 'component.plugin.pluginTemplate.tip2': '2. The same plugin in the plugin template will override one in the plugins' }; diff --git a/web/src/components/Plugin/locales/zh-CN.ts b/web/src/components/Plugin/locales/zh-CN.ts index 425d211145..afc85c13db 100644 --- a/web/src/components/Plugin/locales/zh-CN.ts +++ b/web/src/components/Plugin/locales/zh-CN.ts @@ -19,6 +19,6 @@ export default { 'component.plugin.tip2': '如何更新?', "component.select.pluginTemplate": '选择插件模板', 'component.step.select.pluginTemplate.select.option': '手动配置', - 'component.plugin.pluginTemplate.tips1': '1.如果这个路由已经配置了插件,那么插件模板里面的插件配置会合并进去。', - 'component.plugin.pluginTemplate.tips2': '2.插件模板相同的插件会覆盖掉原有的插件。' + 'component.plugin.pluginTemplate.tip1': '1. 若路由已配置插件,则插件模板数据将与已配置的插件数据合并。', + 'component.plugin.pluginTemplate.tip2': '2. 插件模板相同的插件会覆盖掉原有的插件。' }; diff --git a/web/src/pages/PluginTemplate/Create.tsx b/web/src/pages/PluginTemplate/Create.tsx index 9ebc25af3f..3da8cb1b12 100644 --- a/web/src/pages/PluginTemplate/Create.tsx +++ b/web/src/pages/PluginTemplate/Create.tsx @@ -21,11 +21,11 @@ import { history, useIntl } from 'umi'; import ActionBar from '@/components/ActionBar'; import PluginPage from '@/components/Plugin'; +import { transformLableValueToKeyValue } from '@/helpers'; import Step1 from './components/Step1'; import Preview from './components/Preview'; import { fetchItem, create, update, } from './service'; -import { transformLableValueToKeyValue } from '@/helpers'; const Page: React.FC = (props) => { const [step, setStep] = useState(1); @@ -66,7 +66,7 @@ const Page: React.FC = (props) => { id: 'component.status.success', })}`, }); - history.push('/pluginTemplate/list'); + history.push('/plugin-template/list'); }) .catch(() => { setStep(3); diff --git a/web/src/pages/PluginTemplate/List.tsx b/web/src/pages/PluginTemplate/List.tsx index aec568c092..29535176c2 100644 --- a/web/src/pages/PluginTemplate/List.tsx +++ b/web/src/pages/PluginTemplate/List.tsx @@ -104,7 +104,7 @@ const Page: React.FC = () => { , diff --git a/web/src/pages/Route/List.tsx b/web/src/pages/Route/List.tsx index d2fd3941df..e82dfbea06 100644 --- a/web/src/pages/Route/List.tsx +++ b/web/src/pages/Route/List.tsx @@ -426,7 +426,7 @@ const Page: React.FC = () => { resetText: formatMessage({ id: 'component.global.reset' }), }} toolBarRender={() => [ - , From 2fa3b29b92a29b3dd19ece225519eea021477d9f Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Thu, 4 Mar 2021 16:17:22 +0800 Subject: [PATCH 27/31] feat: update create --- web/src/pages/PluginTemplate/List.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/pages/PluginTemplate/List.tsx b/web/src/pages/PluginTemplate/List.tsx index 29535176c2..6e0fa45a5a 100644 --- a/web/src/pages/PluginTemplate/List.tsx +++ b/web/src/pages/PluginTemplate/List.tsx @@ -147,7 +147,7 @@ const Page: React.FC = () => { resetText: formatMessage({ id: 'component.global.reset' }), }} toolBarRender={() => [ - , From 988227608430bedf3a1f658f6af88f0156c8c47e Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Thu, 4 Mar 2021 18:58:42 +0800 Subject: [PATCH 28/31] feat: stable consumer testcase --- .../consumer/create_and_delete_consumer.spec.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/web/cypress/integration/consumer/create_and_delete_consumer.spec.js b/web/cypress/integration/consumer/create_and_delete_consumer.spec.js index 69cd8c5053..4cd5d51095 100644 --- a/web/cypress/integration/consumer/create_and_delete_consumer.spec.js +++ b/web/cypress/integration/consumer/create_and_delete_consumer.spec.js @@ -36,7 +36,9 @@ context('Create and Delete Consumer', () => { // plugin config cy.contains(this.domSelector.pluginCard, 'key-auth').within(() => { - cy.get('button').first().click(); + cy.get('button').click({ + force: true + }); }); cy.get(this.domSelector.disabledSwitcher).click(); @@ -91,7 +93,9 @@ context('Create and Delete Consumer', () => { // plugin config cy.contains(this.domSelector.pluginCard, 'key-auth').within(() => { - cy.get('button').first().click(); + cy.get('button').click({ + force: true + }); }); // edit codeMirror cy.get(this.domSelector.codeMirror) @@ -107,4 +111,3 @@ context('Create and Delete Consumer', () => { cy.get(this.domSelector.notification).should('contain', this.data.pluginErrorAlert); }); }); - From f925b7e7482604e58b13d0eb2e52020c66b58bb6 Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Thu, 4 Mar 2021 23:46:18 +0800 Subject: [PATCH 29/31] feat: update route CreateStep4 --- web/src/pages/Route/components/CreateStep4/CreateStep4.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/web/src/pages/Route/components/CreateStep4/CreateStep4.tsx b/web/src/pages/Route/components/CreateStep4/CreateStep4.tsx index 11a10340d4..01f31cadf3 100644 --- a/web/src/pages/Route/components/CreateStep4/CreateStep4.tsx +++ b/web/src/pages/Route/components/CreateStep4/CreateStep4.tsx @@ -39,7 +39,7 @@ const style = { const CreateStep4: React.FC = ({ form1, form2, redirect, upstreamRef, ...rest }) => { const { formatMessage } = useIntl(); - const { plugins = {}, script = {} } = rest.step3Data; + const { plugins = {}, script = {}, plugin_config_id = '' } = rest.step3Data; return ( <> @@ -59,9 +59,7 @@ const CreateStep4: React.FC = ({ form1, form2, redirect, upstreamRef, ...

{formatMessage({ id: 'component.global.steps.stepTitle.pluginConfig' })}

- {Boolean(Object.keys(plugins).length !== 0) && ( - - )} + {Boolean(Object.keys(script).length !== 0) && ( {}} /> )} From 71b8e8739d7cb5a793b4e145efdab9310cf0c6e7 Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Fri, 5 Mar 2021 10:46:55 +0800 Subject: [PATCH 30/31] feat: stable route testcase --- .../integration/route/create-edit-delete-route.spec.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/cypress/integration/route/create-edit-delete-route.spec.js b/web/cypress/integration/route/create-edit-delete-route.spec.js index f799a69011..86d9fb261d 100644 --- a/web/cypress/integration/route/create-edit-delete-route.spec.js +++ b/web/cypress/integration/route/create-edit-delete-route.spec.js @@ -73,7 +73,9 @@ context('Create and Delete Route', () => { // config prometheus plugin cy.contains(this.domSelector.pluginCard, 'prometheus').within(() => { - cy.get('button').first().click(); + cy.get('button').first().click({ + force: true + }); }); cy.contains('button', 'Cancel').click(); cy.contains('Next').click(); @@ -138,4 +140,3 @@ context('Create and Delete Route', () => { cy.get(this.domSelector.notification).should('contain', this.data.deleteRouteSuccess); }); }); - From b5d2b32a16e6c89d87c3ec027ca992162d492703 Mon Sep 17 00:00:00 2001 From: litesun <7sunmiao@gmail.com> Date: Fri, 5 Mar 2021 11:37:10 +0800 Subject: [PATCH 31/31] feat: udpate route createStep4 --- web/src/pages/Route/components/CreateStep4/CreateStep4.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/pages/Route/components/CreateStep4/CreateStep4.tsx b/web/src/pages/Route/components/CreateStep4/CreateStep4.tsx index 01f31cadf3..f999f09ceb 100644 --- a/web/src/pages/Route/components/CreateStep4/CreateStep4.tsx +++ b/web/src/pages/Route/components/CreateStep4/CreateStep4.tsx @@ -59,9 +59,9 @@ const CreateStep4: React.FC = ({ form1, form2, redirect, upstreamRef, ...

{formatMessage({ id: 'component.global.steps.stepTitle.pluginConfig' })}

- + {Boolean(Object.keys(plugins).length !== 0 || plugin_config_id !== '') && } {Boolean(Object.keys(script).length !== 0) && ( - {}} /> + { }} /> )} )}