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:40 UTC

[incubator-apisix-dashboard] branch feat-api created (now b3c10ec)

This is an automated email from the ASF dual-hosted git repository.

juzhiyuan pushed a change to branch feat-api
in repository https://gitbox.apache.org/repos/asf/incubator-apisix-dashboard.git.


      at b3c10ec  feat: added API

This branch includes the following new commits:

     new b3c10ec  feat: added API

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[incubator-apisix-dashboard] 01/01: feat: added API

Posted by ju...@apache.org.
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;
+    };
+  };
 }