This is an automated email from the ASF dual-hosted git repository.
zhangzicheng 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 190a7444 change api (#253)
190a7444 is described below
commit 190a7444c36cf4648a2bf9c59d9131695de88de2
Author: dayu <[email protected]>
AuthorDate: Mon Nov 28 22:37:16 2022 +0800
change api (#253)
---
src/locales/en-US.json | 1 +
src/locales/zh-CN.json | 1 +
src/routes/Document/ApiDoc.js | 19 ++-
src/routes/Document/components/ApiDebug.js | 9 +-
src/routes/Document/components/ApiInfo.js | 107 +++++++++++---
src/routes/Document/components/SearchApi.js | 211 ++++++++++++++-------------
src/routes/Document/components/globalData.js | 27 ++++
src/services/api.js | 38 ++++-
8 files changed, 271 insertions(+), 142 deletions(-)
diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index 21de8336..92ac5820 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -310,6 +310,7 @@
"SHENYU.DOCUMENT.APIDOC.INFO.REQUEST.RESULTS": "Request Results",
"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.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 a904e198..bc62cc96 100644
--- a/src/locales/zh-CN.json
+++ b/src/locales/zh-CN.json
@@ -298,6 +298,7 @@
"SHENYU.DOCUMENT.APIDOC.INFO.REQUEST.RESULTS": "请求结果",
"SHENYU.DOCUMENT.APIDOC.CONTENTS.TO.BE.SIGNED": "待签名内容",
"SHENYU.DOCUMENT.APIDOC.SIGNATURE": "签名(sign)",
+ "SHENYU.DOCUMENT.APIDOC.ERROR.CODE.DETAILS": "错误代码",
"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 b7fd63d7..4b24490e 100644
--- a/src/routes/Document/ApiDoc.js
+++ b/src/routes/Document/ApiDoc.js
@@ -15,11 +15,11 @@
* limitations under the License.
*/
-import { Col, Row, Card, BackTop, Empty } from "antd";
+import { Col, Row, Card, BackTop, Empty, message } from "antd";
import React, { useEffect, useState } from "react";
import SearchApi from "./components/SearchApi";
import ApiInfo from "./components/ApiInfo";
-import { getDocItem, getDocMenus } from "../../services/api";
+import { getDocMenus, getApiDetail } from "../../services/api";
import ApiContext from "./components/ApiContext";
function ApiDoc() {
@@ -51,13 +51,20 @@ function ApiDoc() {
const handleSelectNode = async (_, e) => {
const {
node: {
- props: { id }
+ props: {
+ dataRef: { id, isLeaf }
+ }
}
} = e;
- const { code, data } = await getDocItem({ id });
- if (code === 200) {
- setApiDetail(data);
+ if (!isLeaf) {
+ return;
+ }
+ const { code, message: msg, data } = await getApiDetail(id);
+ if (code !== 200) {
+ message.error(msg);
+ return;
}
+ setApiDetail(data);
};
useEffect(() => {
diff --git a/src/routes/Document/components/ApiDebug.js
b/src/routes/Document/components/ApiDebug.js
index 137ac12a..ebdc2860 100644
--- a/src/routes/Document/components/ApiDebug.js
+++ b/src/routes/Document/components/ApiDebug.js
@@ -54,12 +54,7 @@ const FCForm = forwardRef(({ form, onSubmit }, ref) => {
}));
const {
- apiDetail: {
- name: apiUrl,
- httpMethodList,
- requestHeaders,
- requestParameters
- },
+ apiDetail: { apiPath, httpMethodList, requestHeaders, requestParameters },
apiData: { appKey, gatewayUrl, cookie }
} = useContext(ApiContext);
const [questJson, setRequestJson] = useState({});
@@ -154,7 +149,7 @@ const FCForm = forwardRef(({ form, onSubmit }, ref) => {
</Title>
<FormItem label={getIntlContent("SHENYU.DOCUMENT.APIDOC.INFO.ADDRESS")}>
{form.getFieldDecorator("requestUrl", {
- initialValue: gatewayUrl + apiUrl,
+ initialValue: gatewayUrl + apiPath,
rules: [{ type: "string", required: true }]
})(<Input allowClear />)}
</FormItem>
diff --git a/src/routes/Document/components/ApiInfo.js
b/src/routes/Document/components/ApiInfo.js
index 2c3db1bd..a9591a3f 100644
--- a/src/routes/Document/components/ApiInfo.js
+++ b/src/routes/Document/components/ApiInfo.js
@@ -15,26 +15,36 @@
* limitations under the License.
*/
-import { Typography, Table, Tabs, Icon } from "antd";
+import { Typography, Table, Tabs, Icon, Row, Col } from "antd";
import React, { useContext } from "react";
import ApiDebug from "./ApiDebug";
import ApiContext from "./ApiContext";
import { getIntlContent } from "../../../utils/IntlUtils";
+import { Method } from "./globalData";
const { Title, Text, Paragraph } = Typography;
function ApiInfo() {
const {
apiData: { envProps = [] },
- apiDetail: {
- summary,
- name: apiName,
- description,
- requestParameters,
- responseParameters,
- requestHeaders
- }
+ apiDetail,
+ apiDetail: { document, responseParameters, requestHeaders }
} = useContext(ApiContext);
+ let documentJSON = {};
+ try {
+ documentJSON = JSON.parse(document);
+ documentJSON.errorCode = [];
+ Object.keys(documentJSON.responses).forEach(key => {
+ documentJSON.errorCode.push({
+ code: key,
+ description: documentJSON.responses[key].description,
+ content: documentJSON.responses[key].content
+ });
+ });
+ } catch (e) {
+ // eslint-disable-next-line no-console
+ console.log(e);
+ }
const columns = [
{
@@ -43,7 +53,8 @@ function ApiInfo() {
},
{
title: getIntlContent("SHENYU.COMMON.TYPE"),
- dataIndex: "type"
+ dataIndex: "type",
+ render: (_, record) => record?.schema?.type
},
{
title: getIntlContent("SHENYU.COMMON.REQUIRED"),
@@ -69,6 +80,22 @@ function ApiInfo() {
}
];
+ const errorCodeColumns = [
+ {
+ title: "Code",
+ dataIndex: "code"
+ },
+ {
+ title: "Description",
+ dataIndex: "description"
+ },
+ {
+ title: "Content",
+ dataIndex: "content",
+ render: v => JSON.stringify(v)
+ }
+ ];
+
const defaultCommonData = [
{
id: 1,
@@ -107,7 +134,6 @@ function ApiInfo() {
dataIndex: "envDesc"
}
];
-
return (
<>
<Tabs>
@@ -120,15 +146,40 @@ function ApiInfo() {
}
key="1"
>
- <Title level={2}>{summary}</Title>
- <Title level={4}>
- {getIntlContent("SHENYU.DOCUMENT.APIDOC.INFO.INTERFACE.ADDRESS")}
- </Title>
- <Text code>{apiName}</Text>
- <Title level={4}>
- {getIntlContent("SHENYU.DOCUMENT.APIDOC.INFO.DESCRIPTION")}
+ <Title level={2}>
+ {apiDetail.tags[apiDetail.tags.length - 1].name}
</Title>
- <Text type="secondary">{description || "-"}</Text>
+ <Paragraph>
+ <Title level={4}>
+ {getIntlContent("SHENYU.DOCUMENT.APIDOC.INFO.INTERFACE.ADDRESS")}
+ </Title>
+ <Text code>{Method?.[apiDetail.httpMethod]}</Text>
+ <Text code>{apiDetail.apiPath}</Text>
+ <Text code>{apiDetail.version}</Text>
+ </Paragraph>
+ <Paragraph>
+ <Title level={4}>
+ {getIntlContent("SHENYU.DOCUMENT.APIDOC.INFO.DESCRIPTION")}
+ </Title>
+ <Text type="secondary">{apiDetail.apiDesc || "-"}</Text>
+ </Paragraph>
+ <Paragraph>
+ <Row gutter={24}>
+ <Col span={8}>
+ <Title level={4}>Owner</Title>
+ <Text code>{apiDetail.apiOwner}</Text>
+ </Col>
+ <Col span={8}>
+ <Title level={4}>Consume</Title>
+ <Text code>{apiDetail.consume}</Text>
+ </Col>
+ <Col span={8}>
+ <Title level={4}>Produce</Title>
+ <Text code>{apiDetail.produce}</Text>
+ </Col>
+ </Row>
+ </Paragraph>
+
<Title level={4}>
{getIntlContent("SHENYU.DOCUMENT.APIDOC.INFO.ADDRESS")}
</Title>
@@ -145,7 +196,6 @@ function ApiInfo() {
<Title level={2}>
{getIntlContent("SHENYU.DOCUMENT.APIDOC.INFO.REQUEST.PARAMETERS")}
</Title>
-
<Title level={4}>
{getIntlContent(
"SHENYU.DOCUMENT.APIDOC.INFO.SERVICE.REQUEST.HEADERS"
@@ -162,7 +212,6 @@ function ApiInfo() {
columns={columns}
/>
</Paragraph>
-
<Title level={4}>
{getIntlContent(
"SHENYU.DOCUMENT.APIDOC.INFO.SERVICE.REQUEST.PARAMETERS"
@@ -173,13 +222,12 @@ function ApiInfo() {
size="small"
rowKey="id"
bordered
- dataSource={requestParameters}
+ dataSource={documentJSON.parameters || []}
pagination={false}
childrenColumnName="refs"
columns={columns}
/>
</Paragraph>
-
<Title level={2}>
{getIntlContent("SHENYU.DOCUMENT.APIDOC.INFO.RESPONSE.PARAMETERS")}
</Title>
@@ -198,6 +246,19 @@ function ApiInfo() {
columns={columns.filter((_, i) => ![2, 3].includes(i))}
/>
</Paragraph>
+ <Title level={4}>
+ {getIntlContent("SHENYU.DOCUMENT.APIDOC.ERROR.CODE.DETAILS")}
+ </Title>
+ <Paragraph>
+ <Table
+ size="small"
+ rowKey="code"
+ bordered
+ dataSource={documentJSON.errorCode || []}
+ pagination={false}
+ columns={errorCodeColumns}
+ />
+ </Paragraph>
<Title level={4}>
{getIntlContent(
"SHENYU.DOCUMENT.APIDOC.INFO.BUSINESS.RESPONSE.PARAMETERS"
diff --git a/src/routes/Document/components/SearchApi.js
b/src/routes/Document/components/SearchApi.js
index ca9b6a47..6e167acf 100644
--- a/src/routes/Document/components/SearchApi.js
+++ b/src/routes/Document/components/SearchApi.js
@@ -1,128 +1,141 @@
/*
- * 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.
- */
+ * 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 { Tree, Input, Empty } from "antd";
-import React, { useContext, useEffect, useState } from "react";
-import ApiContext from "./ApiContext";
-import { getIntlContent } from "../../../utils/IntlUtils";
+import { Tree, Empty, message, Typography } from "antd";
+import React, { useEffect, useState } from "react";
+// import ApiContext from "./ApiContext";
+// import { getIntlContent } from "../../../utils/IntlUtils";
+import { getRootTag, getParentTagId, getApi } from "../../../services/api";
+import { Method } from "./globalData";
+const { Text } = Typography;
const { TreeNode } = Tree;
-const { Search } = Input;
+// const { Search } = Input;
function SearchApi(props) {
const { onSelect } = props;
- const [expandedKeys, setExpandedKeys] = useState([]);
- const [searchValue, setSearchValue] = useState("");
- const [autoExpandParent, setAutoExpandParent] = useState(true);
- const {
- apiData: { menuProjects }
- } = useContext(ApiContext);
+ // const [searchValue, setSearchValue] = useState("");
- const renderTreeNode = data => {
+ // const handleSearchChange = e => {
+ // const { value } = e.target;
+ // const keys = [];
+ // const findSearchKeys = data =>
+ // data.forEach(item => {
+ // if (item.label.indexOf(value) > -1 || item.name?.indexOf(value) >
-1) {
+ // keys.push(item.key);
+ // }
+ // if (Array.isArray(item.children)) {
+ // findSearchKeys(item.children);
+ // }
+ // });
+ // setSearchValue(value);
+ // };
+
+ const [apiTree, setApiTree] = useState([]);
+
+ const renderTreeNodes = data => {
return data.map(item => {
- const { children, id, label, key, name } = item;
- const index = label.indexOf(searchValue);
- const sameName = name?.indexOf(searchValue);
- const beforeStr = label.substr(0, index);
- const afterStr = label.substr(index + searchValue.length);
- let titleObj = <span>{label}</span>;
- if (index > -1) {
- titleObj = (
- <span>
- {beforeStr}
- <span style={{ color: "#f50" }}>{searchValue}</span>
- {afterStr}
- </span>
+ if (item.children) {
+ return (
+ <TreeNode
+ title={item.title}
+ key={item.key}
+ dataRef={item}
+ selectable={item.isLeaf}
+ isLeaf={item.isLeaf}
+ >
+ {renderTreeNodes(item.children)}
+ </TreeNode>
);
}
- if (searchValue && sameName > -1) {
- titleObj = <span style={{ color: "#f50" }}>{label}</span>;
- }
- return (
- <TreeNode
- key={key}
- title={titleObj}
- selectable={id !== undefined}
- id={id}
- >
- {children?.length && renderTreeNode(children)}
- </TreeNode>
- );
+ return <TreeNode key={item.key} {...item} dataRef={item} />;
});
};
- const handleSearchChange = e => {
- const { value } = e.target;
- const keys = [];
- const findSearchKeys = data =>
- data.forEach(item => {
- if (item.label.indexOf(value) > -1 || item.name?.indexOf(value) > -1) {
- keys.push(item.key);
- }
- if (Array.isArray(item.children)) {
- findSearchKeys(item.children);
- }
- });
- findSearchKeys(menuProjects);
- setExpandedKeys(keys);
- setSearchValue(value);
- setAutoExpandParent(true);
+ const queryRootTag = async () => {
+ const { code, data = [], message: msg } = await getRootTag();
+ if (code !== 200) {
+ message.error(msg);
+ return;
+ }
+ setApiTree(
+ data?.map((item, index) => ({
+ ...item,
+ title: item.name,
+ key: index.toString(),
+ isLeaf: !item.hasChildren
+ })) || []
+ );
};
- const handleExpandChange = keys => {
- setExpandedKeys(keys);
- setAutoExpandParent(false);
+ const onLoadData = async treeNode => {
+ if (treeNode.props.children) {
+ return Promise.resolve();
+ }
+ const { id, hasChildren } = treeNode.props.dataRef;
+ if (hasChildren) {
+ const { code, message: msg, data } = await getParentTagId(id);
+ if (code !== 200) {
+ message.error(msg);
+ return Promise.reject();
+ }
+ treeNode.props.dataRef.children = data?.map((item, index) => ({
+ ...item,
+ title: item.name,
+ key: `${treeNode.props.eventKey}-${index}`
+ }));
+ } else {
+ const { code, message: msg, data } = await getApi(id);
+ if (code !== 200) {
+ message.error(msg);
+ return Promise.reject();
+ }
+ const { dataList } = data;
+ treeNode.props.dataRef.children = dataList?.map((item, index) => ({
+ ...item,
+ title: (
+ <>
+ <Text code>{Method[item.httpMethod]}</Text> {item.apiPath}
+ </>
+ ),
+ key: `${treeNode.props.eventKey}-${index}`,
+ isLeaf: true
+ }));
+ }
+ setApiTree([...apiTree]);
+ return Promise.resolve();
};
- useEffect(
- () => {
- if (Array.isArray(menuProjects)) {
- const allKeys = [];
- const getAllParentsKey = data =>
- data.forEach(item => {
- if (item.children) {
- allKeys.push(item.key);
- getAllParentsKey(item.children);
- }
- });
- getAllParentsKey(menuProjects);
- setExpandedKeys(allKeys);
- }
- },
- [menuProjects]
- );
+ useEffect(() => {
+ queryRootTag();
+ }, []);
return (
<div style={{ overflow: "auto" }}>
- <Search
+ {/* <Search
allowClear
onChange={handleSearchChange}
placeholder={getIntlContent(
"SHENYU.DOCUMENT.APIDOC.SEARCH.PLACEHOLDER"
)}
- />
- {menuProjects?.length ? (
- <Tree
- autoExpandParent={autoExpandParent}
- expandedKeys={expandedKeys}
- onExpand={handleExpandChange}
- onSelect={onSelect}
- >
- {renderTreeNode(menuProjects)}
+ /> */}
+ {apiTree?.length ? (
+ <Tree loadData={onLoadData} onSelect={onSelect}>
+ {renderTreeNodes(apiTree)}
</Tree>
) : (
<Empty style={{ padding: "80px 0" }} description={false} />
diff --git a/src/routes/Document/components/globalData.js
b/src/routes/Document/components/globalData.js
new file mode 100644
index 00000000..e177c8e8
--- /dev/null
+++ b/src/routes/Document/components/globalData.js
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+export const Method = {
+ 0: "GET",
+ 1: "HEAD",
+ 2: "POST",
+ 3: "PUT",
+ 4: "PATCH",
+ 5: "DELETE",
+ 6: "OPTIONS",
+ 7: "TRACE"
+};
diff --git a/src/services/api.js b/src/services/api.js
index 59088490..10d59aef 100644
--- a/src/services/api.js
+++ b/src/services/api.js
@@ -525,13 +525,10 @@ export function fetchPluginHandleByPluginId(params) {
// create plugin resource
export function addPluginResource(params) {
- return request(
- `${baseUrl}/plugin/createPluginResource/${params.id}`,
- {
- method: `PUT`,
- body: params
- },
- );
+ return request(`${baseUrl}/plugin/createPluginResource/${params.id}`, {
+ method: `PUT`,
+ body: params
+ });
}
// fetch dict list
@@ -798,3 +795,30 @@ export function getDocItem(params) {
export function sandboxProxyGateway() {
return `${baseUrl}/sandbox/proxyGateway`;
}
+
+export function getRootTag() {
+ return request(`${baseUrl}/tag/queryRootTag`, {
+ method: `GET`
+ });
+}
+
+/* getParentTagId */
+export function getParentTagId(id) {
+ return request(`${baseUrl}/tag/parentTagId/${id}`, {
+ method: `GET`
+ });
+}
+
+/* queryApi */
+export function getApi(tagId) {
+ return request(`${baseUrl}/api?tagId=${tagId}¤tPage=0&pageSize=100`, {
+ method: `GET`
+ });
+}
+
+/* queryApi */
+export function getApiDetail(id) {
+ return request(`${baseUrl}/api/${id}`, {
+ method: `GET`
+ });
+}