You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by su...@apache.org on 2020/12/31 01:00:01 UTC
[apisix-dashboard] branch master updated: feat(FE): version manager
(#1157)
This is an automated email from the ASF dual-hosted git repository.
sunyi 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 2dea025 feat(FE): version manager (#1157)
2dea025 is described below
commit 2dea0251d2f3cd215aa0a9fa29cc7656ec0d120e
Author: 琚致远 <ju...@apache.org>
AuthorDate: Wed Dec 30 18:59:53 2020 -0600
feat(FE): version manager (#1157)
* style: codes-format
* feat: use labels form item
* style: codes-format
* feat: adjust buttons
* style: 调整按钮位置
* feat: 增加警告标示
* feat: added version
* feat: added version
* fix: redirect
* style: format codes
* fix: style
---
web/src/components/Plugin/PluginDetail.tsx | 4 +-
web/src/components/Plugin/PluginPage.tsx | 2 +-
web/src/constants.ts | 2 +-
web/src/hooks/useForceIntl.ts | 2 +-
web/src/locales/en-US/component.ts | 2 +
web/src/locales/zh-CN/component.ts | 2 +
web/src/pages/Consumer/Create.tsx | 26 +++--
web/src/pages/Plugin/PluginMarket.tsx | 4 +-
web/src/pages/Plugin/components/Step1.tsx | 16 ---
web/src/pages/Plugin/typing.d.ts | 2 +-
web/src/pages/Route/Create.tsx | 4 +-
web/src/pages/Route/List.tsx | 130 ++++++++++++++-------
.../pages/Route/components/Step1/LabelsDrawer.tsx | 25 ++--
web/src/pages/Route/components/Step1/MetaView.tsx | 110 +++++++++++------
web/src/pages/Route/service.ts | 4 +-
web/src/pages/Route/transform.ts | 34 ++++--
web/src/pages/Route/typing.d.ts | 10 +-
17 files changed, 233 insertions(+), 146 deletions(-)
diff --git a/web/src/components/Plugin/PluginDetail.tsx b/web/src/components/Plugin/PluginDetail.tsx
index 932dee6..2ff336c 100644
--- a/web/src/components/Plugin/PluginDetail.tsx
+++ b/web/src/components/Plugin/PluginDetail.tsx
@@ -67,8 +67,8 @@ const PluginDetail: React.FC<Props> = ({
visible,
readonly = false,
initialData = {},
- onClose = () => { },
- onChange = () => { },
+ onClose = () => {},
+ onChange = () => {},
}) => {
const { formatMessage } = useIntl();
const [form] = Form.useForm();
diff --git a/web/src/components/Plugin/PluginPage.tsx b/web/src/components/Plugin/PluginPage.tsx
index 272b00c..cde2897 100644
--- a/web/src/components/Plugin/PluginPage.tsx
+++ b/web/src/components/Plugin/PluginPage.tsx
@@ -48,7 +48,7 @@ const PluginPage: React.FC<Props> = ({
initialData = {},
schemaType = 'route',
type = 'scoped',
- onChange = () => { },
+ onChange = () => {},
}) => {
const [pluginList, setPluginList] = useState<PluginComponent.Meta[]>([]);
const [name, setName] = useState<string>(NEVER_EXIST_PLUGIN_FLAG);
diff --git a/web/src/constants.ts b/web/src/constants.ts
index 217cf40..3df1c0c 100644
--- a/web/src/constants.ts
+++ b/web/src/constants.ts
@@ -32,4 +32,4 @@ export const codeMessage = {
504: '网关超时。',
};
-export const DEFAULT_GLOBAL_RULE_ID = "1"
+export const DEFAULT_GLOBAL_RULE_ID = '1';
diff --git a/web/src/hooks/useForceIntl.ts b/web/src/hooks/useForceIntl.ts
index 8f67bef..890a16a 100644
--- a/web/src/hooks/useForceIntl.ts
+++ b/web/src/hooks/useForceIntl.ts
@@ -29,7 +29,7 @@ const useForceIntl = () => {
}
const { locale } = getIntl();
- if (locale === 'zh-cn') {
+ if (locale === 'zh-CN') {
return;
}
diff --git a/web/src/locales/en-US/component.ts b/web/src/locales/en-US/component.ts
index 946a735..fa58634 100644
--- a/web/src/locales/en-US/component.ts
+++ b/web/src/locales/en-US/component.ts
@@ -28,6 +28,7 @@ export default {
'component.global.add': 'Add',
'component.global.save': 'Save',
'component.global.edit': 'Edit',
+ 'component.global.manage': 'Manage',
'component.global.update': 'Update',
'component.global.get': 'Get',
'component.global.edit.plugin': 'Edit plugin',
@@ -35,6 +36,7 @@ export default {
'component.global.list': 'List',
'component.global.description': 'Description',
'component.global.labels': 'Labels',
+ 'component.global.version': 'Version',
'component.global.operation': 'Operation',
'component.status.success': 'Successfully',
'component.status.fail': 'Failed',
diff --git a/web/src/locales/zh-CN/component.ts b/web/src/locales/zh-CN/component.ts
index d0829c6..210dd86 100644
--- a/web/src/locales/zh-CN/component.ts
+++ b/web/src/locales/zh-CN/component.ts
@@ -28,6 +28,7 @@ export default {
'component.global.add': '新建',
'component.global.save': '保存',
'component.global.edit': '编辑',
+ 'component.global.manage': '管理',
'component.global.update': '更新',
'component.global.get': '获取',
'component.global.edit.plugin': '编辑插件',
@@ -35,6 +36,7 @@ export default {
'component.global.list': '列表',
'component.global.description': '描述',
'component.global.labels': '标签',
+ 'component.global.version': '版本',
'component.global.operation': '操作',
'component.status.success': '成功',
'component.status.fail': '失败',
diff --git a/web/src/pages/Consumer/Create.tsx b/web/src/pages/Consumer/Create.tsx
index 2bfdfe4..217c335 100644
--- a/web/src/pages/Consumer/Create.tsx
+++ b/web/src/pages/Consumer/Create.tsx
@@ -29,7 +29,7 @@ import { fetchItem, create, update, fetchPlugList } from './service';
const Page: React.FC = (props) => {
const [step, setStep] = useState(1);
const [plugins, setPlugins] = useState<PluginComponent.Data>({});
- const [pluginList, setPluginList] = useState<PluginComponent.Meta[]>([])
+ const [pluginList, setPluginList] = useState<PluginComponent.Meta[]>([]);
const [form1] = Form.useForm();
const { formatMessage } = useIntl();
@@ -52,12 +52,13 @@ const Page: React.FC = (props) => {
(username ? update(username, data) : create(data))
.then(() => {
notification.success({
- message: `${username
- ? formatMessage({ id: 'component.global.edit' })
- : formatMessage({ id: 'component.global.create' })
- } ${formatMessage({ id: 'menu.consumer' })} ${formatMessage({
- id: 'component.status.success',
- })}`,
+ message: `${
+ username
+ ? formatMessage({ id: 'component.global.edit' })
+ : formatMessage({ id: 'component.global.create' })
+ } ${formatMessage({ id: 'menu.consumer' })} ${formatMessage({
+ id: 'component.status.success',
+ })}`,
});
history.push('/consumer/list');
})
@@ -76,7 +77,7 @@ const Page: React.FC = (props) => {
if (
!Object.keys(plugins).filter(
(name) =>
- (pluginList.find(item => item.name === name)!.type === 'auth') &&
+ pluginList.find((item) => item.name === name)!.type === 'auth' &&
!plugins[name].disable,
).length
) {
@@ -98,10 +99,11 @@ const Page: React.FC = (props) => {
return (
<>
<PageContainer
- title={`${(props as any).match.params.id
- ? formatMessage({ id: 'component.global.edit' })
- : formatMessage({ id: 'component.global.create' })
- } ${formatMessage({ id: 'menu.consumer' })}`}
+ title={`${
+ (props as any).match.params.id
+ ? formatMessage({ id: 'component.global.edit' })
+ : formatMessage({ id: 'component.global.create' })
+ } ${formatMessage({ id: 'menu.consumer' })}`}
>
<Card bordered={false}>
<Steps current={step - 1} style={{ marginBottom: 30 }}>
diff --git a/web/src/pages/Plugin/PluginMarket.tsx b/web/src/pages/Plugin/PluginMarket.tsx
index 3613e00..44032ec 100644
--- a/web/src/pages/Plugin/PluginMarket.tsx
+++ b/web/src/pages/Plugin/PluginMarket.tsx
@@ -48,9 +48,9 @@ const PluginMarket: React.FC = () => {
...pluginsData,
},
}).then(() => {
- // TODO:
+ // TODO:
window.location.reload();
- })
+ });
}}
/>
</Card>
diff --git a/web/src/pages/Plugin/components/Step1.tsx b/web/src/pages/Plugin/components/Step1.tsx
deleted file mode 100644
index 2944f98..0000000
--- a/web/src/pages/Plugin/components/Step1.tsx
+++ /dev/null
@@ -1,16 +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.
- */
diff --git a/web/src/pages/Plugin/typing.d.ts b/web/src/pages/Plugin/typing.d.ts
index f0f3ac9..6f9c31b 100644
--- a/web/src/pages/Plugin/typing.d.ts
+++ b/web/src/pages/Plugin/typing.d.ts
@@ -25,5 +25,5 @@ declare namespace PluginModule {
type GlobalRule = {
id: string;
plugins: Record<string, object>;
- }
+ };
}
diff --git a/web/src/pages/Route/Create.tsx b/web/src/pages/Route/Create.tsx
index a3d5f53..ab1257f 100644
--- a/web/src/pages/Route/Create.tsx
+++ b/web/src/pages/Route/Create.tsx
@@ -112,8 +112,8 @@ const Page: React.FC<Props> = (props) => {
if (action === 'advancedMatchingRulesChange') {
setAdvancedMatchingRules(data);
}
- if (action === 'labelsChange') {
- form1.setFieldsValue({ ...form1.getFieldsValue(), labels: data });
+ if (action === 'custom_normal_labels') {
+ form1.setFieldsValue({ custom_normal_labels: data });
}
}}
isEdit={props.route.path.indexOf('edit') > 0}
diff --git a/web/src/pages/Route/List.tsx b/web/src/pages/Route/List.tsx
index 304ad3b..4a6c8a6 100644
--- a/web/src/pages/Route/List.tsx
+++ b/web/src/pages/Route/List.tsx
@@ -103,11 +103,13 @@ const Page: React.FC = () => {
title: formatMessage({ id: 'component.global.labels' }),
dataIndex: 'labels',
render: (_, record) => {
- return Object.keys(record.labels || {}).map((item) => (
- <Tag key={Math.random().toString(36).slice(2)}>
- {item}:{record.labels[item]}
- </Tag>
- ));
+ return Object.keys(record.labels || {})
+ .filter((item) => item !== 'API_VERSION')
+ .map((item) => (
+ <Tag key={Math.random().toString(36).slice(2)}>
+ {item}:{record.labels[item]}
+ </Tag>
+ ));
},
renderFormItem: (_, { type }) => {
if (type === 'form') {
@@ -127,18 +129,53 @@ const Page: React.FC = () => {
);
}}
>
- {Object.keys(labelList).map((key) => {
- return (
- <OptGroup label={key} key={Math.random().toString(36).slice(2)}>
- {(labelList[key] || []).map((value: string) => (
- <Option key={Math.random().toString(36).slice(2)} value={`${key}:${value}`}>
- {' '}
- {value}{' '}
- </Option>
- ))}
- </OptGroup>
- );
- })}
+ {Object.keys(labelList)
+ .filter((item) => item !== 'API_VERSION')
+ .map((key) => {
+ return (
+ <OptGroup label={key} key={Math.random().toString(36).slice(2)}>
+ {(labelList[key] || []).map((value: string) => (
+ <Option key={Math.random().toString(36).slice(2)} value={`${key}:${value}`}>
+ {' '}
+ {value}{' '}
+ </Option>
+ ))}
+ </OptGroup>
+ );
+ })}
+ </Select>
+ );
+ },
+ },
+ {
+ title: formatMessage({ id: 'component.global.version' }),
+ dataIndex: 'API_VERSION',
+ render: (_, record) => {
+ return Object.keys(record.labels || {})
+ .filter((item) => item === 'API_VERSION')
+ .map((item) => record.labels[item]);
+ },
+ renderFormItem: (_, { type }) => {
+ if (type === 'form') {
+ return null;
+ }
+
+ return (
+ <Select style={{ width: '100%' }}>
+ {Object.keys(labelList)
+ .filter((item) => item === 'API_VERSION')
+ .map((key) => {
+ return (
+ <OptGroup label={key} key={Math.random().toString(36).slice(2)}>
+ {(labelList[key] || []).map((value: string) => (
+ <Option key={Math.random().toString(36).slice(2)} value={`${key}:${value}`}>
+ {' '}
+ {value}{' '}
+ </Option>
+ ))}
+ </OptGroup>
+ );
+ })}
</Select>
);
},
@@ -169,36 +206,36 @@ const Page: React.FC = () => {
render: (_, record) => (
<>
<Space align="baseline">
- <Button
- type="primary"
- onClick={() => history.push(`/routes/${record.id}/edit`)}
- style={{ marginRight: 10 }}
- >
+ {!record.status ? (
+ <Button
+ type="primary"
+ onClick={() => {
+ handlePublishOffline(record.id, RouteStatus.Publish);
+ }}
+ >
+ {formatMessage({ id: 'page.route.publish' })}
+ </Button>
+ ) : null}
+ {record.status ? (
+ <Popconfirm
+ title={formatMessage({ id: 'page.route.popconfirm.title.offline' })}
+ onConfirm={() => {
+ handlePublishOffline(record.id, RouteStatus.Offline);
+ }}
+ okButtonProps={{
+ danger: true,
+ }}
+ okText={formatMessage({ id: 'component.global.confirm' })}
+ cancelText={formatMessage({ id: 'component.global.cancel' })}
+ >
+ <Button type="primary" danger disabled={Boolean(!record.status)}>
+ {formatMessage({ id: 'page.route.offline' })}
+ </Button>
+ </Popconfirm>
+ ) : null}
+ <Button type="primary" onClick={() => history.push(`/routes/${record.id}/edit`)}>
{formatMessage({ id: 'component.global.edit' })}
</Button>
- <Button
- type="primary"
- onClick={() => {
- handlePublishOffline(record.id, RouteStatus.Publish);
- }}
- style={{ marginRight: 10 }}
- disabled={Boolean(record.status)}
- >
- {formatMessage({ id: 'page.route.publish' })}
- </Button>
- <Popconfirm
- title={formatMessage({ id: 'page.route.popconfirm.title.offline' })}
- onConfirm={() => {
- handlePublishOffline(record.id, RouteStatus.Offline);
- }}
- okText={formatMessage({ id: 'component.global.confirm' })}
- cancelText={formatMessage({ id: 'component.global.cancel' })}
- disabled={Boolean(!record.status)}
- >
- <Button type="primary" danger disabled={Boolean(!record.status)}>
- {formatMessage({ id: 'page.route.offline' })}
- </Button>
- </Popconfirm>
<Popconfirm
title={formatMessage({ id: 'component.global.popconfirm.title.delete' })}
onConfirm={() => {
@@ -210,6 +247,9 @@ const Page: React.FC = () => {
);
});
}}
+ okButtonProps={{
+ danger: true,
+ }}
okText={formatMessage({ id: 'component.global.confirm' })}
cancelText={formatMessage({ id: 'component.global.cancel' })}
>
diff --git a/web/src/pages/Route/components/Step1/LabelsDrawer.tsx b/web/src/pages/Route/components/Step1/LabelsDrawer.tsx
index ba5b221..720a437 100644
--- a/web/src/pages/Route/components/Step1/LabelsDrawer.tsx
+++ b/web/src/pages/Route/components/Step1/LabelsDrawer.tsx
@@ -22,16 +22,20 @@ import { useIntl } from 'umi';
import { transformLableValueToKeyValue } from '../../transform';
import { fetchLabelList } from '../../service';
-interface Props extends Pick<RouteModule.Step1PassProps, 'onChange'> {
- labelsDataSource: string[];
+type Props = {
+ title?: string;
+ actionName: string;
+ dataSource: string[];
disabled: boolean;
onClose(): void;
-}
+} & Pick<RouteModule.Step1PassProps, 'onChange'>;
const LabelList = (disabled: boolean, labelList: RouteModule.LabelList) => {
const { formatMessage } = useIntl();
- const keyOptions = Object.keys(labelList || {}).map((item) => ({ value: item }));
+ const keyOptions = Object.keys(labelList || {})
+ .filter((item) => item !== 'API_VERSION')
+ .map((item) => ({ value: item }));
return (
<Form.List name="labels">
{(fields, { add, remove }) => {
@@ -108,12 +112,14 @@ const LabelList = (disabled: boolean, labelList: RouteModule.LabelList) => {
};
const LabelsDrawer: React.FC<Props> = ({
- disabled,
- labelsDataSource,
+ title = 'Label Manager',
+ actionName = '',
+ disabled = false,
+ dataSource = [],
onClose,
onChange = () => {},
}) => {
- const transformLabel = transformLableValueToKeyValue(labelsDataSource);
+ const transformLabel = transformLableValueToKeyValue(dataSource);
const { formatMessage } = useIntl();
const [form] = Form.useForm();
@@ -126,12 +132,13 @@ const LabelsDrawer: React.FC<Props> = ({
return (
<Drawer
- title="Edit labels"
+ title={title}
placement="right"
width={512}
visible
closable
onClose={onClose}
+ maskClosable={false}
footer={
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<Button onClick={onClose}>{formatMessage({ id: 'component.global.cancel' })}</Button>
@@ -152,7 +159,7 @@ const LabelsDrawer: React.FC<Props> = ({
}
onChange({
- action: 'labelsChange',
+ action: actionName,
data,
});
onClose();
diff --git a/web/src/pages/Route/components/Step1/MetaView.tsx b/web/src/pages/Route/components/Step1/MetaView.tsx
index 25e1f31..09f64e5 100644
--- a/web/src/pages/Route/components/Step1/MetaView.tsx
+++ b/web/src/pages/Route/components/Step1/MetaView.tsx
@@ -14,38 +14,94 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import React, { useState } from 'react';
+import React, { useEffect, useState } from 'react';
import Form from 'antd/es/form';
-import { Input, Switch, Select, Button, Tag } from 'antd';
+import { Input, Switch, Select, Button, Tag, AutoComplete } from 'antd';
import { useIntl } from 'umi';
import { PanelSection } from '@api7-dashboard/ui';
import { FORM_ITEM_WITHOUT_LABEL } from '@/pages/Route/constants';
import LabelsDrawer from './LabelsDrawer';
+import { fetchLabelList } from '../../service';
const MetaView: React.FC<RouteModule.Step1PassProps> = ({ disabled, form, isEdit, onChange }) => {
const { formatMessage } = useIntl();
const [visible, setVisible] = useState(false);
+ const [labelList, setLabelList] = useState<RouteModule.LabelList>({});
- return (
- <PanelSection title={formatMessage({ id: 'page.route.panelSection.title.nameDescription' })}>
- {visible && (
- <Form.Item shouldUpdate noStyle>
- {() => {
- if (form.getFieldValue('labels')) {
+ useEffect(() => {
+ // TODO: use a better state name
+ fetchLabelList().then(setLabelList);
+ }, []);
+
+ const NormalLabelComponent = () => {
+ const field = 'custom_normal_labels';
+ const title = 'Label Manager';
+
+ return (
+ <React.Fragment>
+ <Form.Item label={formatMessage({ id: 'component.global.labels' })} name={field}>
+ <Select
+ mode="tags"
+ style={{ width: '100%' }}
+ placeholder="--"
+ disabled={disabled}
+ open={false}
+ bordered={false}
+ tagRender={(props) => {
+ const { value, closable, onClose } = props;
+ return (
+ <Tag closable={closable && !disabled} onClose={onClose} style={{ marginRight: 3 }}>
+ {value}
+ </Tag>
+ );
+ }}
+ />
+ </Form.Item>
+ <Form.Item {...FORM_ITEM_WITHOUT_LABEL}>
+ <Button type="dashed" disabled={disabled} onClick={() => setVisible(true)}>
+ {formatMessage({ id: 'component.global.manage' })}
+ </Button>
+ </Form.Item>
+ {visible && (
+ <Form.Item shouldUpdate noStyle>
+ {() => {
+ const labels = form.getFieldValue(field) || [];
return (
<LabelsDrawer
- labelsDataSource={form.getFieldValue('labels')}
+ title={title}
+ actionName={field}
+ dataSource={labels}
disabled={disabled || false}
onChange={onChange}
onClose={() => setVisible(false)}
/>
);
- }
- return null;
- }}
+ }}
+ </Form.Item>
+ )}
+ </React.Fragment>
+ );
+ };
+
+ const VersionLabelComponent = () => {
+ return (
+ <React.Fragment>
+ <Form.Item
+ label={formatMessage({ id: 'component.global.version' })}
+ name="custom_version_label"
+ >
+ <AutoComplete
+ options={(labelList.API_VERSION || []).map((item) => ({ value: item }))}
+ disabled={disabled}
+ />
</Form.Item>
- )}
+ </React.Fragment>
+ );
+ };
+
+ return (
+ <PanelSection title={formatMessage({ id: 'page.route.panelSection.title.nameDescription' })}>
<Form.Item
label={formatMessage({ id: 'component.global.name' })}
name="name"
@@ -70,35 +126,17 @@ const MetaView: React.FC<RouteModule.Step1PassProps> = ({ disabled, form, isEdit
disabled={disabled}
/>
</Form.Item>
- <Form.Item label={formatMessage({ id: 'component.global.labels' })} name="labels">
- <Select
- mode="tags"
- style={{ width: '100%' }}
- placeholder="--"
- disabled={disabled}
- open={false}
- bordered={false}
- tagRender={(props) => {
- const { value, closable, onClose } = props;
- return (
- <Tag closable={closable && !disabled} onClose={onClose} style={{ marginRight: 3 }}>
- {value}
- </Tag>
- );
- }}
- />
- </Form.Item>
- <Form.Item {...FORM_ITEM_WITHOUT_LABEL}>
- <Button disabled={disabled} onClick={() => setVisible(true)}>
- {formatMessage({ id: 'component.global.edit' })}
- </Button>
- </Form.Item>
+
+ <NormalLabelComponent />
+ <VersionLabelComponent />
+
<Form.Item label={formatMessage({ id: 'component.global.description' })} name="desc">
<Input.TextArea
placeholder={formatMessage({ id: 'component.global.input.placeholder.description' })}
disabled={disabled}
/>
</Form.Item>
+
<Form.Item
label={formatMessage({ id: 'page.route.publish' })}
name="status"
diff --git a/web/src/pages/Route/service.ts b/web/src/pages/Route/service.ts
index bc1a469..f67283f 100644
--- a/web/src/pages/Route/service.ts
+++ b/web/src/pages/Route/service.ts
@@ -40,12 +40,12 @@ export const fetchItem = (rid: number) =>
request(`/routes/${rid}`).then((data) => transformRouteData(data.data));
export const fetchList = ({ current = 1, pageSize = 10, ...res }) => {
- const { labels } = res;
+ const { labels, API_VERSION } = res;
return request<Res<ResListData<RouteModule.ResponseBody>>>('/routes', {
params: {
name: res.name,
uri: res.uri,
- label: (labels || []).join(','),
+ label: (labels || []).concat(API_VERSION).join(','),
page: current,
page_size: pageSize,
},
diff --git a/web/src/pages/Route/transform.ts b/web/src/pages/Route/transform.ts
index 401b15d..5b10a8c 100644
--- a/web/src/pages/Route/transform.ts
+++ b/web/src/pages/Route/transform.ts
@@ -25,12 +25,15 @@ export const transformLableValueToKeyValue = (data: string[]) => {
});
};
+// Transform Route data then sent to API
export const transformStepData = ({
form1Data,
form2Data,
advancedMatchingRules,
step3Data,
}: RouteModule.RequestData) => {
+ const { custom_normal_labels, custom_version_label, service_id = '' } = form1Data;
+
let redirect: RouteModule.Redirect = {};
const step3DataCloned = cloneDeep(step3Data);
if (form1Data.redirectOption === 'disabled') {
@@ -44,14 +47,16 @@ export const transformStepData = ({
};
}
- const labels = {};
- transformLableValueToKeyValue(form1Data.labels).forEach((item) => {
- labels[item.labelKey] = item.labelValue;
+ const labels: Record<string, string> = {};
+ transformLableValueToKeyValue(custom_normal_labels).forEach(({ labelKey, labelValue }) => {
+ labels[labelKey] = labelValue;
});
- const { service_id = '' } = form1Data;
+ if (custom_version_label) {
+ labels.API_VERSION = custom_version_label;
+ }
const data: Partial<RouteModule.Body> = {
- ...omit(form1Data, 'labels'),
+ ...form1Data,
labels,
...step3DataCloned,
vars: advancedMatchingRules.map((rule) => {
@@ -81,7 +86,7 @@ export const transformStepData = ({
}
if (redirect.http_to_https) {
- if (Object.keys(data.plugins!).length === 0) {
+ if (Object.keys(data.plugins || {}).length === 0) {
data.plugins = {};
}
data.plugins!.redirect = redirect;
@@ -92,6 +97,8 @@ export const transformStepData = ({
// Remove some of the frontend custom variables
return omit(data, [
+ 'custom_version_label',
+ 'custom_normal_labels',
'advancedMatchingRules',
'upstreamHostList',
'upstreamPath',
@@ -100,8 +107,8 @@ export const transformStepData = ({
'ret_code',
'redirectOption',
service_id.length === 0 ? 'service_id' : '',
- !Object.keys(step3DataCloned.plugins || {}).length ? 'plugins' : '',
- !Object.keys(step3DataCloned.script || {}).length ? 'script' : '',
+ !Object.keys(data.plugins || {}).length ? 'plugins' : '',
+ !Object.keys(data.script || {}).length ? 'script' : '',
form1Data.hosts.filter(Boolean).length === 0 ? 'hosts' : '',
form1Data.redirectOption === 'disabled' ? 'redirect' : '',
data.remote_addrs?.filter(Boolean).length === 0 ? 'remote_addrs' : '',
@@ -123,6 +130,7 @@ export const transformStepData = ({
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' : '',
]);
};
@@ -154,11 +162,12 @@ export const transformUpstreamNodes = (
return data;
};
+// Transform response's data
export const transformRouteData = (data: RouteModule.Body) => {
const {
name,
desc,
- labels,
+ labels = {},
methods = [],
uris,
uri,
@@ -173,6 +182,7 @@ export const transformRouteData = (data: RouteModule.Body) => {
priority = 0,
enable_websocket,
} = data;
+
const form1Data: Partial<RouteModule.Form1Data> = {
name,
desc,
@@ -180,7 +190,11 @@ export const transformRouteData = (data: RouteModule.Body) => {
hosts: hosts || (host && [host]) || [''],
uris: uris || (uri && [uri]) || [],
remote_addrs: remote_addrs || [''],
- labels: Object.keys(labels || []).map((item) => `${item}:${labels[item]}`),
+ // NOTE: API_VERSION is a system label
+ custom_version_label: labels.API_VERSION || '',
+ custom_normal_labels: Object.keys(labels)
+ .filter((item) => item !== 'API_VERSION')
+ .map((key) => `${key}:${labels[key]}`),
// @ts-ignore
methods: methods.length ? methods : ['ALL'],
priority,
diff --git a/web/src/pages/Route/typing.d.ts b/web/src/pages/Route/typing.d.ts
index 6b87a0b..60afe5f 100644
--- a/web/src/pages/Route/typing.d.ts
+++ b/web/src/pages/Route/typing.d.ts
@@ -83,7 +83,7 @@ declare namespace RouteModule {
remote_addrs: string[];
vars: [string, Operator, string][];
upstream: {
- type: 'roundrobin' | 'chash';
+ type: 'roundrobin' | 'chash' | 'ewma';
hash_on?: string;
key?: string;
nodes: {
@@ -134,16 +134,14 @@ declare namespace RouteModule {
advancedMatchingRules: MatchingRule[];
disabled?: boolean;
isEdit?: boolean;
- onChange?(data: {
- action: 'redirectOptionChange' | 'advancedMatchingRulesChange' | 'labelsChange';
- data: T;
- }): void;
+ onChange?(data: { action: string; data: T }): void;
};
type Form1Data = {
name: string;
desc: string;
- labels: string[];
+ custom_version_label: string;
+ custom_normal_labels: string[];
priority: number;
websocket: boolean;
hosts: string[];