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

dengliming pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu-dashboard.git


The following commit(s) were added to refs/heads/master by this push:
     new 4d459f55 Add support for APIDoc Insert Update Delete (#4029) (#259)
4d459f55 is described below

commit 4d459f553e656563eda33e7687bdf075b995ae95
Author: 杨文杰 <[email protected]>
AuthorDate: Mon Dec 26 21:56:22 2022 +0800

    Add support for APIDoc Insert Update Delete (#4029) (#259)
    
    * add api doc insert and update
    
    * add update API DOC
    
    * api insert or update
    
    * add or update api
    
    * api insert update
    
    * api insert or update or delete
    
    * change code for lint
    
    * add zh-CN
---
 src/locales/en-US.json                             |  13 +
 src/locales/zh-CN.json                             |  13 +
 src/routes/Document/ApiDoc.js                      |  68 +++-
 .../Document/components/AddAndUpdateApiDoc.js      | 347 +++++++++++++++++++++
 src/routes/Document/components/ApiInfo.js          |   7 +-
 src/routes/Document/components/SearchApi.js        |  12 +-
 src/services/api.js                                |  30 ++
 7 files changed, 484 insertions(+), 6 deletions(-)

diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index 92ac5820..b4a5d581 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -311,6 +311,19 @@
   "SHENYU.DOCUMENT.APIDOC.CONTENTS.TO.BE.SIGNED": "Contents To Be Signed",
   "SHENYU.DOCUMENT.APIDOC.SIGNATURE": "Signature",
   "SHENYU.DOCUMENT.APIDOC.ERROR.CODE.DETAILS": "Error Code Details",
+  "SHENYU.DOCUMENT.APIDOC.CONTEXTPATH": "contextPath",
+  "SHENYU.DOCUMENT.APIDOC.APIPATH": "apiPath",
+  "SHENYU.DOCUMENT.APIDOC.HTTPMETHOD": "httpMethod",
+  "SHENYU.DOCUMENT.APIDOC.CONSUME": "consume",
+  "SHENYU.DOCUMENT.APIDOC.PRODUCE": "produce",
+  "SHENYU.DOCUMENT.APIDOC.VERSION": "version",
+  "SHENYU.DOCUMENT.APIDOC.RPCTYPE": "rpcType",
+  "SHENYU.DOCUMENT.APIDOC.STATE": "state",
+  "SHENYU.DOCUMENT.APIDOC.EXT": "ext",
+  "SHENYU.DOCUMENT.APIDOC.APIOWNER": "apiOwner",
+  "SHENYU.DOCUMENT.APIDOC.APIDESC": "apiDesc",
+  "SHENYU.DOCUMENT.APIDOC.APISOURCE": "apiSource",
+  "SHENYU.DOCUMENT.APIDOC.DOCUMENT": "document",
   "SHENYU.COMMON.REQUIRED": "Required",
   "SHENYU.COMMON.MAX.LENGTH": "Max Length",
   "SHENYU.COMMON.MAX.EXAMPLE": "Example",
diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json
index bc62cc96..2c5c31c4 100644
--- a/src/locales/zh-CN.json
+++ b/src/locales/zh-CN.json
@@ -299,6 +299,19 @@
   "SHENYU.DOCUMENT.APIDOC.CONTENTS.TO.BE.SIGNED": "待签名内容",
   "SHENYU.DOCUMENT.APIDOC.SIGNATURE": "签名(sign)",
   "SHENYU.DOCUMENT.APIDOC.ERROR.CODE.DETAILS": "错误代码",
+  "SHENYU.DOCUMENT.APIDOC.CONTEXTPATH": "上下文路径",
+  "SHENYU.DOCUMENT.APIDOC.APIPATH": "api路径",
+  "SHENYU.DOCUMENT.APIDOC.HTTPMETHOD": "http方法",
+  "SHENYU.DOCUMENT.APIDOC.CONSUME": "消费",
+  "SHENYU.DOCUMENT.APIDOC.PRODUCE": "生产",
+  "SHENYU.DOCUMENT.APIDOC.VERSION": "版本",
+  "SHENYU.DOCUMENT.APIDOC.RPCTYPE": "rpc类型",
+  "SHENYU.DOCUMENT.APIDOC.STATE": "状态",
+  "SHENYU.DOCUMENT.APIDOC.EXT": "ext",
+  "SHENYU.DOCUMENT.APIDOC.APIOWNER": "api持有者",
+  "SHENYU.DOCUMENT.APIDOC.APIDESC": "api描述",
+  "SHENYU.DOCUMENT.APIDOC.APISOURCE": "api来源",
+  "SHENYU.DOCUMENT.APIDOC.DOCUMENT": "文档说明",
   "SHENYU.COMMON.REQUIRED": "必填",
   "SHENYU.COMMON.MAX.LENGTH": "最大长度",
   "SHENYU.COMMON.MAX.EXAMPLE": "示例值",
diff --git a/src/routes/Document/ApiDoc.js b/src/routes/Document/ApiDoc.js
index 4b24490e..bf2aac5c 100644
--- a/src/routes/Document/ApiDoc.js
+++ b/src/routes/Document/ApiDoc.js
@@ -18,13 +18,34 @@
 import { Col, Row, Card, BackTop, Empty, message } from "antd";
 import React, { useEffect, useState } from "react";
 import SearchApi from "./components/SearchApi";
+import AddAndUpdateApiDoc from "./components/AddAndUpdateApiDoc";
 import ApiInfo from "./components/ApiInfo";
-import { getDocMenus, getApiDetail } from "../../services/api";
+import { getDocMenus, getApiDetail, addApi, updateApi, deleteApi } from 
"../../services/api";
 import ApiContext from "./components/ApiContext";
 
 function ApiDoc() {
   const [apiDetail, setApiDetail] = useState({});
   const [apiData, setApiData] = useState({});
+  const [open, setOpen] = useState(false);
+  const [flag, setflag] = useState('add');
+
+  const [initialValue, setInitialValue] = useState({
+    id: '',
+    contextPath: '',
+    apiPath: '',
+    httpMethod: '',
+    consume: '',
+    produce: '',
+    version: '',
+    rpcType: '',
+    state: '',
+    ext: '',
+    apiOwner: '',
+    apiDesc: '',
+    apiSource: '',
+    document: '',
+    tagIds: []
+  })
 
   const initData = async () => {
     const { code, data = {} } = await getDocMenus();
@@ -47,7 +68,6 @@ function ApiDoc() {
       setApiData(data);
     }
   };
-
   const handleSelectNode = async (_, e) => {
     const {
       node: {
@@ -59,13 +79,51 @@ function ApiDoc() {
     if (!isLeaf) {
       return;
     }
+    if (!id) {
+      const targetId = _
+      handleAddApi(targetId)
+      return;
+    }
     const { code, message: msg, data } = await getApiDetail(id);
     if (code !== 200) {
       message.error(msg);
       return;
     }
+    setInitialValue({
+      id
+    });
     setApiDetail(data);
   };
+  const handleAddApi = (targetId) => {
+    setflag('add')
+    setInitialValue({
+      tagIds: [targetId]
+    });
+    setOpen(true)
+  };
+  const callSaveOrUpdateApi = async (params) => {
+    let rs = (flag === 'add' ? await addApi({ ...params, tagIds: 
initialValue.tagIds[0] }) : await updateApi({ ...params, id: initialValue.id, 
tagIds: initialValue.tagIds }));
+    if (rs.code !== 200) {
+      message.error(rs.msg);
+    } else {
+      setOpen(false)
+      location.reload()
+    }
+  };
+  const handleDeleteApi = async () => {
+    const { code, message: msg } = await deleteApi([initialValue.id]);
+    if (code !== 200) {
+      message.error(msg);
+    } else {
+      location.reload()
+    }
+  };
+  const handleUpdateApi = async () => {
+    let queryData = await getApiDetail(initialValue.id)
+    setInitialValue(queryData.data);
+    setOpen(true)
+    setflag('update')
+  }
 
   useEffect(() => {
     initData();
@@ -79,13 +137,17 @@ function ApiDoc() {
       }}
     >
       <Card style={{ margin: 24 }}>
+        {open && <AddAndUpdateApiDoc onCancel={() => setOpen(false)} 
handleOk={callSaveOrUpdateApi} {...initialValue} />
+        }
         <Row gutter={24}>
           <Col span={6}>
             <SearchApi onSelect={handleSelectNode} />
           </Col>
           <Col span={18}>
             {apiDetail.id ? (
-              <ApiInfo />
+              <>
+                <ApiInfo handleUpdateApi={handleUpdateApi} 
handleDeleteApi={handleDeleteApi} />
+              </>
             ) : (
               <Empty description={false} style={{ padding: "160px 0" }} />
             )}
diff --git a/src/routes/Document/components/AddAndUpdateApiDoc.js 
b/src/routes/Document/components/AddAndUpdateApiDoc.js
new file mode 100644
index 00000000..b8b5485e
--- /dev/null
+++ b/src/routes/Document/components/AddAndUpdateApiDoc.js
@@ -0,0 +1,347 @@
+/*
+       * 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.
+       */
+
+import { Modal, Form, Input, Select } from "antd";
+import React, { Component } from "react";
+import { Method } from "./globalData";
+import { getIntlContent } from "../../../utils/IntlUtils";
+
+class AddAndUpdateApiDoc extends Component {
+  render() {
+    const RPCTYPE = [
+      "http",
+      "dubbo",
+      "sofa",
+      "tars",
+      "websocket",
+      "springCloud",
+      "motan",
+      "grpc"
+    ];
+    const API_SOURCE_TYPE = [
+      "swagger",
+      "annotation generation",
+      "create manuallym",
+      "import swagger",
+      "import yapi"
+    ];
+    const {
+      onCancel,
+      form,
+      contextPath = "",
+      apiPath = "",
+      httpMethod = "",
+      consume = "",
+      produce = "",
+      version = "",
+      rpcType = "",
+      state = "",
+      ext = "",
+      apiOwner = "",
+      apiDesc = "",
+      apiSource = "",
+      document = ""
+    } = this.props;
+    const { getFieldDecorator } = form;
+    const formItemLayout = {
+      labelCol: {
+        sm: { span: 5 }
+      },
+      wrapperCol: {
+        sm: { span: 19 }
+      }
+    };
+    const handleSubmit = () => {
+      const { handleOk } = this.props;
+      let newValues = "";
+      form.validateFieldsAndScroll((err, values) => {
+        if (!err) {
+          // eslint-disable-next-line radix
+          values.state = parseInt(values.state);
+          // eslint-disable-next-line radix
+          values.apiSource = parseInt(values.apiSource);
+          // eslint-disable-next-line radix
+          values.httpMethod = parseInt(values.httpMethod);
+          newValues = values;
+        }
+      });
+      handleOk(newValues);
+    };
+    return (
+      <Modal visible onCancel={onCancel} onOk={handleSubmit}>
+        <Form onSubmit={handleSubmit} className="login-form">
+          <Form.Item
+            label={`${getIntlContent("SHENYU.DOCUMENT.APIDOC.CONTEXTPATH")}`}
+            {...formItemLayout}
+          >
+            {getFieldDecorator("contextPath", {
+              rules: [
+                {
+                  required: true,
+                  message: getIntlContent("SHENYU.DOCUMENT.APIDOC.CONTEXTPATH")
+                }
+              ],
+              initialValue: contextPath
+            })(
+              <Input
+                placeholder={getIntlContent(
+                  "SHENYU.DOCUMENT.APIDOC.CONTEXTPATH"
+                )}
+              />
+            )}
+          </Form.Item>
+          <Form.Item
+            label={`${getIntlContent("SHENYU.DOCUMENT.APIDOC.APIPATH")}`}
+            {...formItemLayout}
+          >
+            {getFieldDecorator("apiPath", {
+              rules: [
+                {
+                  required: true,
+                  message: getIntlContent("SHENYU.DOCUMENT.APIDOC.APIPATH")
+                }
+              ],
+              initialValue: apiPath
+            })(
+              <Input
+                placeholder={getIntlContent("SHENYU.DOCUMENT.APIDOC.APIPATH")}
+              />
+            )}
+          </Form.Item>
+          <Form.Item
+            label={`${getIntlContent("SHENYU.DOCUMENT.APIDOC.HTTPMETHOD")}`}
+            {...formItemLayout}
+          >
+            {getFieldDecorator("httpMethod", {
+              rules: [
+                {
+                  required: true,
+                  message: getIntlContent("SHENYU.DOCUMENT.APIDOC.HTTPMETHOD")
+                }
+              ],
+              initialValue: httpMethod
+            })(
+              <Select>
+                {Object.values(Method).map((e, i) => {
+                  return (
+                    <Select.Option key={`${e} ${i}`} value={i}>
+                      {e}
+                    </Select.Option>
+                  );
+                })}
+              </Select>
+            )}
+          </Form.Item>
+          <Form.Item
+            label={`${getIntlContent("SHENYU.DOCUMENT.APIDOC.CONSUME")}`}
+            {...formItemLayout}
+          >
+            {getFieldDecorator("consume", {
+              rules: [
+                {
+                  required: true,
+                  message: getIntlContent("SHENYU.DOCUMENT.APIDOC.CONSUME")
+                }
+              ],
+              initialValue: consume
+            })(
+              <Input
+                placeholder={getIntlContent("SHENYU.DOCUMENT.APIDOC.CONSUME")}
+              />
+            )}
+          </Form.Item>
+          <Form.Item
+            label={`${getIntlContent("SHENYU.DOCUMENT.APIDOC.PRODUCE")}`}
+            {...formItemLayout}
+          >
+            {getFieldDecorator("produce", {
+              rules: [
+                {
+                  required: true,
+                  message: getIntlContent("SHENYU.DOCUMENT.APIDOC.PRODUCE")
+                }
+              ],
+              initialValue: produce
+            })(
+              <Input
+                placeholder={getIntlContent("SHENYU.DOCUMENT.APIDOC.PRODUCE")}
+              />
+            )}
+          </Form.Item>
+          <Form.Item
+            label={`${getIntlContent("SHENYU.DOCUMENT.APIDOC.VERSION")}`}
+            {...formItemLayout}
+          >
+            {getFieldDecorator("version", {
+              rules: [
+                {
+                  required: true,
+                  message: getIntlContent("SHENYU.DOCUMENT.APIDOC.VERSION")
+                }
+              ],
+              initialValue: version
+            })(
+              <Input
+                placeholder={getIntlContent("SHENYU.DOCUMENT.APIDOC.VERSION")}
+              />
+            )}
+          </Form.Item>
+          <Form.Item
+            label={`${getIntlContent("SHENYU.DOCUMENT.APIDOC.RPCTYPE")}`}
+            {...formItemLayout}
+          >
+            {getFieldDecorator("rpcType", {
+              rules: [
+                {
+                  required: true,
+                  message: getIntlContent("SHENYU.DOCUMENT.APIDOC.RPCTYPE")
+                }
+              ],
+              initialValue: rpcType
+            })(
+              <Select>
+                {Object.values(RPCTYPE).map((e, i) => {
+                  return (
+                    <Select.Option key={`${e} ${i}`} value={e}>
+                      {e}
+                    </Select.Option>
+                  );
+                })}
+              </Select>
+            )}
+          </Form.Item>
+          <Form.Item
+            label={`${getIntlContent("SHENYU.DOCUMENT.APIDOC.STATE")}`}
+            {...formItemLayout}
+          >
+            {getFieldDecorator("state", {
+              rules: [
+                {
+                  required: true,
+                  message: getIntlContent("SHENYU.DOCUMENT.APIDOC.STATE")
+                }
+              ],
+              initialValue: state
+            })(
+              <Input
+                placeholder={getIntlContent("SHENYU.DOCUMENT.APIDOC.STATE")}
+              />
+            )}
+          </Form.Item>
+          <Form.Item
+            label={`${getIntlContent("SHENYU.DOCUMENT.APIDOC.EXT")}`}
+            {...formItemLayout}
+          >
+            {getFieldDecorator("ext", {
+              rules: [
+                {
+                  required: true,
+                  message: getIntlContent("SHENYU.DOCUMENT.APIDOC.EXT")
+                }
+              ],
+              initialValue: ext
+            })(
+              <Input
+                placeholder={getIntlContent("SHENYU.DOCUMENT.APIDOC.EXT")}
+              />
+            )}
+          </Form.Item>
+          <Form.Item
+            label={`${getIntlContent("SHENYU.DOCUMENT.APIDOC.APIOWNER")}`}
+            {...formItemLayout}
+          >
+            {getFieldDecorator("apiOwner", {
+              rules: [
+                {
+                  required: true,
+                  message: getIntlContent("SHENYU.DOCUMENT.APIDOC.APIOWNER")
+                }
+              ],
+              initialValue: apiOwner
+            })(
+              <Input
+                placeholder={getIntlContent("SHENYU.DOCUMENT.APIDOC.APIOWNER")}
+              />
+            )}
+          </Form.Item>
+          <Form.Item
+            label={`${getIntlContent("SHENYU.DOCUMENT.APIDOC.APIDESC")}`}
+            {...formItemLayout}
+          >
+            {getFieldDecorator("apiDesc", {
+              rules: [
+                {
+                  required: true,
+                  message: getIntlContent("SHENYU.DOCUMENT.APIDOC.APIDESC")
+                }
+              ],
+              initialValue: apiDesc
+            })(
+              <Input
+                placeholder={getIntlContent("SHENYU.DOCUMENT.APIDOC.APIDESC")}
+              />
+            )}
+          </Form.Item>
+          <Form.Item
+            label={`${getIntlContent("SHENYU.DOCUMENT.APIDOC.APISOURCE")}`}
+            {...formItemLayout}
+          >
+            {getFieldDecorator("apiSource", {
+              rules: [
+                {
+                  required: true,
+                  message: getIntlContent("SHENYU.DOCUMENT.APIDOC.APISOURCE")
+                }
+              ],
+              initialValue: apiSource
+            })(
+              <Select>
+                {Object.values(API_SOURCE_TYPE).map((e, i) => {
+                  return (
+                    <Select.Option key={`${e} ${i}`} value={i}>
+                      {e}
+                    </Select.Option>
+                  );
+                })}
+              </Select>
+            )}
+          </Form.Item>
+          <Form.Item
+            label={`${getIntlContent("SHENYU.DOCUMENT.APIDOC.DOCUMENT")}`}
+            {...formItemLayout}
+          >
+            {getFieldDecorator("document", {
+              rules: [
+                {
+                  required: true,
+                  message: getIntlContent("SHENYU.DOCUMENT.APIDOC.DOCUMENT")
+                }
+              ],
+              initialValue: document
+            })(
+              <Input
+                placeholder={getIntlContent("SHENYU.DOCUMENT.APIDOC.DOCUMENT")}
+              />
+            )}
+          </Form.Item>
+        </Form>
+      </Modal>
+    );
+  }
+}
+
+export default Form.create()(AddAndUpdateApiDoc);
diff --git a/src/routes/Document/components/ApiInfo.js 
b/src/routes/Document/components/ApiInfo.js
index a9591a3f..8ca7e49e 100644
--- a/src/routes/Document/components/ApiInfo.js
+++ b/src/routes/Document/components/ApiInfo.js
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-import { Typography, Table, Tabs, Icon, Row, Col } from "antd";
+import { Typography, Table, Tabs, Icon, Row, Col , Button } from "antd";
 import React, { useContext } from "react";
 import ApiDebug from "./ApiDebug";
 import ApiContext from "./ApiContext";
@@ -24,13 +24,14 @@ import { Method } from "./globalData";
 
 const { Title, Text, Paragraph } = Typography;
 
-function ApiInfo() {
+function ApiInfo(props) {
   const {
     apiData: { envProps = [] },
     apiDetail,
     apiDetail: { document, responseParameters, requestHeaders }
   } = useContext(ApiContext);
   let documentJSON = {};
+  let {handleUpdateApi,handleDeleteApi} = props
   try {
     documentJSON = JSON.parse(document);
     documentJSON.errorCode = [];
@@ -148,6 +149,8 @@ function ApiInfo() {
         >
           <Title level={2}>
             {apiDetail.tags[apiDetail.tags.length - 1].name}
+            <Button style={{float:'right'}} 
onClick={handleDeleteApi}>delete</Button>
+            <Button style={{float:'right'}} 
onClick={handleUpdateApi}>edit</Button>
           </Title>
           <Paragraph>
             <Title level={4}>
diff --git a/src/routes/Document/components/SearchApi.js 
b/src/routes/Document/components/SearchApi.js
index 6e167acf..132e0334 100644
--- a/src/routes/Document/components/SearchApi.js
+++ b/src/routes/Document/components/SearchApi.js
@@ -114,7 +114,17 @@ function SearchApi(props) {
         ),
         key: `${treeNode.props.eventKey}-${index}`,
         isLeaf: true
-      }));
+      }))
+      treeNode.props.dataRef.children.push({
+        title: (
+          <>
+            <Text code>&nbsp;+&nbsp;</Text>
+          </>
+        ),
+        key: treeNode.props.dataRef.id,
+        isLeaf: true
+      })
+      ;
     }
     setApiTree([...apiTree]);
     return Promise.resolve();
diff --git a/src/services/api.js b/src/services/api.js
index 10d59aef..d2682234 100644
--- a/src/services/api.js
+++ b/src/services/api.js
@@ -822,3 +822,33 @@ export function getApiDetail(id) {
     method: `GET`
   });
 }
+
+/** addApi */
+export function addApi(params) {
+  return request(`${baseUrl}/api`, {
+    method: `POST`,
+    body: {
+      ...params
+    }
+  });
+}
+
+/** addApi */
+export function updateApi(params) {
+  return request(`${baseUrl}/api/${params.id}`, {
+    method: `PUT`,
+    body: {
+      ...params
+    }
+  });
+}
+
+/** delete Api */
+export function deleteApi(params) {
+  return request(`${baseUrl}/api/batch`, {
+    method: `DELETE`,
+    body: params
+  });
+}
+
+

Reply via email to