This is an automated email from the ASF dual-hosted git repository. juzhiyuan pushed a commit to branch next in repository https://gitbox.apache.org/repos/asf/incubator-apisix-dashboard.git
The following commit(s) were added to refs/heads/next by this push: new 6bd13a5 feat: added api 6bd13a5 is described below commit 6bd13a58907c9b1a19942f99fbe5280f60681c8c Author: juzhiyuan <jjzhiy...@gmail.com> AuthorDate: Wed Jun 3 19:16:14 2020 +0800 feat: added api --- config/routes.ts | 6 ++ src/locales/en-US/menu.ts | 1 + src/locales/zh-CN/menu.ts | 1 + src/pages/Routes/Create.tsx | 46 +++++++++++---- .../Routes/components/Step1/MatchingRulesView.tsx | 4 +- src/pages/Routes/service.ts | 16 +++-- src/pages/Routes/transform.ts | 69 +++++++++++++++++++++- src/pages/Routes/typing.d.ts | 10 +++- 8 files changed, 130 insertions(+), 23 deletions(-) diff --git a/config/routes.ts b/config/routes.ts index 1fee575..84ea03d 100644 --- a/config/routes.ts +++ b/config/routes.ts @@ -48,6 +48,12 @@ const routes = [ component: './Routes/Create', hideInMenu: true, }, + { + path: '/routes/:rid/edit', + name: 'edit', + component: './Routes/Create', + hideInMenu: true, + }, ], }, { diff --git a/src/locales/en-US/menu.ts b/src/locales/en-US/menu.ts index 17b5b37..1409f6d 100644 --- a/src/locales/en-US/menu.ts +++ b/src/locales/en-US/menu.ts @@ -55,4 +55,5 @@ export default { 'menu.setting': 'Settings', 'menu.routes': 'Route', 'menu.routes.create': 'Create a Route', + 'menu.routes.edit': 'Edit the Route', }; diff --git a/src/locales/zh-CN/menu.ts b/src/locales/zh-CN/menu.ts index 7278b7c..6a6f5c6 100644 --- a/src/locales/zh-CN/menu.ts +++ b/src/locales/zh-CN/menu.ts @@ -55,4 +55,5 @@ export default { 'menu.setting': '设置', 'menu.routes': '路由', 'menu.routes.create': '创建', + 'menu.routes.edit': '编辑', }; diff --git a/src/pages/Routes/Create.tsx b/src/pages/Routes/Create.tsx index 5a7dbad..6584cf4 100644 --- a/src/pages/Routes/Create.tsx +++ b/src/pages/Routes/Create.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { Card, Steps, Form, notification } from 'antd'; import { PageHeaderWrapper } from '@ant-design/pro-layout'; @@ -9,11 +9,11 @@ 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'; +import { createRoute, fetchRoute, updateRoute } from './service'; const { Step } = Steps; -const Create: React.FC = () => { +const Create: React.FC = (props) => { const [step1Data, setStep1Data] = useState(DEFAULT_STEP_1_DATA); const [step2Data, setStep2Data] = useState(DEFAULT_STEP_2_DATA); const [step3Data, setStep3Data] = useState(DEFAULT_STEP_3_DATA); @@ -24,17 +24,35 @@ const Create: React.FC = () => { const [step, setStep] = useState(0); const [stepHeader] = useState(['定义 API 请求', '定义 API 后端服务', '插件配置', '预览']); - const data = { + const routeData = { step1Data, step2Data, step3Data, }; + const initRoute = (rid: number) => { + fetchRoute(rid).then((data) => { + form1.setFieldsValue(data.step1Data); + setStep1Data(data.step1Data); + + form2.setFieldsValue(data.step2Data); + setStep2Data(data.step2Data); + + setStep3Data(data.step3Data); + }); + }; + + useEffect(() => { + if ((props as any).route.name === 'edit') { + initRoute((props as any).match.params.rid); + } + }, []); + const renderStep = () => { if (step === 0) { return ( <Step1 - data={data} + data={routeData} form={form1} onChange={(_data: RouteModule.Step1Data) => { setStep1Data(_data); @@ -46,7 +64,7 @@ const Create: React.FC = () => { if (step === 1) { return ( <Step2 - data={data} + data={routeData} form={form2} onChange={(params: RouteModule.Step2Data) => setStep2Data({ ...step2Data, ...params })} /> @@ -54,11 +72,11 @@ const Create: React.FC = () => { } if (step === 2) { - return <CreateStep3 data={data} onChange={setStep3Data} />; + return <CreateStep3 data={routeData} onChange={setStep3Data} />; } if (step === 3) { - return <CreateStep4 data={data} form1={form1} form2={form2} onChange={() => {}} />; + return <CreateStep4 data={routeData} form1={form1} form2={form2} onChange={() => {}} />; } return null; @@ -86,9 +104,15 @@ const Create: React.FC = () => { setStep(nextStep); } if (nextStep === 4) { - createRoute({ data }).then(() => { - notification.success({ message: '创建路由成功' }); - }); + if ((props as any).route.name === 'edit') { + updateRoute((props as any).match.params.rid, { data: routeData }).then(() => { + notification.success({ message: '更新路由成功' }); + }); + } else { + createRoute({ data: routeData }).then(() => { + notification.success({ message: '创建路由成功' }); + }); + } } }; return ( diff --git a/src/pages/Routes/components/Step1/MatchingRulesView.tsx b/src/pages/Routes/components/Step1/MatchingRulesView.tsx index dc0522b..9bbda2c 100644 --- a/src/pages/Routes/components/Step1/MatchingRulesView.tsx +++ b/src/pages/Routes/components/Step1/MatchingRulesView.tsx @@ -110,8 +110,8 @@ const MatchingRulesView: React.FC<Props> = ({ data, disabled, onChange }) => { rules={[{ required: true, message: '请选择参数位置' }]} > <Select> - <Option value="header">HTTP 请求头</Option> - <Option value="arguments">请求参数</Option> + <Option value="http">HTTP 请求头</Option> + <Option value="arg">请求参数</Option> <Option value="cookie">Cookie</Option> </Select> </Form.Item> diff --git a/src/pages/Routes/service.ts b/src/pages/Routes/service.ts index e4427ba..f53c0f2 100644 --- a/src/pages/Routes/service.ts +++ b/src/pages/Routes/service.ts @@ -1,12 +1,18 @@ import { request } from 'umi'; -import { transformStepData } from './transform'; +import { transformStepData, transformRouteData } from './transform'; -export const createRoute = (data: Pick<RouteModule.Data, 'data'>) => { - return request('/workspaces/default/routes', { +export const createRoute = (data: Pick<RouteModule.Data, 'data'>) => + request('/workspaces/default/routes', { method: 'POST', data: transformStepData(data), }); -}; -export const updateRoute = () => {}; +export const updateRoute = (rid: number, data: Pick<RouteModule.Data, 'data'>, wid: number = 0) => + request(`/workspaces/${wid}/routes/${rid}`, { + method: 'PUT', + data: transformStepData(data), + }); + +export const fetchRoute = (rid: number, wid: number = 0) => + request(`/workspaces/${wid}/routes/${rid}`).then((data) => transformRouteData(data)); diff --git a/src/pages/Routes/transform.ts b/src/pages/Routes/transform.ts index ee3896f..7d36f22 100644 --- a/src/pages/Routes/transform.ts +++ b/src/pages/Routes/transform.ts @@ -35,11 +35,11 @@ export const transformStepData = ({ case 'cookie': key = `cookie_${name}`; break; - case 'header': + case 'http': key = `http_${name}`; break; default: - key = `args_${name}`; + key = `arg_${name}`; } return [key, operator, value]; }), @@ -64,3 +64,68 @@ export const transformStepData = ({ 'timeout', ]); }; + +const transformVarsToRules = ( + data: [string, RouteModule.Operator, string][], +): RouteModule.MatchingRule[] => + data.map(([key, operator, value]) => { + const [position, name] = key.split('_'); + return { + position: position as RouteModule.VarPosition, + name, + value, + operator, + key: Math.random().toString(36).slice(2), + }; + }); + +const transformUpstreamNodes = (nodes: { [key: string]: number }): RouteModule.UpstreamHost[] => { + const data: RouteModule.UpstreamHost[] = []; + Object.entries(nodes).forEach(([k, v]) => { + const [host, port] = k.split(':'); + data.push({ host, port: Number(port), weight: Number(v) }); + }); + return data; +}; + +export const transformRouteData = (data: RouteModule.Body) => { + const { name, desc, methods, uris, protocols, hosts, vars } = data; + // TODO: redirect + + const step1Data: RouteModule.Step1Data = { + name, + desc, + protocols: protocols.filter((item) => item !== 'websocket'), + websocket: protocols.includes('websocket'), + hosts, + paths: uris, + methods, + advancedMatchingRules: transformVarsToRules(vars), + }; + + const { upstream, upstream_path, upstream_header } = data; + + const upstreamHeaderList = Object.entries(upstream_header).map(([k, v]) => { + return { header_name: k, header_value: v, key: Math.random().toString(36).slice(2) }; + }); + + const step2Data: RouteModule.Step2Data = { + // TODO: API + upstreamProtocol: 'original', + upstreamHeaderList, + upstreamHostList: transformUpstreamNodes(upstream.nodes), + upstreamPath: upstream_path.to, + timeout: upstream.timeout, + }; + + const { plugins } = data; + const step3Data: RouteModule.Step3Data = { + plugins, + }; + + return { + step1Data, + step2Data, + step3Data, + }; +}; diff --git a/src/pages/Routes/typing.d.ts b/src/pages/Routes/typing.d.ts index 1bb29e3..fcbcb25 100644 --- a/src/pages/Routes/typing.d.ts +++ b/src/pages/Routes/typing.d.ts @@ -1,8 +1,10 @@ declare namespace RouteModule { type Operator = '==' | '~=' | '>' | '<' | '~~'; + type VarPosition = 'arg' | 'http' | 'cookie'; + interface MatchingRule { - position: 'query' | 'params' | 'header' | 'cookie'; + position: VarPosition; name: string; operator: Operator; value: string; @@ -14,6 +16,7 @@ declare namespace RouteModule { type Step1Data = { name: string; + desc: string; protocols: RequestProtocol[]; websocket: boolean; hosts: string[]; @@ -39,7 +42,7 @@ declare namespace RouteModule { } type UpstreamHost = { - host: ''; + host: string; port: number; weight: number; }; @@ -69,8 +72,9 @@ declare namespace RouteModule { // Request Body or Response Data for API type Body = { + id?: number; name: string; - desc?: string; + desc: string; priority?: number; methods: HttpMethod[]; uris: string[];