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;
+    };
+  };
 }