You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by ju...@apache.org on 2020/09/21 11:16:43 UTC
[apisix-dashboard] branch master updated: feat: improve route (#483)
This is an automated email from the ASF dual-hosted git repository.
juzhiyuan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git
The following commit(s) were added to refs/heads/master by this push:
new 234e1ea feat: improve route (#483)
234e1ea is described below
commit 234e1ea51b55527f9bd9367dc448405fb1095270
Author: litesun <7s...@gmail.com>
AuthorDate: Mon Sep 21 19:16:33 2020 +0800
feat: improve route (#483)
* feat: improve step1
* feat: improve step2
* feat: improve createStep4
* feat: improve transform
* fix: event loop
* feat: clean code
* fix: lost route_group info when enable redirect
* feat: UI improve
---
src/pages/Route/Create.tsx | 143 +++++++--------
.../Route/components/CreateStep4/CreateStep4.tsx | 24 +--
.../Route/components/Step1/MatchingRulesView.tsx | 25 +--
src/pages/Route/components/Step1/MetaView.tsx | 30 ++--
.../Route/components/Step1/RequestConfigView.tsx | 99 ++++++-----
src/pages/Route/components/Step1/index.tsx | 23 +--
.../components/Step2/HttpHeaderRewriteView.tsx | 21 ++-
.../Route/components/Step2/RequestRewriteView.tsx | 191 +++++++++++----------
src/pages/Route/components/Step2/index.tsx | 7 +-
src/pages/Route/constants.ts | 10 +-
src/pages/Route/service.ts | 4 +-
src/pages/Route/transform.ts | 103 ++++++-----
src/pages/Route/typing.d.ts | 131 ++++++++------
13 files changed, 431 insertions(+), 380 deletions(-)
diff --git a/src/pages/Route/Create.tsx b/src/pages/Route/Create.tsx
index 40c6365..759d775 100644
--- a/src/pages/Route/Create.tsx
+++ b/src/pages/Route/Create.tsx
@@ -22,15 +22,7 @@ import { transformer as chartTransformer } from '@api7-dashboard/pluginchart';
import ActionBar from '@/components/ActionBar';
-import {
- create,
- fetchItem,
- fetchUpstreamItem,
- fetchRouteGroupItem,
- update,
- checkUniqueName,
- checkHostWithSSL
-} from './service';
+import { create, fetchItem, update, checkUniqueName, checkHostWithSSL } from './service';
import Step1 from './components/Step1';
import Step2 from './components/Step2';
import Step3 from './components/Step3';
@@ -68,10 +60,11 @@ const Page: React.FC<Props> = (props) => {
formatMessage({ id: 'route.constants.preview' }),
];
- const [step1Data, setStep1Data] = useState(DEFAULT_STEP_1_DATA);
- const [step2Data, setStep2Data] = useState(DEFAULT_STEP_2_DATA);
+ const [advancedMatchingRules, setAdvancedMatchingRules] = useState<RouteModule.MatchingRule[]>(
+ [],
+ );
+ const [upstreamHeaderList, setUpstreamHeaderList] = useState<RouteModule.UpstreamHeader[]>([]);
const [step3Data, setStep3Data] = useState(DEFAULT_STEP_3_DATA);
-
const [redirect, setRedirect] = useState(false);
const [form1] = Form.useForm();
@@ -79,69 +72,51 @@ const Page: React.FC<Props> = (props) => {
const [step, setStep] = useState(1);
const [stepHeader, setStepHeader] = useState(STEP_HEADER_4);
-
const [chart, setChart] = useState(INIT_CHART);
- const routeData = {
- step1Data,
- step2Data,
- step3Data,
- };
const setupRoute = (rid: number) =>
fetchItem(rid).then((data) => {
- form1.setFieldsValue(data.step1Data);
- setStep1Data(data.step1Data as RouteModule.Step1Data);
-
- form2.setFieldsValue(data.step2Data);
- setStep2Data(data.step2Data);
-
+ form1.setFieldsValue(data.form1Data);
+ setAdvancedMatchingRules(data.advancedMatchingRules);
+ form2.setFieldsValue(data.form2Data);
+ setUpstreamHeaderList(data.upstreamHeaderList);
setStep3Data(data.step3Data);
});
- useEffect(() => {
- if (props.route.path.indexOf('edit') !== -1) {
- setupRoute(props.match.params.rid);
- }
- }, []);
-
- useEffect(() => {
- const { redirectOption } = step1Data;
-
- if (redirectOption === 'customRedirect') {
- setRedirect(true);
- setStepHeader(STEP_HEADER_2);
- return;
- }
- setRedirect(false);
- setStepHeader(STEP_HEADER_4);
- }, [step1Data]);
-
const onReset = () => {
- setStep1Data(DEFAULT_STEP_1_DATA);
- setStep2Data(DEFAULT_STEP_2_DATA);
+ setAdvancedMatchingRules([]);
+ setUpstreamHeaderList([]);
setStep3Data(DEFAULT_STEP_3_DATA);
-
form1.setFieldsValue(DEFAULT_STEP_1_DATA);
form2.setFieldsValue(DEFAULT_STEP_2_DATA);
setStep(1);
};
+ useEffect(() => {
+ if (props.route.path.indexOf('edit') !== -1) {
+ setupRoute(props.match.params.rid);
+ } else {
+ onReset();
+ }
+ }, []);
+
const renderStep = () => {
if (step === 1) {
return (
<Step1
- data={routeData}
form={form1}
- onChange={(params: RouteModule.Step1Data) => {
- if (params.route_group_id) {
- fetchRouteGroupItem(params.route_group_id).then((data) => {
- form1.setFieldsValue({
- ...form1.getFieldsValue(),
- ...data,
- });
- });
+ advancedMatchingRules={advancedMatchingRules}
+ onChange={({ action, data }) => {
+ if (action === 'redirectOptionChange' && data === 'customRedirect') {
+ setStepHeader(STEP_HEADER_2);
+ setRedirect(true);
+ } else {
+ setStepHeader(STEP_HEADER_4);
+ setRedirect(false);
+ }
+ if (action === 'advancedMatchingRulesChange') {
+ setAdvancedMatchingRules(data);
}
- setStep1Data({ ...form1.getFieldsValue(), ...step1Data, ...params });
}}
isEdit={props.route.path.indexOf('edit') > 0}
/>
@@ -151,34 +126,25 @@ const Page: React.FC<Props> = (props) => {
if (step === 2) {
if (redirect) {
return (
- <CreateStep4 data={routeData} form1={form1} form2={form2} onChange={() => {}} redirect />
+ <CreateStep4
+ advancedMatchingRules={advancedMatchingRules}
+ upstreamHeaderList={upstreamHeaderList}
+ form1={form1}
+ form2={form2}
+ step3Data={step3Data}
+ redirect
+ />
);
}
return (
<Step2
- data={routeData}
+ upstreamHeaderList={upstreamHeaderList}
form={form2}
- onChange={(params: RouteModule.Step2Data) => {
- if (params.upstream_id) {
- fetchUpstreamItem(params.upstream_id).then((data) => {
- form2.setFieldsValue({
- ...form2.getFieldsValue(),
- ...data,
- });
- setStep2Data({
- ...step2Data,
- ...form2.getFieldsValue(),
- ...params,
- } as RouteModule.Step2Data);
- });
- return;
+ onChange={({ action, data }) => {
+ if (action === 'upstreamHeaderListChange') {
+ setUpstreamHeaderList(data);
}
- setStep2Data({
- ...step2Data,
- ...form2.getFieldsValue(),
- ...params,
- } as RouteModule.Step2Data);
}}
/>
);
@@ -197,7 +163,15 @@ const Page: React.FC<Props> = (props) => {
}
if (step === 4) {
- return <CreateStep4 data={routeData} form1={form1} form2={form2} onChange={() => {}} />;
+ return (
+ <CreateStep4
+ advancedMatchingRules={advancedMatchingRules}
+ upstreamHeaderList={upstreamHeaderList}
+ form1={form1}
+ form2={form2}
+ step3Data={step3Data}
+ />
+ );
}
if (step === 5) {
@@ -216,13 +190,20 @@ const Page: React.FC<Props> = (props) => {
};
const onStepChange = (nextStep: number) => {
+ const routeData = {
+ form1Data: form1.getFieldsValue(),
+ form2Data: form2.getFieldsValue(),
+ step3Data,
+ upstreamHeaderList,
+ advancedMatchingRules,
+ } as RouteModule.RequestData;
const onUpdateOrCreate = () => {
if (props.route.path.indexOf('edit') !== -1) {
- update((props as any).match.params.rid, { data: routeData }).then(() => {
+ update((props as any).match.params.rid, routeData).then(() => {
setStep(5);
});
} else {
- create({ data: routeData }).then(() => {
+ create(routeData).then(() => {
setStep(5);
});
}
@@ -249,7 +230,6 @@ const Page: React.FC<Props> = (props) => {
redirectOption === 'forceHttps' ? checkHostWithSSL(hosts) : Promise.resolve(),
checkUniqueName(value.name, (props as any).match.params.rid || ''),
]).then(() => {
- setStep1Data({ ...step1Data, ...value });
setStep(nextStep);
});
});
@@ -265,8 +245,7 @@ const Page: React.FC<Props> = (props) => {
onUpdateOrCreate();
return;
}
- form2.validateFields().then((value) => {
- setStep2Data({ ...step2Data, ...value });
+ form2.validateFields().then(() => {
setStep(nextStep);
});
return;
diff --git a/src/pages/Route/components/CreateStep4/CreateStep4.tsx b/src/pages/Route/components/CreateStep4/CreateStep4.tsx
index 1d6928a..6b28150 100644
--- a/src/pages/Route/components/CreateStep4/CreateStep4.tsx
+++ b/src/pages/Route/components/CreateStep4/CreateStep4.tsx
@@ -23,11 +23,14 @@ import PluginOrchestration from '@api7-dashboard/pluginchart';
import Step1 from '../Step1';
import Step2 from '../Step2';
-interface Props extends RouteModule.Data {
+type Props = {
form1: FormInstance;
form2: FormInstance;
redirect?: boolean;
-}
+ step3Data: RouteModule.Step3Data;
+ advancedMatchingRules: RouteModule.MatchingRule[];
+ upstreamHeaderList: RouteModule.UpstreamHeader[];
+};
const style = {
marginTop: '40px',
@@ -35,7 +38,7 @@ const style = {
const CreateStep4: React.FC<Props> = ({ form1, form2, redirect, ...rest }) => {
const { formatMessage } = useIntl();
- const { plugins = {}, script = {} } = rest.data.step3Data;
+ const { plugins = {}, script = {} } = rest.step3Data;
return (
<>
@@ -44,17 +47,18 @@ const CreateStep4: React.FC<Props> = ({ form1, form2, redirect, ...rest }) => {
{!redirect && (
<>
<h2 style={style}>{formatMessage({ id: 'route.create.define.api.backend.server' })}</h2>
- <Step2 {...rest} form={form2} disabled />
+ <Step2
+ upstreamHeaderList={rest.upstreamHeaderList}
+ form={form2}
+ disabled
+ onChange={() => {}}
+ />
<h2 style={style}>{formatMessage({ id: 'route.create.plugin.configuration' })}</h2>
{Boolean(Object.keys(plugins).length !== 0) && (
- <PluginPage initialData={rest.data.step3Data.plugins} readonly />
+ <PluginPage initialData={rest.step3Data.plugins} readonly />
)}
{Boolean(Object.keys(script).length !== 0) && (
- <PluginOrchestration
- data={rest.data.step3Data.script.chart}
- readonly
- onChange={() => {}}
- />
+ <PluginOrchestration data={rest.step3Data.script.chart} readonly onChange={() => {}} />
)}
</>
)}
diff --git a/src/pages/Route/components/Step1/MatchingRulesView.tsx b/src/pages/Route/components/Step1/MatchingRulesView.tsx
index ed42f45..5d571c6 100644
--- a/src/pages/Route/components/Step1/MatchingRulesView.tsx
+++ b/src/pages/Route/components/Step1/MatchingRulesView.tsx
@@ -19,11 +19,11 @@ import { Button, Table, Modal, Form, Select, Input, Space } from 'antd';
import { useIntl } from 'umi';
import { PanelSection } from '@api7-dashboard/ui';
-interface Props extends RouteModule.Data {}
-
-const MatchingRulesView: React.FC<Props> = ({ data, disabled, onChange }) => {
- const { advancedMatchingRules } = data.step1Data;
-
+const MatchingRulesView: React.FC<RouteModule.Step1PassProps> = ({
+ advancedMatchingRules,
+ disabled,
+ onChange = () => {},
+}) => {
const [visible, setVisible] = useState(false);
const [mode, setMode] = useState<RouteModule.ModalType>('CREATE');
const [namePlaceholder, setNamePlaceholder] = useState('');
@@ -38,8 +38,8 @@ const MatchingRulesView: React.FC<Props> = ({ data, disabled, onChange }) => {
if (mode === 'EDIT') {
const key = modalForm.getFieldValue('key');
onChange({
- ...data.step1Data,
- advancedMatchingRules: advancedMatchingRules.map((rule) => {
+ action: 'advancedMatchingRulesChange',
+ data: advancedMatchingRules.map((rule) => {
if (rule.key === key) {
return { ...(value as RouteModule.MatchingRule), key };
}
@@ -51,7 +51,10 @@ const MatchingRulesView: React.FC<Props> = ({ data, disabled, onChange }) => {
...(value as RouteModule.MatchingRule),
key: Math.random().toString(36).slice(2),
};
- onChange({ ...data.step1Data, advancedMatchingRules: advancedMatchingRules.concat(rule) });
+ onChange({
+ action: 'advancedMatchingRulesChange',
+ data: advancedMatchingRules.concat(rule),
+ });
}
modalForm.resetFields();
setVisible(false);
@@ -66,8 +69,8 @@ const MatchingRulesView: React.FC<Props> = ({ data, disabled, onChange }) => {
const handleRemove = (key: string) => {
onChange({
- ...data.step1Data,
- advancedMatchingRules: advancedMatchingRules.filter((item) => item.key !== key),
+ action: 'advancedMatchingRulesChange',
+ data: advancedMatchingRules.filter((item) => item.key !== key),
});
};
@@ -164,7 +167,7 @@ const MatchingRulesView: React.FC<Props> = ({ data, disabled, onChange }) => {
cancelText={formatMessage({ id: 'route.match.cancel' })}
destroyOnClose
>
- <Form form={modalForm} labelCol={{ span: 9 }} wrapperCol={{ span: 15 }}>
+ <Form form={modalForm} labelCol={{ span: 4 }}>
<Form.Item
label={formatMessage({ id: 'route.match.parameter.position' })}
name="position"
diff --git a/src/pages/Route/components/Step1/MetaView.tsx b/src/pages/Route/components/Step1/MetaView.tsx
index a90faac..4c87fc4 100644
--- a/src/pages/Route/components/Step1/MetaView.tsx
+++ b/src/pages/Route/components/Step1/MetaView.tsx
@@ -20,15 +20,14 @@ import { Input, Select, Switch } from 'antd';
import { useIntl } from 'umi';
import { PanelSection } from '@api7-dashboard/ui';
-import { fetchRouteGroupList } from '@/pages/Route/service';
+import { fetchRouteGroupList, fetchRouteGroupItem } from '@/pages/Route/service';
-interface Props extends RouteModule.Data {}
-
-const MetaView: React.FC<Props> = ({ data, disabled, onChange, isEdit }) => {
- const { step1Data } = data;
+const MetaView: React.FC<RouteModule.Step1PassProps> = ({ form, disabled, isEdit }) => {
const { formatMessage } = useIntl();
- const routeGroupDisabled = disabled || !!step1Data.route_group_id;
+
const [routeGroups, setRouteGroups] = useState<{ id: string; name: string }[]>();
+ let routeGroupDisabled = disabled || Boolean(form.getFieldValue('route_group_id'));
+
useEffect(() => {
// eslint-disable-next-line no-shadow
fetchRouteGroupList().then(({ data }) => {
@@ -36,11 +35,9 @@ const MetaView: React.FC<Props> = ({ data, disabled, onChange, isEdit }) => {
{ name: formatMessage({ id: 'route.meta.api.create.group.name' }), id: null },
...data,
]);
- if (step1Data.route_group_id) {
- onChange({ route_group_id: step1Data.route_group_id });
- }
});
}, []);
+
return (
<PanelSection title={formatMessage({ id: 'route.meta.name.description' })}>
<Form.Item
@@ -63,9 +60,20 @@ const MetaView: React.FC<Props> = ({ data, disabled, onChange, isEdit }) => {
<Form.Item label={formatMessage({ id: 'route.meta.api.group.name' })} name="route_group_id">
<Select
onChange={(value) => {
- if (step1Data.route_group_id) {
- onChange({ route_group_id: value });
+ if (!value) {
+ form.setFieldsValue({
+ ...form.getFieldsValue(),
+ route_group_name: '',
+ });
+ return;
}
+ fetchRouteGroupItem(value.toString()).then((data) => {
+ form.setFieldsValue({
+ ...form.getFieldsValue(),
+ ...data,
+ });
+ routeGroupDisabled = true;
+ });
}}
disabled={disabled}
>
diff --git a/src/pages/Route/components/Step1/RequestConfigView.tsx b/src/pages/Route/components/Step1/RequestConfigView.tsx
index 93481a6..236e4e6 100644
--- a/src/pages/Route/components/Step1/RequestConfigView.tsx
+++ b/src/pages/Route/components/Step1/RequestConfigView.tsx
@@ -18,7 +18,6 @@ import React from 'react';
import Form from 'antd/es/form';
import { Checkbox, Button, Input, Switch, Select, Row, Col } from 'antd';
import { PlusOutlined, MinusCircleOutlined } from '@ant-design/icons';
-import { CheckboxValueType } from 'antd/lib/checkbox/Group';
import { useIntl } from 'umi';
import { PanelSection } from '@api7-dashboard/ui';
@@ -28,15 +27,11 @@ import {
FORM_ITEM_WITHOUT_LABEL,
} from '@/pages/Route/constants';
-interface Props extends RouteModule.Data {}
-
-const RequestConfigView: React.FC<Props> = ({ data, disabled, onChange }) => {
- const { step1Data } = data;
- const { protocols } = step1Data;
- const onProtocolChange = (e: CheckboxValueType[]) => {
- if (!e.includes('http') && !e.includes('https')) return;
- onChange({ ...data.step1Data, protocols: e });
- };
+const RequestConfigView: React.FC<RouteModule.Step1PassProps> = ({
+ form,
+ disabled,
+ onChange = () => {},
+}) => {
const { formatMessage } = useIntl();
const renderHosts = () => (
<Form.List name="hosts">
@@ -182,12 +177,7 @@ const RequestConfigView: React.FC<Props> = ({ data, disabled, onChange }) => {
},
]}
>
- <Checkbox.Group
- disabled={disabled}
- options={['http', 'https']}
- value={protocols}
- onChange={onProtocolChange}
- />
+ <Checkbox.Group disabled={disabled} options={['http', 'https']} />
</Form.Item>
<Form.Item label="WebSocket" name="websocket" valuePropName="checked">
<Switch disabled={disabled} />
@@ -218,7 +208,12 @@ const RequestConfigView: React.FC<Props> = ({ data, disabled, onChange }) => {
label={formatMessage({ id: 'route.request.config.redirect' })}
name="redirectOption"
>
- <Select disabled={disabled}>
+ <Select
+ disabled={disabled}
+ onChange={(parmas) => {
+ onChange({ action: 'redirectOptionChange', data: parmas });
+ }}
+ >
<Select.Option value="forceHttps">
{formatMessage({ id: 'route.request.config.enable.https' })}
</Select.Option>
@@ -230,34 +225,50 @@ const RequestConfigView: React.FC<Props> = ({ data, disabled, onChange }) => {
</Select.Option>
</Select>
</Form.Item>
- {step1Data.redirectOption === 'customRedirect' && (
- <Form.Item label={formatMessage({ id: 'route.request.config.redirect.custom' })} required>
- <Row gutter={10}>
- <Col>
- <Form.Item name="redirectURI" rules={[{ required: true }]}>
- <Input
- placeholder={formatMessage({
- id: 'route.request.config.redirect.custom.example',
- })}
- disabled={disabled}
- />
- </Form.Item>
- </Col>
- <Col span={10}>
- <Form.Item name="redirectCode" rules={[{ required: true }]}>
- <Select disabled={disabled}>
- <Select.Option value={301}>
- {formatMessage({ id: 'route.request.config.redirect.301' })}
- </Select.Option>
- <Select.Option value={302}>
- {formatMessage({ id: 'route.request.config.redirect.302' })}
- </Select.Option>
- </Select>
+ <Form.Item
+ noStyle
+ shouldUpdate={(prev, next) => {
+ onChange({ action: 'redirectOptionChange', data: next.redirectOption });
+ return prev.redirectOption !== next.redirectOption;
+ }}
+ >
+ {() => {
+ if (form.getFieldValue('redirectOption') === 'customRedirect') {
+ return (
+ <Form.Item
+ label={formatMessage({ id: 'route.request.config.redirect.custom' })}
+ required
+ >
+ <Row gutter={10}>
+ <Col>
+ <Form.Item name="redirectURI" rules={[{ required: true }]}>
+ <Input
+ placeholder={formatMessage({
+ id: 'route.request.config.redirect.custom.example',
+ })}
+ disabled={disabled}
+ />
+ </Form.Item>
+ </Col>
+ <Col span={10}>
+ <Form.Item name="redirectCode" rules={[{ required: true }]}>
+ <Select disabled={disabled}>
+ <Select.Option value={301}>
+ {formatMessage({ id: 'route.request.config.redirect.301' })}
+ </Select.Option>
+ <Select.Option value={302}>
+ {formatMessage({ id: 'route.request.config.redirect.302' })}
+ </Select.Option>
+ </Select>
+ </Form.Item>
+ </Col>
+ </Row>
</Form.Item>
- </Col>
- </Row>
- </Form.Item>
- )}
+ );
+ }
+ return null;
+ }}
+ </Form.Item>
</PanelSection>
);
};
diff --git a/src/pages/Route/components/Step1/index.tsx b/src/pages/Route/components/Step1/index.tsx
index e73b98e..9f8c07f 100644
--- a/src/pages/Route/components/Step1/index.tsx
+++ b/src/pages/Route/components/Step1/index.tsx
@@ -16,7 +16,6 @@
*/
import React from 'react';
import { Form } from 'antd';
-import { FormInstance } from 'antd/lib/form';
import { FORM_ITEM_LAYOUT } from '@/pages/Route/constants';
import styles from '../../Create.less';
@@ -24,28 +23,10 @@ import MetaView from './MetaView';
import RequestConfigView from './RequestConfigView';
import MatchingRulesView from './MatchingRulesView';
-interface Props extends RouteModule.Data {
- form: FormInstance;
- isEdit?: boolean;
-}
-
-const Step1: React.FC<Props> = (props) => {
- const { data, form, onChange } = props;
+const Step1: React.FC<RouteModule.Step1PassProps> = (props) => {
return (
<>
- <Form
- {...FORM_ITEM_LAYOUT}
- form={form}
- layout="horizontal"
- className={styles.stepForm}
- onValuesChange={(field) => {
- if (field.redirectOption === 'forceHttps' || field.redirectOption === 'disabled') {
- form.setFieldsValue({ redirectURI: '' });
- }
- onChange({ ...data.step1Data, ...field });
- }}
- initialValues={data.step1Data}
- >
+ <Form {...FORM_ITEM_LAYOUT} form={props.form} layout="horizontal" className={styles.stepForm}>
<MetaView {...props} />
<RequestConfigView {...props} />
</Form>
diff --git a/src/pages/Route/components/Step2/HttpHeaderRewriteView.tsx b/src/pages/Route/components/Step2/HttpHeaderRewriteView.tsx
index 543db05..bfc4901 100644
--- a/src/pages/Route/components/Step2/HttpHeaderRewriteView.tsx
+++ b/src/pages/Route/components/Step2/HttpHeaderRewriteView.tsx
@@ -20,10 +20,11 @@ import { Button, Table, Space, Modal, Input, Select } from 'antd';
import { useIntl } from 'umi';
import { PanelSection } from '@api7-dashboard/ui';
-interface Props extends RouteModule.Data {}
-
-const HttpHeaderRewriteView: React.FC<Props> = ({ data, disabled, onChange }) => {
- const { upstreamHeaderList = [] } = data.step2Data;
+const HttpHeaderRewriteView: React.FC<RouteModule.Step2PassProps> = ({
+ upstreamHeaderList = [],
+ disabled,
+ onChange,
+}) => {
const [visible, setVisible] = useState(false);
const [modalForm] = Form.useForm();
const [mode, setMode] = useState<RouteModule.ModalType>('CREATE');
@@ -36,7 +37,10 @@ const HttpHeaderRewriteView: React.FC<Props> = ({ data, disabled, onChange }) =>
modalForm.setFieldsValue(record);
};
const handleRemove = (key: string) => {
- onChange({ upstreamHeaderList: upstreamHeaderList.filter((item) => item.key !== key) });
+ onChange({
+ action: 'upstreamHeaderListChange',
+ data: upstreamHeaderList.filter((item) => item.key !== key),
+ });
};
const columns = [
@@ -92,17 +96,18 @@ const HttpHeaderRewriteView: React.FC<Props> = ({ data, disabled, onChange }) =>
if (mode === 'EDIT') {
const key = modalForm.getFieldValue('key');
onChange({
- upstreamHeaderList: upstreamHeaderList.map((item) => {
+ action: 'upstreamHeaderListChange',
+ data: upstreamHeaderList.map((item) => {
if (item.key === key) {
return { ...(value as RouteModule.UpstreamHeader), key };
}
return item;
}),
- key,
});
} else {
onChange({
- upstreamHeaderList: upstreamHeaderList.concat({
+ action: 'upstreamHeaderListChange',
+ data: upstreamHeaderList.concat({
...(value as RouteModule.UpstreamHeader),
key: Math.random().toString(36).slice(2),
}),
diff --git a/src/pages/Route/components/Step2/RequestRewriteView.tsx b/src/pages/Route/components/Step2/RequestRewriteView.tsx
index 12ad7af..7294b0c 100644
--- a/src/pages/Route/components/Step2/RequestRewriteView.tsx
+++ b/src/pages/Route/components/Step2/RequestRewriteView.tsx
@@ -15,7 +15,7 @@
* limitations under the License.
*/
import React, { useEffect, useState } from 'react';
-import Form, { FormInstance } from 'antd/es/form';
+import Form from 'antd/es/form';
import Radio from 'antd/lib/radio';
import { Input, Row, Col, InputNumber, Button, Select } from 'antd';
import { PlusOutlined, MinusCircleOutlined } from '@ant-design/icons';
@@ -29,15 +29,23 @@ import {
HASH_ON_LIST,
} from '@/pages/Route/constants';
import styles from '../../Create.less';
-import { fetchUpstreamList } from '../../service';
+import { fetchUpstreamList, fetchUpstreamItem } from '../../service';
-interface Props extends RouteModule.Data {
- form: FormInstance;
-}
-const RequestRewriteView: React.FC<Props> = ({ data, form, disabled, onChange }) => {
- const { step2Data } = data;
+const RequestRewriteView: React.FC<RouteModule.Step2PassProps> = ({ form, disabled }) => {
const [upstearms, setUpstreams] = useState<{ id: string; name: string }[]>();
- const upstreamDisabled = disabled || !!step2Data.upstream_id;
+ const [upstreamId, setUpstreamId] = useState(form.getFieldValue('upstream_id'));
+ // TODO: need to check
+ let upstreamDisabled = disabled || Boolean(form.getFieldValue('upstream_id'));
+
+ if (upstreamId) {
+ fetchUpstreamItem(upstreamId).then((data) => {
+ form.setFieldsValue({
+ ...form.getFieldsValue(),
+ ...data,
+ });
+ upstreamDisabled = true;
+ });
+ }
const { formatMessage } = useIntl();
useEffect(() => {
@@ -47,41 +55,45 @@ const RequestRewriteView: React.FC<Props> = ({ data, form, disabled, onChange })
{ name: formatMessage({ id: 'route.request.override.input' }), id: null },
...data,
]);
- if (step2Data.upstream_id) {
- onChange({ upstream_id: step2Data.upstream_id });
- }
});
}, []);
const renderUpstreamMeta = () => (
<>
<Form.Item label="类型" name="type" rules={[{ required: true }]}>
- <Select disabled={upstreamDisabled} onChange={(params) => onChange({ type: params })}>
+ <Select disabled={upstreamDisabled}>
<Select.Option value="roundrobin">roundrobin</Select.Option>
<Select.Option value="chash">chash</Select.Option>
</Select>
</Form.Item>
- {step2Data.type === 'chash' && (
- <>
- <Form.Item label="Hash On" name="hash_on">
- <Select disabled={upstreamDisabled}>
- {HASH_ON_LIST.map((item) => (
- <Select.Option value={item} key={item}>
- {item}
- </Select.Option>
- ))}
- </Select>
- </Form.Item>
- <Form.Item label="Key" name="key">
- <Select disabled={upstreamDisabled}>
- {HASH_KEY_LIST.map((item) => (
- <Select.Option value={item} key={item}>
- {item}
- </Select.Option>
- ))}
- </Select>
- </Form.Item>
- </>
- )}
+ <Form.Item noStyle shouldUpdate={(prev, next) => prev.type !== next.type}>
+ {() => {
+ if (form.getFieldValue('type') === 'chash') {
+ return (
+ <>
+ <Form.Item label="Hash On" name="hash_on">
+ <Select disabled={upstreamDisabled}>
+ {HASH_ON_LIST.map((item) => (
+ <Select.Option value={item} key={item}>
+ {item}
+ </Select.Option>
+ ))}
+ </Select>
+ </Form.Item>
+ <Form.Item label="Key" name="key">
+ <Select disabled={upstreamDisabled}>
+ {HASH_KEY_LIST.map((item) => (
+ <Select.Option value={item} key={item}>
+ {item}
+ </Select.Option>
+ ))}
+ </Select>
+ </Form.Item>
+ </>
+ );
+ }
+ return null;
+ }}
+ </Form.Item>
<Form.List name="upstreamHostList">
{(fields, { add, remove }) => (
<>
@@ -209,13 +221,7 @@ const RequestRewriteView: React.FC<Props> = ({ data, form, disabled, onChange })
const renderTimeUnit = () => <span style={{ margin: '0 8px' }}>ms</span>;
return (
<PanelSection title={formatMessage({ id: 'route.request.override' })}>
- <Form
- {...FORM_ITEM_LAYOUT}
- form={form}
- layout="horizontal"
- className={styles.stepForm}
- initialValues={step2Data}
- >
+ <Form {...FORM_ITEM_LAYOUT} form={form} layout="horizontal" className={styles.stepForm}>
<Form.Item
label={formatMessage({ id: 'route.request.override.protocol' })}
name="upstream_protocol"
@@ -226,71 +232,78 @@ const RequestRewriteView: React.FC<Props> = ({ data, form, disabled, onChange })
},
]}
>
- <Radio.Group
- onChange={(e) => {
- onChange({ upstream_protocol: e.target.value });
- }}
- name="upstream_protocol"
- disabled={disabled}
- >
+ <Radio.Group name="upstream_protocol" disabled={disabled}>
<Radio value="keep">{formatMessage({ id: 'route.request.override.stay.same' })}</Radio>
<Radio value="http">HTTP</Radio>
<Radio value="https">HTTPS</Radio>
</Radio.Group>
</Form.Item>
<Form.Item label={formatMessage({ id: 'route.request.override.path' })} name="rewriteType">
- <Radio.Group
- onChange={(e) => {
- onChange({ rewriteType: e.target.value });
- }}
- disabled={disabled}
- >
+ <Radio.Group disabled={disabled}>
<Radio value="keep">{formatMessage({ id: 'route.request.override.stay.same' })}</Radio>
<Radio value="static">{formatMessage({ id: 'page.route.radio.static' })}</Radio>
<Radio value="regx">{formatMessage({ id: 'page.route.radio.regx' })}</Radio>
</Radio.Group>
</Form.Item>
- {step2Data.rewriteType === 'regx' && (
- <Form.Item
- label={formatMessage({ id: 'page.route.form.itemLabel.from' })}
- name="mappingStrategy"
- rules={[
- {
- required: true,
- message: formatMessage({ id: 'route.request.override.input.path' }),
- },
- ]}
- >
- <Input
- disabled={disabled}
- placeholder={formatMessage({ id: 'route.request.override.path.example' })}
- />
- </Form.Item>
- )}
- {(step2Data.rewriteType === 'static' || step2Data.rewriteType === 'regx') && (
- <Form.Item
- label={formatMessage({ id: 'route.request.override.new.path' })}
- name="upstreamPath"
- rules={[
- {
- required: true,
- message: formatMessage({ id: 'route.request.override.input.path' }),
- },
- ]}
- >
- <Input
- disabled={disabled}
- placeholder={formatMessage({ id: 'route.request.override.path.example' })}
- />
- </Form.Item>
- )}
+ <Form.Item noStyle shouldUpdate={(prev, next) => prev.rewriteType !== next.rewriteType}>
+ {() => {
+ if (form.getFieldValue('rewriteType') === 'regx') {
+ return (
+ <Form.Item
+ label={formatMessage({ id: 'page.route.form.itemLabel.from' })}
+ name="mappingStrategy"
+ rules={[
+ {
+ required: true,
+ message: formatMessage({ id: 'route.request.override.input.path' }),
+ },
+ ]}
+ >
+ <Input
+ disabled={disabled}
+ placeholder={formatMessage({ id: 'route.request.override.path.example' })}
+ />
+ </Form.Item>
+ );
+ }
+ return null;
+ }}
+ </Form.Item>
+ <Form.Item noStyle shouldUpdate={(prev, next) => prev.rewriteType !== next.rewriteType}>
+ {() => {
+ if (
+ form.getFieldValue('rewriteType') === 'static' ||
+ form.getFieldValue('rewriteType') === 'regx'
+ ) {
+ return (
+ <Form.Item
+ label={formatMessage({ id: 'route.request.override.new.path' })}
+ name="upstreamPath"
+ rules={[
+ {
+ required: true,
+ message: formatMessage({ id: 'route.request.override.input.path' }),
+ },
+ ]}
+ >
+ <Input
+ disabled={disabled}
+ placeholder={formatMessage({ id: 'route.request.override.path.example' })}
+ />
+ </Form.Item>
+ );
+ }
+ return null;
+ }}
+ </Form.Item>
+
<Form.Item
label={formatMessage({ id: 'route.request.override.upstream' })}
name="upstream_id"
>
<Select
onChange={(value) => {
- onChange({ upstream_id: value });
+ setUpstreamId(value);
}}
disabled={disabled}
>
diff --git a/src/pages/Route/components/Step2/index.tsx b/src/pages/Route/components/Step2/index.tsx
index a5d8eb4..ba60b12 100644
--- a/src/pages/Route/components/Step2/index.tsx
+++ b/src/pages/Route/components/Step2/index.tsx
@@ -15,16 +15,11 @@
* limitations under the License.
*/
import React from 'react';
-import { FormInstance } from 'antd/es/form';
import RequestRewriteView from './RequestRewriteView';
import HttpHeaderRewriteView from './HttpHeaderRewriteView';
-interface Props extends RouteModule.Data {
- form: FormInstance;
-}
-
-const Step2: React.FC<Props> = (props) => {
+const Step2: React.FC<RouteModule.Step2PassProps> = (props) => {
return (
<>
<RequestRewriteView {...props} />
diff --git a/src/pages/Route/constants.ts b/src/pages/Route/constants.ts
index bf23e59..d421b57 100644
--- a/src/pages/Route/constants.ts
+++ b/src/pages/Route/constants.ts
@@ -40,8 +40,8 @@ export const FORM_ITEM_WITHOUT_LABEL = {
},
};
-export const DEFAULT_STEP_1_DATA: RouteModule.Step1Data = {
- route_group_id: '',
+export const DEFAULT_STEP_1_DATA: RouteModule.Form1Data = {
+ route_group_id: null,
route_group_name: '',
name: '',
desc: '',
@@ -55,13 +55,13 @@ export const DEFAULT_STEP_1_DATA: RouteModule.Step1Data = {
redirectURI: '',
redirectCode: 302,
methods: HTTP_METHOD_OPTION_LIST,
- advancedMatchingRules: [],
};
-export const DEFAULT_STEP_2_DATA: RouteModule.Step2Data = {
+export const DEFAULT_STEP_2_DATA: RouteModule.Form2Data = {
upstream_protocol: 'keep',
upstreamHostList: [{} as RouteModule.UpstreamHost],
- upstreamHeaderList: [],
+ upstream_id: null,
+ pass_host: 'pass',
upstreamPath: undefined,
type: 'roundrobin',
mappingStrategy: undefined,
diff --git a/src/pages/Route/service.ts b/src/pages/Route/service.ts
index 92b7092..62046da 100644
--- a/src/pages/Route/service.ts
+++ b/src/pages/Route/service.ts
@@ -24,13 +24,13 @@ import {
transformRouteDebugData,
} from './transform';
-export const create = (data: Pick<RouteModule.Data, 'data'>) =>
+export const create = (data: RouteModule.RequestData) =>
request(`/routes`, {
method: 'POST',
data: transformStepData(data),
});
-export const update = (rid: number, data: Pick<RouteModule.Data, 'data'>) =>
+export const update = (rid: number, data: RouteModule.RequestData) =>
request(`/routes/${rid}`, {
method: 'PUT',
data: transformStepData(data),
diff --git a/src/pages/Route/transform.ts b/src/pages/Route/transform.ts
index bc65add..6b07be3 100644
--- a/src/pages/Route/transform.ts
+++ b/src/pages/Route/transform.ts
@@ -18,49 +18,53 @@
import { omit, pick } from 'lodash';
export const transformStepData = ({
- data: { step1Data, step2Data, step3Data },
-}: Pick<RouteModule.Data, 'data'>) => {
+ form1Data,
+ form2Data,
+ advancedMatchingRules,
+ upstreamHeaderList,
+ step3Data,
+}: RouteModule.RequestData) => {
const nodes = {};
- step2Data.upstreamHostList.forEach((node) => {
+ (form2Data.upstreamHostList || []).forEach((node) => {
nodes[`${node.host}:${node.port}`] = node.weight;
});
const upstream_header = {};
- (step2Data.upstreamHeaderList || []).forEach((header) => {
+ (upstreamHeaderList || []).forEach((header) => {
upstream_header[header.header_name] = header.header_value || '';
});
const chashData: any = {};
- if (step2Data.type === 'chash') {
- chashData.key = step2Data.key;
- chashData.hash_on = step2Data.hash_on;
+ if (form2Data.type === 'chash') {
+ chashData.key = form2Data.key;
+ chashData.hash_on = form2Data.hash_on;
}
let redirect: RouteModule.Redirect = {};
- if (step1Data.redirectOption === 'disabled') {
+ if (form1Data.redirectOption === 'disabled') {
redirect = {};
- } else if (step1Data.redirectOption === 'forceHttps') {
+ } else if (form1Data.redirectOption === 'forceHttps') {
redirect = { http_to_https: true };
- } else if (step1Data.redirectURI !== '') {
+ } else if (form1Data.redirectURI !== '') {
redirect = {
- code: step1Data.redirectCode,
- uri: step1Data.redirectURI,
+ code: form1Data.redirectCode,
+ uri: form1Data.redirectURI,
};
}
- let { protocols } = step1Data;
- if (step1Data.websocket) {
+ let { protocols } = form1Data;
+ if (form1Data.websocket) {
protocols = protocols.concat('websocket');
}
const data: Partial<RouteModule.Body> = {
- ...step1Data,
- ...step2Data,
+ ...form1Data,
+ ...form2Data,
...step3Data,
protocols,
- uris: step1Data.paths,
+ uris: form1Data.paths,
redirect,
- vars: step1Data.advancedMatchingRules.map((rule) => {
+ vars: advancedMatchingRules.map((rule) => {
const { operator, position, name, value } = rule;
let key = '';
switch (position) {
@@ -76,34 +80,33 @@ export const transformStepData = ({
return [key, operator, value];
}),
upstream: {
- type: step2Data.type,
+ type: form2Data.type,
...chashData,
nodes,
- timeout: step2Data.timeout,
+ timeout: form2Data.timeout,
},
upstream_header,
};
- if (step2Data.upstreamPath) {
+ if (form2Data.upstreamPath) {
data.upstream_path = {
- to: step2Data.upstreamPath,
+ to: form2Data.upstreamPath,
};
- if (step2Data.mappingStrategy) {
+ if (form2Data.mappingStrategy) {
data.upstream_path = {
...data.upstream_path,
- from: step2Data.mappingStrategy,
+ from: form2Data.mappingStrategy,
type: 'regx',
};
}
}
- if (step3Data.plugins.prometheus) {
- // eslint-disable-next-line no-param-reassign
- step3Data.plugins.prometheus = {};
- }
-
// 未启用 redirect
if (!redirect.uri) {
+ if (step3Data.plugins.prometheus) {
+ // eslint-disable-next-line no-param-reassign
+ step3Data.plugins.prometheus = {};
+ }
// 移除前端部分自定义变量
return omit(data, [
'advancedMatchingRules',
@@ -118,12 +121,23 @@ export const transformStepData = ({
'redirectCode',
'forceHttps',
'redirectOption',
- step1Data.redirectOption === 'disabled' ? 'redirect' : '',
- step2Data.upstream_id ? 'upstream' : 'upstream_id',
+ form1Data.redirectOption === 'disabled' ? 'redirect' : '',
+ form2Data.upstream_id ? 'upstream' : 'upstream_id',
]);
}
- return pick(data, ['name', 'desc', 'protocols', 'hosts', 'uris', 'methods', 'redirect', 'vars']);
+ return pick(data, [
+ 'name',
+ 'desc',
+ 'protocols',
+ 'hosts',
+ 'uris',
+ 'methods',
+ 'redirect',
+ 'vars',
+ 'route_group_id',
+ 'route_group_name',
+ ]);
};
const transformVarsToRules = (
@@ -169,7 +183,7 @@ export const transformRouteData = (data: RouteModule.Body) => {
status,
} = data;
- const step1Data: Partial<RouteModule.Step1Data> = {
+ const form1Data: Partial<RouteModule.Form1Data> = {
name,
route_group_id,
route_group_name,
@@ -180,17 +194,17 @@ export const transformRouteData = (data: RouteModule.Body) => {
hosts,
paths: uris,
methods,
- advancedMatchingRules: transformVarsToRules(vars),
};
+ const advancedMatchingRules: RouteModule.MatchingRule[] = transformVarsToRules(vars);
if (redirect?.http_to_https) {
- step1Data.redirectOption = 'forceHttps';
+ form1Data.redirectOption = 'forceHttps';
} else if (redirect?.uri) {
- step1Data.redirectOption = 'customRedirect';
- step1Data.redirectCode = redirect?.code;
- step1Data.redirectURI = redirect?.uri;
+ form1Data.redirectOption = 'customRedirect';
+ form1Data.redirectCode = redirect?.code;
+ form1Data.redirectURI = redirect?.uri;
} else {
- step1Data.redirectOption = 'disabled';
+ form1Data.redirectOption = 'disabled';
}
const {
@@ -218,7 +232,7 @@ export const transformRouteData = (data: RouteModule.Body) => {
};
});
- const step2Data: RouteModule.Step2Data = {
+ const form2Data: RouteModule.Form2Data = {
upstream_protocol,
upstreamHeaderList,
type: upstream ? upstream.type : 'roundrobin',
@@ -238,18 +252,21 @@ export const transformRouteData = (data: RouteModule.Body) => {
const { plugins, script } = data;
- if (plugins.prometheus) {
+ if (plugins && plugins.prometheus) {
plugins.prometheus = { enabled: true };
}
+
const step3Data: RouteModule.Step3Data = {
plugins,
script,
};
return {
- step1Data,
- step2Data,
+ form1Data,
+ form2Data,
step3Data,
+ upstreamHeaderList,
+ advancedMatchingRules,
};
};
diff --git a/src/pages/Route/typing.d.ts b/src/pages/Route/typing.d.ts
index e6bab1b..6675054 100644
--- a/src/pages/Route/typing.d.ts
+++ b/src/pages/Route/typing.d.ts
@@ -39,41 +39,12 @@ declare namespace RouteModule {
status: boolean;
};
- type Step1Data = {
- name: string;
- desc: string;
- status: boolean;
- priority: number;
- protocols: RequestProtocol[];
- websocket: boolean;
- hosts: string[];
- paths: string[];
- methods: HttpMethod[];
- redirectOption: 'forceHttps' | 'customRedirect' | 'disabled';
- redirectURI?: string;
- redirectCode?: number;
- advancedMatchingRules: MatchingRule[];
- route_group_id?: string;
- route_group_name: string;
- };
-
type Step3Data = {
plugins: PluginPage.PluginData;
// TEMP
script: any;
};
- interface Data {
- disabled?: boolean;
- data: {
- step1Data: Step1Data;
- step2Data: Step2Data;
- step3Data: Step3Data;
- };
- onChange(data: T): void;
- isEdit?: boolean;
- }
-
type UpstreamHost = {
host: string;
port: number;
@@ -89,24 +60,6 @@ declare namespace RouteModule {
key: string;
}
- type Step2Data = {
- upstream_protocol: 'http' | 'https' | 'keep';
- type: 'roundrobin' | 'chash';
- hash_on?: string;
- key?: string;
- upstreamHostList: UpstreamHost[];
- mappingStrategy: string | undefined;
- rewriteType: string | undefined;
- upstreamPath: string | undefined;
- upstreamHeaderList: UpstreamHeader[];
- upstream_id?: string;
- timeout: {
- connect: number;
- send: number;
- read: number;
- };
- };
-
type ModalType = 'CREATE' | 'EDIT';
type Redirect = {
@@ -118,7 +71,7 @@ declare namespace RouteModule {
// Request Body or Response Data for API
type Body = {
id?: number;
- route_group_id?: string;
+ route_group_id: string;
route_group_name: string;
status: boolean;
name: string;
@@ -223,4 +176,86 @@ declare namespace RouteModule {
};
};
};
+
+ // step1
+ interface MatchingRule {
+ position: VarPosition;
+ name: string;
+ operator: Operator;
+ value: string;
+ key: string;
+ }
+
+ type Step1PassProps = {
+ form: FormInstance;
+ advancedMatchingRules: MatchingRule[];
+ disabled?: boolean;
+ isEdit?: boolean;
+ onChange?(data: {
+ action: 'redirectOptionChange' | 'advancedMatchingRulesChange';
+ data: T;
+ }): void;
+ };
+
+ type Form1Data = {
+ name: string;
+ desc: string;
+ route_group_id: string | null;
+ route_group_name: string;
+ priority: number;
+ protocols: RequestProtocol[];
+ websocket: boolean;
+ hosts: string[];
+ paths: string[];
+ methods: HttpMethod[];
+ redirectOption: 'forceHttps' | 'customRedirect' | 'disabled';
+ redirectURI?: string;
+ redirectCode?: number;
+ status: boolean;
+ };
+
+ type AvancedMatchingRules = {
+ advancedMatchingRules: MatchingRule[];
+ };
+
+ // step2
+ type UpstreamHeader = {
+ key: string;
+ header_name: string;
+ header_value: string;
+ };
+
+ type Step2PassProps = {
+ form: FormInstance;
+ upstreamHeaderList: UpstreamHeader[] | undefined;
+ disabled?: boolean;
+ onChange(data: { action: 'upstreamHeaderListChange'; data: T }): void;
+ };
+
+ type Form2Data = {
+ upstream_protocol: 'http' | 'https' | 'keep';
+ type: 'roundrobin' | 'chash';
+ hash_on?: string;
+ key?: string;
+ mappingStrategy?: string;
+ rewriteType?: string;
+ upstreamPath?: string;
+ upstream_id: string | null;
+ timeout: {
+ connect: number;
+ send: number;
+ read: number;
+ };
+ pass_host: 'pass' | 'node' | 'rewrite';
+ upstream_host?: string;
+ upstreamHostList: UpstreamHost[];
+ };
+
+ type RequestData = {
+ form1Data: Form1Data;
+ form2Data: Form2Data;
+ step3Data: Step3Data;
+ upstreamHeaderList: UpstreamHeader[];
+ advancedMatchingRules: MatchingRule[];
+ };
}