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/06/03 09:11:41 UTC
[incubator-apisix-dashboard] 01/01: feat: added API
This is an automated email from the ASF dual-hosted git repository.
juzhiyuan pushed a commit to branch feat-api
in repository https://gitbox.apache.org/repos/asf/incubator-apisix-dashboard.git
commit b3c10ec393fb2451862c98226021ad589ddcf9f7
Author: juzhiyuan <jj...@gmail.com>
AuthorDate: Wed Jun 3 17:08:53 2020 +0800
feat: added API
---
src/pages/Routes/Create.tsx | 46 ++++++++--------
.../Routes/components/Step1/MatchingRulesView.tsx | 28 +++++-----
src/pages/Routes/components/Step1/MetaView.tsx | 1 -
.../Routes/components/Step1/RequestConfigView.tsx | 4 +-
.../components/Step2/HttpHeaderRewriteView.tsx | 11 +---
.../Routes/components/Step2/RequestRewriteView.tsx | 6 +--
src/pages/Routes/constants.ts | 4 +-
src/pages/Routes/service.ts | 9 ++++
src/pages/Routes/transform.ts | 63 ++++++++++++++++++++++
src/pages/Routes/typing.d.ts | 61 ++++++++++++++++++---
10 files changed, 170 insertions(+), 63 deletions(-)
diff --git a/src/pages/Routes/Create.tsx b/src/pages/Routes/Create.tsx
index b9e8cca..5a7dbad 100644
--- a/src/pages/Routes/Create.tsx
+++ b/src/pages/Routes/Create.tsx
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
-import { Card, Steps, Form } from 'antd';
+import { Card, Steps, Form, notification } from 'antd';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import Step1 from './components/Step1';
@@ -9,6 +9,7 @@ import CreateStep3 from './components/CreateStep3';
import ActionBar from './components/ActionBar';
import CreateStep4 from './components/CreateStep4';
import { DEFAULT_STEP_1_DATA, DEFAULT_STEP_2_DATA, DEFAULT_STEP_3_DATA } from './constants';
+import { createRoute } from './service';
const { Step } = Steps;
@@ -64,28 +65,31 @@ const Create: React.FC = () => {
};
const onStepChange = (nextStep: number) => {
- const nextStepAction = () => {
+ if (nextStep === 0) {
setStep(nextStep);
- window.scrollTo({ top: 0 });
- };
- if (nextStep > step && nextStep < 3) {
- // Form Validation
- if (step === 0) {
- form1.validateFields().then((value) => {
- setStep1Data({ ...step1Data, ...value });
- nextStepAction();
- });
- return;
- }
- if (step === 1) {
- form2.validateFields().then((value) => {
- setStep1Data({ ...step1Data, ...value });
- nextStepAction();
- });
- return;
- }
}
- nextStepAction();
+ if (nextStep === 1) {
+ form1.validateFields().then((value) => {
+ setStep1Data({ ...step1Data, ...value });
+ setStep(nextStep);
+ });
+ return;
+ }
+ if (nextStep === 2) {
+ form2.validateFields().then((value) => {
+ setStep2Data({ ...step2Data, ...value });
+ setStep(nextStep);
+ });
+ return;
+ }
+ if (nextStep === 3) {
+ setStep(nextStep);
+ }
+ if (nextStep === 4) {
+ createRoute({ data }).then(() => {
+ notification.success({ message: '创建路由成功' });
+ });
+ }
};
return (
<>
diff --git a/src/pages/Routes/components/Step1/MatchingRulesView.tsx b/src/pages/Routes/components/Step1/MatchingRulesView.tsx
index 40eb4b0..ee13c2a 100644
--- a/src/pages/Routes/components/Step1/MatchingRulesView.tsx
+++ b/src/pages/Routes/components/Step1/MatchingRulesView.tsx
@@ -56,23 +56,23 @@ const MatchingRulesView: React.FC<Props> = ({ data, disabled, onChange }) => {
const columns = [
{
title: '参数位置',
- dataIndex: 'paramsLocation',
- key: 'paramsLocation',
+ dataIndex: 'position',
+ key: 'position',
},
{
title: '参数名称',
- dataIndex: 'paramsName',
- key: 'paramsName',
+ dataIndex: 'name',
+ key: 'name',
},
{
title: '运算符',
- dataIndex: 'paramsExpresstion',
- key: 'paramsExpresstion',
+ dataIndex: 'operator',
+ key: 'operator',
},
{
title: '参数值',
- dataIndex: 'paramsValue',
- key: 'paramsValue',
+ dataIndex: 'value',
+ key: 'value',
},
disabled
? {}
@@ -104,7 +104,7 @@ const MatchingRulesView: React.FC<Props> = ({ data, disabled, onChange }) => {
<Form form={modalForm} labelCol={{ span: 4 }} wrapperCol={{ span: 20 }}>
<Form.Item
label="参数位置"
- name="paramsLocation"
+ name="position"
rules={[{ required: true, message: '请选择参数位置' }]}
>
<Select>
@@ -115,14 +115,14 @@ const MatchingRulesView: React.FC<Props> = ({ data, disabled, onChange }) => {
</Form.Item>
<Form.Item
label="参数名称"
- name="paramsName"
+ name="name"
rules={[{ required: true, message: '请输入参数名称' }]}
>
<Input />
</Form.Item>
<Form.Item
label="运算符"
- name="paramsExpresstion"
+ name="operator"
rules={[{ required: true, message: '请选择运算符' }]}
>
<Select>
@@ -133,11 +133,7 @@ const MatchingRulesView: React.FC<Props> = ({ data, disabled, onChange }) => {
<Option value="~~">正则匹配</Option>
</Select>
</Form.Item>
- <Form.Item
- label="值"
- name="paramsValue"
- rules={[{ required: true, message: '请输入参数值' }]}
- >
+ <Form.Item label="值" name="value" rules={[{ required: true, message: '请输入参数值' }]}>
<Input />
</Form.Item>
</Form>
diff --git a/src/pages/Routes/components/Step1/MetaView.tsx b/src/pages/Routes/components/Step1/MetaView.tsx
index 3c1679d..69e64c7 100644
--- a/src/pages/Routes/components/Step1/MetaView.tsx
+++ b/src/pages/Routes/components/Step1/MetaView.tsx
@@ -2,7 +2,6 @@ import React from 'react';
import Form from 'antd/es/form';
import { Input } from 'antd';
-// import { FORM_ITEM_LAYOUT } from '@/pages/Routes/constants';
import PanelSection from '../PanelSection';
interface Props extends RouteModule.Data {}
diff --git a/src/pages/Routes/components/Step1/RequestConfigView.tsx b/src/pages/Routes/components/Step1/RequestConfigView.tsx
index dc24649..a52e647 100644
--- a/src/pages/Routes/components/Step1/RequestConfigView.tsx
+++ b/src/pages/Routes/components/Step1/RequestConfigView.tsx
@@ -126,14 +126,14 @@ const RequestConfigView: React.FC<Props> = ({ data, disabled, onChange }) => {
onChange={onProtocolChange}
/>
</Form.Item>
- <Form.Item label="WebSocket" name="WebSocket" valuePropName="checked">
+ <Form.Item label="WebSocket" name="websocket" valuePropName="checked">
<Switch disabled={disabled} />
</Form.Item>
{renderHosts()}
<Form.Item label="路径">{renderPaths()}</Form.Item>
<Form.Item
label="HTTP 方法"
- name="httpMethods"
+ name="methods"
rules={[{ required: true, message: '请勾选 HTTP 方法' }]}
>
<Checkbox.Group options={HTTP_METHOD_OPTION_LIST} disabled={disabled} />
diff --git a/src/pages/Routes/components/Step2/HttpHeaderRewriteView.tsx b/src/pages/Routes/components/Step2/HttpHeaderRewriteView.tsx
index e06cb31..3baff95 100644
--- a/src/pages/Routes/components/Step2/HttpHeaderRewriteView.tsx
+++ b/src/pages/Routes/components/Step2/HttpHeaderRewriteView.tsx
@@ -1,7 +1,6 @@
import React, { useState } from 'react';
import Form from 'antd/es/form';
import { Button, Table, Space, Modal, Input } from 'antd';
-import TextArea from 'antd/lib/input/TextArea';
import PanelSection from '../PanelSection';
@@ -33,11 +32,6 @@ const HttpHeaderRewriteView: React.FC<Props> = ({ data, disabled, onChange }) =>
dataIndex: 'header_value',
key: 'header_value',
},
- {
- title: '描述',
- dataIndex: 'header_desc',
- key: 'header_desc',
- },
disabled
? {}
: {
@@ -93,7 +87,7 @@ const HttpHeaderRewriteView: React.FC<Props> = ({ data, disabled, onChange }) =>
return (
<Modal
- title={mode === 'EDIT' ? '编辑' : '增加'}
+ title={mode === 'EDIT' ? '编辑请求头' : '增加请求头'}
centered
visible={visible}
onOk={handleOk}
@@ -118,9 +112,6 @@ const HttpHeaderRewriteView: React.FC<Props> = ({ data, disabled, onChange }) =>
>
<Input />
</Form.Item>
- <Form.Item label="描述" name="header_desc">
- <TextArea />
- </Form.Item>
</Form>
</Modal>
);
diff --git a/src/pages/Routes/components/Step2/RequestRewriteView.tsx b/src/pages/Routes/components/Step2/RequestRewriteView.tsx
index 373c534..4e338e5 100644
--- a/src/pages/Routes/components/Step2/RequestRewriteView.tsx
+++ b/src/pages/Routes/components/Step2/RequestRewriteView.tsx
@@ -118,7 +118,7 @@ const RequestRewriteView: React.FC<Props> = ({ data, form, disabled, onChange })
>
<Input disabled={disabled} />
</Form.Item>
- <Form.Item label="连接超时">
+ <Form.Item label="连接超时" required>
<Form.Item
name={['timeout', 'connect']}
noStyle
@@ -128,7 +128,7 @@ const RequestRewriteView: React.FC<Props> = ({ data, form, disabled, onChange })
</Form.Item>
{renderTimeUnit()}
</Form.Item>
- <Form.Item label="发送超时">
+ <Form.Item label="发送超时" required>
<Form.Item
name={['timeout', 'send']}
noStyle
@@ -138,7 +138,7 @@ const RequestRewriteView: React.FC<Props> = ({ data, form, disabled, onChange })
</Form.Item>
{renderTimeUnit()}
</Form.Item>
- <Form.Item label="接收超时">
+ <Form.Item label="接收超时" required>
<Form.Item
name={['timeout', 'read']}
noStyle
diff --git a/src/pages/Routes/constants.ts b/src/pages/Routes/constants.ts
index b7d9574..6ccce6a 100644
--- a/src/pages/Routes/constants.ts
+++ b/src/pages/Routes/constants.ts
@@ -27,10 +27,10 @@ export const FORM_ITEM_WITHOUT_LABEL = {
export const DEFAULT_STEP_1_DATA: RouteModule.Step1Data = {
name: '',
protocols: ['HTTP', 'HTTPS'],
- WebSocket: false,
+ websocket: false,
hosts: [''],
paths: [],
- httpMethods: HTTP_METHOD_OPTION_LIST,
+ methods: HTTP_METHOD_OPTION_LIST,
advancedMatchingRules: [],
};
diff --git a/src/pages/Routes/service.ts b/src/pages/Routes/service.ts
index e69de29..be34816 100644
--- a/src/pages/Routes/service.ts
+++ b/src/pages/Routes/service.ts
@@ -0,0 +1,9 @@
+import { request } from 'umi';
+
+import { transformStepData } from './transform';
+
+export const createRoute = (data: Pick<RouteModule.Data, 'data'>) => {
+ return request('/workspaces/default/routes', { data: transformStepData(data) });
+};
+
+export const updateRoute = () => {};
diff --git a/src/pages/Routes/transform.ts b/src/pages/Routes/transform.ts
new file mode 100644
index 0000000..dbaa55f
--- /dev/null
+++ b/src/pages/Routes/transform.ts
@@ -0,0 +1,63 @@
+import { omit } from 'lodash';
+
+export const transformStepData = ({
+ data: { step1Data, step2Data, step3Data },
+}: Pick<RouteModule.Data, 'data'>) => {
+ const nodes = {};
+ step2Data.upstreamHostList.forEach((node) => {
+ nodes[`${node.host}:${node.port}`] = node.weight;
+ });
+
+ const upstream_header = {};
+ step2Data.upstreamHeaderList.forEach((header) => {
+ upstream_header[header.header_name] = header.header_value;
+ });
+
+ let data: RouteModule.Body = {
+ ...step1Data,
+ ...step2Data,
+ ...step3Data,
+ priority: 0,
+ protocols: step1Data.protocols.concat(step1Data.websocket ? 'websocket' : []),
+ uris: step1Data.paths,
+ redirect: {
+ redirect_to_https: true,
+ },
+ vars: step1Data.advancedMatchingRules.map((rule) => {
+ const { operator, position, name, value } = rule;
+ let key = '';
+ switch (position) {
+ case 'cookie':
+ key = `cookie_${name}`;
+ break;
+ case 'header':
+ key = `http_${name}`;
+ break;
+ default:
+ key = `args_${name}`;
+ }
+ return [key, operator, value];
+ }),
+ upstream: {
+ type: 'roundrobin',
+ nodes,
+ timeout: step2Data.timeout,
+ },
+ upstream_header,
+ upstream_path: {
+ to: step2Data.upstreamPath,
+ },
+ };
+
+ data = omit(data, [
+ 'advancedMatchingRules',
+ 'upstreamProtocol',
+ 'upstreamHostList',
+ 'upstreamPath',
+ 'upstreamHeaderList',
+ 'websocket',
+ 'timeout',
+ ]) as RouteModule.Body;
+
+ return data;
+};
diff --git a/src/pages/Routes/typing.d.ts b/src/pages/Routes/typing.d.ts
index a50f0fe..f8229b3 100644
--- a/src/pages/Routes/typing.d.ts
+++ b/src/pages/Routes/typing.d.ts
@@ -1,22 +1,24 @@
declare namespace RouteModule {
+ type Operator = '==' | '~=' | '>' | '<' | '~~';
+
interface MatchingRule {
- paramsLocation: 'query' | 'params' | 'header' | 'cookie';
- paramsName: string;
- paramsExpresstion: '==' | '~=' | '>' | '<' | '~~';
- paramsValue: string;
+ position: 'query' | 'params' | 'header' | 'cookie';
+ name: string;
+ operator: Operator;
+ value: string;
key: string;
}
type HttpMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'OPTIONS' | 'PATCH';
- type RequestProtocol = 'HTTPS' | 'HTTP' | 'WebSocket';
+ type RequestProtocol = 'HTTPS' | 'HTTP' | 'websocket';
type Step1Data = {
name: string;
protocols: RequestProtocol[];
- WebSocket: boolean;
+ websocket: boolean;
hosts: string[];
paths: string[];
- httpMethods: HttpMethod[];
+ methods: HttpMethod[];
advancedMatchingRules: MatchingRule[];
};
@@ -45,7 +47,9 @@ declare namespace RouteModule {
interface UpstreamHeader {
header_name: string;
header_value: string;
- header_desc: string;
+ }
+
+ interface UpstreamHeader {
key: string;
}
@@ -62,4 +66,45 @@ declare namespace RouteModule {
};
type ModalType = 'CREATE' | 'EDIT';
+
+ // Request Body or Response Data for API
+ type Body = {
+ name: string;
+ desc?: string;
+ priority?: number;
+ methods: HttpMethod[];
+ uris: string[];
+ hosts: string[];
+ protocols: RequestProtocol[];
+ redirect:
+ | {
+ code: 301 | 302;
+ uri: string;
+ }
+ | {
+ redirect_to_https?: boolean;
+ };
+ vars: [string, Operator, string][];
+ upstream: {
+ type: 'roundrobin' | 'chash';
+ nodes: {
+ [key: string]: number;
+ };
+ timeout: {
+ connect: number;
+ send: number;
+ read: number;
+ };
+ };
+ upstream_path: {
+ from?: string;
+ to: string;
+ };
+ upstream_header: {
+ [key: string]: string;
+ };
+ plugins: {
+ [name: string]: any;
+ };
+ };
}