From 594b9a1743b53a3c911417f6a47be95c5849b929 Mon Sep 17 00:00:00 2001 From: yini-chen Date: Tue, 2 Jan 2024 14:17:44 +0800 Subject: [PATCH 01/32] =?UTF-8?q?feat:=20api=20=E8=B0=83=E8=AF=95=E8=83=BD?= =?UTF-8?q?=E5=8A=9B=20=E4=BA=A4=E4=BA=921=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- media/src/components/APIPage/API.tsx | 171 +- .../APIPage/APIDebugger/APIDebugger.tsx | 42 +- .../APIPage/APIDebugger/RegionSelector.tsx | 53 + .../src/components/APIPage/TryAPI/TryAPI.tsx | 2 +- media/src/components/APIPage/context.ts | 2 +- .../langs/zh_CN/apiDebugger.ts | 12 +- .../widgets/xconsole/JsonEdit.tsx | 2 +- .../widgets/xconsole/Object.tsx | 2 +- .../widgets/xconsole/checkbox.tsx | 2 +- .../SemixFormRender/widgets/xconsole/enum.tsx | 2 +- .../SemixFormRender/widgets/xconsole/map.tsx | 2 +- .../widgets/xconsole/number.tsx | 2 +- .../widgets/xconsole/radio.tsx | 2 +- .../widgets/xconsole/string.tsx | 2 +- .../widgets/xconsole/swtich.tsx | 2 +- media/src/document.css | 3 - media/src/main.css | 42 + media/src/mocks/endpoints.ts | 264 ++ media/src/mocks/routerMeta.ts | 2999 ++++++++++++++++- media/src/pages/_app.tsx | 1 + media/src/pages/document/index.scss | 11 - media/src/service/UIService.ts | 7 +- src/Service.ts | 28 +- src/openApiService/request/request.ts | 2 - src/utils.ts | 1 + 25 files changed, 3478 insertions(+), 180 deletions(-) create mode 100644 media/src/components/APIPage/APIDebugger/RegionSelector.tsx create mode 100644 media/src/mocks/endpoints.ts diff --git a/media/src/components/APIPage/API.tsx b/media/src/components/APIPage/API.tsx index 1438de5..50c36dc 100644 --- a/media/src/components/APIPage/API.tsx +++ b/media/src/components/APIPage/API.tsx @@ -30,6 +30,7 @@ export class APIProps { export const API: React.FC = (props) => { const { selectedApi, definitions } = props; + const [mode, changeMode] = React.useState("doc" as any); const getSchema = React.useCallback( ($ref: any) => { @@ -96,6 +97,100 @@ export const API: React.FC = (props) => { getCustomWidget: getCustomWidget, }); + const tabs = [ + { tab: "文档", key: "doc" }, + { tab: "调试", key: "debug" }, + { tab: "代码示例", key: "sdk" }, + ]; + + const renderContent = React.useMemo(() => { + const documentComp = ( +
+ {selectedApi?.description ? ( +
+ +
+ ) : null} +
+
入参
+ +
+
+
出参
+ { + return ( + + + 无出参定义 + + + ); + }} + /> +
+ {props.renderMore?.()} +
+ ); + const debugComp = ( + <> +
+ +
+
+ { + changeMode(key); + }} + > + + {documentComp} + + +
敬请期待...
+
+ + {selectedApi?.externalDocs ? ( + + ) : null} + {/*
*/} +
+
+
+ + ); + switch (mode) { + case "doc": + return documentComp; + case "debug": + return debugComp; + case "sdk": + return debugComp; + default: + return debugComp; + } + }, [mode]); + return (
{/* */} @@ -120,20 +215,11 @@ export const API: React.FC = (props) => {
{pathEle ? apiNameEle : null} - {selectedApi?.externalDocs ? ( - - ) : null} + changeMode(val)}> + {tabs.map((tab) => ( + + ))} +
{selectedApi?.summary ? ( @@ -144,62 +230,7 @@ export const API: React.FC = (props) => { ) : null} -
- {/*
- -
*/} -
- - -
- {selectedApi?.description ? ( -
- -
- ) : null} -
-
入参
- -
-
-
出参
- { - return ( - - - 无出参定义 - - - ); - }} - /> -
- {props.renderMore?.()} -
-
- -
敬请期待...
-
- -
敬请期待...
- {/*
*/} -
-
-
-
+
{renderContent}
) : null} diff --git a/media/src/components/APIPage/APIDebugger/APIDebugger.tsx b/media/src/components/APIPage/APIDebugger/APIDebugger.tsx index c9fea4d..8ef45b8 100644 --- a/media/src/components/APIPage/APIDebugger/APIDebugger.tsx +++ b/media/src/components/APIPage/APIDebugger/APIDebugger.tsx @@ -1,21 +1,40 @@ /** - * @author nianyi - * @description API调试器 + * @author yini-chen + * @description API debugger */ import React from "react"; import { APIPageContext } from "../context"; import { Input, Button } from "@alicloud/console-components"; import { SemixForm } from "../../SemixFormRender"; +import I18N from "../../../utils/I18N"; +import { PontUIService } from "../../../service/UIService"; +import { endpointsMocks } from "../../../mocks/endpoints"; +import RegionSelector from "./RegionSelector"; export class APIDebuggerProps {} export const APIDebugger: React.FC = (props) => { const { apiMeta, schemaForm, product, version, onDebug } = APIPageContext.useContainer(); + const [regionId, setRegionId] = React.useState(""); + const [endpoints, setEndpoints] = React.useState([]); + + React.useEffect(() => { + if (endpoints.length === 0) { + // get endpoints list + PontUIService.requestEndpoints(product).then((res) => { + console.log(res); + setEndpoints(res?.length ? res : endpointsMocks); + }); + } + }, [product]); return React.useMemo(() => { return (
- {apiMeta.title} +
+ +
+
= (props) => { // }); // }} > - + + {I18N.main.explorer.debug} +
+
); - }, [schemaForm.formData]); + }, [schemaForm.formData, regionId, endpoints]); }; APIDebugger.defaultProps = new APIDebuggerProps(); export default APIDebugger; diff --git a/media/src/components/APIPage/APIDebugger/RegionSelector.tsx b/media/src/components/APIPage/APIDebugger/RegionSelector.tsx new file mode 100644 index 0000000..2d7b936 --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/RegionSelector.tsx @@ -0,0 +1,53 @@ +/** +* @author yini-chen +* @description +*/ +import React from 'react' +import { Select } from "@alicloud/console-components"; +import _ from "lodash"; +import I18N from '../../../utils/I18N'; + +export class RegionSelectorProps { + regionId: string; + setRegionId: (endpoint: string) => void; + product: string; + endpoints: any[]; +} + +export const RegionSelector: React.FC = (props) => { + + const dataSource = React.useMemo(() => { + if (props.endpoints.length) { + const areaGroups = _.groupBy(props.endpoints, "areaId"); + const areaGroupsOrder = ["asiaPacific", "europeAmerica", "middleEast", "industryCloud"]; + return areaGroupsOrder?.map((area) => { + return { + label: areaGroups[area]?.[0]?.areaName, + children: areaGroups[area]?.map((endpoint) => { + return { + label: endpoint.regionName, + key: endpoint.regionId, + value: endpoint.regionId, + }; + }), + }; + }); + } + return [] + }, [props.endpoints]); + + return ( +
+
{I18N.main.explorer.endPoint}
+ +
+ ); + +} +RegionSelector.defaultProps = new RegionSelectorProps(); +export default RegionSelector; \ No newline at end of file diff --git a/media/src/components/APIPage/TryAPI/TryAPI.tsx b/media/src/components/APIPage/TryAPI/TryAPI.tsx index 47a7c29..ef0d3da 100644 --- a/media/src/components/APIPage/TryAPI/TryAPI.tsx +++ b/media/src/components/APIPage/TryAPI/TryAPI.tsx @@ -1,5 +1,5 @@ /** - * @author nianyi + * @author yini-chen * @description API 试用 */ diff --git a/media/src/components/APIPage/context.ts b/media/src/components/APIPage/context.ts index 77ce6cc..5be27ff 100644 --- a/media/src/components/APIPage/context.ts +++ b/media/src/components/APIPage/context.ts @@ -1,5 +1,5 @@ /** - * @author nianyi + * @author yini-chen */ import { createContainer } from 'unstated-next'; import { SemixFormProps } from '../SemixFormRender'; diff --git a/media/src/components/SemixFormRender/langs/zh_CN/apiDebugger.ts b/media/src/components/SemixFormRender/langs/zh_CN/apiDebugger.ts index 177f9fe..b1020b7 100644 --- a/media/src/components/SemixFormRender/langs/zh_CN/apiDebugger.ts +++ b/media/src/components/SemixFormRender/langs/zh_CN/apiDebugger.ts @@ -1,9 +1,9 @@ export default { - "index": { - "empty": "清空", - "submissionFailure": "提交失败。", - "submittedSuccessfully": "提交成功", - "submit": "提交", - "lookOnlyAtRequired": "只看必填", + index: { + empty: '清空', + submissionFailure: '提交失败。', + submittedSuccessfully: '提交成功', + submit: '提交', + lookOnlyAtRequired: '只看必填', }, }; diff --git a/media/src/components/SemixFormRender/widgets/xconsole/JsonEdit.tsx b/media/src/components/SemixFormRender/widgets/xconsole/JsonEdit.tsx index 6a55121..aa72ec1 100644 --- a/media/src/components/SemixFormRender/widgets/xconsole/JsonEdit.tsx +++ b/media/src/components/SemixFormRender/widgets/xconsole/JsonEdit.tsx @@ -1,5 +1,5 @@ /** - * @author nianyi + * @author yini-chen * @description 表单 Json 编辑器 */ diff --git a/media/src/components/SemixFormRender/widgets/xconsole/Object.tsx b/media/src/components/SemixFormRender/widgets/xconsole/Object.tsx index c61d75c..11a477f 100644 --- a/media/src/components/SemixFormRender/widgets/xconsole/Object.tsx +++ b/media/src/components/SemixFormRender/widgets/xconsole/Object.tsx @@ -1,5 +1,5 @@ /** - * @author nianyi + * @author yini-chen * @description amp定制object,object回填value可能为string */ import * as React from "react"; diff --git a/media/src/components/SemixFormRender/widgets/xconsole/checkbox.tsx b/media/src/components/SemixFormRender/widgets/xconsole/checkbox.tsx index 3e91376..cd26aad 100644 --- a/media/src/components/SemixFormRender/widgets/xconsole/checkbox.tsx +++ b/media/src/components/SemixFormRender/widgets/xconsole/checkbox.tsx @@ -1,5 +1,5 @@ /** - * @author nianyi + * @author yini-chen * @description 枚举选择 */ diff --git a/media/src/components/SemixFormRender/widgets/xconsole/enum.tsx b/media/src/components/SemixFormRender/widgets/xconsole/enum.tsx index ddc7644..792ad30 100644 --- a/media/src/components/SemixFormRender/widgets/xconsole/enum.tsx +++ b/media/src/components/SemixFormRender/widgets/xconsole/enum.tsx @@ -1,5 +1,5 @@ /** - * @author nianyi + * @author yini-chen * @description 枚举选择 */ diff --git a/media/src/components/SemixFormRender/widgets/xconsole/map.tsx b/media/src/components/SemixFormRender/widgets/xconsole/map.tsx index 5ece83a..fdf387e 100644 --- a/media/src/components/SemixFormRender/widgets/xconsole/map.tsx +++ b/media/src/components/SemixFormRender/widgets/xconsole/map.tsx @@ -1,5 +1,5 @@ /** - * @author nianyi + * @author yini-chen * @description semix-map */ import * as React from "react"; diff --git a/media/src/components/SemixFormRender/widgets/xconsole/number.tsx b/media/src/components/SemixFormRender/widgets/xconsole/number.tsx index 7d74956..124b6fa 100644 --- a/media/src/components/SemixFormRender/widgets/xconsole/number.tsx +++ b/media/src/components/SemixFormRender/widgets/xconsole/number.tsx @@ -1,5 +1,5 @@ /** - * @author nianyi + * @author yini-chen * @description 输入数值 */ import { NumberPicker } from "@alicloud/console-components"; diff --git a/media/src/components/SemixFormRender/widgets/xconsole/radio.tsx b/media/src/components/SemixFormRender/widgets/xconsole/radio.tsx index 9d45620..a8edfef 100644 --- a/media/src/components/SemixFormRender/widgets/xconsole/radio.tsx +++ b/media/src/components/SemixFormRender/widgets/xconsole/radio.tsx @@ -1,5 +1,5 @@ /** - * @author nianyi + * @author yini-chen * @description 枚举选择 */ diff --git a/media/src/components/SemixFormRender/widgets/xconsole/string.tsx b/media/src/components/SemixFormRender/widgets/xconsole/string.tsx index 4ff6642..23e493f 100644 --- a/media/src/components/SemixFormRender/widgets/xconsole/string.tsx +++ b/media/src/components/SemixFormRender/widgets/xconsole/string.tsx @@ -1,5 +1,5 @@ /** - * @author nianyi + * @author yini-chen * @description string类型 */ diff --git a/media/src/components/SemixFormRender/widgets/xconsole/swtich.tsx b/media/src/components/SemixFormRender/widgets/xconsole/swtich.tsx index dd491d5..fd86bca 100644 --- a/media/src/components/SemixFormRender/widgets/xconsole/swtich.tsx +++ b/media/src/components/SemixFormRender/widgets/xconsole/swtich.tsx @@ -1,5 +1,5 @@ /** - * @author nianyi + * @author yini-chen * @description Switch */ import * as React from "react"; diff --git a/media/src/document.css b/media/src/document.css index fc68afb..76a2141 100644 --- a/media/src/document.css +++ b/media/src/document.css @@ -49350,9 +49350,6 @@ code { .pontx-ui-api .semix-table-header-th.param-name { min-width: 100px; } -.pontx-ui-api .api-page-content { - padding: 12px 20px 12px; -} .pontx-ui-api .desc-mod { background-color: #fff; border: 1px solid #89a6b5; diff --git a/media/src/main.css b/media/src/main.css index 4a502b6..9fde540 100644 --- a/media/src/main.css +++ b/media/src/main.css @@ -9,4 +9,46 @@ html body { padding: 20px; width: 100%; max-width: 100%; + .api-page-content { + position: relative; + margin-top: 16px; + border: 1px #ccc solid; + display: flex; + width: 100%; + .content{ + width: 100%; + padding:0 20px 20px + } + .left-panel{ + width: 25%; + .head-content{ + padding: 20px; + border-bottom: 1px #ccc solid; + } + .middle-content{ + /* border: 1px #ccc solid; + border-top: 0px; */ + padding: 20px; + height: calc(100vh - 330px); + overflow: auto; + } + .footer-content{ + padding: 20px; + border-top: 1px #ccc solid; + display: flex; + justify-content: space-between; + } + + } + .right-panel { + width: 75%; + border-left: 1px #ccc solid; + padding-left:16px; + .content{ + padding: 0px 20px 20px 0; + height: calc(100vh - 200px); + overflow: auto; + } + } + } } diff --git a/media/src/mocks/endpoints.ts b/media/src/mocks/endpoints.ts new file mode 100644 index 0000000..9a6ae5b --- /dev/null +++ b/media/src/mocks/endpoints.ts @@ -0,0 +1,264 @@ +export const endpointsMocks = [ + { + "regionId": "us-west-1", + "regionName": "美国(硅谷)", + "areaId": "europeAmerica", + "areaName": "欧洲与美洲", + "public": "ecs.us-west-1.aliyuncs.com", + "vpc": "ecs-vpc.us-west-1.aliyuncs.com" + }, + { + "regionId": "us-east-1", + "regionName": "美国(弗吉尼亚)", + "areaId": "europeAmerica", + "areaName": "欧洲与美洲", + "public": "ecs.us-east-1.aliyuncs.com", + "vpc": "ecs-vpc.us-east-1.aliyuncs.com" + }, + { + "regionId": "me-east-1", + "regionName": "阿联酋(迪拜)", + "areaId": "middleEast", + "areaName": "中东与印度", + "public": "ecs.me-east-1.aliyuncs.com", + "vpc": "ecs-vpc.me-east-1.aliyuncs.com" + }, + { + "regionId": "me-central-1", + "regionName": "沙特(利雅得)", + "areaId": "middleEast", + "areaName": "中东与印度", + "vpc": "ecs-vpc.me-central-1.aliyuncs.com" + }, + { + "regionId": "eu-west-1", + "regionName": "英国(伦敦)", + "areaId": "europeAmerica", + "areaName": "欧洲与美洲", + "public": "ecs.eu-west-1.aliyuncs.com", + "vpc": "ecs-vpc.eu-west-1.aliyuncs.com" + }, + { + "regionId": "eu-central-1", + "regionName": "德国(法兰克福)", + "areaId": "europeAmerica", + "areaName": "欧洲与美洲", + "public": "ecs.eu-central-1.aliyuncs.com", + "vpc": "ecs-vpc.eu-central-1.aliyuncs.com" + }, + { + "regionId": "cn-zhangjiakou", + "regionName": "华北3(张家口)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.cn-zhangjiakou.aliyuncs.com", + "vpc": "ecs-vpc.cn-zhangjiakou.aliyuncs.com" + }, + { + "regionId": "cn-wulanchabu", + "regionName": "华北6(乌兰察布)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.cn-wulanchabu.aliyuncs.com", + "vpc": "ecs-vpc.cn-wulanchabu.aliyuncs.com" + }, + { + "regionId": "cn-shenzhen-finance-1", + "regionName": "华南1 金融云", + "areaId": "industryCloud", + "areaName": "行业云", + "public": "ecs-cn-hangzhou.aliyuncs.com", + "vpc": "ecs-vpc.cn-shenzhen-finance-1.aliyuncs.com" + }, + { + "regionId": "cn-shenzhen", + "regionName": "华南1(深圳)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.cn-shenzhen.aliyuncs.com", + "vpc": "ecs-vpc.cn-shenzhen.aliyuncs.com" + }, + { + "regionId": "cn-shanghai-finance-1", + "regionName": "华东2 金融云", + "areaId": "industryCloud", + "areaName": "行业云", + "public": "ecs-cn-hangzhou.aliyuncs.com", + "vpc": "ecs-vpc.cn-shanghai-finance-1.aliyuncs.com" + }, + { + "regionId": "cn-shanghai", + "regionName": "华东2(上海)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.cn-shanghai.aliyuncs.com", + "vpc": "ecs-vpc.cn-shanghai.aliyuncs.com" + }, + { + "regionId": "cn-qingdao", + "regionName": "华北1(青岛)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.cn-qingdao.aliyuncs.com", + "vpc": "ecs-vpc.cn-qingdao.aliyuncs.com" + }, + { + "regionId": "cn-nanjing", + "regionName": "华东5(南京-本地地域)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.cn-nanjing.aliyuncs.com", + "vpc": "ecs-vpc.cn-nanjing.aliyuncs.com" + }, + { + "regionId": "cn-huhehaote", + "regionName": "华北5(呼和浩特)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.cn-huhehaote.aliyuncs.com", + "vpc": "ecs-vpc.cn-huhehaote.aliyuncs.com" + }, + { + "regionId": "cn-hongkong", + "regionName": "中国香港", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.cn-hongkong.aliyuncs.com", + "vpc": "ecs-vpc.cn-hongkong.aliyuncs.com" + }, + { + "regionId": "cn-heyuan", + "regionName": "华南2(河源)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.cn-heyuan.aliyuncs.com", + "vpc": "ecs-vpc.cn-heyuan.aliyuncs.com" + }, + { + "regionId": "cn-hangzhou-finance", + "regionName": "华东1 金融云", + "areaId": "industryCloud", + "areaName": "行业云", + "public": "ecs.aliyuncs.com" + }, + { + "regionId": "cn-hangzhou", + "regionName": "华东1(杭州)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs-cn-hangzhou.aliyuncs.com", + "vpc": "ecs-vpc.cn-hangzhou.aliyuncs.com" + }, + { + "regionId": "cn-guangzhou", + "regionName": "华南3(广州)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.cn-guangzhou.aliyuncs.com", + "vpc": "ecs-vpc.cn-guangzhou.aliyuncs.com" + }, + { + "regionId": "cn-fuzhou", + "regionName": "华东6(福州-本地地域)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.cn-fuzhou.aliyuncs.com", + "vpc": "ecs-vpc.cn-fuzhou.aliyuncs.com" + }, + { + "regionId": "cn-chengdu", + "regionName": "西南1(成都)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.cn-chengdu.aliyuncs.com", + "vpc": "ecs-vpc.cn-chengdu.aliyuncs.com" + }, + { + "regionId": "cn-beijing-finance-1", + "regionName": "华北2 金融云(邀测)", + "areaId": "industryCloud", + "areaName": "行业云", + "public": "ecs.cn-beijing-finance-1.aliyuncs.com", + "vpc": "ecs-vpc.cn-beijing-finance-1.aliyuncs.com" + }, + { + "regionId": "cn-beijing", + "regionName": "华北2(北京)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.cn-beijing.aliyuncs.com", + "vpc": "ecs-vpc.cn-beijing.aliyuncs.com" + }, + { + "regionId": "ap-southeast-7", + "regionName": "泰国(曼谷)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.ap-southeast-7.aliyuncs.com", + "vpc": "ecs-vpc.ap-southeast-7.aliyuncs.com" + }, + { + "regionId": "ap-southeast-6", + "regionName": "菲律宾(马尼拉)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.ap-southeast-6.aliyuncs.com", + "vpc": "ecs-vpc.ap-southeast-6.aliyuncs.com" + }, + { + "regionId": "ap-southeast-5", + "regionName": "印度尼西亚(雅加达)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.ap-southeast-5.aliyuncs.com", + "vpc": "ecs-vpc.ap-southeast-5.aliyuncs.com" + }, + { + "regionId": "ap-southeast-3", + "regionName": "马来西亚(吉隆坡)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.ap-southeast-3.aliyuncs.com", + "vpc": "ecs-vpc.ap-southeast-3.aliyuncs.com" + }, + { + "regionId": "ap-southeast-2", + "regionName": "澳大利亚(悉尼)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.ap-southeast-2.aliyuncs.com", + "vpc": "ecs-vpc.ap-southeast-2.aliyuncs.com" + }, + { + "regionId": "ap-southeast-1", + "regionName": "新加坡", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.ap-southeast-1.aliyuncs.com", + "vpc": "ecs-vpc.ap-southeast-1.aliyuncs.com" + }, + { + "regionId": "ap-south-1", + "regionName": "印度(孟买)", + "areaId": "middleEast", + "areaName": "中东与印度", + "public": "ecs.ap-south-1.aliyuncs.com", + "vpc": "ecs-vpc.ap-south-1.aliyuncs.com" + }, + { + "regionId": "ap-northeast-2", + "regionName": "韩国(首尔)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.ap-northeast-2.aliyuncs.com", + "vpc": "ecs-vpc.ap-northeast-2.aliyuncs.com" + }, + { + "regionId": "ap-northeast-1", + "regionName": "日本(东京)", + "areaId": "asiaPacific", + "areaName": "亚太", + "public": "ecs.ap-northeast-1.aliyuncs.com", + "vpc": "ecs-vpc.ap-northeast-1.aliyuncs.com" + } +] diff --git a/media/src/mocks/routerMeta.ts b/media/src/mocks/routerMeta.ts index 343d0a8..2e4d37b 100644 --- a/media/src/mocks/routerMeta.ts +++ b/media/src/mocks/routerMeta.ts @@ -1,17 +1,1653 @@ export const routerMeta = { - "specName": "ocr-api::2021-07-07", + "specName": "Ecs::2014-05-26", "modName": "", "spec": { "deprecated": false, - "description": "#### 本接口适用场景\n* 阿里云通用手写体识别,是阿里云官方自研OCR文字识别产品,适用于获取手写体书面形式的文字场景,适用于各类手写笔记、板书等。\n* 阿里云OCR产品基于阿里巴巴达摩院强大的AI技术及海量数据,历经多年沉淀打磨,具有服务稳定、操作简易、实时性高、能力全面等几大优势。\n* 本接口图片示例\n

\n\n#### 本接口核心能力\n \n|分类 |概述|\n|---|---------|\n|多文字形式|支持中文手写体、英文手写体、数字手写体。|\n|图像增强|默认支持图像增强,包括图像自动旋转、畸变自动矫正、模糊图片自动增强等能力。|\n|多类型覆盖|支持模糊、光照不均、透视畸变、任意背景等低质量图像识别。|\n|高精度识别|总体识别准确率可达98%。|\n\n#### 如何使用本接口\n\n|步骤|概述|\n|--|---|\n|1|开通 [通用文字识别](https://common-buy.aliyun.com?commodityCode=ocr_general_public_cn) 服务。开通服务前后,您可以通过[体验馆](https://duguang.aliyun.com/experience?type=universal&subtype=shouxie#intro)免费体验本功能识别效果。|\n|2|购买[通用手写体识别资源包](https://common-buy.aliyun.com/?commodityCode=ocr_general_dp_cn&request=%7B%22ord_time%22:%221:Year%22,%22order_num%22:1,%22pack%22:%22ocr_general_dp_cn_20211103172431_0249%22,%22flowout_spec%22:%22500%22%7D)。本API会赠送免费额度,可使用免费额度测试。|\n|3|可以参照[调试页面](https://next.api.aliyun.com/api/ocr-api/2021-07-07/RecognizeHandwriting?sdkStyle=dara)提供的代码示例完成API接入开发。接入完成后,调用API获取识别结果。如果使用子账号调用接口,需要阿里云账号(主账号)对RAM账号进行授权。创建RAM用户的具体操作,请参考:[创建RAM用户。](https://help.aliyun.com/document_detail/93720.html)文字识别服务提供一种系统授权策略,即**AliyunOCRFullAccess**。具体授权操作,请参见[在用户页面为RAM用户授权。](https://help.aliyun.com/document_detail/116146.html)|\n\n#### 重要提示\n|类型|概述|\n|----|-------------------|\n|图片格式|
  • 本接口支持:PNG、JPG、JPEG、BMP、GIF、TIFF、WebP。暂不支持PDF格式。
|\n|图片尺寸|
  • 图片长宽需要大于15像素,小于8192像素。
  • 长宽比需要小于50。
  • 如需达到较好识别效果,建议长宽均大于500px。
  • 图片尺寸过小,会影响识别精度。图片内单字大小在10-50px内时,识别效果较好。
|\n|图片大小|
  • 图片二进制文件不能超过10MB。
  • 图片过大会影响接口响应速度,建议使用小于1.5M图片进行识别,且通过传图片URL的方式调用接口。
|\n|其他提示|
  • 接口响应速度和图片中的文字数量有关,如果图片中文字数量越多,接口响应可能越慢。
  • 接口会自动处理反光、扭曲等干扰信息,但会影响精度。请尽量选择清晰度高、无反光、无扭曲的图片。
|\n|相关能力|
  • [云市场手写体识别。](https://market.aliyun.com/products/57124001/cmapi00040832.html?#sku=yuncode3483200001)
|", + "description": "## 接口说明\n\n- **准备工作**:\n - 通过实名认证。更多信息,请参见[账号实名认证相关文档](https://help.aliyun.com/document_detail/48263.html)。 \n - 成本估算:了解云服务器ECS的计费方式。更多信息,请参见[计费概述](https://help.aliyun.com/document_detail/25398.html)。\n - 产品选型:调用[DescribeInstanceTypes](https://help.aliyun.com/document_detail/25620.html)查看目标实例规格的性能数据,或者参见[选型配置](https://help.aliyun.com/document_detail/58291.html)了解如何选择实例规格。\n - 查询库存:调用[DescribeAvailableResource](https://help.aliyun.com/document_detail/66186.html)查看指定地域或者可用区内的资源供给情况。\n - 网络规划:您需要确保您已经有可用的安全组。更多信息,请参见[CreateSecurityGroup](https://help.aliyun.com/document_detail/25553.html)。创建专有网络VPC类型实例前,您需要预先在相应的阿里云地域[创建VPC](https://help.aliyun.com/document_detail/65430.html)。\n\n- **与CreateInstance对比差异**:\n\n 和CreateInstance接口相比,RunInstances接口有以下优点:\n \n - 单次最多可以创建100台实例,避免重复多次调用CreateInstance。\n - 实例创建之后,实例会自动变成`Starting`状态,然后变成`Running`状态,不需要再调用[StartInstance](https://help.aliyun.com/document_detail/25500.html)启动实例。\n - 创建实例时可以指定`InternetMaxBandwidthOut`为ECS实例分配公网IP,不需要您再调用[AllocatePublicIpAddress](https://help.aliyun.com/document_detail/25544.html)分配公网IP。\n - 您可以指定`AutoReleaseTime`参数来设定自动释放时间,不需要再调用[ModifyInstanceAutoReleaseTime](https://help.aliyun.com/document_detail/47576.html)设置自动释放时间。\n - 您可以指定`LaunchTemplateId`和`LaunchTemplateVersion`使用启动模板,可以免除您每次创建实例时都需要填入大量配置参数。\n - 可以指定`UniqueSuffix`参数批量设置有序的实例名称或主机名称,方便管理与检索。\n - 使用RunInstances创建实例时支持设置Enclave机密计算模式和可信系统模式。\n - 可以指定 `NetworkOptions.EnableJumboFrame`参数为true在创建时开启Jumbo frame特性。 更多信息,请参见 [ECS实例MTU](https://help.aliyun.com/document_detail/200512.html)。\n\n- **注意事项**:\n\n - 单次最多能创建100台实例。\n - 您可以指定参数`AutoReleaseTime`设置实例自动释放时间。\n - 创建成功后会返回实例ID列表,您可以通过[DescribeInstances](https://help.aliyun.com/document_detail/25506.html)查询新建实例状态。\n - 创建实例时,默认自动启动实例,直到实例状态变成运行中(`Running`)。\n - 自2020年11月27日起,创建和变配ECS实例时带宽峰值受账户限速策略影响。如需更大带宽峰值,请提交工单。具体限速策略:单个地域下,所有按使用流量计费ECS实例的实际运行带宽峰值总和不大于5 Gbit/s;所有按固定带宽计费ECS实例的实际运行带宽峰值总和不大于50 Gbit/s。\n - 与[CreateInstance](https://help.aliyun.com/document_detail/25499.html)相比,通过`RunInstances`创建的实例如果参数`InternetMaxBandwidthOut`的值大于0,则自动为实例分配公网IP。\n - RunInstances支持以下任一方式绑定主网卡,但需要注意一次调用只能选用一种方式配置主网卡,同时使用两种方式将会调用失败并返回错误信息。\n - 通过`SecurityGroupId`、`VSwitchId`、`PrivateIpAddress`、`NetworkInterfaceQueueNumber`与`Ipv6AddressCount`等参数直接设置主网卡的相关配置信息。\n - 通过`NetworkInterface.N.*`设置主网卡以及辅助网卡的配置信息。当`NetworkInterface.N.InstanceType`取值为`Primary`时,表示设置主网卡;当`NetworkInterface.N.InstanceType`取值为`Secondary`或空值时,表示设置辅助网卡。\n - 提交创建任务后,参数不合法或者库存不足的情况下会报错,具体的报错原因参见错误码。\n\n - 如果创建实例时返回`QuotaExceed.ElasticQuota`错误,表示您在当前地域选择的实例规格所要创建的台数超出系统限额,或者全实例规格vCPU配额超出系统限额,您可以前往[ECS管理控制台](https://ecs.console.aliyun.com/?spm=a2c8b.12215451.favorites.decs.5e3a336aMGTtzy#/privileges/quota)或[配额中心](https://quotas.console.aliyun.com/products/ecs/quotas)申请提高限额。\n\n - 如果创建实例时返回`QuotaExceed.DiskCapacity`错误,表示您当前选择的磁盘类型所要创建的总容量超出指定可用区的系统限额,您可以前往[配额中心](https://quotas.console.aliyun.com/products/disk/quotas)查询和申请提高磁盘容量配额。\n\n- **最佳实践**:\n\n - 单次大批量创建ECS实例(大于100台)遇到库存不足的场景;对实例规格或可用区等资源配置无指定要求,更关注如何快速创建实例的场景;对ECS实例数量无指定要求,更关注总算力vCPU个数等场景下,阿里云推荐您使用弹性供应组。您可以通过[CreateAutoProvisioningGroup](https://help.aliyun.com/document_detail/122738.html)创建弹性供应组,一键式地部署跨计费方式、跨可用区、跨实例规格族的实例集群。更多信息,请参见[使用弹性供应组API批量创建ECS实例](https://help.aliyun.com/document_detail/200772.html)。\n\n - `RunInstances`可以执行批量创建任务,为便于管理与检索,建议您为每批次启动的实例指定标签(`Tag.N.Key`和`Tag.N.Value`),并且为主机名(`HostName`)和实例名称(`InstanceName`)添加有序后缀(`UniqueSuffix`)。\n\n - 实例启动模板能免除您每次创建实例时都需要填入大量配置参数,您可以创建实例启动模板([CreateLaunchTemplate](https://help.aliyun.com/document_detail/74686.html))后,在`RunInstances`请求中指定`LaunchTemplateId`和`LaunchTemplateVersion`使用启动模板。\n\n - 您可以在[ECS管理控制台](https://ecs.console.aliyun.com/)创建ECS实例时获取`RunInstances`的最佳实践建议。确认订单时,左侧**API 工作流**罗列出`RunInstances`能使用的关联API以及请求参数的值。右侧提供面向编程语言的SDK示例,目前支持**Java**和**Python**示例。\n\n### 示例1:创建包年包月实例\n\n实例所在地域为华东1(杭州),计费方式为包年包月,购买时长一个月,到期自动续费一个月,镜像ID为:aliyun_3_x64_20G_alibase_20221102.vhd,实例规格为:ecs.g7.large,40 GiB ESSD云盘,挂载100 GiB ESSD云数据盘,公网出带宽为10 Mbit/s,自动分配私网IP和公网IP,实例名称为ECS-test,登录密码为ECS@test1234,数量为1台。\n\n```\nhttp(s)://ecs.aliyuncs.com/?Action=RunInstances\n&RegionId=cn-hangzhou\n&ImageId=aliyun_3_x64_20G_alibase_20221102.vhd\n&InstanceType=ecs.g7.large\n&SecurityGroupId=sg-bp150uqocpf9jj70****\n&VSwitchId=vsw-bp1qo7s91cbch5i4l****\n&InstanceChargeType=PrePaid\n&SystemDisk.Size=40\n&DataDisk.1.Size=100\n&DataDisk.1.Category=cloud_essd\n&SystemDisk.Category=cloud_essd\n&Amount=1\n&Period=1\n&PeriodUnit=Month\n&AutoRenew=true\n&AutoRenewPeriod=1\n&HostName=ECS-test\n&Password=ECS@test1234\n&InternetMaxBandwidthOut=10\n&公共请求参数\n```\n\n### 示例2:创建按量付费实例\n\n实例所在地域为华东1(杭州),计费方式为按量付费,镜像ID为:aliyun_3_x64_20G_alibase_20221102.vhd,实例规格为:ecs.g7.large,40 GiB ESSD云盘,挂载100 GiB ESSD云数据盘,公网出带宽为10 Mbit/s,自动分配私网IP和公网IP,实例名称为ECS-test,登录密码为ECS@test1234,数量为1台。\n\n```\nhttp(s)://ecs.aliyuncs.com/?Action=RunInstances\n&RegionId=cn-hangzhou\n&ImageId=aliyun_3_x64_20G_alibase_20221102.vhd\n&InstanceType=ecs.g7.large\n&SecurityGroupId=sg-bp150uqocpf9jj70****\n&VSwitchId=vsw-bp1qo7s91cbch5i4l****\n&InstanceChargeType=PostPaid\n&SystemDisk.Size=40\n&DataDisk.1.Size=100\n&DataDisk.1.Category=cloud_essd\n&SystemDisk.Category=cloud_essd\n&HostName=ECS-test\n&Password=ECS@test1234\n&InternetMaxBandwidthOut=10\n&公共请求参数\n```\n\n### 示例3:创建抢占式实例\n\n实例所在地域为华东1(杭州),计费方式为抢占式实例,竞价策略为系统自动出价,跟随当前市场实际价格,实例保留时长为1小时,镜像ID为:aliyun_3_x64_20G_alibase_20221102.vhd,实例规格为:ecs.g7.large,40 GiB ESSD云盘,挂载100 GiB ESSD云数据盘,公网出带宽为10 Mbit/s,自动分配私网IP和公网IP,实例名称为ECS-test,登录密码为ECS@test1234,数量为1台。\n\n```\nhttp(s)://ecs.aliyuncs.com/?Action=RunInstances\n&RegionId=cn-hangzhou\n&ImageId=aliyun_3_x64_20G_alibase_20221102.vhd\n&InstanceType=ecs.g7.large\n&SecurityGroupId=sg-bp150uqocpf9jj70****\n&VSwitchId=vsw-bp1qo7s91cbch5i4l****\n&InstanceChargeType=PostPaid\n&SystemDisk.Size=40\n&DataDisk.1.Size=100\n&DataDisk.1.Category=cloud_essd\n&SystemDisk.Category=cloud_essd\n&HostName=ECS-test\n&Password=ECS@test1234\n&InternetMaxBandwidthOut=10\n&SpotStrategy=SpotAsPriceGo\n&SpotDuration=1\n&公共请求参数\n```\n\n### 示例4:在专有宿主机上创建包年包月实例\n\n实例所在地域为华东1(杭州),专有宿主机为dh-bp12w10wll9xcjq2****,计费方式包年包月,购买时长一个月,镜像ID为:aliyun_3_x64_20G_alibase_20221102.vhd,实例规格为:ecs.g7.large,40 GiB ESSD云盘,挂载100 GiB ESSD云数据盘,公网出带宽为10 Mbit/s,自动分配私网IP和公网IP,实例名称为ECS-test,登录密码为ECS@test1234,购买数量为1台。\n\n```\nhttp(s)://ecs.aliyuncs.com/?Action=RunInstances\n&RegionId=cn-hangzhou\n&ImageId=aliyun_3_x64_20G_alibase_20221102.vhd\n&InstanceType=ecs.g7.large\n&SecurityGroupId=sg-bp150uqocpf9jj70****\n&VSwitchId=vsw-bp1qo7s91cbch5i4l****\n&InstanceChargeType=PrePaid\n&Amount=1\n&Period=1\n&PeriodUnit=Month\n&SystemDisk.Size=40\n&DataDisk.1.Size=100\n&DataDisk.1.Category=cloud_essd\n&SystemDisk.Category=cloud_essd\n&HostName=ECS-test\n&Password=ECS@test1234\n&InternetMaxBandwidthOut=10\n&DedicatedHostId=dh-bp12w10wll9xcjq2****\n&公共请求参数\n```", "ext": { + "errorCodes": { + "400": [ + { + "errorCode": "LoginAsNonRoot.ImageNotSupport", + "errorMessage": "The specified image does not support login as non-root." + }, + { + "errorCode": "InvalidParam.NotSupportJumboFrame", + "errorMessage": "Not support jumbo frame." + }, + { + "errorCode": "InsufficientBalance.AgentCredit", + "errorMessage": "Insufficient agent credit. Please contact your agent." + }, + { + "errorCode": "QuotaExceed.DiskCapacity", + "errorMessage": "The used capacity of disk type has exceeded the quota in the zone, %s." + }, + { + "errorCode": "InvalidPeriod.ExceededDedicatedHost", + "errorMessage": "Instance expired date can not exceed dedicated host expired date." + }, + { + "errorCode": "InvalidParam.SecondaryNetworkInterface", + "errorMessage": "When min amount greater than 1 and the PrivateIpAddress or Primary NetworkInterface is specified,the Secondary NetworkInterface IP parameter cannot be specified." + }, + { + "errorCode": "InvalidPrimaryIpAddress.SizeInvalid", + "errorMessage": "The NetworkInterface PrimaryIpAddress is used to create only one instance." + }, + { + "errorCode": "InvalidStorageClusterId.CapacityNotEnough", + "errorMessage": "The remaining capacity of the current dedicated storage cluster is less than the size of disk." + }, + { + "errorCode": "InvalidStorageClusterId.StatusNotSupport", + "errorMessage": "The current status of the dedicated storage cluster cannot create a cloud disk yet." + }, + { + "errorCode": "InvalidStorageClusterId.ZoneIdEmpty", + "errorMessage": "The specified param ZoneId cannot be empty when StorageClusterId given." + }, + { + "errorCode": "InvalidStorageClusterId.PerformanceLevelNotMatch", + "errorMessage": "The current dedicated storage cluster cannot create this performance level of disk." + }, + { + "errorCode": "InvalidStorageClusterId.CategoryNotMatch", + "errorMessage": "The current dedicated storage cluster cannot create this category of disk." + }, + { + "errorCode": "InvalidStorageClusterId.DiskSizeEmpty", + "errorMessage": "The specified param DiskSize cannot be empty when StorageClusterId given." + }, + { + "errorCode": "InvalidInstanceType.ValueUnauthorized", + "errorMessage": "The specified InstanceType is not authorized." + }, + { + "errorCode": "InvalidInstanceType.ValueNotSupported", + "errorMessage": "The specified InstanceType beyond the permitted range." + }, + { + "errorCode": "InvalidDescription.Malformed", + "errorMessage": "The specified parameter \"Description\" is not valid." + }, + { + "errorCode": "InvalidInternetChargeType.ValueNotSupported", + "errorMessage": "The specified InternetChargeType is not valid." + }, + { + "errorCode": "InvalidParameter", + "errorMessage": "The specified parameter \"InternetMaxBandwidthOut\" is not valid." + }, + { + "errorCode": "InvalidHostName.Malformed", + "errorMessage": "The specified parameter \"HostName\" is not valid." + }, + { + "errorCode": "InvalidPassword.Malformed", + "errorMessage": "The specified parameter \"Password\" is not valid." + }, + { + "errorCode": "InvalidPasswordParam.Mismatch", + "errorMessage": "The input password should be null when passwdInherit is true." + }, + { + "errorCode": "InvalidSystemDiskCategory.ValueNotSupported", + "errorMessage": "The specified parameter \"SystemDisk.Category\" is not valid." + }, + { + "errorCode": "InvalidDiskName.Malformed", + "errorMessage": "The specified parameter \"SyatemDisk.DiskName or DataDisk.n.DiskName\" is not valid." + }, + { + "errorCode": "InvalidDiskDescription.Malformed", + "errorMessage": "The specified parameter \"SyatemDisk.DiskDescription\" or \"DataDisk.n.Description\" is not valid." + }, + { + "errorCode": "InvalidDataDiskSize.ValueNotSupported", + "errorMessage": "The specified DataDisk.n.Size beyond the permitted range, or the capacity of snapshot exceeds the size limit of the specified disk category." + }, + { + "errorCode": "InvalidDataDiskCategory.ValueNotSupported", + "errorMessage": "The specified parameter \"DataDisk.n.Category\" is not valid." + }, + { + "errorCode": "InvalidDataDevice.Malformed", + "errorMessage": "The specified parameter \"DataDisk.n.Device\" is not valid." + }, + { + "errorCode": "InvalidNodeControllerId.Malformed", + "errorMessage": "The specified parameter \"NodeControllerId\" is not valid." + }, + { + "errorCode": "InvalidInnerIpAddress.Malformed", + "errorMessage": "The specified parameter \"InnerIpAddress\" is not valid." + }, + { + "errorCode": "InvalidInnerIpAddress.Unusable", + "errorMessage": "The specified InnerIpAddress is already used or not found in usable ip range." + }, + { + "errorCode": "InvalidParameter.Conflict", + "errorMessage": "The specified image does not support the specified instance type." + }, + { + "errorCode": "ImageNotSupportCloudInit", + "errorMessage": "The specified image does not support cloud-init." + }, + { + "errorCode": "InvalidSnapshotId.BasedSnapshotTooOld", + "errorMessage": "The specified snapshot is created before 2013-07-15." + }, + { + "errorCode": "QuotaExceed.AfterpayInstance", + "errorMessage": "Living afterpay instances quota exceeded." + }, + { + "errorCode": "InvalidInstanceName.Malformed", + "errorMessage": "The specified parameter \"InstanceName\" is not valid." + }, + { + "errorCode": "InvalidInstanceName.CustomMalformed", + "errorMessage": "Customized section of instance or host name is invalid, please use valid format: [], [,], [m,], [,n], [m,n]." + }, + { + "errorCode": "InvalidParameter.Mismatch", + "errorMessage": "Specified security group and virtual switch are not in the same VPC." + }, + { + "errorCode": "InvalidNetworkType.Mismatch", + "errorMessage": "Specified parameter \"InternetMaxBandwidthIn\" or \"InternetMaxBandwidthOut\" conflict with instance network type." + }, + { + "errorCode": "InvalidPrivateIpAddress", + "errorMessage": "Specified private IP address is not in the CIDR block of virtual switch." + }, + { + "errorCode": "InvalidPrivateIpAddress.Malformed", + "errorMessage": "Specified private IP address is malformed." + }, + { + "errorCode": "InvalidPrivateIpAddress.Duplicated", + "errorMessage": "Specified private IP address is duplicated." + }, + { + "errorCode": "QuotaExceeded.PrivateIpAddress", + "errorMessage": "Don't have enough private IPs in this switch." + }, + { + "errorCode": "QuotaExceeded", + "errorMessage": "Living instances quota exceeded in this VPC." + }, + { + "errorCode": "IncorrectVSwitchStatus", + "errorMessage": "The current status of virtual switch does not support this operation." + }, + { + "errorCode": "ResourceNotAvailable", + "errorMessage": "Resource you requested is not available in this region or zone." + }, + { + "errorCode": "MissingParameter", + "errorMessage": "The input parameter \"VSwitchId\" that is mandatory for processing this request is not supplied." + }, + { + "errorCode": "InvalidDiskCategory.Mismatch", + "errorMessage": "The specified disk categories' combination is not supported." + }, + { + "errorCode": "MissingParamter", + "errorMessage": "The specified parameter \"Period\" is not null." + }, + { + "errorCode": "InvalidPeriod", + "errorMessage": "The specified period is not valid." + }, + { + "errorCode": "InstanceDiskCategoryLimitExceed", + "errorMessage": "The specified DataDisk.n.Size beyond the permitted range, or the capacity of snapshot exceeds the size limit of the specified disk category." + }, + { + "errorCode": "InvalidClientToken.ValueNotSupported", + "errorMessage": "The ClientToken provided is invalid." + }, + { + "errorCode": "InvalidIoOptimize.ValueNotSupported", + "errorMessage": "The specified IoOptimize is not valid." + }, + { + "errorCode": "InvalidSecurityGroupId.NotFound", + "errorMessage": "The SecurityGroupId provided does not exist in our records." + }, + { + "errorCode": "InvalidInternetMaxBandwidthOut.Malformed", + "errorMessage": "The specified parameter internetMaxBandwidthOut is not valid." + }, + { + "errorCode": "InvalidInternetMaxBandwidthIn.Malformed", + "errorMessage": "The specified parameter internetMaxBandwidthIn is not valid." + }, + { + "errorCode": "InvalidSnapshotId.NotFound", + "errorMessage": "The specified parameter SnapshotId is not exist." + }, + { + "errorCode": "InvalidTagKey.Malformed", + "errorMessage": "The specified Tag.n.Key is not valid." + }, + { + "errorCode": "InvalidTagValue.Malformed", + "errorMessage": "The specified Tag.n.Value is not valid." + }, + { + "errorCode": "InvalidTag.Mismatch", + "errorMessage": "The specified Tag.n.Key and Tag.n.Value are not match." + }, + { + "errorCode": "InvalidTagCount", + "errorMessage": "The specified tags are beyond the permitted range." + }, + { + "errorCode": "InvalidMinAmount.Malformed", + "errorMessage": "The specified parameter MinAmount is not valid." + }, + { + "errorCode": "InvalidMaxAmount.Malformed", + "errorMessage": "The specified parameter MaxAmount is not valid." + }, + { + "errorCode": "InvalidAutoReleaseTime.Malformed", + "errorMessage": "The specified parameter AutoReleaseTime is not valid." + }, + { + "errorCode": "OperationDenied.NoVlan", + "errorMessage": "The specified parameter \"VlanId\" is not valid or vlan has not enough IP address." + }, + { + "errorCode": "OperationDenied.QuotaExceed", + "errorMessage": "The quota of tags on resource is beyond permitted range." + }, + { + "errorCode": "Account.Arrearage", + "errorMessage": "Your account has been in arrears." + }, + { + "errorCode": "InvalidUserData.SizeExceeded", + "errorMessage": "The specified parameter \"UserData\" exceeds the size." + }, + { + "errorCode": "InvalidUserData.NotSupported", + "errorMessage": "TThe specified parameter \"UserData\" only support the vpc and IoOptimized Instance." + }, + { + "errorCode": "InvalidUserData.Base64FormatInvalid", + "errorMessage": "The specified UserData is not valid." + }, + { + "errorCode": "InstanceDiskNumber.LimitExceed", + "errorMessage": "The total number of specified disk in an instance exceeds." + }, + { + "errorCode": "InvalidDiskCategory.ValueNotSupported", + "errorMessage": "The specified parameter \"DiskCategory\" is not valid." + }, + { + "errorCode": "InvalidSpotStrategy", + "errorMessage": "The specified SpotStrategy is not valid." + }, + { + "errorCode": "InvalidSpotParam.EmptyZoneID", + "errorMessage": "The specified zoneid is empty when SpotStrategy is set." + }, + { + "errorCode": "InvalidSpotPriceLimit", + "errorMessage": "The specified SpotPriceLimitis not valid." + }, + { + "errorCode": "InvalidSpotDuration", + "errorMessage": "The specified SpotDuration is not valid." + }, + { + "errorCode": "InvalidSpotAuthorized", + "errorMessage": "The specified Spot param is unauthorized." + }, + { + "errorCode": "InvalidSpotPrepaid", + "errorMessage": "The specified Spot type is not support PrePay Instance." + }, + { + "errorCode": "InvalidSpotAliUid", + "errorMessage": "The specified UID is not authorized to use SPOT instance." + }, + { + "errorCode": "InvalidParameter.Bandwidth", + "errorMessage": "%s" + }, + { + "errorCode": "RegionUnauthorized", + "errorMessage": "%s" + }, + { + "errorCode": "Zone.NotOnSale", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidSystemDiskSize.ValueNotSupported", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidInstanceType.ElasticNetworkInterfaceNotSupported", + "errorMessage": "The specified instance type does not support Elastic Network Interface, you can not attach Elastic Network Interface to generation I instances." + }, + { + "errorCode": "InvalidParameter.EncryptedIllegal", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidParameter.EncryptedNotSupported", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidParameter.Encrypted.KmsNotEnabled", + "errorMessage": "The encrypted disk need enable KMS." + }, + { + "errorCode": "InvalidParameter.KmsNotEnabled", + "errorMessage": "The specified operation need enable KMS." + }, + { + "errorCode": "InvalidSpotPriceLimit.LowerThanPublicPrice", + "errorMessage": "The specified parameter \"spotPriceLimit\" can't be lower than current public price." + }, + { + "errorCode": "InvalidRelationResource.NotFound", + "errorMessage": "The relation resource has been deleted." + }, + { + "errorCode": "IncorrectRecycleBinStatus", + "errorMessage": "The operation is not permitted due to resource status." + }, + { + "errorCode": "InvalidHpcClusterId.Unnecessary", + "errorMessage": "The specified HpcClusterId is unnecessary." + }, + { + "errorCode": "InvalidVSwitchId.Necessary", + "errorMessage": "The VSwitchId is necessary." + }, + { + "errorCode": "InvalidHpcClusterId.Necessary", + "errorMessage": "The HpcClusterId is necessary." + }, + { + "errorCode": "InvalidHpcClusterId.NotFound", + "errorMessage": "The specified HpcClusterId is not found." + }, + { + "errorCode": "InvalidHpcClusterId.Creating", + "errorMessage": "The specified HpcClusterId is creating." + }, + { + "errorCode": "InvalidParameter.VSwitchId", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidSecurityGroup.NotInDefaultVpc", + "errorMessage": "%s" + }, + { + "errorCode": "VpcNotFound", + "errorMessage": "Vpc is not found according to the specified VSwitch or the vpc does not belong to you." + }, + { + "errorCode": "InvalidSystemDiskSize.ImageNotSupportResize", + "errorMessage": "The specified image does not support resize." + }, + { + "errorCode": "InvalidSpotInterruptionBehavior", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidDeploymentOnHost", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidInstanceChargeType.NotSupport", + "errorMessage": "The Dedicated host not support the specified Instance charge type." + }, + { + "errorCode": "InvalidNetworkType.NotSupported", + "errorMessage": "The classic networkType not support create ECS on dedicatedHost." + }, + { + "errorCode": "NoAvaliableDedicatedHost", + "errorMessage": "No available dedicated host or not enough resource on dedicated host." + }, + { + "errorCode": "InvalidDedicatedHostId.NotFound", + "errorMessage": "The specified DedicatedHostId does not exist." + }, + { + "errorCode": "InvalidDedicatedHostStatus.NotSupport", + "errorMessage": "Operation denied due to dedicated host status." + }, + { + "errorCode": "IncorrectDedicatedHostStatus", + "errorMessage": "The current status of the resource does not support this operation." + }, + { + "errorCode": "ChargeTypeViolation.PostPaidDedicatedHost", + "errorMessage": "Prepaid instance onto postpaid dedicated host is not allowed." + }, + { + "errorCode": "DedicatedHostType.Unmatched", + "errorMessage": "The specified DedicatedHostType doesn?t match the instance type." + }, + { + "errorCode": "InvalidParam.NetworkInterface", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidParams.CreateEniParams", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidParameter.CreditSpecification", + "errorMessage": "The specified CreditSpecification is not supported in this region." + }, + { + "errorCode": "IncorrectVpcStatus", + "errorMessage": "Current VPC status does not support this operation." + }, + { + "errorCode": "InvalidInstanceType.NotSupported", + "errorMessage": "The specified instanceType is not supported by the deployment set." + }, + { + "errorCode": "InvalidVpcZone.NotSupported", + "errorMessage": "The specified operation is not allowed in the zone to which your VPC belongs, please try in other zones." + }, + { + "errorCode": "IncorrectDefaultVpcStatus", + "errorMessage": "The status of the default VPC is invalid." + }, + { + "errorCode": "InvalidAutoRenewPeriod.ValueNotSupported", + "errorMessage": "The specified autoRenewPeriod is invalid." + }, + { + "errorCode": "InvalidMarketImageChargeType.NotSupport", + "errorMessage": "The specified chargeType of marketImage is unsupported." + }, + { + "errorCode": "OperationDenied", + "errorMessage": "The specified instanceType or zone is not available or not authorized." + }, + { + "errorCode": "InvalidPeriodType.ValueNotSupported", + "errorMessage": "The specified parameter PeriodType is invalid." + }, + { + "errorCode": "IncorrectImageStatus", + "errorMessage": "The specified image is an Alibaba Cloud Marketplace image. The sale of this image has ended. For more information, contact the image service provider." + }, + { + "errorCode": "InvalidParam.Tenancy", + "errorMessage": "The specified Tenancy is invalid." + }, + { + "errorCode": "InvalidParameter.Affinity", + "errorMessage": "The specified Affinity is invalid." + }, + { + "errorCode": "InvalidCustomInstanceType.NotSupported", + "errorMessage": "The specified custom instance type is invalid." + }, + { + "errorCode": "IoOptimized.NotSupported", + "errorMessage": "The specified instance must be IoOptimized instance when kmsKeyId is not empty." + }, + { + "errorCode": "InvalidSnapshotId.Malformed", + "errorMessage": "The specified SnapshotId is not valid." + }, + { + "errorCode": "InvalidCapacityReservationId.NotFound", + "errorMessage": "The specified CapacityReservationId does not exist." + }, + { + "errorCode": "LackResource", + "errorMessage": "There's no enough resource on the specified capacity reservation." + }, + { + "errorCode": "Duplicate.TagKey", + "errorMessage": "The Tag.N.Key contain duplicate key." + }, + { + "errorCode": "InvalidSecurityGroup.NetworkType", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidSecurityGroup.VpcMismatch", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidParameter.SecurityGroupIdRepeated", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidSecurityGroupId.SingleIdAndMultiIdConflict", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidSecurityGroupId.MultiGroupIdNetworkTypeConflict", + "errorMessage": "%s" + }, + { + "errorCode": "JoinedGroupLimitExceed", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidAccountStatus.PayAmountLimitExceeded", + "errorMessage": "Your account is being restricted, because you have no default payment method or you are not authorized." + }, + { + "errorCode": "InvalidPerformanceLevel.Malformed", + "errorMessage": "The specified parameter DataDisk.n.PerformanceLevel is not valid." + }, + { + "errorCode": "InvalidOperation.EniCountExceeded", + "errorMessage": "The maximum number of eni in a enterprise security group is exceeded." + }, + { + "errorCode": "QuotaExceeded.PrepayDataDiskCapacity", + "errorMessage": "The quota of prepay data disk capacity exceeds." + }, + { + "errorCode": "InvalidDiskCategory.ConflictSnapshotCategory", + "errorMessage": "The specified disk category conflict with snapshot category." + }, + { + "errorCode": "AccountForbidden.ProductCreationLimited", + "errorMessage": "The commodity must be officially operated by Aliyun and in pay-as-you-go billing method." + }, + { + "errorCode": "UnexpectedImageFamily.ImageIdSupplied", + "errorMessage": "The input parameter ImageFamily must be null when image id is set." + }, + { + "errorCode": "InvalidEncrypted.NotMatchEncryptAlgorithm", + "errorMessage": "The specified parameter Encrypted must be true when EncryptAlgorithm is not empty." + }, + { + "errorCode": "InvalidEncrypted.NotMatchKmsKeyId", + "errorMessage": "The specified parameter Encrypted must be true when KmsKeyId is not empty." + }, + { + "errorCode": "InvalidEncrypted.NotMatchSnapshot", + "errorMessage": "The specified parameter Encrypted is different from the Encrypted of the snapshot." + }, + { + "errorCode": "InvalidEncryptAlgorithm.NotMatchSnapshot", + "errorMessage": "The specified parameter EncryptAlgorithm is different from the encrypt algorithm of the snapshot." + }, + { + "errorCode": "InvalidKmsKeyId.NotMatchSnapshot", + "errorMessage": "The specified parameter KmsKeyId is different from the KmsKeyId of the snapshot." + }, + { + "errorCode": "InvalidEncryptAlgorithm", + "errorMessage": "The specified parameter EncryptAlgorithm is not valid." + }, + { + "errorCode": "InvalidHttpEndpoint.NotSupported", + "errorMessage": "The specified HttpEndpoint not supported, you can use enabled(default) or disabled." + }, + { + "errorCode": "InvalidHttpTokens.NotSupported", + "errorMessage": "The specified HttpTokens not supported, you can use optional(default) or required." + }, + { + "errorCode": "InvalidHttpPutResponseHopLimit.NotSupported", + "errorMessage": "The specified HttpPutResponseHopLimit not supported, more than 1 and less than 64 is reasonable." + }, + { + "errorCode": "InvalidOperation.VpcHasEnabledAdvancedNetworkFeature", + "errorMessage": "The specified vpc has enabled advanced network feature." + }, + { + "errorCode": "InvalidChargeType.CapacityReservationNotSupported", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidPerformanceLevel.ValueNotSupported", + "errorMessage": "The current ZoneId or InstanceType does not support PL0 of cloud_essd." + }, + { + "errorCode": "InvalidKMSKeyId.NotSymmetric", + "errorMessage": "The specified parameter KmsKeyId must be symmetric." + }, + { + "errorCode": "InvalidParameter.Arns", + "errorMessage": "The specified Arns is not valid." + }, + { + "errorCode": "InvalidDedicatedHostClusterId.NotFound", + "errorMessage": "The specified DedicatedHostClusterId does not exist." + }, + { + "errorCode": "InvalidDedicatedHostClusterId.InValid", + "errorMessage": "The specified Dedicated Host Cluster is invalid." + }, + { + "errorCode": "InvalidDeploymentSetId.NotFound", + "errorMessage": "The parameter DeploymentSetId is invalid." + }, + { + "errorCode": "InvalidOperation.UserNotSupported", + "errorMessage": "Reseller user do not support purchase at the moment." + }, + { + "errorCode": "InvalidManagedPrivateSpaceId.NotFound", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidSchedulerOptions", + "errorMessage": "The specified parameter SchedulerOptions is not valid." + }, + { + "errorCode": "MissingParameter.PrivatePoolOptionsId", + "errorMessage": "The specified PrivatePoolOptions.Id should not be null." + }, + { + "errorCode": "Invalid.PrivatePoolOptionsId", + "errorMessage": "The specified PrivatePoolOptions.Id is invalid." + }, + { + "errorCode": "DedicatedHostNotSupported", + "errorMessage": "DedicatedHost is not supported for PrivatePool." + }, + { + "errorCode": "SpotNotSupported", + "errorMessage": "Spot is not supported for PrivatePool." + }, + { + "errorCode": "ClassicNetworkNotSupported", + "errorMessage": "Classic network is not supported for PrivatePool." + }, + { + "errorCode": "Invalid.InstanceId", + "errorMessage": "Instance does not exist." + }, + { + "errorCode": "Invalid.PrivatePoolOptions.MatchCriteria", + "errorMessage": "Target mode does not support this operation." + }, + { + "errorCode": "MissingParameter.PrivatePoolOptions.Id", + "errorMessage": "The specified PrivatePoolOptions.Id should not be null." + }, + { + "errorCode": "Invalid.PrivatePoolOptions.Id", + "errorMessage": "The PrivatePool does not exist." + }, + { + "errorCode": "Invalid.InstanceType", + "errorMessage": "The InstanceType does not match the PrivatePool." + }, + { + "errorCode": "Invalid.InstanceChargeType", + "errorMessage": "The InstanceChargeType does not match the PrivatePool." + }, + { + "errorCode": "Invalid.ZoneId", + "errorMessage": "The ZoneId does not match the PrivatePool." + }, + { + "errorCode": "Invalid.PrivatePoolOptions.NoStock", + "errorMessage": "The PrivatePool has been used up." + }, + { + "errorCode": "InvalidPlatform.ValueNotSupported", + "errorMessage": "The Platform does not match the PrivatePool." + }, + { + "errorCode": "Invalid.PrivatePoolOptions.status", + "errorMessage": "The PrivatePool is expired or inactive." + }, + { + "errorCode": "InvalidAliUid", + "errorMessage": "The PrivatePool does not belong to the user of the Instance." + }, + { + "errorCode": "InvalidBandwidthOut.LessThanZero", + "errorMessage": "The bandwidth must be larger than 0 when specifying isp." + }, + { + "errorCode": "HibernationConfigured.InstanceTypeNotSupport", + "errorMessage": "The specified instance type is not support." + }, + { + "errorCode": "HibernationConfigured.ImageNotEncrypted", + "errorMessage": "The hibernation configured instance only support encrypted image." + }, + { + "errorCode": "HibernationConfigured.MemorySizeTooBig", + "errorMessage": "The hibernation configured instance memory size is too big." + }, + { + "errorCode": "InvalidSystemDiskSize.LessThanMemSize", + "errorMessage": "The specified parameter SystemDisk.Size is less than the memory size." + }, + { + "errorCode": "InvalidCloudBoxZone.OperationNotSupported", + "errorMessage": "The cloud box zone does not support creating prepaid or encrypted resources." + }, + { + "errorCode": "ProvisionedIopsForDiskCategoryUnsupported", + "errorMessage": "The specified disk category does not support provisioned iops." + }, + { + "errorCode": "InvalidProvisionedIops.LimitExceed", + "errorMessage": "The provisioned iops exceeds the limit." + }, + { + "errorCode": "BurstingEnabledForDiskCategoryUnsupported", + "errorMessage": "The specified disk category does not support bursting enabled." + }, + { + "errorCode": "BurstingEnabledForMultiAttachDiskUnsupported", + "errorMessage": "The multi attach disk does not support bursting enabled." + }, + { + "errorCode": "ProvisionedIopsForDiskCategoryRequired", + "errorMessage": "The provisioned iops is required for this disk category." + }, + { + "errorCode": "NotSupportSnapshotEncrypted.InstanceType", + "errorMessage": "The specified instance type does not support creating encrypted disks with native snapshot encrypt." + }, + { + "errorCode": "NotSupportSnapshotEncrypted.RegionId", + "errorMessage": "The specified region does not support creating encrypted disks with native snapshot encrypt." + }, + { + "errorCode": "NotSupportSnapshotEncrypted.ZoneId", + "errorMessage": "The specified zone does not support creating encrypted disks with native snapshot encrypt." + }, + { + "errorCode": "NotSupportSnapshotEncrypted.ShareImage", + "errorMessage": "Shared snapshot creating encrypted disks with native snapshot encrypt is not supported." + }, + { + "errorCode": "NotSupportSnapshotEncrypted.ImageOwnerAlias", + "errorMessage": "The specified image category does not support creating encrypted disks with native snapshot encrypt." + }, + { + "errorCode": "NotSupportSnapshotEncrypted.DiskCategory", + "errorMessage": "The specified disk category does not support creating encrypted disks with native snapshot encrypt." + }, + { + "errorCode": "NotSupport.SnapshotEncryptedAlgorithmConflict", + "errorMessage": "Changing encrypt algorithm with encrypted snapshot is not supported." + }, + { + "errorCode": "NoPermission.SystemTag", + "errorMessage": "The operator is not permission for the system tag." + }, + { + "errorCode": "NumberExceed.Tags", + "errorMessage": "The Tags parameter's number is exceed , Valid : 20." + }, + { + "errorCode": "InvalidZoneId.NotSupportShareEncryptedImage", + "errorMessage": "Creating instances by shared encrypted images is not supported in this zone." + }, + { + "errorCode": "InvalidDiskCategory.NotSupported", + "errorMessage": "The specified disk category is not supported." + }, + { + "errorCode": "InvalidParameter.KMSKeyId.CMKUnauthorized", + "errorMessage": "This operation for kmsKeyId is forbidden by KMS. If you need further assistance, you can contact the KMS Technical Support." + }, + { + "errorCode": "InvalidParameter.CloudboxNotSupported", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidZoneId.NotSupportCreateWithShareEncryptedImage", + "errorMessage": "You cannot use shared encrypted images to create instances in this zone." + }, + { + "errorCode": "InvalidParameter.NetworkCardIndexInvalid", + "errorMessage": "Invalid network card index, please check your instance type." + }, + { + "errorCode": "InvalidOperation.UserNotSupportNetworkCard", + "errorMessage": "User not support network card." + }, + { + "errorCode": "InvalidAccount.NotSupportSpot", + "errorMessage": "According to business rules, this account cannot purchase ECS Spot instances." + }, + { + "errorCode": "AccountForbidden.AssociatedWithResellerPartner", + "errorMessage": "Your account is associated with your reseller partner. Your account or your reseller partner's account has been forbidden to create orders, please contact your reseller partner." + }, + { + "errorCode": "InvalidDestinationZone.DeploymentSetMismatch", + "errorMessage": "Error happened, %s." + }, + { + "errorCode": "NoPermission.Price", + "errorMessage": "The operation requires price permission. Please either apply for permission from your main account, or set the parameter AutoPay as true." + }, + { + "errorCode": "InvalidAutoPay.PostPaidUnsupported", + "errorMessage": "The specified parameter AutoPay must be set as true for postpaid instance." + }, + { + "errorCode": "InvalidParam.EncryptedMismatch", + "errorMessage": "Creating encrypted disks with shared encrypted snapshots requires replacing encryption keys." + }, + { + "errorCode": "InvalidParameter.DedicatedRegionNotSupported", + "errorMessage": "The specified action is rejected because the specified ECS instance in the dedicated region does not support public IP." + }, + { + "errorCode": "InvalidParameter.CpuOptionsTopologyType", + "errorMessage": "The specified parameter CpuOptions.TopologyType: %s is not valid." + }, + { + "errorCode": "InvalidInstanceType.NotSupportCpuOptionsTopologyType", + "errorMessage": "The specified instance type does not support CpuOptions.TopologyType: %s." + } + ], + "401": [ + { + "errorCode": "InvalidRamRole.NotEcsRole", + "errorMessage": "The specified ram role is not authorized for ecs, please check your role policy." + } + ], + "403": [ + { + "errorCode": "InvalidParameter.PrivateIpAddressRepeated", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidOperation.HighPerformanceEniPerInstanceLimitExceeded", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidParameter.EniNumExceededWithLaunchEcs", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidOperation.InstanceTypeNotSupportHighPerformanceTrafficMode", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidParameter.QueuePairNumberMustEmpty", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidParameter.EniTrafficMode", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidParameter.InvalidQueuePairNumber", + "errorMessage": "The parameter of QueuePairNumber is invalid." + }, + { + "errorCode": "InvalidParam.IpCount", + "errorMessage": "The parameter of ip count is invalid." + }, + { + "errorCode": "InvalidParameter.EniType", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidOperation.HighPerformanceTrafficModeIsNotAllowed", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidParameter.NetworkInterface", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidParameter.Combination", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidHostname.SingleAndMultiConflict", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidHostname.SizeInvalid", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidParams.InstanceNameExceed", + "errorMessage": "The uniqueSuffix takes three naming places, please shorten your InstanceName." + }, + { + "errorCode": "InvalidParams.HostnameExceed", + "errorMessage": "The uniqueSuffix takes three naming places, please shorten your Hostname." + }, + { + "errorCode": "ImageNotSubscribed", + "errorMessage": "The specified image has not be subscribed." + }, + { + "errorCode": "InvalidSystemDiskCategory.ValueUnauthorized", + "errorMessage": "The disk category is not authorized." + }, + { + "errorCode": "InvalidSnapshotId.NotReady", + "errorMessage": "The specified snapshot has not completed yet." + }, + { + "errorCode": "InstanceDiskCategoryLimitExceed", + "errorMessage": "The total size of specified disk category in an instance exceeds." + }, + { + "errorCode": "InvalidDevice.InUse", + "errorMessage": "The specified device has been occupied." + }, + { + "errorCode": "ImageRemovedInMarket", + "errorMessage": "The specified market image is not available, Or the specified user defined image includes product code because it is based on an image subscribed from marketplace, and that image in marketplace includeing exact the same product code has been removed." + }, + { + "errorCode": "CategoryNotSupported", + "errorMessage": "The specified zone does not offer the specified disk category." + }, + { + "errorCode": "QuotaExceed.PortableCloudDisk", + "errorMessage": "The quota of portable cloud disk exceeds." + }, + { + "errorCode": "SecurityGroupInstanceLimitExceed", + "errorMessage": "Exceeding the allowed amount of instances of a security group." + }, + { + "errorCode": "NodeControllerUnavailable", + "errorMessage": "The Node Controller is temporarily unavailable." + }, + { + "errorCode": "RegionUnauthorized", + "errorMessage": "There is no authority to create instance in the specified region." + }, + { + "errorCode": "InvalidSnapshotId.NotDataDiskSnapshot", + "errorMessage": "The specified snapshot is system disk snapshot." + }, + { + "errorCode": "Forbbiden", + "errorMessage": "User not authorized to operate on the specified resource." + }, + { + "errorCode": "DeleteWithInstance.Conflict", + "errorMessage": "The specified disk is not a portable disk and cannot be set to DeleteWithInstance attribute." + }, + { + "errorCode": "InstanceDiskNumLimitExceed", + "errorMessage": "The number of specified disk in an instance exceeds." + }, + { + "errorCode": "IoOptimized.NotSupported", + "errorMessage": "The specified image is not support IoOptimized Instance." + }, + { + "errorCode": "InvalidDiskSize.TooSmall", + "errorMessage": "Specified disk size is less than the size of snapshot." + }, + { + "errorCode": "InvalidDiskCategory.Mismatch", + "errorMessage": "The specified disk categories combination is not supported." + }, + { + "errorCode": "InvalidDiskCategory.NotSupported", + "errorMessage": "The specified disk category is not support the specified instance type." + }, + { + "errorCode": "QuotaExceed.BuyImage", + "errorMessage": "The specified image is from the image market?You have not bought it or your quota has been exceeded." + }, + { + "errorCode": "InvalidResourceId.NotSupported", + "errorMessage": "The specified ResourceId does not support tagging." + }, + { + "errorCode": "OperationDenied", + "errorMessage": "The specified RegionId does not support the creation of the network type ECS instance." + }, + { + "errorCode": "OperationDenied.ImageNotValid", + "errorMessage": "The specified Image is disabled or is deleted." + }, + { + "errorCode": "OperationDenied.SnapshotNotValid", + "errorMessage": "The specified snapshot is not allowed to create disk." + }, + { + "errorCode": "OperationDenied.SnapshotNotAllowed", + "errorMessage": "The specified snapshot is not allowed to create disk." + }, + { + "errorCode": "OperationDenied.ZoneNotAllowed", + "errorMessage": "The creation of Instance to the specified Zone is not allowed." + }, + { + "errorCode": "OperationDenied.ZoneSystemCategoryNotMatch", + "errorMessage": "The specified Zone or cluster does not offer the specified disk category or the speicified zone and cluster do not match." + }, + { + "errorCode": "OperationDenied.ResourceControl", + "errorMessage": "The specified region is in resource control, please try later." + }, + { + "errorCode": "OperationDenied.NoStock", + "errorMessage": "The resource is out of usage." + }, + { + "errorCode": "OperationDenied.SnapshotParamsNotValid", + "errorMessage": "The capacity of snapshot exceeds the size limit of the specified disk category or the specified category is not authorizied." + }, + { + "errorCode": "OperationDenied.DiskTypeNotSupport", + "errorMessage": "The type of the disk does not support the operation." + }, + { + "errorCode": "InvalidUserData.Forbidden", + "errorMessage": "User not authorized to input the parameter \"UserData\", please apply for permission \"UserData\"." + }, + { + "errorCode": "Zone.NotOpen", + "errorMessage": "The specified zone is not granted to you to buy resources yet." + }, + { + "errorCode": "Zone.NotOnSale", + "errorMessage": "The resource in the specified zone is no longer available for sale. Please try other regions and zones." + }, + { + "errorCode": "InvalidClusterId.NotFound", + "errorMessage": "The specified clusterId does not exist." + }, + { + "errorCode": "InvalidResourceType.NotSupported", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidInstanceType.ValueNotSupported", + "errorMessage": "The specified InstanceType beyond the permitted range." + }, + { + "errorCode": "InvalidInstanceType.ZoneNotSupported", + "errorMessage": "The specified zone does not support this instancetype." + }, + { + "errorCode": "InstanceType.Offline", + "errorMessage": "%s" + }, + { + "errorCode": "DependencyViolation.WindowsInstance", + "errorMessage": "The instance creating is window, cannot use ssh key pair to login." + }, + { + "errorCode": "InvalidNetworkType.MismatchRamRole", + "errorMessage": "A RAM role can?t be used for classic instance." + }, + { + "errorCode": "InvalidUser.PassRoleForbidden", + "errorMessage": "The RAM user does not have the privilege to pass a RAM role." + }, + { + "errorCode": "InvalidParam.TrustedSystemMode", + "errorMessage": "The specified TrustedSystemMode is invalid." + }, + { + "errorCode": "InvalidParam.ConfidentialComputingMode", + "errorMessage": "The specified ConfidentialComputingMode is invalid." + }, + { + "errorCode": "InvalidInstanceType.NotSupported", + "errorMessage": "The specified instance type does not support trusted system." + }, + { + "errorCode": "InvalidSecurityOptions.NotSupported", + "errorMessage": "SecurityOptions for vTPM and Enclave can not both be set." + }, + { + "errorCode": "InvalidImage.NotSupported", + "errorMessage": "The specified vTPM instance need UEFI image." + }, + { + "errorCode": "Forbidden.RiskControl", + "errorMessage": "This operation is forbidden by Aliyun RiskControl system." + }, + { + "errorCode": "InvalidInstance.UnPaidOrder", + "errorMessage": "The specified Instance has unpaid order." + }, + { + "errorCode": "Account.Arrearage", + "errorMessage": "Your account has been in arrears." + }, + { + "errorCode": "RealNameAuthenticationError", + "errorMessage": "Your account has not passed the real-name authentication yet." + }, + { + "errorCode": "InvalidPayMethod", + "errorMessage": "The specified pay method is not valid." + }, + { + "errorCode": "InvalidAccountStatus.NotEnoughBalance", + "errorMessage": "Your account does not have enough balance." + }, + { + "errorCode": "ImageNotSupportInstanceType", + "errorMessage": "The specified image does not support the specified InstanceType." + }, + { + "errorCode": "DryRun.InvalidAmount", + "errorMessage": "%s" + }, + { + "errorCode": "OperationDenied.InvalidNetworkType", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidSpotInterruptionBehavior.ClassicNetworkNotSupport", + "errorMessage": "The specified SpotInterruptionBehavior does not support Classic network Instance." + }, + { + "errorCode": "InvalidSpotInterruptionBehavior.LocalDiskNotSupport", + "errorMessage": "The specified SpotInterruptionBehavior does not support local disk instance." + }, + { + "errorCode": "QuotaExceed.PostPaidDisk", + "errorMessage": "Living postPaid disks quota exceeded." + }, + { + "errorCode": "InvalidParameter.NotMatch", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidVSwitch.DefaultVSwitchNotSupport", + "errorMessage": "The specified zone in vpc can't support create default vSwitch." + }, + { + "errorCode": "OperationDenied.LocalDiskUnsupported", + "errorMessage": "The configuration change is not allowed when the specified instance has local disks mounted." + }, + { + "errorCode": "OperationDenied.InconsistentNetwork", + "errorMessage": "The specified security group and vswitch are not in the same vpc." + }, + { + "errorCode": "DefaultVswitch.Existed", + "errorMessage": "The default vswitch for VPC already exists." + }, + { + "errorCode": "IncorrectInstanceStatus", + "errorMessage": "The current status of the resource does not support this operation." + }, + { + "errorCode": "CategoryViolation", + "errorMessage": "The specified instance does not support this operation because of its disk category." + }, + { + "errorCode": "ResourcesNotInSameZone", + "errorMessage": "The specified instance and dedicated host are not in the same zone." + }, + { + "errorCode": "InvalidDisk.SystemDiskSize", + "errorMessage": "The specified SystemDiskSize beyond the permitted range." + }, + { + "errorCode": "InsufficientBalance", + "errorMessage": "Your account does not have enough balance." + }, + { + "errorCode": "InvalidOperation.NetworkInterface", + "errorMessage": "%s" + }, + { + "errorCode": "MaxEniIpv6IpsCountExceeded", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidIp.IpRepeated", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidIp.IpAssigned", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidIp.Address", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidOperation.EniCountExceeded", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidOperation.Ipv4CountExceeded", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidOperation.Ipv6CountExceeded", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidOperation.Ipv6NotSupport", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidOperation.Ipv4NotSupport", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidParam.SecondaryIp", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidVSwitch.Ipv6NotTurnOn", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidParam.IpAssign", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidParam.Amount", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidParam.CpuOptionsCore", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidParam.CpuOptionsNuma", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidVSwitchId.IpInvalid", + "errorMessage": "%s" + }, + { + "errorCode": "Forbidden.RegionId", + "errorMessage": "%s" + }, + { + "errorCode": "QuotaExceed.DeploymentSetInstanceQuotaFull", + "errorMessage": "The instance quota in one deployment set exceeded." + }, + { + "errorCode": "InvalidChargeType.ValueNotSupported", + "errorMessage": "Deletion protection is only valid for postPaid instance, not for prePaid or spot instance." + }, + { + "errorCode": "InvalidRegion.NotSupport", + "errorMessage": "The specified region does not support byok." + }, + { + "errorCode": "UserNotInTheWhiteList", + "errorMessage": "The user is not in byok white list." + }, + { + "errorCode": "InvalidParameter.KMSKeyId.CMKNotEnabled", + "errorMessage": "The CMK needs to be enabled." + }, + { + "errorCode": "InvalidParameter.KMSKeyId.KMSUnauthorized", + "errorMessage": "ECS service have no right to access your KMS." + }, + { + "errorCode": "SecurityRisk.3DVerification", + "errorMessage": "We have detected a security risk with your default credit or debit card. Please proceed with verification via the link in your email." + }, + { + "errorCode": "Mayi.InternalError", + "errorMessage": "The request processing has failed due to some unknown error." + }, + { + "errorCode": "InvalidNetworkType", + "errorMessage": "The network type is not support in this region." + }, + { + "errorCode": "InvalidAccountStatus", + "errorMessage": "Your account status is invalid, please contact customer service." + }, + { + "errorCode": "QuotaExceeded.PrivateIpAddress", + "errorMessage": "%s" + }, + { + "errorCode": "OperationDenied.PerformanceLevelNotMatch", + "errorMessage": "The specified DataDisk.n.PerformanceLevel and DataDisk.n.Size do not match." + }, + { + "errorCode": "InvalidStorageSetName.Malformed", + "errorMessage": "Specified parameter StorageSetName is not valid." + }, + { + "errorCode": "InvalidDescription.Malformed", + "errorMessage": "Specified parameter Description is not valid." + }, + { + "errorCode": "InvalidMaxPartitionNumber.Malformed", + "errorMessage": "Specified parameter MaxPartitionNumber is not valid." + }, + { + "errorCode": "InvalidParameter.StorageSetPartitionNumber", + "errorMessage": "Specified parameter StorageSetPartitionNumber is not valid." + }, + { + "errorCode": "InvalidParameter.StorageSetId", + "errorMessage": "Specified parameter StorageSetId is not valid." + }, + { + "errorCode": "InvalidParameter.StorageSetZoneId", + "errorMessage": "Specified parameter StorageSetZoneId is not valid." + }, + { + "errorCode": "EnterpriseGroupLimited.MutliGroupType", + "errorMessage": "The specified instance can't join multi SecurityGroup types." + }, + { + "errorCode": "EnterpriseGroupLimited.InstanceType", + "errorMessage": "The specified instance type doesn't support Enterprise SecurityGroup." + }, + { + "errorCode": "QuotaExceed.Tags", + "errorMessage": "%s" + }, + { + "errorCode": "OperationDenied.RegionIdNotSupported", + "errorMessage": "The specified region does not support spot duration instance." + }, + { + "errorCode": "OperationDenied.FlavorNotSupported", + "errorMessage": "Flavor not support spot duration instance." + }, + { + "errorCode": "OperationDenied.TimestampNotSupported", + "errorMessage": "Timestamp not support spot duration instance." + }, + { + "errorCode": "OperationDenied.PaygNotAvailable", + "errorMessage": "Pay-as-you-go instance is not available now." + }, + { + "errorCode": "OperationDenied.PrepayNotAvailable", + "errorMessage": "Prepay instance is not available now." + }, + { + "errorCode": "OperationDenied.BidOwnResource", + "errorMessage": "Bid user can not own resource." + }, + { + "errorCode": "OperationDenied.CloudSSDNotSupported", + "errorMessage": "The specified available zone does not offer the cloud_ssd disk, use cloud_essd instead." + }, + { + "errorCode": "QuotaExceed.ElasticQuota", + "errorMessage": "No additional quota is available for the specified ECS instance type." + }, + { + "errorCode": "QuotaExceeded.PostpaidDataDiskCapacity", + "errorMessage": "The quota of postpaid data disk capacity exceeds." + }, + { + "errorCode": "InvalidImageFamily.MissingAvailableImage", + "errorMessage": "There is no available image related to the specified image family." + }, + { + "errorCode": "InvalidRegionId.NotSupportEncryptAlgorithm", + "errorMessage": "The current region does not support creating encrypted disks with EncryptAlgorithm." + }, + { + "errorCode": "InvalidOperation.ResourceManagedByCloudProduct", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidManagedPrivateSpaceId.DedicatedHostIdConflict", + "errorMessage": "ManagedPrivateSpaceId and DedicatedHostId cannot be specified at the same time." + }, + { + "errorCode": "InvalidManagedPrivateSpaceId.TenancyConflict", + "errorMessage": "ManagedPrivateSpaceId and Tenancy cannot be specified at the same time." + }, + { + "errorCode": "InvalidParameter.InvalidEniQueueNumber", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidOperation.MaxEniQueueNumberExceeded", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidOperation.ExceedInstanceTypeQueueNumber", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidIspType.ValueNotSupported", + "errorMessage": "%s" + }, + { + "errorCode": "UnsupportedIspChargeType", + "errorMessage": "%s" + }, + { + "errorCode": "UnsupportedIspClassicNetwork", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidIspBandwidthOut", + "errorMessage": "%s" + }, + { + "errorCode": "UnsupportedIspNetworkChargeType", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidIspUID", + "errorMessage": "%s" + }, + { + "errorCode": "UnsupportedIspRegion", + "errorMessage": "%s" + }, + { + "errorCode": "Forbidden.OnlySupportEnterpriseGroup", + "errorMessage": "%s" + }, + { + "errorCode": "HibernationConfigured.InstanceOperationForbidden", + "errorMessage": "The operation is not permitted due to limit of the hibernation configured instance." + }, + { + "errorCode": "InstanceDiskLimitExceeded", + "errorMessage": "The amount of the disk on instance reach its limits." + }, + { + "errorCode": "InvalidInstanceType.NotSupportDiskCategory", + "errorMessage": "The instanceType of the specified instance does not support this disk category." + }, + { + "errorCode": "InvalidOperation.ConfidentialComputingModeInInviteOnlyTesting", + "errorMessage": "The specified confidential computing mode is in invite only testing: %s." + }, + { + "errorCode": "InvalidOperation.InvalidNetworkInterfaceId", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidOperation.RegionNotSupportNetworkInterfaceId", + "errorMessage": "The specific region does not support network interface id." + }, + { + "errorCode": "InvalidEniId.NotFound", + "errorMessage": "%s" + } + ], + "404": [ + { + "errorCode": "InvalidStorageClusterId.NotExist", + "errorMessage": "The specified StorageClusterId does not exist in current region." + }, + { + "errorCode": "InvalidRegionId.NotFound", + "errorMessage": "The RegionId provided does not exist in our records." + }, + { + "errorCode": "InvalidZoneId.NotFound", + "errorMessage": "The ZoneId provided does not exist in our records." + }, + { + "errorCode": "InvalidSecurityGroupId", + "errorMessage": "The specified SecurityGroupId is invalid or does not exist." + }, + { + "errorCode": "InvalidSecurityGroupId.NotFound", + "errorMessage": "The SecurityGroupId provided does not exist in our records." + }, + { + "errorCode": "InvalidDataDiskSnapshotId.NotFound", + "errorMessage": "The specified parameter \"DataDisk.n.SnapshotId\" is not valid." + }, + { + "errorCode": "InvalidClusterId.NotFound", + "errorMessage": "The ClusterId provided does not exist in our records." + }, + { + "errorCode": "InvalidVSwitchId.NotFound", + "errorMessage": "Specified virtual switch does not exist." + }, + { + "errorCode": "InvalidImageId.NotFound", + "errorMessage": "The specified ImageId does not exist." + }, + { + "errorCode": "IoOptimized.NotSupported", + "errorMessage": "The specified instancetype is not support IoOptimized instance." + }, + { + "errorCode": "InvalidInstanceChargeType.NotFound", + "errorMessage": "The InstanceChargeType does not exist in our records." + }, + { + "errorCode": "DependencyViolation.IoOptimized", + "errorMessage": "The specified instancetype must be IoOptimized instance." + }, + { + "errorCode": "PaymentMethodNotFound", + "errorMessage": "No payment method has been registered on the account." + }, + { + "errorCode": "HOSTNAME_ILLEGAL", + "errorMessage": "The specified parameter HostName is not valid." + }, + { + "errorCode": "InvalidSystemDiskSize.LessThanImageSize", + "errorMessage": "The specified parameter SystemDisk.Size is less than the image size." + }, + { + "errorCode": "InvalidSystemDiskSize.LessThanMinSize", + "errorMessage": "The specified parameter SystemDisk.Size is less than the min size." + }, + { + "errorCode": "InvalidSystemDiskSize.MoreThanMaxSize", + "errorMessage": "The specified parameter SystemDisk.Size is more than the max size." + }, + { + "errorCode": "OperationDenied.ImageNotValid", + "errorMessage": "The specified Image is disabled or is deleted." + }, + { + "errorCode": "OperationDenied.CreatingConflict", + "errorMessage": "Another Instance has been creating." + }, + { + "errorCode": "InvalidKeyPairName.NotFound", + "errorMessage": "The specified parameter KeyPairName does not exist in our records." + }, + { + "errorCode": "InvalidResourceGroup.NotFound", + "errorMessage": "The ResourceGroup provided does not exist in our records." + }, + { + "errorCode": "InvalidRamRole.NotFound", + "errorMessage": "The specified parameter \"RAMRoleName\" does not exist." + }, + { + "errorCode": "InvalidLaunchTemplate.NotFound", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidLaunchTemplateVersion.NotFound", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidVSwitchId.NotExist", + "errorMessage": "%s" + }, + { + "errorCode": "InvalidMarketImage.NotFound", + "errorMessage": "The specified marketplace image does not exist, please change the imageId and try again." + }, + { + "errorCode": "DeploymentSet.NotFound", + "errorMessage": "The specified deployment set does not exist." + }, + { + "errorCode": "InvalidParameter.DeploymentSetGroupNo", + "errorMessage": "Parameter DeploymentSetGroupNo is invalid." + }, + { + "errorCode": "InvalidParameter.KMSKeyId.NotFound", + "errorMessage": "The specified KMSKeyId does not exist." + }, + { + "errorCode": "InvalidDiskIds.NotPortable", + "errorMessage": "The specified DiskId is not portable." + }, + { + "errorCode": "InvalidAutoSnapshotPolicyId.NotFound", + "errorMessage": "Specified parameter AutoSnapshotPolicyId not found." + } + ], + "429": [ + { + "errorCode": "Throttling.Resource", + "errorMessage": "The request throttle by resource operation." + } + ], + "500": [ + { + "errorCode": "InternalError", + "errorMessage": "The request processing has failed due to some unknown error." + }, + { + "errorCode": "InvalidParameter.DataEncryptedKeyCreateFailed", + "errorMessage": "Create kms data encrypted key fail. If you need further assistance, you can contact the KMS Technical Support." + } + ] + }, + "extraInfo": "\n", "methods": [ - "get", - "post" + "post", + "get" ], - "operationType": "read", - "responseDemo": "[{\"type\":\"json\",\"example\":\"{\\n \\\"RequestId\\\": \\\"43A29C77-405E-4CC0-BC55-EE694AD00655\\\",\\n \\\"Data\\\": \\\"{\\\\\\\"content\\\\\\\":\\\\\\\"炼句 提问方式 1.请赏析诗歌某一联(句) 2.赏析某一联(句)的妙处 3.请赏析诗歌某、角度抒胸意、借景抒情、托物\\\\\\\",\\\\\\\"height\\\\\\\":1277,\\\\\\\"orgHeight\\\\\\\":1277,\\\\\\\"orgWidth\\\\\\\":1080,\\\\\\\"prism_version\\\\\\\":\\\\\\\"1.0.9\\\\\\\",\\\\\\\"prism_wnum\\\\\\\":26,\\\\\\\"prism_wordsInfo\\\\\\\":[{\\\\\\\"angle\\\\\\\":-87,\\\\\\\"direction\\\\\\\":0,\\\\\\\"height\\\\\\\":83,\\\\\\\"pos\\\\\\\":[{\\\\\\\"x\\\\\\\":177,\\\\\\\"y\\\\\\\":56},{\\\\\\\"x\\\\\\\":260,\\\\\\\"y\\\\\\\":60},{\\\\\\\"x\\\\\\\":259,\\\\\\\"y\\\\\\\":88},{\\\\\\\"x\\\\\\\":176,\\\\\\\"y\\\\\\\":84}],\\\\\\\"prob\\\\\\\":96,\\\\\\\"width\\\\\\\":28,\\\\\\\"word\\\\\\\":\\\\\\\"炼句\\\\\\\",\\\\\\\"x\\\\\\\":203,\\\\\\\"y\\\\\\\":30}],\\\\\\\"width\\\\\\\":1080}\\\",\\n \\\"Code\\\": \\\"200\\\",\\n \\\"Message\\\": \\\"message\\\"\\n}\",\"errorExample\":\"\"},{\"type\":\"xml\",\"example\":\"\\n 4AA3FA7B-CB2D-4314-B812-A598B540BADA\\n {\\\"content\\\":\\\"炼句 提问方式 1.请赏析诗歌某一联(句) 2.赏析某一联(句)的妙处 3.请赏析诗歌某一联1句)的表达效果 常规答题思路 1.手法: 1写景的句子侧重于写景手法(感官、角度.动静、虚实、典型画面、寓情于景等) ②写人的句子侧重于写人手法(动作、心理、肖像、细节等) ③抒情的句子侧重于抒情方式(直抒胸意、借景抒情、托物言志、用典抒情、 借古讽今等) 2.内容:写出…内容,描绘…的情景(画面),体现.特点 3.情感:表达了……的情感心理),或者渲染了…..的意境氛围 4.结构: 照应1与标题或者前后某内容照应),铺垫(为下文作了铺垫) 说明 1.所考的是诗歌的第一句,有时要考虑:点明时令,总领全诗,奠定情感基调 所考的是诗歌的最后一句,有时要考虑:言有尽而意无穷,给读者留下 想象空间。 2.如果该句没有什么突出手法,那么我们的精力应该花在内容、情感结构上 题型迁移 二 提问方式:某句诗有什么作用 作用题的答题模式约等于“炼句题” 说明:“炼句“与某句诗作用”的区别 “炼句”侧重于本句的手法,而“某句诗作用“则是侧重于结构与内容。 \\\",\\\"height\\\":1277,\\\"orgHeight\\\":1277,\\\"orgWidth\\\":1080,\\\"prism_version\\\":\\\"1.0.9\\\",\\\"prism_wnum\\\":26,\\\"prism_wordsInfo\\\":[{\\\"angle\\\":-87,\\\"direction\\\":0,\\\"height\\\":83,\\\"pos\\\":[{\\\"x\\\":177,\\\"y\\\":56},{\\\"x\\\":260,\\\"y\\\":60},{\\\"x\\\":259,\\\"y\\\":88},{\\\"x\\\":176,\\\"y\\\":84}],\\\"prob\\\":96,\\\"width\\\":28,\\\"word\\\":\\\"炼句\\\",\\\"x\\\":203,\\\"y\\\":30},{\\\"angle\\\":-87,\\\"direction\\\":0,\\\"height\\\":115,\\\"pos\\\":[{\\\"x\\\":172,\\\"y\\\":114},{\\\"x\\\":287,\\\"y\\\":120},{\\\"x\\\":286,\\\"y\\\":146},{\\\"x\\\":171,\\\"y\\\":141}],\\\"prob\\\":98,\\\"width\\\":27,\\\"word\\\":\\\"提问方式\\\",\\\"x\\\":215,\\\"y\\\":71},{\\\"angle\\\":-89,\\\"direction\\\":0,\\\"height\\\":280,\\\"pos\\\":[{\\\"x\\\":287,\\\"y\\\":119},{\\\"x\\\":567,\\\"y\\\":119},{\\\"x\\\":567,\\\"y\\\":146},{\\\"x\\\":287,\\\"y\\\":146}],\\\"prob\\\":97,\\\"width\\\":26,\\\"word\\\":\\\"1.请赏析诗歌某一联(句)\\\",\\\"x\\\":414,\\\"y\\\":-8},{\\\"angle\\\":0,\\\"direction\\\":0,\\\"height\\\":27,\\\"pos\\\":[{\\\"x\\\":286,\\\"y\\\":167},{\\\"x\\\":576,\\\"y\\\":164},{\\\"x\\\":576,\\\"y\\\":192},{\\\"x\\\":286,\\\"y\\\":194}],\\\"prob\\\":98,\\\"width\\\":289,\\\"word\\\":\\\"2.赏析某一联(句)的妙处\\\",\\\"x\\\":286,\\\"y\\\":165},{\\\"angle\\\":0,\\\"direction\\\":0,\\\"height\\\":30,\\\"pos\\\":[{\\\"x\\\":285,\\\"y\\\":213},{\\\"x\\\":667,\\\"y\\\":207},{\\\"x\\\":667,\\\"y\\\":236},{\\\"x\\\":285,\\\"y\\\":242}],\\\"prob\\\":96,\\\"width\\\":382,\\\"word\\\":\\\"3.请赏析诗歌某一联1句)的表达效果\\\",\\\"x\\\":285,\\\"y\\\":209},{\\\"angle\\\":-90,\\\"direction\\\":0,\\\"height\\\":156,\\\"pos\\\":[{\\\"x\\\":165,\\\"y\\\":284},{\\\"x\\\":321,\\\"y\\\":284},{\\\"x\\\":321,\\\"y\\\":310},{\\\"x\\\":165,\\\"y\\\":310}],\\\"prob\\\":98,\\\"width\\\":26,\\\"word\\\":\\\"常规答题思路\\\",\\\"x\\\":231,\\\"y\\\":219},{\\\"angle\\\":-2,\\\"direction\\\":0,\\\"height\\\":26,\\\"pos\\\":[{\\\"x\\\":357,\\\"y\\\":288},{\\\"x\\\":441,\\\"y\\\":286},{\\\"x\\\":442,\\\"y\\\":312},{\\\"x\\\":358,\\\"y\\\":314}],\\\"prob\\\":97,\\\"width\\\":84,\\\"word\\\":\\\"1.手法:\\\",\\\"x\\\":357,\\\"y\\\":287},{\\\"angle\\\":0,\\\"direction\\\":0,\\\"height\\\":32,\\\"pos\\\":[{\\\"x\\\":163,\\\"y\\\":332},{\\\"x\\\":958,\\\"y\\\":319},{\\\"x\\\":959,\\\"y\\\":351},{\\\"x\\\":163,\\\"y\\\":364}],\\\"prob\\\":91,\\\"width\\\":796,\\\"word\\\":\\\"1写景的句子侧重于写景手法(感官、角度.动静、虚实、典型画面、寓情于景等)\\\",\\\"x\\\":163,\\\"y\\\":324},{\\\"angle\\\":0,\\\"direction\\\":0,\\\"height\\\":30,\\\"pos\\\":[{\\\"x\\\":161,\\\"y\\\":380},{\\\"x\\\":768,\\\"y\\\":374},{\\\"x\\\":768,\\\"y\\\":404},{\\\"x\\\":162,\\\"y\\\":410}],\\\"prob\\\":96,\\\"width\\\":607,\\\"word\\\":\\\"②写人的句子侧重于写人手法(动作、心理、肖像、细节等)\\\",\\\"x\\\":161,\\\"y\\\":376},{\\\"angle\\\":0,\\\"direction\\\":0,\\\"height\\\":30,\\\"pos\\\":[{\\\"x\\\":159,\\\"y\\\":430},{\\\"x\\\":951,\\\"y\\\":417},{\\\"x\\\":951,\\\"y\\\":448},{\\\"x\\\":159,\\\"y\\\":460}],\\\"prob\\\":91,\\\"width\\\":792,\\\"word\\\":\\\"③抒情的句子侧重于抒情方式(直抒胸意、借景抒情、托物言志、用典抒情、\\\",\\\"x\\\":159,\\\"y\\\":423},{\\\"angle\\\":-89,\\\"direction\\\":0,\\\"height\\\":141,\\\"pos\\\":[{\\\"x\\\":155,\\\"y\\\":477},{\\\"x\\\":297,\\\"y\\\":479},{\\\"x\\\":296,\\\"y\\\":507},{\\\"x\\\":155,\\\"y\\\":505}],\\\"prob\\\":96,\\\"width\\\":27,\\\"word\\\":\\\"借古讽今等)\\\",\\\"x\\\":212,\\\"y\\\":420},{\\\"angle\\\":0,\\\"direction\\\":0,\\\"height\\\":31,\\\"pos\\\":[{\\\"x\\\":154,\\\"y\\\":527},{\\\"x\\\":830,\\\"y\\\":522},{\\\"x\\\":830,\\\"y\\\":553},{\\\"x\\\":154,\\\"y\\\":559}],\\\"prob\\\":97,\\\"width\\\":677,\\\"word\\\":\\\"2.内容:写出…内容,描绘…的情景(画面),体现.特点\\\",\\\"x\\\":154,\\\"y\\\":524},{\\\"angle\\\":0,\\\"direction\\\":0,\\\"height\\\":30,\\\"pos\\\":[{\\\"x\\\":150,\\\"y\\\":579},{\\\"x\\\":826,\\\"y\\\":571},{\\\"x\\\":827,\\\"y\\\":602},{\\\"x\\\":150,\\\"y\\\":610}],\\\"prob\\\":91,\\\"width\\\":677,\\\"word\\\":\\\"3.情感:表达了……的情感心理),或者渲染了…..的意境氛围\\\",\\\"x\\\":150,\\\"y\\\":575},{\\\"angle\\\":-1,\\\"direction\\\":0,\\\"height\\\":28,\\\"pos\\\":[{\\\"x\\\":148,\\\"y\\\":635},{\\\"x\\\":262,\\\"y\\\":630},{\\\"x\\\":263,\\\"y\\\":658},{\\\"x\\\":149,\\\"y\\\":663}],\\\"prob\\\":97,\\\"width\\\":115,\\\"word\\\":\\\"4.结构:\\\",\\\"x\\\":148,\\\"y\\\":631},{\\\"angle\\\":0,\\\"direction\\\":0,\\\"height\\\":29,\\\"pos\\\":[{\\\"x\\\":273,\\\"y\\\":631},{\\\"x\\\":916,\\\"y\\\":621},{\\\"x\\\":917,\\\"y\\\":651},{\\\"x\\\":274,\\\"y\\\":661}],\\\"prob\\\":96,\\\"width\\\":643,\\\"word\\\":\\\"照应1与标题或者前后某内容照应),铺垫(为下文作了铺垫)\\\",\\\"x\\\":273,\\\"y\\\":626},{\\\"angle\\\":-90,\\\"direction\\\":0,\\\"height\\\":60,\\\"pos\\\":[{\\\"x\\\":150,\\\"y\\\":712},{\\\"x\\\":210,\\\"y\\\":712},{\\\"x\\\":210,\\\"y\\\":738},{\\\"x\\\":150,\\\"y\\\":738}],\\\"prob\\\":99,\\\"width\\\":26,\\\"word\\\":\\\"说明\\\",\\\"x\\\":167,\\\"y\\\":694},{\\\"angle\\\":0,\\\"direction\\\":0,\\\"height\\\":31,\\\"pos\\\":[{\\\"x\\\":155,\\\"y\\\":764},{\\\"x\\\":988,\\\"y\\\":752},{\\\"x\\\":989,\\\"y\\\":783},{\\\"x\\\":155,\\\"y\\\":794}],\\\"prob\\\":95,\\\"width\\\":834,\\\"word\\\":\\\"1.所考的是诗歌的第一句,有时要考虑:点明时令,总领全诗,奠定情感基调\\\",\\\"x\\\":154,\\\"y\\\":757},{\\\"angle\\\":0,\\\"direction\\\":0,\\\"height\\\":31,\\\"pos\\\":[{\\\"x\\\":189,\\\"y\\\":815},{\\\"x\\\":954,\\\"y\\\":809},{\\\"x\\\":954,\\\"y\\\":840},{\\\"x\\\":189,\\\"y\\\":846}],\\\"prob\\\":98,\\\"width\\\":765,\\\"word\\\":\\\"所考的是诗歌的最后一句,有时要考虑:言有尽而意无穷,给读者留下\\\",\\\"x\\\":188,\\\"y\\\":811},{\\\"angle\\\":-89,\\\"direction\\\":0,\\\"height\\\":120,\\\"pos\\\":[{\\\"x\\\":147,\\\"y\\\":874},{\\\"x\\\":267,\\\"y\\\":874},{\\\"x\\\":267,\\\"y\\\":902},{\\\"x\\\":147,\\\"y\\\":902}],\\\"prob\\\":98,\\\"width\\\":28,\\\"word\\\":\\\"想象空间。\\\",\\\"x\\\":191,\\\"y\\\":827},{\\\"angle\\\":0,\\\"direction\\\":0,\\\"height\\\":32,\\\"pos\\\":[{\\\"x\\\":140,\\\"y\\\":957},{\\\"x\\\":1002,\\\"y\\\":950},{\\\"x\\\":1002,\\\"y\\\":983},{\\\"x\\\":140,\\\"y\\\":990}],\\\"prob\\\":96,\\\"width\\\":862,\\\"word\\\":\\\"2.如果该句没有什么突出手法,那么我们的精力应该花在内容、情感结构上\\\",\\\"x\\\":139,\\\"y\\\":953},{\\\"angle\\\":-89,\\\"direction\\\":0,\\\"height\\\":127,\\\"pos\\\":[{\\\"x\\\":133,\\\"y\\\":1043},{\\\"x\\\":260,\\\"y\\\":1045},{\\\"x\\\":260,\\\"y\\\":1076},{\\\"x\\\":133,\\\"y\\\":1074}],\\\"prob\\\":97,\\\"width\\\":31,\\\"word\\\":\\\"题型迁移\\\",\\\"x\\\":180,\\\"y\\\":995},{\\\"angle\\\":-90,\\\"direction\\\":0,\\\"height\\\":38,\\\"pos\\\":[{\\\"x\\\":5,\\\"y\\\":1109},{\\\"x\\\":44,\\\"y\\\":1109},{\\\"x\\\":44,\\\"y\\\":1150},{\\\"x\\\":5,\\\"y\\\":1150}],\\\"prob\\\":79,\\\"width\\\":40,\\\"word\\\":\\\"二\\\",\\\"x\\\":4,\\\"y\\\":1108},{\\\"angle\\\":0,\\\"direction\\\":0,\\\"height\\\":34,\\\"pos\\\":[{\\\"x\\\":130,\\\"y\\\":1103},{\\\"x\\\":468,\\\"y\\\":1098},{\\\"x\\\":469,\\\"y\\\":1131},{\\\"x\\\":130,\\\"y\\\":1136}],\\\"prob\\\":97,\\\"width\\\":339,\\\"word\\\":\\\"提问方式:某句诗有什么作用\\\",\\\"x\\\":129,\\\"y\\\":1099},{\\\"angle\\\":-89,\\\"direction\\\":0,\\\"height\\\":411,\\\"pos\\\":[{\\\"x\\\":495,\\\"y\\\":1099},{\\\"x\\\":905,\\\"y\\\":1101},{\\\"x\\\":905,\\\"y\\\":1132},{\\\"x\\\":494,\\\"y\\\":1130}],\\\"prob\\\":97,\\\"width\\\":30,\\\"word\\\":\\\"作用题的答题模式约等于“炼句题”\\\",\\\"x\\\":684,\\\"y\\\":910},{\\\"angle\\\":-1,\\\"direction\\\":0,\\\"height\\\":33,\\\"pos\\\":[{\\\"x\\\":125,\\\"y\\\":1168},{\\\"x\\\":552,\\\"y\\\":1159},{\\\"x\\\":553,\\\"y\\\":1191},{\\\"x\\\":126,\\\"y\\\":1201}],\\\"prob\\\":98,\\\"width\\\":427,\\\"word\\\":\\\"说明:“炼句“与某句诗作用”的区别\\\",\\\"x\\\":124,\\\"y\\\":1163},{\\\"angle\\\":0,\\\"direction\\\":0,\\\"height\\\":33,\\\"pos\\\":[{\\\"x\\\":131,\\\"y\\\":1226},{\\\"x\\\":897,\\\"y\\\":1221},{\\\"x\\\":898,\\\"y\\\":1255},{\\\"x\\\":132,\\\"y\\\":1260}],\\\"prob\\\":95,\\\"width\\\":766,\\\"word\\\":\\\"“炼句”侧重于本句的手法,而“某句诗作用“则是侧重于结构与内容。\\\",\\\"x\\\":130,\\\"y\\\":1224}],\\\"width\\\":1080}\\n\",\"errorExample\":\"\"}]", - "responseParamsDescription": "#### 返回参数说明\n
\n\n|字段|类型|说明|\n|-----|---|--|\n|angle|int|图片的角度(当NeedRotate=true时,返回此字段)。0表示正向,90表示图片朝右,180朝下,270朝左。|\n|content|string|识别出图片的文字块汇总。|\n|prism_wordsInfo|list|文字块信息。|\n|prism_tablesInfo|list|表格信息(当OutputTable=true时,返回此字段)。|\n|prism_wnum|int|识别的文字块的数量,prism_wordsInfo数组的大小。|\n|height|int|算法矫正图片后的高度。|\n|width|int|算法矫正图片后的宽度。|\n|orgHeight|int|原图的高度。|\n|orgWidth|int|原图的宽度。|\n\n#### 文字块信息(prism_wordsInfo字段。)\n|字段|类型|说明|\n|-----|---|--|\n|angle|int|文字块的角度。|\n|height|int|算法矫正图片后的高度。|\n|width|int|算法矫正图片后的宽度。|\n|pos|list|文字块的外矩形四个点的坐标按顺时针排列(左上、右上、右下、左下)。当NeedRotate=true时,如果最外层的angle不为0,需要按照angle矫正图片后,坐标才准确。|\n|word|string|文字块的文字内容。|\n|tableId|int|表格的id(当OutputTable=true时,返回此字段)。|\n|tableCellId|int|表格中单元格的id(当OutputTable=true时,返回此字段)。|\n|charInfo|list|单字信息。|\n\n#### 单字信息(charInfo字段。当OutputCharInfo=true时,返回此字段。)\n|字段|类型|说明|\n|-----|---|--|\n|word|string|单字文字。|\n|prob|int|置信度。|\n|x|int|单字左上角横坐标。|\n|y|int|单字左上角纵坐标。|\n|w|int|单字宽度。|\n|h|int|单字高度。|\n\n#### 表格信息(prism_tablesInfo字段。当OutputTable=true时,返回此字段。)\n|字段|类型|说明|\n|-----|---|--|\n|tableId|int|表格id,和prism_wordsInfo信息中的tableId对应。|\n|xCellSize|int|表格中横坐标单元格的数量。|\n|yCellSize|int|表格中纵坐标单元格的数量。|\n|cellInfos|list|单元格信息。|\n\n#### 单元格信息(cellInfos字段。)\n|字段|类型|说明|\n|-----|---|--|\n|tableCellId|int|表格中单元格id,和prism_wordsInfo信息中的tableCellId对应。|\n|word|string|单元格中的文字。|\n|xsc|int|xStartCell缩写,表示横轴方向该单元格起始在第几个单元格,第一个单元格值为0。|\n|xec|int|xEndCell缩写,表示横轴方向该单元格结束在第几个单元格,第一个单元格值为0,如果xsc和xec都为0说明该文字在横轴方向占据了一个单元格并且在第一个单元格内。|\n|ysc|int|yStartCell缩写,表示纵轴方向该单元格起始在第几个单元格,第一个单元格值为0。|\n|yec|int|yEndCell缩写,表示纵轴方向该单元格结束在第几个单元格,第一个单元格值为0。|\n|pos|list|单元格位置,按照单元格四个角的坐标顺时针排列,分别为左上XY坐标、右上XY坐标、右下XY坐标、左下XY坐标。|", + "operationType": "readAndWrite", + "requestParamsDescription": " ", + "responseDemo": "[{\"type\":\"json\",\"example\":\"{\\n \\\"RequestId\\\": \\\"473469C7-AA6F-4DC5-B3DB-A3DC0DE3****\\\",\\n \\\"OrderId\\\": \\\"123456****\\\",\\n \\\"TradePrice\\\": 0.165,\\n \\\"InstanceIdSets\\\": {\\n \\\"InstanceIdSet\\\": [\\n \\\"[\\\\\\\"i-bp67acfmxazb4pd2****\\\\\\\", \\\\\\\"i-bp1i43l28m7u48p1****\\\\\\\", \\\\\\\"i-bp12yqg7jdyxl11f****\\\\\\\"]\\\"\\n ]\\n }\\n}\",\"errorExample\":\"\"},{\"type\":\"xml\",\"example\":\"\\n 473469C7-AA6F-4DC5-B3DB-A3DC0DE3****\\n 123456****\\n 0.165\\n [\\\"i-bp67acfmxazb4pd2****\\\", \\\"i-bp1i43l28m7u48p1****\\\", \\\"i-bp12yqg7jdyxl11f****\\\"]\\n\",\"errorExample\":\"\"}]", + "responseParamsDescription": " ", "schemes": [ "http", "https" @@ -20,87 +1656,1321 @@ export const routerMeta = { { "AK": [] } - ], - "summary": "通用手写体识别。", - "title": "通用手写体识别" - }, - "externalDocs": { - "description": "去调试", - "url": "https://api.aliyun.com/api/ocr-api/2021-07-07/RecognizeHandwriting" - }, - "method": "get", - "name": "RecognizeHandwriting", - "parameters": [ + ], + "summary": "创建一台或多台按量付费或者包年包月ECS实例。", + "systemTags": { + "operationType": "create" + }, + "title": "创建一台或多台按量付费或者包年包月ECS实例" + }, + "externalDocs": { + "description": "去调试", + "url": "https://api.aliyun.com/api/Ecs/2014-05-26/RunInstances" + }, + "method": "post", + "name": "RunInstances", + "parameters": [ + { + "in": "query", + "name": "RegionId", + "required": true, + "schema": { + "description": "实例所属的地域ID。您可以调用[DescribeRegions](https://help.aliyun.com/document_detail/25609.html)查看最新的阿里云地域列表。", + "example": "cn-hangzhou", + "required": true, + "type": "string" + } + }, + { + "in": "query", + "name": "ImageId", + "required": false, + "schema": { + "description": "镜像ID,启动实例时选择的镜像资源。您可以通过[DescribeImages](https://help.aliyun.com/document_detail/25534.html)查询您可以使用的镜像资源。如果您不指定`LaunchTemplateId`或`LaunchTemplateName`以确定启动模板,也不通过指定`ImageFamily`选用镜像族系最新可用镜像,则`ImageId`为必选参数。", + "example": "aliyun_2_1903_x64_20G_alibase_20200324.vhd", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "ImageFamily", + "required": false, + "schema": { + "description": "镜像族系名称,通过设置该参数来获取当前镜像族系内最新可用镜像来创建实例。\n- 设置了参数`ImageId`,则不能设置该参数。\n- 未设置参数`ImageId`,但指定的`LaunchTemplateId`或`LaunchTemplateName`对应的启动模板设置了`ImageId`,则不能设置该参数。\n- 未设置`ImageId`,且指定的`LaunchTemplateId`或`LaunchTemplateName`对应的启动模板未设置`ImageId`,则可以设置该参数。\n- 未设置`ImageId`,且未设置`LaunchTemplateId`、`LaunchTemplateName`参数,则可以设置该参数。\n> 阿里云官方镜像关联的镜像族系信息请参见[公共镜像概述](https://help.aliyun.com/document_detail/108393.html)。", + "example": "hangzhou-daily-update", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "InstanceType", + "required": false, + "schema": { + "description": "实例的资源规格。如果您不指定`LaunchTemplateId`或`LaunchTemplateName`以确定启动模板,`InstanceType`为必选参数。 \n\n- 产品选型:参见[实例规格族](https://help.aliyun.com/document_detail/25378.html)或调用[DescribeInstanceTypes](https://help.aliyun.com/document_detail/25620.html)查看目标实例规格的性能数据,或者参见[选型配置](https://help.aliyun.com/document_detail/58291.html)了解如何选择实例规格。\n- 查询库存:调用[DescribeAvailableResource](https://help.aliyun.com/document_detail/66186.html)查看指定地域或者可用区内的资源供给情况。", + "example": "ecs.g6.large", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "SecurityGroupId", + "required": false, + "schema": { + "description": "新创建实例所属于的安全组ID。同一个安全组内的实例之间可以互相访问,一个安全组能容纳的实例数量视安全组类型而定,具体请参见[使用限制](https://help.aliyun.com/document_detail/25412.html#SecurityGroupQuota)的安全组章节。\n\n> `SecurityGroupId`决定了实例的网络类型,例如,如果设置的安全组的网络类型为专有网络VPC,实例则为VPC类型,并同时需要指定参数`VSwitchId`。\n\n如果您不设置`LaunchTemplateId`或`LaunchTemplateName`以确定实例启动模板,则安全组ID为必选参数。您需要注意:\n\n- 您可以通过`SecurityGroupId`设置一个安全组,也可以通过`SecurityGroupIds.N`设置一个或多个安全组,但不支持同时设置`SecurityGroupId`和`SecurityGroupIds.N`。\n\n- 如果`NetworkInterface.N.InstanceType`取值为`Primary`,则不能设置`SecurityGroupId`或`SecurityGroupIds.N`,只能设置`NetworkInterface.N.SecurityGroupId`或`NetworkInterface.N.SecurityGroupIds.N`。", + "example": "sg-bp15ed6xe1yxeycg7****", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "VSwitchId", + "required": false, + "schema": { + "description": "虚拟交换机ID。如果您创建的是VPC类型ECS实例,必须指定虚拟交换机ID,且安全组和虚拟交换机在同一个专有网络VPC中。您可以调用[DescribeVSwitches](https://help.aliyun.com/document_detail/35748.html)查询已创建的交换机的相关信息。\n\n您需要注意:\n\n- 如果您设置了`VSwitchId`参数,则设置的`ZoneId`参数必须和交换机所在的可用区保持一致。您也可以不设置`ZoneId`参数,系统将自动选择指定交换机所在的可用区。\n\n- 如果`NetworkInterface.N.InstanceType`取值为`Primary`,则不能设置`VSwitchId`,只能设置`NetworkInterface.N.VSwitchId`。", + "example": "vsw-bp1s5fnvk4gn2tws0****", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "InstanceName", + "required": false, + "schema": { + "description": "实例名称。长度为2~128个字符,必须以大小写字母或中文开头,不能以`http://`和`https://`开头。可以包含中文、英文、数字、半角冒号(:)、下划线(_)、半角句号(.)或者短划线(-)。默认值为实例的`InstanceId`。\n\n创建多台ECS实例时,您可以批量设置有序的实例名称。具体操作,请参见[批量设置有序的实例名称或主机名称](https://help.aliyun.com/document_detail/196048.html)。", + "example": "k8s-node-[1,4]-alibabacloud", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "Description", + "required": false, + "schema": { + "description": "实例的描述。长度为2~256个英文或中文字符,不能以`http://`和`https://`开头。", + "example": "Instance_Description", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "InternetMaxBandwidthIn", + "required": false, + "schema": { + "description": "公网入带宽最大值,单位为Mbit/s。取值范围:\n\n- 当所购公网出带宽小于等于10 Mbit/s时:1~10,默认为10。\n- 当所购公网出带宽大于10 Mbit/s时:1~`InternetMaxBandwidthOut`的取值,默认为`InternetMaxBandwidthOut`的取值。", + "example": "10", + "format": "int32", + "required": false, + "type": "integer" + } + }, + { + "in": "query", + "name": "InternetMaxBandwidthOut", + "required": false, + "schema": { + "description": "公网出带宽最大值,单位为Mbit/s。取值范围:0~100。\n\n默认值:0。", + "example": "10", + "format": "int32", + "required": false, + "type": "integer" + } + }, + { + "in": "query", + "name": "HostName", + "required": false, + "schema": { + "description": "实例主机名称。限制说明如下:\n\n- 半角句号(.)和短划线(-)不能作为首尾字符,更不能连续使用。\n- Windows实例:字符长度为2~15,不支持半角句号(.),不能全是数字。允许包含大小写英文字母、数字和短划线(-)。\n- 其他类型实例(Linux等):\n - 字符长度为2~64,支持多个半角句号(.),点之间为一段,每段允许包含大小写英文字母、数字和短划线(-)。\n - 支持通过占位符`${instance_id}`将实例ID写入`HostName`参数。例如:`HostName=k8s-${instance_id}`,并且创建的ECS实例ID为`i-123abc****`,则该实例的主机名为`k8s-i-123abc****`。\n\n创建多台ECS实例时,您可以:\n\n- 批量设置有序的主机名。具体操作,请参见[批量设置有序的实例名称或主机名称](https://help.aliyun.com/document_detail/196048.html)。\n- 通过`HostNames.N`参数,为多台实例分别设置主机名。需要注意`HostName`参数和`HostNames.N`参数不能同时设置。", + "example": "k8s-node-[1,4]-ecshost", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "UniqueSuffix", + "required": false, + "schema": { + "description": "当创建多台实例时,是否为`HostName`和`InstanceName`自动添加有序后缀。有序后缀从001开始递增,最大不能超过999。取值范围:\n- true:添加。\n- false:不添加。\n\n默认值:false。\n\n当`HostName`或`InstanceName`按照指定排序格式设置,未设置命名后缀`name_suffix`,即命名格式为`name_prefix[begin_number,bits]`时,`UniqueSuffix`不生效,名称仅按照指定顺序排序。\n\n更多信息,请参见[批量设置有序的实例名称或主机名称](https://help.aliyun.com/document_detail/196048.html)。", + "example": "true", + "required": false, + "type": "boolean" + } + }, + { + "in": "query", + "name": "Password", + "required": false, + "schema": { + "description": "实例的密码。长度为8至30个字符,必须同时包含大小写英文字母、数字和特殊符号中的三类字符。特殊符号可以是:\n\n```\n()`~!@#$%^&*-_+=|{}[]:;'<>,.?/\n```\n\n其中,Windows实例不能以正斜线(/)为密码首字符。\n\n> 如果传入`Password`参数,建议您使用HTTPS协议发送请求,避免密码泄露。", + "example": "EcsV587!", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "PasswordInherit", + "required": false, + "schema": { + "description": "是否使用镜像预设的密码。取值范围:\n\n- true:使用。\n- false:不使用。\n\n默认值:false。\n\n> 使用该参数时,Password参数必须为空,同时您需要确保使用的镜像已经设置了密码。", + "example": "false", + "required": false, + "type": "boolean" + } + }, + { + "in": "query", + "name": "ZoneId", + "required": false, + "schema": { + "description": "实例所属的可用区ID,您可以调用[DescribeZones](https://help.aliyun.com/document_detail/25610.html)获取可用区列表。\n\n> 如果您指定了`VSwitchId`参数,则指定的`ZoneId`参数必须和交换机所在的可用区保持一致。您也可以不指定`ZoneId`参数,系统将自动选择指定的交换机所在的可用区。\n\n默认值:系统自动选择。", + "example": "cn-hangzhou-g", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "InternetChargeType", + "required": false, + "schema": { + "description": "网络计费类型。取值范围:\n\n- PayByBandwidth:按固定带宽计费。\n- PayByTraffic:按使用流量计费。\n\n默认值:PayByTraffic。\n\n> **按使用流量计费**模式下的出入带宽峰值都是带宽上限,不作为业务承诺指标。当出现资源争抢时,带宽峰值可能会受到限制。如果您的业务需要有带宽的保障,请使用**按固定带宽计费**模式。\n", + "example": "PayByTraffic", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "SystemDisk.Size", + "required": false, + "schema": { + "description": "系统盘大小,单位为GiB。取值范围:\n\n- 普通云盘:20~500\n\n- 其他类型云盘:20~2048\n\n该参数的取值必须大于或者等于max{20, ImageSize}。\n\n默认值:max{40, 参数ImageId对应的镜像大小}。", + "example": "40", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "SystemDisk.Category", + "required": false, + "schema": { + "description": "系统盘的云盘种类。取值范围:\n\n- cloud_efficiency:高效云盘。\n- cloud_ssd:SSD云盘。\n- cloud_essd:ESSD云盘。\n- cloud:普通云盘。\n- cloud_auto:ESSD AutoPL云盘。\n- cloud_essd_entry:ESSD Entry云盘。\n>仅当`InstanceType`设置为`ecs.u1`或`ecs.e`规格族时,该参数支持`cloud_essd_entry`。\n\n已停售的实例规格且非I/O优化实例默认值为cloud,否则默认值为cloud_efficiency。\n", + "example": "cloud_ssd", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "SystemDisk.DiskName", + "required": false, + "schema": { + "description": "系统盘名称。长度为2~128个英文或中文字符。必须以大小写字母或中文开头,不能以`http://`和`https://`开头。可以包含数字、半角句号(.)、半角冒号(:)、下划线(_)或者短划线(-)。", + "example": "cloud_ssdSystem", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "SystemDisk.Description", + "required": false, + "schema": { + "description": "系统盘的描述。长度为2~256个英文或中文字符,不能以`http://`和`https://`开头。", + "example": "SystemDisk_Description", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "SystemDisk.PerformanceLevel", + "required": false, + "schema": { + "description": "创建ESSD云盘作为系统盘使用时,设置云盘的性能等级。取值范围:\n\n- PL0:单盘最高随机读写IOPS 1万。\n- PL1(默认):单盘最高随机读写IOPS 5万。\n- PL2:单盘最高随机读写IOPS 10万。\n- PL3:单盘最高随机读写IOPS 100万。\n\n有关如何选择ESSD性能等级,请参见[ESSD云盘](https://help.aliyun.com/document_detail/122389.html)。", + "example": "PL0", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "SystemDisk.AutoSnapshotPolicyId", + "required": false, + "schema": { + "description": "系统盘采用的自动快照策略ID。", + "example": "sp-bp67acfmxazb4p****", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "IoOptimized", + "required": false, + "schema": { + "description": "是否为I/O优化实例。[已停售的实例规格](https://help.aliyun.com/document_detail/55263.html)实例默认值是none,其他实例规格默认值是optimized。取值范围:\n\n- none:非I/O优化。\n- optimized:I/O优化。", + "example": "optimized", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "UserData", + "required": false, + "schema": { + "description": "实例自定义数据。需要以Base64方式编码,原始数据最多为16 KB。\n\n> 若实例满足[实例自定义数据](https://help.aliyun.com/document_detail/49121.html)的限制,您可传入UserData信息。因为传输API请求时,不会加密您设置的UserData,建议不要以明文方式传入机密的信息,例如密码和私钥等。如果必须传入,建议加密后,然后以Base64的方式编码后再传入,在实例内部以同样的方式反解密。", + "example": "ZWNobyBoZWxsbyBlY3Mh", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "KeyPairName", + "required": false, + "schema": { + "description": "密钥对名称。\n>Windows实例,忽略该参数。默认为空。即使填写了该参数,仍旧只执行`Password`的内容。", + "example": "KeyPair_Name", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "RamRoleName", + "required": false, + "schema": { + "description": "实例RAM角色名称。您可以使用RAM API [ListRoles](https://help.aliyun.com/document_detail/28713.html)查询您已创建的实例RAM角色。", + "example": "RAM_Name", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "Amount", + "required": false, + "schema": { + "default": "1", + "description": "指定创建ECS实例的数量。取值范围:1~100。\n\n默认值:1。", + "example": "3", + "format": "int32", + "maximum": "1000", + "minimum": "1", + "required": false, + "type": "integer" + } + }, + { + "in": "query", + "name": "MinAmount", + "required": false, + "schema": { + "description": "指定ECS实例最小购买数量。取值范围:1~100。\n\n- 当ECS库存数量小于最小购买数量,会创建失败。\n- 当ECS库存数量大于等于最小购买数量,按照库存数量创建。", + "example": "2", + "format": "int32", + "maximum": "100", + "minimum": "1", + "required": false, + "type": "integer" + } + }, + { + "in": "query", + "name": "AutoReleaseTime", + "required": false, + "schema": { + "description": "按量付费实例的自动释放时间。按照[ISO 8601](https://help.aliyun.com/document_detail/25696.html)标准表示,使用UTC+0时间。格式为:`yyyy-MM-ddTHH:mm:ssZ`。\n\n- 如果秒(`ss`)取值不是`00`,则自动取为当前分钟(`mm`)开始时。\n\n- 最短释放时间为当前时间半小时之后。\n\n- 最长释放时间不能超过当前时间三年。", + "example": "2018-01-01T12:05:00Z", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "SpotStrategy", + "required": false, + "schema": { + "description": "按量付费实例的竞价策略。当参数`InstanceChargeType`取值为`PostPaid`时生效。取值范围:\n\n- NoSpot:正常按量付费实例。\n- SpotWithPriceLimit:设置上限价格的抢占式实例。\n- SpotAsPriceGo:系统自动出价,跟随当前市场实际价格。\n\n默认值:NoSpot。", + "example": "NoSpot", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "SpotDuration", + "required": false, + "schema": { + "description": "抢占式实例的保留时长,单位为小时。 默认值:1。取值范围:\n- 1:创建后阿里云会保证实例运行1小时不会被自动释放;超过1小时后,系统会自动比较出价与市场价格、检查资源库存,来决定实例的持有和回收。\n- 0:创建后,阿里云不保证实例运行1小时,系统会自动比较出价与市场价格、检查资源库存,来决定实例的持有和回收。\n\n实例回收前5分钟阿里云会通过ECS系统事件向您发送通知。抢占式实例按秒计费,建议您结合具体任务执行耗时来选择合适的保留时长。", + "example": "1", + "format": "int32", + "maximum": "6", + "minimum": "0", + "required": false, + "type": "integer" + } + }, + { + "in": "query", + "name": "SpotPriceLimit", + "required": false, + "schema": { + "description": "设置实例的每小时最高价格。支持最大3位小数,参数`SpotStrategy`取值为`SpotWithPriceLimit`时生效。", + "example": "0.97", + "format": "float", + "maximum": "999999", + "minimum": "0", + "required": false, + "type": "number" + } + }, + { + "in": "query", + "name": "SpotInterruptionBehavior", + "required": false, + "schema": { + "description": "抢占实例中断模式。目前仅支持Terminate(默认)直接释放实例。", + "example": "Terminate", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "SecurityEnhancementStrategy", + "required": false, + "schema": { + "description": "是否开启安全加固。取值范围:\n\n- Active:启用安全加固,只对公共镜像生效。\n- Deactive:不启用安全加固,对所有镜像类型生效。", + "example": "Active", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "ClientToken", + "required": false, + "schema": { + "description": "保证请求幂等性。从您的客户端生成一个参数值,确保不同请求间该参数值唯一。**ClientToken**只支持ASCII字符,且不能超过64个字符。更多信息,请参见[如何保证幂等性](https://help.aliyun.com/document_detail/25693.html)。", + "example": "123e4567-e89b-12d3-a456-426655440000", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "HpcClusterId", + "required": false, + "schema": { + "description": "实例所属的HPC集群ID。 \n\n创建SCC实例时,该参数为必选参数。您可以参考[CreateHpcCluster](https://help.aliyun.com/document_detail/109138.html)创建HPC集群。\n", + "example": "hpc-bp67acfmxazb4p****", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "DryRun", + "required": false, + "schema": { + "description": "是否只预检此次请求。取值范围:\n\n- true:发送检查请求,不会创建实例。检查项包括是否填写了必需参数、请求格式、业务限制和ECS库存。如果检查不通过,则返回对应错误。如果检查通过,则返回错误码`DryRunOperation`。\n- false(默认):发送正常请求,通过检查后直接创建实例。", + "example": "false", + "required": false, + "type": "boolean" + } + }, + { + "in": "query", + "name": "DedicatedHostId", + "required": false, + "schema": { + "description": "是否在专有宿主机上创建ECS实例。由于专有宿主机不支持创建抢占式实例,指定`DedicatedHostId`参数后,会自动忽略请求中的`SpotStrategy`和`SpotPriceLimit`设置。\n\n您可以通过[DescribeDedicatedHosts](https://help.aliyun.com/document_detail/134242.html)查询专有宿主机ID列表。\n", + "example": "dh-bp67acfmxazb4p****", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "LaunchTemplateId", + "required": false, + "schema": { + "description": "启动模板ID。更多信息,请调用[DescribeLaunchTemplates](https://help.aliyun.com/document_detail/73759.html)。\n\n使用启动模板创建实例时,您必须指定`LaunchTemplateId`或`LaunchTemplateName`确定启动模板。", + "example": "lt-bp1apo0bbbkuy0rj****", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "LaunchTemplateName", + "required": false, + "schema": { + "description": "启动模板名称。\n\n使用启动模板创建实例时,您必须指定`LaunchTemplateId`或`LaunchTemplateName`确定启动模板。", + "example": "LaunchTemplate_Name", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "LaunchTemplateVersion", + "required": false, + "schema": { + "description": "启动模板版本。如果您指定了`LaunchTemplateId`或`LaunchTemplateName`而不指定启动模板版本号,则采用默认版本。 ", + "example": "3", + "format": "int64", + "required": false, + "type": "integer" + } + }, + { + "in": "query", + "name": "ResourceGroupId", + "required": false, + "schema": { + "description": "实例所在的企业资源组ID。", + "example": "rg-bp67acfmxazb4p****", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "Period", + "required": false, + "schema": { + "description": "购买资源的时长,单位由`PeriodUnit`指定。当参数`InstanceChargeType`取值为`PrePaid`时才生效且为必选值。一旦指定了`DedicatedHostId`,则取值范围不能超过专有宿主机的订阅时长。取值范围:\n\n- PeriodUnit=Week时,Period取值:1、2、3、4。\n- PeriodUnit=Month时,Period取值:1、2、3、4、5、6、7、8、9、12、24、36、48、60。", + "example": "1", + "format": "int32", + "required": false, + "type": "integer" + } + }, + { + "in": "query", + "name": "PeriodUnit", + "required": false, + "schema": { + "description": "包年包月计费方式的时长单位。取值范围: \n\n- Week。\n- Month(默认)。\n", + "example": "Month", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "AutoRenew", + "required": false, + "schema": { + "description": "是否要自动续费。当参数`InstanceChargeType`取值`PrePaid`时才生效。取值范围:\n\n- true:自动续费。\n- false:不自动续费。\n\n默认值:false。", + "example": "true", + "required": false, + "type": "boolean" + } + }, + { + "in": "query", + "name": "AutoRenewPeriod", + "required": false, + "schema": { + "description": "单次自动续费的续费时长。取值范围: \n \n- PeriodUnit=Week时:1、2、3。\n- PeriodUnit=Month时:1、2、3、6、12、24、36、48、60。\n\n默认值:1。", + "example": "1", + "format": "int32", + "required": false, + "type": "integer" + } + }, + { + "in": "query", + "name": "InstanceChargeType", + "required": false, + "schema": { + "description": "实例的付费方式。取值范围:\n\n- PrePaid:包年包月。\n- PostPaid:按量付费。\n\n默认值:PostPaid。\n\n选择包年包月时,您必须确认自己的账号支持余额支付或者信用支付,否则将返回`InvalidPayMethod`的错误提示。", + "example": "PrePaid", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "DeploymentSetId", + "required": false, + "schema": { + "description": "部署集ID。", + "example": "ds-bp1brhwhoqinyjd6****", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "DeploymentSetGroupNo", + "required": false, + "schema": { + "description": "如果您设置的部署集对应的策略为部署集组高可用策略(AvailabilityGroup),可以通过该参数指定实例在部署集中的分组号。取值范围:1~7。", + "example": "1", + "format": "int32", + "required": false, + "type": "integer" + } + }, + { + "in": "query", + "name": "PrivateIpAddress", + "required": false, + "schema": { + "description": "实例私网IP地址。专有网络VPC类型ECS实例设置私网IP地址时,必须从虚拟交换机(`VSwitchId`)的空闲网段中选择。\n\n您需要注意:\n\n- 设置`PrivateIpAddress`后:\n - 如果`Amount`参数取值为1,则表示为创建的一台ECS实例分配私网IP地址。\n - 如果`Amount`参数取值大于1,则表示在批量创建ECS实例时,以指定的私网IP地址为起始地址,依次为多台ECS实例分配连续的私网IP地址,但需要注意,此时不支持再为实例绑定辅助网卡(即不支持再设置`NetworkInterface.N.*`这类参数)。\n\n- 如果`NetworkInterface.N.InstanceType`取值为`Primary`,则不能设置`PrivateIpAddress`,只能设置`NetworkInterface.N.PrimaryIpAddress`。", + "example": "10.1.**.**", + "required": false, + "type": "string" + } + }, { "in": "query", - "name": "Url", + "name": "CreditSpecification", "required": false, "schema": { - "description": "* 本字段和body字段二选一,不可同时透传或同时为空。\n* 图片链接(长度不超2048,不支持base64)。", - "example": "https://img.alicdn.com/tfs/TB1Wo7eXAvoK1RjSZFDXXXY3pXa-2512-3509.jpg", + "description": "设置突发性能实例的运行模式。取值范围:\n\n- Standard:标准模式,实例性能请参见[什么是突发性能实例](https://help.aliyun.com/document_detail/59977.html)下的性能约束模式章节。\n- Unlimited:无性能约束模式,实例性能请参见[什么是突发性能实例](https://help.aliyun.com/document_detail/59977.html)下的无性能约束模式章节。", + "example": "Standard", "required": false, - "title": "图片链接(长度不超 2048,不支持 base64)", "type": "string" } }, { - "in": "body", - "name": "body", + "in": "query", + "name": "Ipv6AddressCount", "required": false, "schema": { - "description": "* 本字段和URL字段二选一,不可同时透传或同时为空。\n* 图片二进制文件,最大10MB。\n* 使用HTTP方式调用,把图片二进制文件放到HTTP body中上传即可。\n* 使用SDK的方式调用,把图片放到SDK的body中即可。", - "example": "图片二进制文件", - "format": "binary", + "description": "为主网卡指定随机生成的IPv6地址数量。取值范围:1~10。\n \n您需要注意:\n\n- 您不能同时设置`Ipv6Address.N`和`Ipv6AddressCount`。\n\n- 如果`NetworkInterface.N.InstanceType`取值为`Primary`,则不能设置`Ipv6Address.N`或`Ipv6AddressCount`,只能设置`NetworkInterface.N.Ipv6Address.N`或`NetworkInterface.N.Ipv6AddressCount`。", + "example": "1", + "format": "int32", "required": false, - "title": "图片二进制字节流,最大10MB", - "type": "string" + "type": "integer" } }, { "in": "query", - "name": "OutputCharInfo", + "name": "NetworkInterfaceQueueNumber", "required": false, "schema": { - "description": "* 是否输出单字识别结果,默认不需要。\n* true:需要;false:不需要。", - "example": "false", + "description": "主网卡队列数。您需要注意:\n\n- 不能超过实例规格允许的单块网卡最大队列数。\n\n- 实例的所有网卡累加队列数不能超过实例规格允许的队列数总配额。实例规格的单块网卡最大队列数和总配额可以通过[DescribeInstanceTypes](https://help.aliyun.com/document_detail/25620.html)接口查询`MaximumQueueNumberPerEni`、`TotalEniQueueQuantity`字段。\n\n- 如果`NetworkInterface.N.InstanceType`取值为`Primary`,则不能设置`NetworkInterfaceQueueNumber`,只能设置`NetworkInterface.N.QueueNumber`。", + "example": "8", + "format": "int32", + "minimum": "1", "required": false, - "title": "是否输出单字识别结果", - "type": "boolean" + "type": "integer" } }, { "in": "query", - "name": "NeedRotate", + "name": "DeletionProtection", "required": false, "schema": { - "description": "* 是否需要自动旋转功能,默认不需要。\n* true:需要;false:不需要。", + "description": "实例释放保护属性,指定是否支持通过控制台或API([DeleteInstance](https://help.aliyun.com/document_detail/25507.html))释放实例。取值范围: \n\n- true:开启实例释放保护。\n- false:关闭实例释放保护。\n\n默认值:false。\n\n> 该属性仅适用于按量付费实例,且只能限制手动释放操作,对系统释放操作不生效。", "example": "false", "required": false, - "title": "是否需要自动旋转功能(结构化检测、混贴场景、教育相关场景会自动做旋转,无需设置),返回角度信息", "type": "boolean" } }, { "in": "query", - "name": "OutputTable", + "name": "HibernationOptions.Configured", "required": false, "schema": { - "description": "* 是否输出表格识别结果,包含单元格信息,默认不需要。\n* true:需要;false:不需要。", + "description": ">该参数正在邀测中,暂未开放使用。", "example": "false", "required": false, - "title": "是否输出表格识别结果,包含单元格信息", "type": "boolean" } }, { "in": "query", - "name": "NeedSortPage", + "name": "Affinity", "required": false, "schema": { - "description": "* 是否按顺序输出文字块,默认为false。\n* false表示从左往右,从上到下的顺序;true表示从上到下,从左往右的顺序。", - "example": "false", + "description": "专有宿主机实例是否与专有宿主机关联。取值范围:\n\n- default:实例不与专有宿主机关联。已启用节省停机模式的实例,停机后再次启动时,若原专有宿主机可用资源不足,则实例被放置在自动部署资源池的其它专有宿主机上。\n\n- host:实例与专有宿主机关联。已启用节省停机模式的实例,停机后再次启动时,仍放置在原专有宿主机上。若原专有宿主机可用资源不足,则实例重启失败。\n\n默认值:default。", + "example": "default", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "Tenancy", + "required": false, + "schema": { + "description": "是否在专有宿主机上创建实例。取值范围:\n\n- default:创建非专有宿主机实例。\n\n- host:创建专有宿主机实例。若您不指定`DedicatedHostId`,则由阿里云自动选择专有宿主机放置实例。\n\n默认值:default。", + "example": "default", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "StorageSetId", + "required": false, + "schema": { + "description": "存储集ID。", + "example": "ss-bp67acfmxazb4p****", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "StorageSetPartitionNumber", + "required": false, + "schema": { + "description": "存储集中的最大分区数量。取值范围:大于等于2。", + "example": "2", + "format": "int32", + "minimum": "1", + "required": false, + "type": "integer" + } + }, + { + "in": "query", + "name": "CpuOptions.Core", + "required": false, + "schema": { + "description": "CPU核心数。该参数不支持自定义设置,只能采用默认值。\n\n默认值:请参见[自定义CPU选项](https://help.aliyun.com/document_detail/145895.html)。", + "example": "2", + "format": "int32", + "required": false, + "type": "integer" + } + }, + { + "in": "query", + "name": "CpuOptions.ThreadsPerCore", + "required": false, + "schema": { + "description": "CPU线程数。ECS实例的vCPU数=`CpuOptions.Core`取值*`CpuOptions.ThreadsPerCore`取值。\n\n- `CpuOptions.ThreadsPerCore=1`表示关闭CPU超线程。\n\n- 仅部分实例规格支持设置CPU线程数。\n\n取值范围和默认值:请参见[自定义CPU选项](https://help.aliyun.com/document_detail/145895.html)。", + "example": "2", + "format": "int32", + "required": false, + "type": "integer" + } + }, + { + "in": "query", + "name": "CpuOptions.Numa", + "required": false, + "schema": { + "description": "该参数已弃用。", + "example": "1", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "CpuOptions.TopologyType", + "required": false, + "schema": { + "description": "实例的Cpu拓扑类型。取值范围:\n\n- ContinuousCoreToHTMapping:当选择`ContinuousCoreToHTMapping`时,实例的Cpu拓扑中,实例的同一个Core的HT是连续的。\n- DiscreteCoreToHTMapping:当选择`DiscreteCoreToHTMapping`时,实例的同一个Core的HT是离散的。\n\n默认值:无。\n\n>仅部分实例规格族支持使用本参数,具体支持实例规格族请参见[查看和修改CPU拓扑结构](https://help.aliyun.com/document_detail/2636059.html)。", + "example": "DiscreteCoreToHTMapping", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "SecurityOptions.TrustedSystemMode", + "required": false, + "schema": { + "description": "可信系统模式。取值:vTPM。\n\n目前,可信系统模式支持的实例规格族:\n- g7、c7、r7。\n- 安全增强型(g7t、c7t、r7t)。\n\n当您创建以上实例规格族的ECS实例时,需要设置该参数。具体说明如下:\n\n- 如果您使用阿里云可信系统,请将该参数值设置为vTPM,在实例启动时即可通过阿里云可信系统完成可信校验。\n- 如果您不使用阿里云可信系统,可以不设置该参数值,但您需要注意,如果您所创建的ECS实例使用了Enclave机密计算模式(`SecurityOptions.ConfidentialComputingMode=Enclave`),则该ECS实例也会启用可信系统。\n- 通过OpenAPI创建可信系统的ECS实例时,只能调用`RunInstances`实现,`CreateInstance`目前不支持设置`SecurityOptions.TrustedSystemMode`参数。\n>如果您在创建实例的时候指定其为可信实例,那么当您更换系统盘时只能使用支持可信系统的镜像。\n\n关于可信系统的更多信息,请参见[安全增强型实例可信功能概述](https://help.aliyun.com/document_detail/201394.html)。", + "example": "vTPM", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "SecurityOptions.ConfidentialComputingMode", + "required": false, + "schema": { + "description": "机密计算模式。取值:Enclave。\n\n该参数取值为Enclave时,表示ECS实例使用Enclave构建机密计算环境。目前仅实例规格族c7、g7、r7,支持调用`RunInstances`时指定该参数使用Enclave机密计算。您需要注意:\n\n- 机密计算功能正在邀测中。\n\n- 通过OpenAPI创建Enclave机密计算的ECS实例时,只能调用`RunInstances`实现,`CreateInstance`目前不支持设置`SecurityOptions.ConfidentialComputingMode`参数。\n\n- Enclave机密计算依托可信系统(vTPM)实现,当您指定ECS实例使用Enclave构建机密计算环境时,该实例同时也会启用可信系统。因此,调用该接口时,如果设置了`SecurityOptions.ConfidentialComputingMode=Enclave`,则无论您是否设置了`SecurityOptions.TrustedSystemMode=vTPM`,最终创建的ECS实例均会启用Enclave机密计算模式以及可信系统。\n\n关于机密计算的更多信息,请参见[使用Enclave构建机密计算环境](https://help.aliyun.com/document_detail/203433.html)。\n", + "example": "Enclave", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "HttpEndpoint", + "required": false, + "schema": { + "description": "是否启用实例元数据的访问通道。取值范围:\n- enabled:启用。\n- disabled:禁用。\n\n默认值:enabled。\n>有关实例元数据的信息,请参见[实例元数据概述](https://help.aliyun.com/document_detail/49122.html)。", + "example": "enabled", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "HttpTokens", + "required": false, + "schema": { + "description": "访问实例元数据时是否强制使用加固模式(IMDSv2)。取值范围:\n- optional:不强制使用。\n- required:强制使用。设置该取值后,普通模式无法访问实例元数据。\n\n默认值:optional。\n>有关访问实例元数据的模式,请参见[实例元数据访问模式](https://help.aliyun.com/document_detail/150575.html)。", + "example": "optional", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "HttpPutResponseHopLimit", + "required": false, + "schema": { + "description": "实例元数据请求所需的HTTP PUT响应跃点限制。取值范围:1~64。\n \n默认值:1。\n", + "example": "3", + "format": "int32", + "required": false, + "type": "integer" + } + }, + { + "in": "query", + "name": "PrivatePoolOptions.MatchCriteria", + "required": false, + "schema": { + "description": "实例启动的私有池容量选项。弹性保障服务或容量预定服务在生效后会生成私有池容量,供实例启动时选择。取值范围:\n\n- Open:开放模式。将自动匹配开放类型的私有池容量。如果没有符合条件的私有池容量,则使用公共池资源启动。该模式下无需设置`PrivatePoolOptions.Id`参数。\n- Target:指定模式。使用指定的私有池容量启动实例,如果该私有池容量不可用,则实例会启动失败。该模式下必须指定私有池ID,即`PrivatePoolOptions.Id`参数为必填项。\n- None:不使用模式。实例启动将不使用私有池容量。\n\n默认值:None。\n\n以下任一场景,实例启动的私有池容量选项只能取值`None`或不传值。\n- 创建抢占式实例。\n- 创建经典网络类型的ECS实例。\n- 在专有宿主机DDH上创建ECS实例。", + "example": "Open", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "PrivatePoolOptions.Id", + "required": false, + "schema": { + "description": "私有池ID。即弹性保障服务ID或容量预定服务ID。", + "example": "eap-bp67acfmxazb4****", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "Isp", + "required": false, + "schema": { + "description": ">该参数正在邀测中,暂未开放使用。", + "example": "null", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "SchedulerOptions.DedicatedHostClusterId", + "required": false, + "schema": { + "description": "指定ECS实例所属的专有宿主机集群,系统会自动选择该专有宿主机集群中的一台专有宿主机部署ECS实例。\n\n> 仅在`Tenancy`设置为`host`时生效。\n\n在您同时指定了专有宿主机(`DedicatedHostId`)和专有宿主机集群(`SchedulerOptions.DedicatedHostClusterId`)时:\n- 如果专有宿主机属于专有宿主机集群,则优先将ECS实例部署在指定的专有宿主机上。\n- 如果专有宿主机不属于专有宿主机集群,则ECS实例创建失败。\n\n您可以通过[DescribeDedicatedHostClusters](https://help.aliyun.com/document_detail/184145.html)查询专有宿主机集群ID列表。\n\n", + "example": "dc-bp12wlf6am0vz9v2****", + "required": false, + "type": "string" + } + }, + { + "in": "query", + "name": "SecurityGroupIds", + "required": false, + "schema": { + "description": "将实例同时加入多个安全组。N的取值范围与实例能够加入安全组配额有关。更多信息,请参见[安全组限制](https://help.aliyun.com/document_detail/101348.html)。\n\n您需要注意:\n\n- 不支持同时设置`SecurityGroupId`和`SecurityGroupIds.N`。\n- 如果`NetworkInterface.N.InstanceType`取值为`Primary`,则不能设置`SecurityGroupId`或`SecurityGroupIds.N`,只能设置`NetworkInterface.N.SecurityGroupId`或`NetworkInterface.N.SecurityGroupIds.N`。", + "example": "sg-bp15ed6xe1yxeycg7****", + "ext": { + "style": "repeatList" + }, + "items": { + "description": "将实例同时加入多个安全组。N的取值范围与实例能够加入安全组配额有关。更多信息,请参见[安全组限制](https://help.aliyun.com/document_detail/101348.html)。\n\n您需要注意:\n\n- 不支持同时设置`SecurityGroupId`和`SecurityGroupIds.N`。\n- 如果`NetworkInterface.N.InstanceType`取值为`Primary`,则不能设置`SecurityGroupId`或`SecurityGroupIds.N`,只能设置`NetworkInterface.N.SecurityGroupId`或`NetworkInterface.N.SecurityGroupIds.N`。", + "example": "sg-bp15ed6xe1yxeycg7****", + "required": false, + "type": "string" + }, + "maxItems": 16, + "required": false, + "type": "array" + } + }, + { + "in": "query", + "name": "HostNames", + "required": false, + "schema": { + "description": "创建多台实例时,为每台实例指定不同的主机名。", + "example": "ecs-host-01", + "ext": { + "style": "repeatList" + }, + "items": { + "description": "创建多台实例时,为每台实例指定不同的主机名。限制说明如下:\n\n- N的个数需要和`Amount`参数值保持一致。例如,`Amount=2`时,通过该参数指定主机名的格式为`HostNames.1=test1`和`HostNames.2=test2`。\n- 不支持同时设置`HostName`参数和`HostNames.N`参数。\n- 半角句号(.)和短划线(-)不能作为首尾字符,更不能连续使用。\n- Windows实例:字符长度为2~15,不支持半角句号(.),不能全是数字。允许包含大小写英文字母、数字和短划线(-)。\n- 其他类型实例(Linux等):字符长度为2~64,支持多个半角句号(.),点之间为一段,每段允许包含大小写英文字母、数字和短划线(-)。", + "example": "ecs-host-01", + "required": false, + "type": "string" + }, + "maxItems": 1000, + "required": false, + "type": "array" + } + }, + { + "in": "query", + "name": "DataDisk", + "required": false, + "schema": { + "description": "数据盘信息集合。", + "ext": { + "style": "repeatList" + }, + "items": { + "description": "数据盘信息集合。", + "properties": { + "AutoSnapshotPolicyId": { + "description": "数据盘采用的自动快照策略ID。", + "example": "sp-bp67acfmxazb4p****", + "required": false, + "type": "string" + }, + "BurstingEnabled": { + "description": "是否开启Burst(性能突发)。取值范围:\n\n- true:是。\n- false:否。\n\n>当DiskCategory取值为cloud_auto时才支持设置该参数。更多信息,请参见[ESSD AutoPL云盘](https://help.aliyun.com/document_detail/368372.html)。", + "example": "false", + "required": false, + "type": "boolean" + }, + "Category": { + "description": "数据盘N的云盘种类。取值范围:\n\n- cloud_efficiency:高效云盘。\n- cloud_ssd:SSD云盘。\n- cloud_essd:ESSD云盘。\n- cloud:普通云盘。\n- cloud_auto:ESSD AutoPL云盘。\n- cloud_essd_entry:ESSD Entry云盘。\n>仅当`InstanceType`设置为`ecs.u1`或`ecs.e`规格族时,该参数支持`cloud_essd_entry`。\n\n对于I/O优化实例,默认值为cloud_efficiency。对于非I/O优化实例,默认值为cloud。", + "example": "cloud_ssd", + "required": false, + "type": "string" + }, + "DeleteWithInstance": { + "description": "表示数据盘是否随实例释放。取值范围:\n- true:数据盘随实例释放。\n- false:数据盘不随实例释放。\n\n默认值为true。", + "example": "true", + "required": false, + "type": "boolean" + }, + "Description": { + "description": "数据盘的描述。长度为2~256个英文或中文字符,不能以`http://`和`https://`开头。", + "example": "DataDisk_Description", + "required": false, + "type": "string" + }, + "Device": { + "description": "数据盘的挂载点。挂载的数据盘数量不同,挂载点的命名不同:\n\n- 1~25块数据盘:/dev/xvd`[b-z]`\n\n- 大于25块数据盘:/dev/xvd`[aa-zz]`,例如第26块数据盘会被命名为/dev/xvdaa,第27块数据盘为/dev/xvdab,以此类推。\n\n>该参数仅用于全镜像(整机镜像)场景。您可以通过将此参数设置为全镜像中数据盘对应的挂载点,并修改对应的`DataDisk.N.Size`和`DataDisk.N.Category`参数,达到修改全镜像中数据盘磁盘种类和大小的目的。", + "example": "/dev/xvdb", + "required": false, + "type": "string" + }, + "DiskName": { + "description": "数据盘名称。长度为2~128个英文或中文字符。必须以大小写字母或中文开头,不能以`http://`和`https://`开头。可以包含数字、半角句号(.)、半角冒号(:)、下划线(_)或者短划线(-)。", + "example": "cloud_ssdData", + "required": false, + "type": "string" + }, + "EncryptAlgorithm": { + "description": ">该参数暂未开放使用。", + "example": "null", + "required": false, + "type": "string" + }, + "Encrypted": { + "description": "数据盘N是否加密。取值范围:\n- true:加密。\n- false:不加密。\n\n默认值:false。", + "example": "false", + "required": false, + "type": "string" + }, + "KMSKeyId": { + "description": "数据盘对应的KMS密钥ID。", + "example": "0e478b7a-4262-4802-b8cb-00d3fb40****", + "required": false, + "type": "string" + }, + "PerformanceLevel": { + "description": "创建ESSD云盘作为数据盘使用时,设置云盘的性能等级。N的取值必须和`DataDisk.N.Category=cloud_essd`中的N保持一致。取值范围:\n\n- PL0:单盘最高随机读写IOPS 1万。\n- PL1(默认):单盘最高随机读写IOPS 5万。\n- PL2:单盘最高随机读写IOPS 10万。\n- PL3:单盘最高随机读写IOPS 100万。\n\n有关如何选择ESSD性能等级,请参见[ESSD云盘](https://help.aliyun.com/document_detail/122389.html)。", + "example": "PL1", + "required": false, + "type": "string" + }, + "ProvisionedIops": { + "description": "ESSD AutoPL云盘预配置的读写IOPS。可能值:0~min{50,000, 1000*容量-基准性能}。\n\n基准性能=min{1,800+50*容量, 50000}。\n\n>当DiskCategory取值为cloud_auto时才支持设置该参数。更多信息,请参见[ESSD AutoPL云盘](https://help.aliyun.com/document_detail/368372.html)。", + "example": "40000", + "format": "int64", + "minimum": "0", + "required": false, + "type": "integer" + }, + "Size": { + "description": "第n个数据盘的容量大小,N的取值范围为1~16,内存单位为GiB。取值范围:\n\n- cloud_efficiency:20~32768。\n- cloud_ssd:20~32768。\n- cloud_essd:具体取值范围与`DataDisk.N.PerformanceLevel`的取值有关。 \n - PL0:40~32768。\n - PL1:20~32768。\n - PL2:461~32768。\n - PL3:1261~32768。\n- cloud:5~2000。\n- cloud_auto:40~32768。\n- cloud_essd_entry:10~32768。\n\n该参数的取值必须大于等于参数`SnapshotId`指定的快照的大小。\n", + "example": "2000", + "format": "int32", + "required": false, + "type": "integer" + }, + "SnapshotId": { + "description": "创建数据盘N使用的快照。N的取值范围为1~16。\n\n指定参数`DataDisk.N.SnapshotId`后,参数`DataDisk.N.Size`会被忽略,实际创建的云盘大小为指定的快照的大小。不能使用早于2013年7月15日(含)创建的快照,请求会报错被拒绝。", + "example": "s-bp17441ohwka0yuh****", + "required": false, + "type": "string" + }, + "StorageClusterId": { + "description": "专属块存储集群ID。如果您在创建ECS实例时,需要使用专属块存储集群中的云盘资源作为数据盘,请设置该参数。", + "example": "dbsc-j5e1sf2vaf5he8m2****", + "required": false, + "type": "string" + } + }, + "required": false, + "type": "object" + }, + "maxItems": 16, + "required": false, + "type": "array" + } + }, + { + "in": "query", + "name": "Arn", + "required": false, + "schema": { + "description": "该参数暂未开放使用。", + "ext": { + "style": "repeatList" + }, + "items": { + "description": "该参数暂未开放使用。", + "properties": { + "AssumeRoleFor": { + "description": "该参数暂未开放使用。", + "example": "null", + "format": "int64", + "required": false, + "type": "integer" + }, + "RoleType": { + "description": "该参数暂未开放使用。", + "example": "null", + "required": false, + "type": "string" + }, + "Rolearn": { + "description": "该参数暂未开放使用。", + "example": "null", + "required": false, + "type": "string" + } + }, + "required": false, + "type": "object" + }, + "maxItems": 16, + "required": false, + "type": "array" + } + }, + { + "in": "query", + "name": "NetworkInterface", + "required": false, + "schema": { + "description": "弹性网卡信息。", + "ext": { + "style": "repeatList" + }, + "items": { + "description": "弹性网卡信息。", + "properties": { + "DeleteOnRelease": { + "description": "释放实例时是否保留网卡。取值范围:\n\n- true:不保留。\n\n- false:保留。\n\n默认值:true。\n\n>该参数只对辅助网卡生效。", + "example": "true", + "required": false, + "type": "boolean" + }, + "Description": { + "description": "弹性网卡的描述。\n\n您需要注意:\n\n- N的取值范围为1~2,设置1个弹性网卡时,支持设置1个主网卡或1个辅助网卡;设置2个弹性网卡时,仅支持同时设置1个主网卡和1个辅助网卡。\n- 长度为2~256个英文或中文字符,不能以`http://`或`https://`开头。\n- 如果`NetworkInterface.N.InstanceType`取值为`Primary`,则无需设置该参数。", + "example": "Network_Description", + "required": false, + "type": "string" + }, + "InstanceType": { + "description": "弹性网卡类型。N的取值范围为1~2,设置1个弹性网卡时,支持设置1个主网卡或1个辅助网卡;设置2个弹性网卡时,仅支持同时设置1个主网卡和1个辅助网卡。\n\n参数取值范围:\n\n- Primary:主网卡。\n- Secondary:辅助网卡。\n\n默认值:Secondary。", + "example": "Secondary", + "required": false, + "type": "string" + }, + "Ipv6Address": { + "description": "为主网卡指定一个或多个IPv6地址。支持设置最多10个IPv6地址,即第二个N的取值范围:1~10。\n\n取值示例:`Ipv6Address.1=2001:db8:1234:1a00::***`\n\n您需要注意:\n\n- `NetworkInterface.N.InstanceType`取值为`Primary`时,设置该参数才会生效。如果`NetworkInterface.N.InstanceType`取值为`Secondary`或空值,则不能设置该参数。\n\n- 设置该参数后,`Amount`取值只能为1,且不能再设置`Ipv6AddressCount`、`Ipv6Address.N`或`NetworkInterface.N.Ipv6AddressCount`。", + "items": { + "description": "为主网卡指定一个或多个IPv6地址。支持设置最多10个IPv6地址,即第二个N的取值范围:1~10\n\n取值示例:`Ipv6Address.1=2001:db8:1234:1a00::***`\n\n您需要注意:\n\n- `NetworkInterface.N.InstanceType`取值为`Primary`时,设置该参数才会生效。如果`NetworkInterface.N.InstanceType`取值为`Secondary`或空值,则不能设置该参数。\n\n- 设置该参数后,`Amount`取值只能为1,且不能再设置`Ipv6AddressCount`、`Ipv6Address.N`或`NetworkInterface.N.Ipv6AddressCount`。", + "example": "2001:db8:1234:1a00::***", + "required": false, + "type": "string" + }, + "maxItems": 10, + "required": false, + "type": "array" + }, + "Ipv6AddressCount": { + "description": "为主网卡指定随机生成的IPv6地址数量。取值范围:1~10\n\n您需要注意:\n\n- `NetworkInterface.N.InstanceType`取值为`Primary`时,设置该参数才会生效。如果`NetworkInterface.N.InstanceType`取值为`Secondary`或空值,则不能设置该参数。\n\n- 设置该参数后,您不能再设置`Ipv6AddressCount`、`Ipv6Address.N`或`NetworkInterface.N.Ipv6Address.N`。", + "example": "1", + "format": "int64", + "required": false, + "type": "integer" + }, + "NetworkCardIndex": { + "description": "网卡指定的物理网卡索引。\n\n您需要注意:\n- 只有特定实例规格支持指定物理网卡索引。\n- NetworkInterface.N.InstanceType取值为Primary时,对于支持物理网卡的实例规格,如果设置此参数,只能设置为0。\n- NetworkInterface.N.InstanceType取值为Secondary或者空值,对于支持物理网卡的实例规格,此参数可以依据实例规格设置。更多信息,请参见[实例规格族](https://help.aliyun.com/document_detail/25378.html)。", + "example": "0", + "format": "int32", + "required": false, + "type": "integer" + }, + "NetworkInterfaceId": { + "description": "随实例附加的弹性网卡ID。\n\n>该参数只对辅助网卡生效。", + "example": "eni-bp1gn106np8jhxhj****", + "required": false, + "type": "string" + }, + "NetworkInterfaceName": { + "description": "弹性网卡名称。\n\n您需要注意:\n\n- N的取值范围为1~2,设置1个弹性网卡时,支持设置1个主网卡或1个辅助网卡;设置2个弹性网卡时,仅支持同时设置1个主网卡和1个辅助网卡。\n\n- 如果`NetworkInterface.N.InstanceType`取值为`Primary`,则无需设置该参数。", + "example": "Network_Name", + "required": false, + "type": "string" + }, + "NetworkInterfaceTrafficMode": { + "description": "网卡的通讯模式。参数取值范围:\n\n- Standard:使用TCP通讯模式。\n- HighPerformance:开启ERI(Elastic RDMA Interface)接口,使用RDMA通讯模式。\n\n默认值:Standard。\n\n>RDMA模式的弹性网卡数量不能超过该实例规格族的限制。更多信息,请参见[实例规格族](https://help.aliyun.com/document_detail/25378.html)。", + "example": "Standard", + "required": false, + "type": "string" + }, + "PrimaryIpAddress": { + "description": "添加一张弹性网卡并设置主IP地址。\n\n您需要注意:\n\n- N的取值范围为1~2:\n - 设置1个弹性网卡时,支持设置1个主网卡或1个辅助网卡。如果`Amount`参数取值大于1,且设置了主网卡并设置了该参数,则表示在批量创建ECS实例时,以指定的主IP地址为起始地址,依次为多台ECS实例分配连续的主IP地址,但需要注意,此时不支持再为实例绑定辅助网卡。\n - 设置2个弹性网卡时,仅支持同时设置1个主网卡和1个辅助网卡,但需要注意,如果`Amount`参数取值大于1且已为主网卡设置了该参数,则不支持再设置辅助网卡(即不支持再设置`NetworkInterface.2.InstanceType=Secondary`)。\n\n- 如果`NetworkInterface.N.InstanceType`取值为`Primary`,则该参数的作用等同于`PrivateIpAddress`,但需要注意不能同时设置`PrivateIpAddress`参数。\n\n- 如果`NetworkInterface.N.InstanceType`取值为`Secondary`或空值,表示为辅助网卡设置主IP地址。默认从网卡所属的交换机网段中随机选择一个IP地址。\n\n> 创建ECS实例时,您最多能添加一张辅助网卡。实例创建成功后,您可以调用[CreateNetworkInterface](https://help.aliyun.com/document_detail/58504.html)和[AttachNetworkInterface](https://help.aliyun.com/document_detail/58515.html)添加更多的辅助网卡。", + "example": "172.16.**.**", + "required": false, + "type": "string" + }, + "QueueNumber": { + "description": "弹性网卡队列数。\n\n您需要注意:\n\n- N的取值范围为1~2,设置1个弹性网卡时,支持设置1个主网卡或1个辅助网卡;设置2个弹性网卡时,仅支持同时设置1个主网卡和1个辅助网卡。\n\n- 不能超过实例规格允许的单块网卡最大队列数。\n\n- 实例的所有网卡累加队列数不能超过实例规格允许的队列数总配额。实例规格的单块网卡最大队列数和总配额可以通过[DescribeInstanceTypes](https://help.aliyun.com/document_detail/25620.html)接口查询`MaximumQueueNumberPerEni`、`TotalEniQueueQuantity`字段。\n\n- 如果`NetworkInterface.N.InstanceType`取值为`Primary`,且设置了该参数取值,则不能再设置`NetworkInterfaceQueueNumber`参数。", + "example": "8", + "format": "int32", + "minimum": "1", + "required": false, + "type": "integer" + }, + "QueuePairNumber": { + "description": "RDMA网卡队列数。", + "example": "0", + "format": "int64", + "required": false, + "type": "integer" + }, + "SecurityGroupId": { + "description": "弹性网卡所属的安全组ID。\n\n您需要注意:\n\n- N的取值范围为1~2,设置1个弹性网卡时,支持设置1个主网卡或1个辅助网卡;设置2个弹性网卡时,仅支持同时设置1个主网卡和1个辅助网卡。\n\n- 如果`NetworkInterface.N.InstanceType`取值为`Primary`,则必须设置该参数。此时该参数的作用等同于`SecurityGroupId`,但需要注意不能再设置`SecurityGroupId`、`SecurityGroupIds.N`或`NetworkInterface.N.SecurityGroupIds.N`。\n\n- 如果`NetworkInterface.N.InstanceType`取值为`Secondary`或空值,则该参数为非必填参数。默认值为ECS实例所属的安全组。", + "example": "sg-bp67acfmxazb4p****", + "required": false, + "type": "string" + }, + "SecurityGroupIds": { + "description": "弹性网卡所属的一个或多个安全组ID。\n\n- 第一个N的取值范围为1~2,设置1个弹性网卡时,支持设置1个主网卡或1个辅助网卡;设置2个弹性网卡时,仅支持同时设置1个主网卡和1个辅助网卡。\n- 第二个N表示可以指定一个或多个安全组ID。N的取值范围与实例能够加入安全组配额有关。更多信息,请参见[安全组限制](https://help.aliyun.com/document_detail/25412.html#SecurityGroupQuota1)。\n\n您需要注意:\n\n- 如果`NetworkInterface.N.InstanceType`取值为`Primary`,则必须设置该参数或`NetworkInterface.N.SecurityGroupId`。此时该参数的作用等同于`SecurityGroupIds.N`,但需要注意不能再设置`SecurityGroupId`、`SecurityGroupIds.N`或`NetworkInterface.N.SecurityGroupId`。\n\n- 如果`NetworkInterface.N.InstanceType`取值为`Secondary`或空值,则该参数为非必填参数。默认值为ECS实例所属的安全组。", + "example": "sg-bp15ed6xe1yxeycg7****", + "items": { + "description": "弹性网卡所属的一个或多个安全组ID。\n\n- 第一个N的取值范围为1~2,设置1个弹性网卡时,支持设置1个主网卡或1个辅助网卡;设置2个弹性网卡时,仅支持同时设置1个主网卡和1个辅助网卡。\n- 第二个N表示可以指定一个或多个安全组ID。N的取值范围与实例能够加入安全组配额有关。更多信息,请参见[安全组限制](https://help.aliyun.com/document_detail/25412.html#SecurityGroupQuota1)。\n\n您需要注意:\n\n- 如果`NetworkInterface.N.InstanceType`取值为`Primary`,则必须设置该参数或`NetworkInterface.N.SecurityGroupId`。此时该参数的作用等同于`SecurityGroupIds.N`,但需要注意不能再设置`SecurityGroupId`、`SecurityGroupIds.N`或`NetworkInterface.N.SecurityGroupId`。\n\n- 如果`NetworkInterface.N.InstanceType`取值为`Secondary`或空值,则该参数为非必填参数。默认值为ECS实例所属的安全组。", + "example": "sg-bp15ed6xe1yxeycg7****", + "required": false, + "type": "string" + }, + "maxItems": 16, + "required": false, + "type": "array" + }, + "VSwitchId": { + "description": "弹性网卡所属的虚拟交换机ID。\n\n您需要注意:\n\n- N的取值范围为1~2,设置1个弹性网卡时,支持设置1个主网卡或1个辅助网卡;设置2个弹性网卡时,仅支持同时设置1个主网卡和1个辅助网卡。\n\n- 如果`NetworkInterface.N.InstanceType`取值为`Primary`,则必须设置该参数。此时该参数的作用等同于`VSwitchId`,但需要注意不能同时设置`VSwitchId`参数。\n\n- 如果`NetworkInterface.N.InstanceType`取值为`Secondary`或空值,则该参数为非必填参数。默认值为ECS实例所属的虚拟交换机。", + "example": "vsw-bp67acfmxazb4p****", + "required": false, + "type": "string" + } + }, + "required": false, + "type": "object" + }, + "maxItems": 8, + "required": false, + "type": "array" + } + }, + { + "in": "query", + "name": "Tag", + "required": false, + "schema": { + "description": "实例、云盘和主网卡的标签信息。", + "ext": { + "style": "repeatList" + }, + "items": { + "description": "实例、云盘和主网卡的标签信息。", + "properties": { + "Key": { + "description": "实例、云盘和主网卡的标签键。N的取值范围:1~20。一旦传入该值,则不允许为空字符串。最多支持128个字符,不能以aliyun和acs:开头,不能包含http://或 https://。", + "example": "TestKey", + "required": false, + "type": "string" + }, + "Value": { + "description": "实例、云盘和主网卡的标签值。N的取值范围:1~20。一旦传入该值,可以为空字符串。最多支持128个字符,不能以acs:开头,不能包含http://或者https://。", + "example": "TestValue", + "required": false, + "type": "string" + } + }, + "required": false, + "type": "object" + }, + "maxItems": 71, + "required": false, + "type": "array" + } + }, + { + "in": "query", + "name": "Ipv6Address", + "required": false, + "schema": { + "description": "为主网卡指定一个或多个IPv6地址。支持设置最多10个IPv6地址,即N的取值范围:1~10。\n\n取值示例:`Ipv6Address.1=2001:db8:1234:1a00::***`。\n\n您需要注意:\n\n- 设置了`Ipv6Address.N`时,`Amount`参数取值只能为1,且不能同时设置`Ipv6AddressCount`。\n\n- 如果`NetworkInterface.N.InstanceType`取值为`Primary`,则不能设置`Ipv6Addresses.N`或`Ipv6AddressCount`,而是需要设置`NetworkInterface.N.Ipv6Addresses.N`或`NetworkInterface.N.Ipv6AddressCount`。", + "example": "Ipv6Address.1=2001:db8:1234:1a00::***", + "ext": { + "style": "repeatList" + }, + "items": { + "description": "为主网卡指定一个或多个IPv6地址。支持设置最多10个IPv6地址,即N的取值范围:1~10。\n\n取值示例:`Ipv6Address.1=2001:db8:1234:1a00::***`。\n\n您需要注意:\n\n- 设置了`Ipv6Address.N`时,`Amount`参数取值只能为1,且不能同时设置`Ipv6AddressCount`。\n\n- 如果`NetworkInterface.N.InstanceType`取值为`Primary`,则不能设置`Ipv6Addresses.N`或`Ipv6AddressCount`,而是需要设置`NetworkInterface.N.Ipv6Addresses.N`或`NetworkInterface.N.Ipv6AddressCount`。", + "example": "2001:db8:1234:1a00::***", + "required": false, + "type": "string" + }, + "maxItems": 10, + "required": false, + "type": "array" + } + }, + { + "in": "query", + "name": "SystemDisk", + "required": false, + "schema": { + "description": "系统盘相关参数,目前专属块存储集群ID(`StorageClusterId`)需要通过该参数设置参数值。", + "ext": { + "style": "flat" + }, + "properties": { + "BurstingEnabled": { + "description": "是否开启Burst(性能突发)。取值范围:\n\n- true:是。\n- false:否。\n\n>当`SystemDisk.Category`取值为`cloud_auto`时才支持设置该参数。更多信息,请参见[ESSD AutoPL云盘](https://help.aliyun.com/document_detail/368372.html)。", + "example": "false", + "required": false, + "type": "boolean" + }, + "EncryptAlgorithm": { + "description": ">该参数暂未开放使用。", + "example": "null", + "required": false, + "type": "string" + }, + "Encrypted": { + "description": "系统盘是否加密。取值范围:\n\n- true:加密。\n\n- false:不加密。\n\n默认值:false。\n\n>中国(香港)D可用区、新加坡A可用区暂不支持在创建实例时加密系统盘。", + "example": "false", + "required": false, + "type": "string" + }, + "KMSKeyId": { + "description": "系统盘对应的KMS密钥ID。", + "example": "0e478b7a-4262-4802-b8cb-00d3fb40****", + "required": false, + "type": "string" + }, + "ProvisionedIops": { + "description": "ESSD AutoPL云盘预配置的读写IOPS。可能值:0~min{50,000, 1000*容量-基准性能}。\n\n基准性能=min{1,800+50*容量, 50,000}。\n\n>当`SystemDisk.Category`取值为`cloud_auto`时才支持设置该参数。更多信息,请参见[ESSD AutoPL云盘](https://help.aliyun.com/document_detail/368372.html)。", + "example": "40000", + "format": "int64", + "minimum": "0", + "required": false, + "type": "integer" + }, + "StorageClusterId": { + "description": "专属块存储集群ID。如果您在创建ECS实例时,需要使用专属块存储集群中的云盘资源作为系统盘,请设置该参数。", + "example": "dbsc-j5e1sf2vaf5he8m2****", + "required": false, + "type": "string" + } + }, + "required": false, + "type": "object" + } + }, + { + "in": "query", + "name": "ImageOptions", + "required": false, + "schema": { + "description": "镜像相关属性信息。", + "ext": { + "style": "flat" + }, + "properties": { + "LoginAsNonRoot": { + "description": "使用该镜像的实例是否支持使用ecs-user用户登录。可能值:\n\n- true:是\n\n- false:否", + "example": "false", + "required": false, + "type": "boolean" + } + }, + "required": false, + "type": "object" + } + }, + { + "in": "query", + "name": "NetworkOptions", + "required": false, + "schema": { + "description": "网络相关属性参数。", + "ext": { + "style": "flat" + }, + "properties": { + "EnableJumboFrame": { + "description": "实例是否开启Jumbo frame特性。参数取值范围:\n\n- false:不开启Jumbo frame, 该实例下的所有网卡(包括主网卡及辅助网卡)MTU取值为1500。\n\n- true:开启Jumbo frame, 该实例下的所有网卡(包括主网卡及辅助网卡)的MTU取值为8500。\n\n默认值:true。\n\n>只有八代以上部分实例规格支持开启Jumbo frame特性。更多信息请参见[ECS实例MTU](https://help.aliyun.com/document_detail/200512.html)。", + "example": "false", + "required": false, + "type": "boolean" + } + }, + "required": false, + "type": "object" + } + }, + { + "in": "query", + "name": "AutoPay", + "required": false, + "schema": { + "description": "创建实例时,是否自动支付。取值范围:\n\n- true:自动支付。\n\n > 自动支付时,请确保支付方式余额充足,否则会生成异常订单,只能作废订单。如果您的支付方式余额不足,可以将参数`AutoPay`置为`false`,此时会生成未支付订单,您可以登录ECS管理控制台自行支付。\n\n- false:只生成订单不扣费。\n\n > 当`InstanceChargeType` 取值为`PostPaid`时,`AutoPay`不能设置为`false`。\n\n默认值:true。\n\n", + "example": "true", "required": false, - "title": "是否按顺序输出文字块。false表示从左往右,从上到下的顺序;true表示从上到下,从左往右的顺序", "type": "boolean" } } @@ -108,38 +2978,41 @@ export const routerMeta = { "responses": { "200": { "schema": { - "description": "Schema of Response", "properties": { - "Code": { - "description": "状态码", - "example": "200", - "type": "string" - }, - "Data": { - "description": "返回数据", - "example": "{\"content\":\"炼句 提问方式 1.请赏析诗歌某一联(句) 2.赏析某一联(句)的妙处 3.请赏析诗歌某、角度抒胸意、借景抒情、托物\",\"height\":1277,\"orgHeight\":1277,\"orgWidth\":1080,\"prism_version\":\"1.0.9\",\"prism_wnum\":26,\"prism_wordsInfo\":[{\"angle\":-87,\"direction\":0,\"height\":83,\"pos\":[{\"x\":177,\"y\":56},{\"x\":260,\"y\":60},{\"x\":259,\"y\":88},{\"x\":176,\"y\":84}],\"prob\":96,\"width\":28,\"word\":\"炼句\",\"x\":203,\"y\":30}],\"width\":1080}", - "type": "string" + "InstanceIdSets": { + "description": "实例ID(`InstanceIdSet`)列表。", + "items": { + "description": "实例ID(`InstanceIdSet`)列表。", + "example": "[\"i-bp67acfmxazb4pd2****\", \"i-bp1i43l28m7u48p1****\", \"i-bp12yqg7jdyxl11f****\"]", + "type": "string" + }, + "type": "array" }, - "Message": { - "description": "详细信息 ", - "example": "message", + "OrderId": { + "description": "订单ID。该参数只有创建包年包月ECS实例(请求参数`InstanceChargeType=PrePaid`)时有返回值。", + "example": "123456****", "type": "string" }, "RequestId": { - "description": "请求唯一 ID", - "example": "43A29C77-405E-4CC0-BC55-EE694AD00655", + "description": "请求ID。", + "example": "473469C7-AA6F-4DC5-B3DB-A3DC0DE3****", "type": "string" + }, + "TradePrice": { + "description": "订单成交价。", + "example": "0.165", + "format": "float", + "type": "number" } }, - "title": "Schema of Response", "type": "object" } } }, - "summary": "通用手写体识别。", - "title": "通用手写体识别" + "summary": "创建一台或多台按量付费或者包年包月ECS实例。", + "title": "创建一台或多台按量付费或者包年包月ECS实例" }, - "name": "RecognizeHandwriting", + "name": "RunInstances", "pageType": "document", "schemaType": "api" } \ No newline at end of file diff --git a/media/src/pages/_app.tsx b/media/src/pages/_app.tsx index 9179c2e..6b5403c 100644 --- a/media/src/pages/_app.tsx +++ b/media/src/pages/_app.tsx @@ -1,6 +1,7 @@ import type { AppProps } from "next/app"; import "./document/index.scss"; import "../styles/globals.css"; +import "../main.css" import "@alicloud/console-components/dist/wind.css"; import React from "react"; diff --git a/media/src/pages/document/index.scss b/media/src/pages/document/index.scss index 5e32002..c62ebc9 100644 --- a/media/src/pages/document/index.scss +++ b/media/src/pages/document/index.scss @@ -101,17 +101,6 @@ $create-color: #007100; min-width: 100px; } - .api-page-content { - padding: 12px 20px 12px; - display: flex; - .api-debug { - width: 356px; - } - .right-panel { - width: 100%; - } - } - .desc-mod { border: 1px solid #89a6b5; border-radius: 2px; diff --git a/media/src/service/UIService.ts b/media/src/service/UIService.ts index 5f23a8d..6fbd085 100644 --- a/media/src/service/UIService.ts +++ b/media/src/service/UIService.ts @@ -50,6 +50,11 @@ export const PontUIService = { spec: any; }): Promise => {}, - /** 发起调用 */ + /** request openapi */ openAPIRequest: async (params = {}): Promise => new ExtensionResponse, + + /** get endpoints list */ + requestEndpoints: async (product: string) => { + return [] as any; + }, }; \ No newline at end of file diff --git a/src/Service.ts b/src/Service.ts index 326d0f3..4b12497 100644 --- a/src/Service.ts +++ b/src/Service.ts @@ -140,6 +140,16 @@ export class AlicloudAPIService { return {}; } + async requestEndpoints(product: string) { + const resStr = await fetch( + `https://pre-api.aliyun.com/meta/v1/products/${product}/endpoints.json?language=zh-CN`, + {}, + ).then((res) => res.text()); + const res = JSON.parse(resStr); + return res?.data?.endpoints || []; + } + + async loadProfiles() { const configFilePath = path.join(os.homedir(), ".aliyun/config.json"); const { R_OK, W_OK } = fs.constants; @@ -154,29 +164,35 @@ export class AlicloudAPIService { } async openAPIRequest(requestData) { - const { apiMeta, paramsValue, product, version } = requestData; + const { apiMeta, paramsValue, product, version, endpoint } = requestData; const newParamsValue = getFormatValues(paramsValue, apiMeta?.parameters); - // 定义了一个post变量,用于暂存请求体的信息 - let post = ""; let response = ""; let data; const profilesInfo = await this.loadProfiles(); const profiles = profilesInfo?.profiles; // TODO:用户可以选择使用哪个profile + const security = apiMeta?.ext?.security; + const defaultCredentialType = + security?.length > 0 + ? security.indexOf("AK") < 0 + ? security.indexOf("BearerToken") < 0 + ? "anonymous" + : "bearer" + : "ak" + : "ak"; if (profiles?.length) { try { data = await request({ accessKeyId: profiles[0]?.access_key_id, accessKeySecret: profiles[0]?.access_key_secret, - // TODO:可选服务地址 - endpoint: "ecs-cn-hangzhou.aliyuncs.com", + endpoint: endpoint, action: apiMeta?.name, apiVersion: version, params: newParamsValue || {}, productName: product, meta: this.pontManager.localPontSpecs[0], bodyStyle: undefined, - credential: "AK", + credential: {tyep: defaultCredentialType}, }); response = data; // 设置状态码 diff --git a/src/openApiService/request/request.ts b/src/openApiService/request/request.ts index 3133f76..f07d55c 100644 --- a/src/openApiService/request/request.ts +++ b/src/openApiService/request/request.ts @@ -232,9 +232,7 @@ export const request = async function (options: OpenAPIOptions) { action, reqBodyType: requestType, bodyType: responseType, - // TODO:根据元数据判断鉴权方式 authType:'AK', - // authType: credential && credential.type === 'anonymous' ? 'Anonymous' : 'AK' }; return await client.doRequest(data, request, {}); }; \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index 1c1b922..915a0ae 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -175,6 +175,7 @@ export class VSCodeLogger extends PontLogger { export const htmlTemplate = (context: { cspSource: string; getUri: (uri: string) => any }, pageConfig: any) => { // const initContent = createServerContent(pageConfig); + console.log("pageconfig",pageConfig) return ` From 34b8631ade529e2750ce9a8364944bd6febc80f7 Mon Sep 17 00:00:00 2001 From: yini-chen Date: Tue, 2 Jan 2024 15:13:10 +0800 Subject: [PATCH 02/32] fix : styles --- media/src/components/APIPage/API.module.css | 7 ++++++ media/src/components/APIPage/API.tsx | 24 ++++++++++--------- .../src/components/APIPage/TryAPI/TryAPI.tsx | 2 +- media/src/pages/_app.tsx | 1 + media/src/pages/document/index.module.scss | 14 ++--------- media/src/pages/document/index.scss | 1 + 6 files changed, 25 insertions(+), 24 deletions(-) create mode 100644 media/src/components/APIPage/API.module.css diff --git a/media/src/components/APIPage/API.module.css b/media/src/components/APIPage/API.module.css new file mode 100644 index 0000000..ef2eb45 --- /dev/null +++ b/media/src/components/APIPage/API.module.css @@ -0,0 +1,7 @@ +.pontx-ui-api { + .web-debug{ + margin: 12px 40px 12px; + float:right + } +} + diff --git a/media/src/components/APIPage/API.tsx b/media/src/components/APIPage/API.tsx index 50c36dc..58a8658 100644 --- a/media/src/components/APIPage/API.tsx +++ b/media/src/components/APIPage/API.tsx @@ -160,18 +160,20 @@ export const API: React.FC = (props) => { {selectedApi?.externalDocs ? ( +
+ type="normal" + component="a" + href={selectedApi?.externalDocs?.url} + target="_blank" + + // onClick={() => { + // window.open(selectedApi?.externalDocs?.url, "_blank"); + // }} + > + 去门户网页版页调试 + +
) : null} {/*
*/}
diff --git a/media/src/components/APIPage/TryAPI/TryAPI.tsx b/media/src/components/APIPage/TryAPI/TryAPI.tsx index ef0d3da..7298eff 100644 --- a/media/src/components/APIPage/TryAPI/TryAPI.tsx +++ b/media/src/components/APIPage/TryAPI/TryAPI.tsx @@ -40,7 +40,7 @@ const TAB_PANES = [ export const TryAPI: React.FC = (props) => { const { openAPIResponses, isApiResultLoading,version,apiMeta, product } = APIPageContext.useContainer(); const doc = `${product}::${version}::${apiMeta.name}` - const apiResult = openAPIResponses?.[doc]; + const apiResult = openAPIResponses?.[doc] || apiResponse; const noShowMonacoEditor = ["byte"]; diff --git a/media/src/pages/_app.tsx b/media/src/pages/_app.tsx index 6b5403c..8c36329 100644 --- a/media/src/pages/_app.tsx +++ b/media/src/pages/_app.tsx @@ -1,4 +1,5 @@ import type { AppProps } from "next/app"; +import "./document/index.module.scss"; import "./document/index.scss"; import "../styles/globals.css"; import "../main.css" diff --git a/media/src/pages/document/index.module.scss b/media/src/pages/document/index.module.scss index 26d0cca..abc8ab5 100644 --- a/media/src/pages/document/index.module.scss +++ b/media/src/pages/document/index.module.scss @@ -1,7 +1,9 @@ +@import "semix-schema-table/module/Markdown.scss"; @import "semix-schema-table/module/SchemaTable.scss"; @import "../../components/SemixFormRender/SemixForm.scss"; @import "../../styles/variable.scss"; @import "../../styles/Markdown.scss"; +@import "../../components/APIPage/API.module.css"; $primary-color: #448ef7; $nav-height: 36px; @@ -100,19 +102,7 @@ $create-color: #007100; min-width: 100px; } - .api-page-content { - padding: 12px 20px 12px; - display: flex; - .api-debug { - width: 356px; - } - .right-panel { - width: 100%; - } - } - .desc-mod { - background-color: #d6e0e5; border: 1px solid #89a6b5; border-radius: 2px; padding: 8px 18px; diff --git a/media/src/pages/document/index.scss b/media/src/pages/document/index.scss index c62ebc9..abc8ab5 100644 --- a/media/src/pages/document/index.scss +++ b/media/src/pages/document/index.scss @@ -3,6 +3,7 @@ @import "../../components/SemixFormRender/SemixForm.scss"; @import "../../styles/variable.scss"; @import "../../styles/Markdown.scss"; +@import "../../components/APIPage/API.module.css"; $primary-color: #448ef7; $nav-height: 36px; From 8db97f4be6ee3fefd129be4b60ced27c2391211d Mon Sep 17 00:00:00 2001 From: yini-chen Date: Wed, 3 Jan 2024 10:27:15 +0800 Subject: [PATCH 03/32] feat: update styles and interaction --- media/package.json | 7 +- media/src/components/APIPage/API.tsx | 4 +- .../APIPage/APIDebugger/APIDebugger.tsx | 3 +- .../APIPage/TryAPI/TryAPI.module.css | 175 ++++++++++++++++++ .../src/components/APIPage/TryAPI/TryAPI.tsx | 15 +- media/src/components/APIPage/context.ts | 1 + media/src/mocks/openApiResponse.ts | 1 + media/src/pages/document/index.module.scss | 1 + media/src/pages/document/index.scss | 1 + src/Service.ts | 8 +- 10 files changed, 200 insertions(+), 16 deletions(-) create mode 100644 media/src/components/APIPage/TryAPI/TryAPI.module.css diff --git a/media/package.json b/media/package.json index bf4a8a7..227583c 100644 --- a/media/package.json +++ b/media/package.json @@ -15,9 +15,11 @@ "@alicloud/console-components": "^1.6.2", "@monaco-editor/react": "^4.6.0", "@vercel/ncc": "^0.38.1", + "@vscode-elements/elements": "^1.0.0-pre.10", "antd": "^5.12.3", "intl-format": "^1.2.0", "load-script": "^2.0.0", + "lodash": "^4.17.20", "next": "^14.0.4", "pontx-ui": "latest", "react": "^18.2.0", @@ -26,8 +28,7 @@ "sass": "^1.69.5", "semix-schema-table": "^0.1.3", "styled-components": "^6.1.1", - "xml2js": "^0.6.2", - "lodash": "^4.17.20" + "xml2js": "^0.6.2" }, "resolutions": { "semix-schema-table": "^0.1.3" @@ -44,6 +45,6 @@ "@types/react-dom": "^18.0.3", "@types/vscode": "^1.47.0", "@types/vscode-webview": "^1.57.0", - "@types/blueimp-md5":"^2.7.0" + "@types/blueimp-md5": "^2.7.0" } } diff --git a/media/src/components/APIPage/API.tsx b/media/src/components/APIPage/API.tsx index 58a8658..38a4df8 100644 --- a/media/src/components/APIPage/API.tsx +++ b/media/src/components/APIPage/API.tsx @@ -175,7 +175,7 @@ export const API: React.FC = (props) => { ) : null} - {/*
*/} +
@@ -197,7 +197,7 @@ export const API: React.FC = (props) => {
{/* */} {selectedApi ? ( diff --git a/media/src/components/APIPage/APIDebugger/APIDebugger.tsx b/media/src/components/APIPage/APIDebugger/APIDebugger.tsx index 8ef45b8..0311c44 100644 --- a/media/src/components/APIPage/APIDebugger/APIDebugger.tsx +++ b/media/src/components/APIPage/APIDebugger/APIDebugger.tsx @@ -14,7 +14,7 @@ import RegionSelector from "./RegionSelector"; export class APIDebuggerProps {} export const APIDebugger: React.FC = (props) => { - const { apiMeta, schemaForm, product, version, onDebug } = APIPageContext.useContainer(); + const { apiMeta, schemaForm, product, version, onDebug, changeMode } = APIPageContext.useContainer(); const [regionId, setRegionId] = React.useState(""); const [endpoints, setEndpoints] = React.useState([]); @@ -54,6 +54,7 @@ export const APIDebugger: React.FC = (props) => {
{ + return + }} form={schemaForm as any} // onChange={(value): void => { // callVscode({ data: value, type: "openAPIDebug", requestId: 100 }, (res) => { diff --git a/media/src/components/APIPage/APIDebugger/APIGuide.module.scss b/media/src/components/APIPage/APIDebugger/APIGuide.module.scss new file mode 100644 index 0000000..cab44bf --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/APIGuide.module.scss @@ -0,0 +1,44 @@ +.api-debugger-guide-wrapper { + .guide { + display: flex; + .must { + line-height: 22px; + vertical-align: middle; + + &:before { + display: inline-block; + margin-right: 8px; + color: red; + line-height: 22px; + content: '*'; + } + } + .name { + margin-right: 4px; + // margin-right: 8px; + color: #333333; + font-weight: 500; + font-size: 12px; + font-family: Alibaba-PuHuiTi; + line-height: 22px; + vertical-align: 0; + } + .desc { + display: inline-block; + max-width: 290px; + margin-right: 4px; + overflow: hidden; + color: #787878; + font-size: 12px; + line-height: 24px; + white-space: nowrap; + text-overflow: ellipsis; + vertical-align: middle; + vertical-align: -0.6em; + cursor: pointer; + } + .api-debugger-param-desc { + color: #787878; + } + } +} \ No newline at end of file diff --git a/media/src/components/APIPage/APIDebugger/APIGuide.tsx b/media/src/components/APIPage/APIDebugger/APIGuide.tsx new file mode 100644 index 0000000..d4594f9 --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/APIGuide.tsx @@ -0,0 +1,185 @@ +/** + * @author 念依 + * @description 参数Title + */ + +// import { AmpIcon } from '@ali/amp-base'; +import { Balloon, Icon, Tag } from '@alicloud/console-components'; +// import { LinkButton } from '@alicloud/console-components-actions'; +import { RightOutlined } from '@ant-design/icons'; +import React from 'react'; +import { stringToType } from './widgets/xconsole/utils'; +import { SemixMarkdown } from 'semix-schema-table'; +// import { stringToType } from './utils'; + +export class APIGuideProps { + schema?: any; + isRequired?: boolean; + fieldName?: string; + dataPath?: string; + collapsed?: boolean; + setCollapsed?: (collapsed: boolean) => void; + deleteHeader?: Function; + changeValue?: (dataPath: string, value: string | number | boolean) => void; + // titleOperator?: any; + product:string; + version:string; +} + +export const APIGuide = React.memo((props: APIGuideProps) => { + const isNewHeader = props.schema?.temperary; + + const description = props.schema?.description || props.schema?.title; + + const getShortName = (des: string) => { + const endIndex = Math.min( + ...[',', '\n', '。'].map((ch) => des.indexOf(ch)).map((num) => (num === -1 ? Number.MAX_VALUE : num)), + ); + let shortName = des; + + if (endIndex !== -1) { + shortName = des.slice(0, endIndex); + } + + if (shortName.startsWith('/g) || []; + repalceStrs?.map((str) => { + shortName = shortName.replace(str, ''); + }); + } + + if (shortName.length > 19) { + return `${shortName.slice(0, 16)}...`; + } + return shortName; + }; + const shortDesc = getShortName(description || ''); + + let hasCollapsedIcon = ['object', 'array'].includes(props.schema.type as any); + if (props.schema?.type === 'array' && ['string', 'number', 'integer'].includes(props.schema?.items?.type as any)) { + hasCollapsedIcon = false; + } + + const hasHelpTip = !!( + description?.length > shortDesc.length || + props.schema?.example || + props.schema?.maximum || + props.schema?.minimum || + props.schema?.enum?.length || + props.schema?.pattern || + props.schema?.format + ); + + const paramTitle = !props.schema?.isItems + ? props.fieldName + : props.fieldName.substring(props.fieldName.lastIndexOf('.') + 1, props.fieldName.length); + + if (!paramTitle?.length) { + return null; + } + + return ( +
+ {!props.schema?.isRoot ? ( +
+ {hasCollapsedIcon ? ( +
+ { + props.setCollapsed(!props.collapsed); + }} + style={{ marginRight: 8, paddingTop: 5 }} + rotate={props.collapsed ? 0 : 90} + translate={undefined} + /> +
+ ) : null} +
+
+ {props.isRequired ? : null} + {paramTitle} + + {hasHelpTip ? ( + + {shortDesc} + +
+
+ } + > +
+ 描述 +
+ {props.schema?.example ? ( + <> +
+ 示例值 +
+ +
{props.schema?.example}
+ + ) : null} + {props.schema?.maximum ? ( + <> +
最大值
+
{props.schema?.maximum}
+ + ) : null} + {props.schema?.minimum ? ( + <> +
最小值
+
{props.schema?.minimum}
+ + ) : null} + {props.schema?.format ? ( + <> +
格式
+
{props.schema?.format}
+ + ) : null} + {props.schema?.enum?.length ? ( + <> +
枚举值
+ {props.schema?.enum?.map((item) => ( + + {item.value || item} + + ))} + + ) : null} + {props.schema?.pattern ? ( + <> +
正则
+
{props.schema?.pattern}
+ + ) : null} +
+ +
+ ) : null} + +
+
+ {/* {isNewHeader && props.deleteHeader ? ( + + { + props.deleteHeader(props.schema); + }} + /> + + ) : null} */} +
+ ) : null} +
+ ); +}); diff --git a/media/src/components/APIPage/APIDebugger/utils.ts b/media/src/components/APIPage/APIDebugger/utils.ts index 861dd01..47d37b4 100644 --- a/media/src/components/APIPage/APIDebugger/utils.ts +++ b/media/src/components/APIPage/APIDebugger/utils.ts @@ -1,35 +1,35 @@ -import _ from "lodash"; -import { SemixUISchema } from "../../SemixFormRender"; - -export const getCustomWidget = (schema: SemixUISchema): string => { - if (schema?.customWidget === 'conditionInfo') { - return 'conditionInfo'; +export const getCustomWidget = (schema): string => { + if (schema?.type === 'array') { + console.log("array") + // if (schema.items?.type === 'string' && schema?.widget === 'stringList') { + // return 'stringList'; + // } + return 'list'; + } + if (schema?.type === 'string') { + if (schema?.isFileTransferUrl) { + return 'fileTypeSelect'; } - if (schema?.type === 'array') { - if (schema.items?.type === 'string' && schema?.widget === 'stringList') { - return 'stringList'; - } - return 'list'; + if (schema?.format === 'binary') { + return 'file'; } - - if (schema?.type === 'string') { - if (schema?.format === 'binary') { - return 'binary'; - } - if (schema?.enumValueTitles && Object.keys(schema?.enumValueTitles || {}).length < 5) { - return 'radio'; - } else if (schema?.enum && schema?.enum?.length < 5) { - return 'radio'; - } else if (schema?.enum || schema?.enumValueTitles || schema?.assistSchema?.dynamicEnumConfig) { - return 'enum'; - } - return 'string'; - } else if (schema?.type === 'boolean') { - return 'booleanSwitch'; - } else if (schema?.type === 'object' && !_.isEmpty(schema?.additionalProperties)) { - return 'map'; - } else if (schema?.type === 'object' && !schema?.properties) { - return 'json'; + if (schema?.enum || schema?.enumValueTitles) { + return 'enum'; } - return ''; - }; \ No newline at end of file + return 'string'; + } else if (schema?.type === 'boolean') { + return 'booleanSwitch'; + } else if (schema?.type === 'object' && schema?.additionalProperties) { + return 'map'; + } else if (schema?.type === 'object' && !schema?.properties) { + return 'json'; + } else if (schema?.type === 'any') { + return 'typeSelect'; + } else if ((schema?.ref || schema?.$ref) && !schema?.properties && !schema?.items) { + return 'struct'; + } + // else if (schema?.type === 'number' || (schema?.type === 'integer' && schema?.format === 'int64')){ + // return "string" + // } + return ''; +}; \ No newline at end of file diff --git a/media/src/components/APIPage/APIDebugger/widgets/emitter.tsx b/media/src/components/APIPage/APIDebugger/widgets/emitter.tsx new file mode 100644 index 0000000..5d2d338 --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/emitter.tsx @@ -0,0 +1,116 @@ +/** + * 一个 EventEmitter 实例 + * + * @author 奇阳 + * @description + */ +import * as EventEmitter from "events"; +import { ErrorObject } from "semix-validate"; + +/** + * 定义每一个事件的名称及回调参数类型 + */ +class EventTypes { + openAssistant: { href: string; pageType: string }; + scrollToRowKeys: { rowKeys: string[]; tableType: string }; + /** 关闭某一个 tab, tabId */ + // todo any : AmpWorkspaceTabPane + closeTab: any; + + // todo any : AmpWorkspaceTabPane + openTab: any; + + // todo any : AmpWorkspaceTabPane + openTabConflict: any; + + // todo any : AmpWorkspaceTabPane + addTabPane: any; + + refreshResource: { branchId: number }; + + /** 刷新 API */ + refreshAPIEntities: void; + + /** 切换 Tab */ + switchResourceAPITab: { activeMethod: string }; + + /** 切换 Tab */ + switchResourceProperties: { attributeName: string }; + + /** 切换资源关系 Tab */ + switchResourceRelationTab: { id: string }; + + /** 切换资源关系第二级 Tab */ + switchResourceRelationSecondLevelTab: { id: string }; + + /** 切换工作空间子 Tab */ + switchWorkPaneSubTab: string; + + /** 刷新 资源属性列表页 */ + refreshPropertiesConfig: string; + + refreshResourceList: never; + + refreshRamAPiMetaList: void; + + switchProject: { projectId: string }; + + // 同步api出参或者入参 + syncApiParams: void; + + // 获取到schema + getSchema: string; + + checkoutEditableCell: string; + + // todo any => AuditCommentDTO + openAuditComment: any; + + refreshApiPage: string; //刷新 API 页面 + + refreshUnRelatedApi: string; + + resetMetaMapOpenRows: string; //重置metaMap 展开的行 + + jumpToCode: ErrorObject; + + addStruct: any; // 获取到数据结构 + + /** + * 打开快捷调试 + */ + openNewQuickTest: string; + +} + +// =================== +// 以下内容无需关心 +// =================== +export type EventNames = keyof EventTypes; + +class AmpEventEmitter extends EventEmitter.EventEmitter { + on(eventName: K, listener: (params?: EventTypes[K]) => void): this { + return super.on.call(this, eventName, listener); + } + + once(eventName: K, listener: (params?: EventTypes[K]) => void): this { + return super.once.call(this, eventName, listener); + } + + emit(eventName: K, params?: EventTypes[K]): boolean { + return super.emit.call(this, eventName, params); + } + + removeListener(eventName: K, listener: (params?: EventTypes[K]) => void): this { + return super.removeListener.call(this, eventName, listener); + } + + removeAllListeners(eventName: K): this { + return super.removeAllListeners.call(this, eventName); + } +} + +const emitter = new AmpEventEmitter(); +emitter.setMaxListeners(0); + +export { emitter }; diff --git a/media/src/components/APIPage/APIDebugger/widgets/types.ts b/media/src/components/APIPage/APIDebugger/widgets/types.ts new file mode 100644 index 0000000..608fa8d --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/types.ts @@ -0,0 +1,81 @@ +import { SemixJsonSchema } from "semix-core"; + +export class ErrorField { + dataPath: string; + message: string; +} + +export type AlicloudUISchema = SemixJsonSchema & { + widget?: string; + + disabled?: boolean; + + hidden?: boolean; + + isRoot?: boolean; + + unCheckedChildren?: string; + + checkedChildren?: string; + + mode?: "simple" | "card"; + + labelWidth?: number; + + placeholder?: string; + + /** 是否存在,一般用于条件式存在的表单项 */ + exists?: boolean; + + // properties?: SchemaMap; + properties?: any; + additionalProperties?: AlicloudUISchema; + items?: AlicloudUISchema; + + /** 枚举值为空时,下拉框展示内容,一般用于推荐枚举值为空时引导用户 */ + emptyEnumContent?: any; +}; + +export class CommonWidgetProps { + value: any; + schema = {} as AlicloudUISchema; + onChange(value: any) {} +} + +export class ListProps { + addItem: () => any; + copyItem: (index: number) => any; + deleteItem: (index: number) => any; + errorFields: ErrorField[]; + dataPath: string; + schemaPath: string; + schema: AlicloudUISchema; + displayList: any[]; + fieldName: string; + renderTitle?: Function; +} + +export class MapProps { + editItemKey: (index: number, newKey: string) => any; + addItem: () => any; + copyItem: (index: number) => any; + deleteItem: (index: number) => any; + errorFields: ErrorField[]; + dataPath: string; + schema: AlicloudUISchema; + displayList: any[]; + fieldName: string; + renderTitle?: Function; +} + +export class AnyProps { + handleTypeChange: (type: string) => any; + type: string; + dataPath: string; + schema: AlicloudUISchema; + onChange(value: any) {} + widgets: any; + Icon: any; +} + +export type SimpleTypes = "array" | "boolean" | "integer" | "null" | "number" | "object" | "string"; diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/FileUploadType.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/FileUploadType.tsx new file mode 100644 index 0000000..7ed2fa7 --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/FileUploadType.tsx @@ -0,0 +1,54 @@ +// /** +// * @author 念依 +// * @description 文件上传方式选择组件 +// */ + +// import React from 'react'; +// import { Switch } from '@alicloud/console-components'; +// import { xconsoleWidgets } from '.'; +// import { CommonWidgetProps } from '../types'; + +// export class FileUploadTypeProps extends CommonWidgetProps { +// dataPath?: string; +// } + +// export const FileUploadType: React.FC = (props) => { +// const { dataPath, schema, onChange } = props; +// const [isFile, changeIsFile] = React.useState(false); +// const curvalue = props.value || ''; + +// const handleTypeChange = React.useCallback((value: boolean) => { +// changeIsFile(value); +// }, []); + +// const getUIType = (isFile: boolean) => { +// if (isFile) return 'file'; +// return 'string'; +// }; +// const getText = (isFIle: boolean) => { +// return isFIle ? '输入URL' : '上传文件'; +// }; +// const UIType = getUIType(isFile); +// const text = getText(isFile); + +// // 变化的参数组件,默认为字符串 +// const ParamUI = React.useMemo(() => { +// return ampWidgets[UIType] || ampWidgets['string']; +// }, [UIType]); + +// return ( +//
+// { +// handleTypeChange(value); +// }} +// /> +// {text} +//
+// +//
+//
+// ); +// }; + +// FileUploadType.defaultProps = new FileUploadTypeProps(); diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/JsonEdit.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/JsonEdit.tsx new file mode 100644 index 0000000..562f680 --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/JsonEdit.tsx @@ -0,0 +1,52 @@ +// /** +// * @author 念依 +// * @description 表单 Json 编辑器 +// */ + +// import { WhiteMonacoEditor } from '@ali/amp-base'; +// import { CommonWidgetProps } from '@ali/api-component'; +// import * as React from 'react'; + +// export class JsonEditProps extends CommonWidgetProps {} + +// export const JsonEdit: React.FC = (props) => { +// const [curval, setCurval] = React.useState(''); +// const [errMsg, setErrMsg] = React.useState(''); +// React.useEffect(() => { +// setErrMsg(''); +// if (typeof props.value !== 'string') { +// try { +// const obj = JSON.stringify(props.value, null, 2); +// setCurval(obj); +// } catch {} +// } +// }, [props.value]); + +// return ( +//
{ +// try { +// const val = JSON.parse(curval); +// props.onChange(val); +// } catch { +// setErrMsg('json 格式错误'); +// } +// }} +// > +// { +// setCurval(value); +// }} +// > +// {errMsg?.length ?
{errMsg}
: null} +//
+// ); +// }; + +// JsonEdit.defaultProps = new JsonEditProps(); diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/Object.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/Object.tsx new file mode 100644 index 0000000..b156a67 --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/Object.tsx @@ -0,0 +1,112 @@ +/** + * @author 念依 + * @description object,object回填value可能为string + */ +import * as _ from 'lodash'; +import * as React from 'react'; +import { AlicloudUISchema } from '../types'; +import { FormStoreContext } from '../../../../SemixFormRender/context'; +import { Field } from '../../../../SemixFormRender/common/Field'; +import { getNextDataPath } from '../../../../SemixFormRender/utils'; +import { getNextSchemaPath } from './utils'; + +export class RenderObjectProps { + schema: AlicloudUISchema; + dataPath: string; + schemaPath: string; + value: any; + onChange: (value: any) => void; +} + +export const RenderObject: React.FC = (props) => { + console.log(props.dataPath) + if (props.value && typeof props.value === 'string') { + let value = {}; + try { + const tmpValue = JSON.parse(props.value); + if (typeof tmpValue === 'object') { + value = tmpValue; + } + } catch {} + props.onChange(value); + } + const [groupCollapsed, setGroupCollapsed] = React.useState({} as { [x: string]: boolean }); + const store = FormStoreContext.useContainer(); + + return React.useMemo(() => { + if (props.schema?.properties) { + const propItems = Object.keys((props.schema?.properties as any) || {}).map((key) => { + return { key, schema: props.schema?.properties[key] }; + }); + const propItemsGroups = _.groupBy(propItems, (item) => { + return item.schema?.group || ''; + }); + const renderField = (key: string) => { + return ( + + ); + }; + const noGroupPropItems = (propItemsGroups?.[''] || []).map((item) => { + return renderField(item?.key); + }); + const groups = _.map(propItemsGroups, (items, groupKey) => { + if (groupKey) { + const titleProps = { + key: groupKey, + index: items?.[0]?.schema?.groupIndex, + collapsed: groupCollapsed[groupKey], + setCollapsed: (newCollapsed: boolean) => + setGroupCollapsed((boolMap) => { + return { + ...boolMap, + [groupKey]: newCollapsed, + }; + }), + }; + const title = store.renderGroupArea?.(titleProps) ||
{groupKey}
; + + return { + dom: ( +
+ {title} + {groupCollapsed[groupKey] ? null : ( +
{(items || []).map((item) => renderField(item?.key))}
+ )} +
+ ), + index: items?.[0]?.schema?.groupIndex, + }; + } + return null; + }).filter((id) => id); + + const result = [ + ...groups, + { + dom: noGroupPropItems, + index: 0, + }, + ] + .sort((pre, next) => pre.index - next.index) + .map((item) => { + return item.dom; + }); + + return ( +
+
{result}
+
+ ); + } + return null; + }, [groupCollapsed, props.schema, props.dataPath]); +}; + +RenderObject.defaultProps = new RenderObjectProps(); diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/RenderMap.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/RenderMap.tsx new file mode 100644 index 0000000..6cc9a1d --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/RenderMap.tsx @@ -0,0 +1,121 @@ +/** + * @author 念依 + * @description Map + */ +import * as React from "react"; +import * as _ from "lodash"; +import { AlicloudUISchema, ErrorField, MapProps } from "../types"; +import { FormStoreContext } from "../../../../SemixFormRender/context"; + +export class RenderMapProps { + dataPath = ""; + schema = {} as AlicloudUISchema; + errorFields = [] as ErrorField[]; + fieldName: string; + renderTitle?: Function; +} + +export const RenderMap: React.FC = (props) => { + const { formData, onItemChange, widgets, errorFields } = FormStoreContext.useContainer(); + + const typeOfValue = props?.schema?.additionalProperties?.type || 'string' + + const InitValue = { + string: "", + number: 0, + object: {}, + array: [], + boolean: false, + integer: 0, + map: {}, + $ref: {}, + any:"" + } + + const MapUI = widgets["simpleMap"]; + + const [mapData, setMapData] = React.useState({ "": InitValue[typeOfValue] || "" } as { [key: string]: any }); + const [displayList, setDisplayList] = React.useState([{ key: "", value: InitValue[typeOfValue] || "" } as { key: string; value: any }]); + + React.useEffect(() => { + if (_.get(formData, props.dataPath) !== mapData) { + setMapData(_.get(formData, props.dataPath)); + } + }, [_.get(formData, props.dataPath)]); + + React.useEffect(() => { + const uiList = + typeof mapData === "object" && Object.keys(mapData).length + ? Object.keys(mapData).map((key) => { + return { key, value: mapData[key] }; + }) + : [{ key: "", value: "" as any } as { key: string; value: any }]; + if (!_.isEqual(uiList, displayList)) { + setDisplayList(uiList); + } + }, [mapData]); + + const addItem = () => { + const newItem = InitValue[typeOfValue] || ""; + const newList = [...displayList, { key: "", value: newItem }]; + setDisplayList([...newList]); + const newIndex = newList.length - 1; + return newIndex; + }; + + const copyItem = (index: number) => { + const newItem = { + key: displayList[index].key + "-copy", + value: displayList[index].value, + }; + const newList = [...displayList.slice(0, index), newItem, ...displayList.slice(index)]; + setDisplayList([...newList]); + }; + + const editItemKey = (index: number, newKey: string) => { + const tempList = displayList; + tempList[index].key = newKey; + setDisplayList([...tempList]); + calculateValue(); + }; + + const calculateValue = React.useCallback(() => { + const resultValue = {} as { [key: string]: any }; + displayList.map((item) => { + resultValue[item.key] = item.value; + }); + onItemChange(props.dataPath, resultValue); + setMapData(resultValue); + }, [displayList]); + + const deleteItem = (index: number) => { + // 删除元素的时候,也需要delete相对于的校验信息(errorFields) + const newErrors = errorFields.filter((error) => { + return error.dataPath !== props.dataPath + "." + displayList[index].key; + }); + // setErrorFields(newErrors); + const newList = displayList.filter((item, kdx) => kdx !== index); + setDisplayList(newList); + }; + + React.useEffect(() => { + calculateValue(); + }, [displayList]); + + const mapProps = { + editItemKey, + addItem, + copyItem, + deleteItem, + errorFields: props.errorFields, + dataPath: props.dataPath, + schema: props.schema?.additionalProperties, + displayList, + fieldName: props.fieldName, + renderTitle: props.renderTitle || null, + } as MapProps; + + return ; +}; + +RenderMap.defaultProps = new RenderMapProps(); diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/Struct.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/Struct.tsx new file mode 100644 index 0000000..f908215 --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/Struct.tsx @@ -0,0 +1,46 @@ +/** + * @author 念依 + * @description 数据结构 + */ +// import { AmpIcon } from '@ali/amp-base'; +// import { CommonWidgetProps } from '@ali/api-component'; +// import { LinkButton } from '@alicloud/console-components-actions'; +import React from 'react'; +import { CommonWidgetProps } from '../types'; +import { addParamStruct } from './utils'; +// import { addParamStruct } from '../utils'; + +export class StructProps extends CommonWidgetProps { + onChange: (value: any) => void; + schemaPath: string; +} + +export const Struct: React.FC = (props) => { + if (props.value) { + if (typeof props.value === 'string') { + let value = props.value; + try { + value = JSON.parse(props.value); + } catch {} + props.onChange(value); + } + addParamStruct(props.schemaPath, props.schema); + } + return ( + + ); +}; + +Struct.defaultProps = new StructProps(); diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/UploadFile.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/UploadFile.tsx new file mode 100644 index 0000000..6b8e599 --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/UploadFile.tsx @@ -0,0 +1,70 @@ +// /** +// * @author 念依 +// * @description 上传文件 +// */ + +// import { Button, message, Upload } from 'antd'; +// import { UploadOutlined } from '@ant-design/icons'; +// import * as React from 'react'; +// import { CommonWidgetProps } from '@ali/api-component'; +// import { useLocation } from 'react-router-dom'; + +// export class UploadFileProps extends CommonWidgetProps {} + +// export const UploadFile: React.FC = (props) => { +// const [isUpload, changeIsUpload] = React.useState(false); +// const location = useLocation(); +// const [product, version, apiName] = location?.pathname?.replace('/api/', '').split('/'); +// const [fileInfo, setFileInfo] = React.useState({ fileName: '', mime: '' } as FileInfo); + +// type FileInfo = { +// fileName: string; +// mime: string; +// }; + +// const getUploadPath = (product: string, version: string, apiName: string, fileInfo: FileInfo) => { +// let path = `/api/auth/product/oss/file`; +// if (props.schema?.isFileTransferUrl) { +// path = `/api/auth/product/oss/file?product=${product}&apiName=${apiName}&apiVersion=${version}&name=${fileInfo.fileName}&mime=${fileInfo.mime}`; +// } +// return path; +// }; + +// return ( +// { +// if (val?.file?.status === 'done') { +// props.onChange(val?.file?.response?.tmpId || ''); +// } +// }} +// onRemove={() => { +// changeIsUpload(false); +// props.onChange(''); +// }} +// maxCount={1} +// beforeUpload={(file) => { +// if (file.size > 52428800) { +// message.error('暂不支持上传超过50M的文件哦!'); +// return Upload.LIST_IGNORE; +// } +// const nameArr = file?.name?.split('.'); +// const mime = Array.isArray(nameArr) ? nameArr[nameArr.length - 1] : ''; +// const fileName = file?.name?.replace('.' + mime, ''); +// setFileInfo({ fileName: fileName, mime: mime }); +// return true; +// }} +// > +// +// +// ); +// }; + +// UploadFile.defaultProps = new UploadFileProps(); diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/any.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/any.tsx new file mode 100644 index 0000000..4ee3c88 --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/any.tsx @@ -0,0 +1,84 @@ +/** + * @author 念依 + * @description any类型 + */ + +import * as React from "react"; +import _ from "lodash"; +import { Select } from "@alicloud/console-components"; +import { Tooltip } from "antd"; +import { xconsoleWidgets } from "."; +import { getTypeOfValue, simpleType } from "./utils"; +import { AnyProps } from "../types"; + + +export class TypeSelectorProps extends AnyProps { + value: any; +} + +export const TypeSelector: React.FC = (props) => { + const { dataPath, onChange, schema, Icon, value } = props; + + const [type, handleTypeChange] = React.useState(value !== undefined ? getTypeOfValue(value) : "string" as any); + + React.useEffect(() => { + props.onChange(undefined); + schema.type = type + }, [type]); + + + const getUIType = (type: string) => { + if (type === "boolean") return "booleanSwitch"; + if (type === "array") return "json"; + if (type === "object") return "json"; + return type; + }; + const UIType = getUIType(type); + + // 变化的参数组件,默认为字符串 + const ParamUI = React.useMemo(() => { + return xconsoleWidgets[UIType] || xconsoleWidgets["string"]; + }, [UIType]); + + // 可选择的数据类型 + const renderTypes = React.useMemo(() => { + const typeList = simpleType; + return typeList.map((item) => { + return ( + + {item} + + ); + }); + }, []); + + return ( +
+ 参数类型 + {Icon ? ( + + + + + + ) : null} + : + +
+ +
+
+ ); +}; + +TypeSelector.defaultProps = new TypeSelectorProps(); diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/base/List.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/base/List.tsx new file mode 100644 index 0000000..176bf6d --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/base/List.tsx @@ -0,0 +1,118 @@ +/** + * @author + * @description 列表 + */ +import * as _ from 'lodash'; +import * as React from 'react'; +import { SemixJsonSchema } from 'semix-core'; +import { ErrorField } from '../../types'; +import { FormStoreContext } from '../../../../../SemixFormRender/context'; +import { ListProps } from '../../../../../SemixFormRender'; +// import { ErrorField, FormStoreContext, ListProps } from 'semix-form-render'; + +export class RenderListProps { + dataPath = ''; + schema = {} as SemixJsonSchema; + errorFields = [] as ErrorField[]; + fieldName: string; + renderTitle?: (props) => void; + schemaPath? = ''; +} + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +function getSchemaDefaultValue(schema: SemixJsonSchema) { + switch (schema.type) { + case 'object': { + // if (schema.properties) { + // return mapObject(schema.properties, (property: SemixJsonSchema, key) => { + // return getSchemaDefaultValue(property); + // }); + // } + + return {}; + } + case 'array': { + // if (schema.items) { + // return []; + // } + return []; + } + } + + return undefined; +} + +export const RenderList: React.FC = (props) => { + const { formData, onItemChange, widgets, errorFields } = FormStoreContext.useContainer(); + + const customWidget = props.schema?.customWidget || 'simpleList'; + + const ListUI = widgets[customWidget] || widgets['simpleList']; + + let listData = _.get(formData, props.dataPath); + const displayList = Array.isArray(listData) && listData.length ? listData : [""]; + + const addItem = (): number => { + const newItem = props.schema.items ? getSchemaDefaultValue(props.schema.items) || '' : ''; + const newList = [...displayList, newItem]; + const newIndex = newList.length - 1; + onItemChange(props.dataPath, newList); + return newIndex; + }; + + const changeItemValue = (value, index): void => { + let newList = [...displayList]; + if (index === displayList?.length) { + newList.push(value); + } else { + newList[index] = value; + } + onItemChange(props.dataPath, newList); + }; + + const copyItem = (index: number): void => { + const newItem = displayList[index]; + const newList = [...displayList.slice(0, index), newItem, ...displayList.slice(index)]; + onItemChange(props.dataPath, newList); + }; + + const deleteItem = (index: number): void => { + // 删除元素的时候,也需要delete相对于的校验信息(errorFields) + const newErrors = errorFields.filter((error) => { + return !error.dataPath.includes(props.dataPath + '.' + index); + }); + // setErrorFields(newErrors); + const newList = displayList.filter((item, kdx) => kdx !== index); + onItemChange(props.dataPath, newList); + }; + + const listProps = { + addItem, + copyItem, + deleteItem, + errorFields: props.errorFields, + dataPath: props.dataPath, + schemaPath: props.schemaPath, + schema: props.schema?.items, + displayList, + fieldName: props.fieldName, + renderTitle: props.renderTitle || null, + parentSchema: props.schema, + /** + * 根据index改变对应值 + */ + changeItemValue, + /** + * 改变当前dataPath的值 + */ + onChange: onItemChange, + formData: formData || {}, + } as unknown as ListProps; + // return <>ddd; + + return ; +}; + +RenderList.defaultProps = new RenderListProps(); + +export default RenderList; diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/enum.module.scss b/media/src/components/APIPage/APIDebugger/widgets/xconsole/enum.module.scss new file mode 100644 index 0000000..688a544 --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/enum.module.scss @@ -0,0 +1,24 @@ +.workbench-enum-item { + position: relative; + &:hover .input-emptystring-button { + display: block; + } + .input-emptystring-button { + position: absolute; + bottom: 16px; + left: 160px; + left: 156px; + z-index: 99; + display: none; + font-size: 12px; + } + .clear-emptystring-button { + position: absolute; + bottom: 16px; + left: 160px; + left: 156px; + z-index: 99; + display: block; + font-size: 12px; + } +} diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/enum.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/enum.tsx new file mode 100644 index 0000000..94d1be5 --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/enum.tsx @@ -0,0 +1,91 @@ +/** + * @author 念依 + * @description 枚举选择 + */ + +// import { CommonWidgetProps } from '@ali/api-component'; +import { Select } from '@alicloud/console-components'; +import * as React from 'react'; +import './enum.module.scss'; +import { CommonWidgetProps } from '../types'; + +export class EnumSelectProps extends CommonWidgetProps {} + +export const EnumSelect: React.FC = (props) => { + const { schema, ...rest } = props; + + const [curvalue, setCurvalue] = React.useState(undefined); + + let enumValueTitles = schema?.enum ? [...schema?.enum] : []; + if (schema?.enumValueTitles) { + Object.keys(schema?.enumValueTitles)?.map((key) => { + if (schema?.enum?.includes(key) && enumValueTitles.indexOf(key) !== -1) { + enumValueTitles.splice(enumValueTitles.indexOf(key), 1); + } + enumValueTitles = [ + ...enumValueTitles, + { + value: key, + title: schema?.enumValueTitles[key], + }, + ]; + }); + } + const options = enumValueTitles?.map((item: any) => { + if (item.value === '') { + return; + } + return { + label: ( +
+ {item.value ? item.value : item} + {item.title} +
+ ), + value: item.value ? item.value : item, + key: item.value ? item.value : item, + }; + }); + + const changeCurValue = React.useCallback((value) => { + if (value) { + setCurvalue(value); + } else { + props.onChange(undefined); + setCurvalue(undefined); + } + }, []); + + const emitvalues = React.useCallback(() => { + if (props.value === curvalue) { + return; + } + props.onChange(curvalue); + }, [curvalue]); + + React.useEffect(() => { + setCurvalue(props.value); + }, [props.value]); + + return ( +
+ {/* onGetRandomValue(props.onChange, props.schema)} + changeValue={changeCurValue} + param={props.schema as any}> */} + + {/* */} +
+ ); +}; + +EnumSelect.defaultProps = new EnumSelectProps(); diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/index.module.scss b/media/src/components/APIPage/APIDebugger/widgets/xconsole/index.module.scss new file mode 100644 index 0000000..40b9274 --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/index.module.scss @@ -0,0 +1,11 @@ +.workbench-complex-type-item { + .item-treenode-style { + margin-left: -23px; + } + .array-delete-icon { + margin: 0 5px; + } + .op { + margin-right: 8px; + } +} \ No newline at end of file diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/index.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/index.tsx new file mode 100644 index 0000000..12dae06 --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/index.tsx @@ -0,0 +1,41 @@ +/** + * @description 门户组件 + * 1、用于api-design-form + * 2、用于api-debugger + */ + +import "./index.module.scss"; +import { String } from "./string"; +import { BooleanSwitch } from "./swtich"; +import { NumberInput } from "./number"; +import { SimpleList } from "./list"; +// import { UploadFile } from "./UploadFile"; +import { EnumSelect } from "./enum"; +import { SimpleMap } from "./map"; +import { TypeSelector } from "./any"; +// import { JsonEdit } from "./JsonEdit"; +// import { FileUploadType } from "./FileUploadType"; +import { Struct } from "./Struct"; +import { RenderObject } from "./Object"; +import { RenderMap } from "./RenderMap"; +import RenderList from "./base/List"; + +export const xconsoleWidgets = { + object: RenderObject, + map: RenderMap, + string: String, + booleanSwitch: BooleanSwitch, + number: NumberInput, + integer: NumberInput, + list: RenderList, + // binary: UploadFile, // 文件上传,amp暂时不支持 + enum: EnumSelect, + simpleMap: SimpleMap, + typeSelect: TypeSelector, // any类型支持选择各种类型 + simpleList: SimpleList, + // list: SimpleList, + // file: UploadFile, + // json: JsonEdit, + // fileTypeSelect: FileUploadType, // 文件上传切换,amp暂时不支持 + struct: Struct, // 数据结构 +}; diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/list.module.scss b/media/src/components/APIPage/APIDebugger/widgets/xconsole/list.module.scss new file mode 100644 index 0000000..2434196 --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/list.module.scss @@ -0,0 +1,26 @@ +.alicloud-schema-form-list { + .list-item { + position: relative; + padding: 5px; + .title-operator { + position: absolute; + top: 14px; + right: 0; + z-index: 999; + } + + .op { + margin-right: 8px; + text-decoration: none; + } + .key-item { + background-color: aqua; + .right-outlined { + display: none !important; + } + } + } + // .list-item:hover { + // background-color: #f7f7f7; + // } +} diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/list.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/list.tsx new file mode 100644 index 0000000..0457f45 --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/list.tsx @@ -0,0 +1,72 @@ +/** + * @author 奇阳 + * @description + */ +// import { AmpIcon } from '@ali/amp-base'; +// import { LinkButton } from '@alicloud/console-components-actions'; +import * as React from 'react'; +import './list.module.scss'; +import { ListProps } from '../types'; +import { Field } from '../../../../SemixFormRender/common/Field'; +import { Button } from '@alicloud/console-components'; + +export class SimpleListProps extends ListProps {} + +export const SimpleList: React.FC = (props) => { + const children = (props.displayList || []).map((value, index) => { + return ( +
+ + +
+ ); + }); + return ( + + ); +}; + +SimpleList.defaultProps = new SimpleListProps(); diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/map.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/map.tsx new file mode 100644 index 0000000..5b2d87c --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/map.tsx @@ -0,0 +1,112 @@ +/** + * @author 念依 + * @description workbench-map + */ +// import { immutableSet } from '@/utils/utils'; +// import { AmpIcon } from '@ali/amp-base'; +// import { Field, MapProps, getNextDataPath } from '@ali/api-component'; +import { Input } from '@alicloud/console-components'; +// import { LinkButton } from '@alicloud/console-components-actions'; +import * as React from 'react'; +import { getNextDataPath, immutableSet } from '../../../../SemixFormRender/utils'; +import { MapProps } from '../types'; +import { Field } from '../../../../SemixFormRender/common/Field'; + +export class SimpleMapProps extends MapProps {} + +export const SimpleMap: React.FC = (props) => { + const [listKeys, changeListKeys] = React.useState((props.displayList || []).map(({ key }) => key)); + + React.useEffect(() => { + changeListKeys((props.displayList || []).map(({ key }) => key)); + }, [props.displayList]); + + const children = React.useMemo(() => { + return (props.displayList || []).map(({ key, value }, index) => { + const keyItem = ( + <> + { + changeListKeys(immutableSet(index, newValue, listKeys) as any); + }} + onBlur={() => { + props.editItemKey(index, listKeys[index]); + }} + /> + + {props.schema?.widget === 'typeSelect' ? null : ( + { + props.copyItem(index); + }} + > +
+
+ )} + { + props.deleteItem(index); + }} + > + delete + +
+ + ); + const valueItem = ( +
+ +
+ ); + + return ( +
+ +
+ {keyItem} + + {' '} + : + +
+
{valueItem}
+
+
+ ); + }); + }, [props.displayList, listKeys]); + return ( + + ); +}; + +SimpleMap.defaultProps = new SimpleMapProps(); diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/number.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/number.tsx new file mode 100644 index 0000000..bf14ffc --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/number.tsx @@ -0,0 +1,78 @@ +/** + * @author 念依 + * @description 输入数值 + */ +import { NumberPicker } from '@alicloud/console-components'; +import * as React from 'react'; +import { CommonWidgetProps } from '../types'; +// import APIParamGenerate from "viteSrc/pages/designAwesome/APIDesign/QuickTest/APIParams/APIParamGenerate"; + +export class NumberInputProps extends CommonWidgetProps {} + +export const NumberInput: React.FC = (props) => { + const { schema, ...rest } = props; + const [curvalue, setCurvalue] = React.useState(undefined); + + const textMap = { + double: '浮点数值', + float: '浮点数值', + int32: '整型数字', + int64: '整型数字', + } as any; + + // const numberProps = {} as any; + // if (schema.maximum) { + // numberProps.max = schema.maximum; + // } + // if (schema.minimum) { + // numberProps.min = schema.minimum; + // } + + const emitvalues = React.useCallback(() => { + if (props.value === curvalue) { + return; + } + props.onChange(curvalue); + }, [curvalue]); + + const changeCurValue = (value) => { + if (value || value === 0) { + setCurvalue(Number(value)); + } else { + props.onChange(undefined); + setCurvalue(undefined); + } + }; + + React.useEffect(() => { + setCurvalue(props.value); + }, [props.value]); + + const uischema = { + ...props.schema, + type: props.schema?.format?.includes('int') ? 'integer' : props?.schema?.type, + }; + + return ( + // { + // onGetRandomValue(props.onChange, uischema); + // }} + // changeValue={props.onChange} + // param={uischema as any}> + + // + ); +}; + +NumberInput.defaultProps = new NumberInputProps(); diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/string.module.scss b/media/src/components/APIPage/APIDebugger/widgets/xconsole/string.module.scss new file mode 100644 index 0000000..b156db5 --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/string.module.scss @@ -0,0 +1,60 @@ +.workbench-string-item { + .input-area { + position: relative; + &:hover .input-emptystring-button { + display: block; + } + .input-emptystring-button { + position: absolute; + bottom: -1px; + left: 156px; + z-index: 99; + display: none; + font-size: 12px; + } + .clear-emptystring-button { + position: absolute; + bottom: -1px; + left: 156px; + z-index: 99; + display: block; + font-size: 12px; + } + .close-mini { + margin-right: 6px; + color: #7a7a7a; + } + .close-mini:hover { + background-color: #f7f9fa; + border-radius: 50%; + } + } + .enum-item { + display: flex; + flex-wrap: nowrap; + justify-content: space-between; + .enum-item-title { + margin-left: 10px; + overflow: hidden; + color: #787878; + font-size: 12px; + white-space: nowrap; + text-overflow: ellipsis; + } + } + .generate-form-text-area { + position: relative; + .generate-form-text-area-icon { + position: absolute; + right: 5px; + bottom: 5px; + cursor: pointer; + opacity: 0; + } + &:hover { + .generate-form-text-area-icon { + opacity: 1; + } + } + } +} diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/string.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/string.tsx new file mode 100644 index 0000000..5e878bb --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/string.tsx @@ -0,0 +1,197 @@ +/** + * @author 念依 + * @description string类型 + */ +// import { SwapOutlined } from '@ant-design/icons'; +import { Tooltip } from 'antd'; + +// import { AmpIcon } from '@ali/amp-base'; +// import { CommonWidgetProps } from '@ali/api-component'; +import { Input } from '@alicloud/console-components'; +import * as React from 'react'; +// import APIParamGenerate from 'viteSrc/pages/designAwesome/APIDesign/QuickTest/APIParams/APIParamGenerate'; +// import APIParamGenerate from '../APIParamGenerate'; +import './string.module.scss'; +import { CommonWidgetProps } from '../types'; +// import { onGetRandomValue } from './utils'; + +export class StringProps extends CommonWidgetProps {} + +export const String: React.FC = (props) => { + const { schema, ...rest } = props; + const [curvalue, setCurvalue] = React.useState(''); + const [isToTextArea, setIsToTextArea] = React.useState(false); + + React.useEffect(() => { + if (props.value !== curvalue) { + if (props.value && typeof props.value !== 'string') { + setCurvalue(props.value + ''); + props.onChange(props.value + ''); + } else { + setCurvalue(props.value); + } + } + }, [props.value]); + + React.useEffect(() => { + if (schema.constant && curvalue !== schema.constant) { + setCurvalue(schema.constant); + } + }, [schema.constant]); + + const ref = React.useRef(); + const textareaRef = React.useRef(); + + const changeCurValue = React.useCallback((val: any) => { + const value = val === '' ? (undefined as any) : val; + if (value) { + setCurvalue(value); + } else { + props.onChange(undefined); + setCurvalue(undefined); + } + }, []); + + const emitvalues = React.useCallback(() => { + if (props.value === curvalue) { + return; + } + props.onChange(curvalue); + }, [curvalue]); + + const getNewTextareaValueAndPosition = (newValue: string) => { + if (!ref || !ref.current || isToTextArea) return { value: newValue, position: newValue.length }; + if (!curvalue) return { value: newValue, position: newValue.length }; + try { + // @ts-ignore + const { selectionStart } = ref.current.input; + const startV = curvalue.substring(0, selectionStart); + const endV = curvalue.substring(selectionStart, curvalue.length); + return { + value: `${startV}${newValue}${endV}`, + position: selectionStart + (newValue?.length || 0), + }; + } catch (error) { + return { value: newValue, position: newValue.length }; + } + }; + + let formItem = null; + let inputType = 'input'; + + if ((isToTextArea || (typeof curvalue === 'string' && curvalue.includes('\n'))) && props.schema?.type !== 'number') { + inputType = 'textarea'; + } + + const placeholder = React.useMemo(() => { + if (curvalue === '') { + return '空字符串'; + } else if (props.schema?.format === 'int64' || props.schema?.format === 'double') { + return '请输入数值'; + } else { + return schema?.placeholder || '请输入字符串'; + } + }, [curvalue, props.schema]); + + const errorMsg = React.useMemo(() => { + if (props.schema?.format === 'int64' || props.schema?.format === 'double') { + return !curvalue || Number(curvalue) || Number(curvalue) === 0 ? null : '请输入数值'; + } + return null; + }, [props.schema, curvalue]); + + switch (inputType) { + case 'textarea': + formItem = ( +
+ + + + { + setCurvalue((curvalue || '').replace(/\n/gi, '')); + setIsToTextArea(false); + }} + > + {/* */} + transfer + + +
+ ); + break; + default: + case 'input': + formItem = ( +
+ changeCurValue(undefined)} + // > + // ) : null + // } + value={curvalue} + onBlur={emitvalues} + disabled={schema.disabled} + onChange={changeCurValue} + ref={ref} + onPaste={(e) => { + const v = e.clipboardData.getData('text'); + if (typeof v === 'string' && v.includes('\n')) { + setIsToTextArea(true); + e.preventDefault(); + const { value: newValue, position } = getNewTextareaValueAndPosition(v); + setCurvalue(newValue); + Promise.resolve().then(() => { + // @ts-ignore + textareaRef?.current?.resizableTextArea?.textArea?.focus(); + // @ts-ignore + textareaRef?.current?.resizableTextArea?.textArea?.setSelectionRange(position, position); + }); + } + }} + onKeyDown={(e) => { + if (e.key === 'Enter') { + setIsToTextArea(true); + e.preventDefault(); + const { value: newValue, position } = getNewTextareaValueAndPosition('\n'); + setCurvalue(newValue); + Promise.resolve().then(() => { + // @ts-ignore + textareaRef?.current?.resizableTextArea?.textArea?.focus(); + // @ts-ignore + textareaRef?.current?.resizableTextArea?.textArea?.setSelectionRange(position, position); + }); + } + }} + > +
+ ); + break; + } + + return ( +
+
{formItem}
+
{errorMsg}
+
+ ); +}; + +String.defaultProps = new StringProps(); diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/swtich.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/swtich.tsx new file mode 100644 index 0000000..917ad3e --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/swtich.tsx @@ -0,0 +1,36 @@ +/** + * @author 念依 + * @description Switch + */ +import * as React from 'react'; +// import { CommonWidgetProps } from '@ali/api-component'; +import { Switch } from '@alicloud/console-components'; +import { CommonWidgetProps } from '../types'; + +export class BooleanSwitchProps extends CommonWidgetProps {} + +export const BooleanSwitch: React.FC = (props) => { + const { schema, ...rest } = props; + + let switchDefault = props?.schema?.default; + + if (typeof switchDefault === 'string') { + switchDefault = switchDefault === 'true'; + } + + return ( + { + props.onChange(value); + }} + /> + ); +}; + +BooleanSwitch.defaultProps = new BooleanSwitchProps(); diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/utils.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/utils.tsx new file mode 100644 index 0000000..e7b0e74 --- /dev/null +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/utils.tsx @@ -0,0 +1,309 @@ +import { Message } from "@alicloud/console-components"; + +export const simpleType: any[] = ["string", "boolean", "number", "array", "object"]; + +// export const onGetRandomValue = async (changeValue, schema) => { +// try { +// const randomValueData: any = await API.apiTest.popFlow.getRandomParameter.request({}, schema); +// if(['number','integer'].includes(schema.type) && schema.format !== "int64"){ +// changeValue(Number(randomValueData.data)); +// } +// else{ +// changeValue(randomValueData.data); +// } +// return randomValueData.data +// } catch (err) { +// Message.error(err?.message); +// return null +// } +// }; + + export const getTypeOfValue = (value)=>{ + if(Array.isArray(value)){ + return 'array'; + } + return typeof value + } + + export const getNextSchemaPath = (schemaPath: any, key: string) => { + const next = key?.includes(".") ? `["${key}"]` : key; + return schemaPath ? schemaPath + ".properties." + next : "properties." + next; + }; + + /** + * @author 念依 + * @description ampDebugger 工具函数 + */ +// import { emitter } from '@/utils/emitter'; +// import { ErrorField } from '@ali/api-component'; +import _ from 'lodash'; +import { emitter } from "../emitter"; +import { ErrorField } from "../../../../SemixFormRender/context"; + +export const schemaTraversal = (schema, parentPath?: string) => { + if (!schema) return; + schema.path = parentPath ? parentPath + '.' + schema?.name : schema?.name; + // 删除空的enum + if (Array.isArray(schema?.enum) && schema?.enum?.length === 0) { + delete schema.enum; + } + if (schema.hasOwnProperty('exclusiveMaximum')) { + delete schema.exclusiveMaximum; + } + if (schema.hasOwnProperty('exclusiveMinimum')) { + delete schema.exclusiveMinimum; + } + if (schema?.format === 'int32' && !schema?.maximum) { + schema.maximum = 2147483647; + } + if ( + schema?.type === 'number' || + (schema?.type === 'integer' && schema?.format === 'int64') || + schema?.format === 'double' + ) { + schema.type = 'string'; + schema.pattern = schema?.pattern?.length ? schema.pattern : '^(-|+)?d+(.d+)?$'; + } + if (schema?.$ref) { + schema.ref = schema.$ref; + delete schema.$ref; + } + if (schema?.type && schema?.type === 'array' && schema?.items) { + schema.items.name = 'items'; + schemaTraversal(schema?.items, schema.path); + } + if (schema?.type && schema?.type === 'object' && schema?.properties) { + Object.keys(schema?.properties).map((key) => { + schema.properties[key].name = key; + schemaTraversal(schema?.properties[key], `${schema.path}.properties`); + }); + } + if (schema?.type && schema?.type === 'object' && schema?.additionalProperties) { + schemaTraversal(schema?.additionalProperties, `${schema.path}.additionalProperties`); + } + if (!schema?.description && schema?.title) { + schema['description'] = schema.title; + } + schemaTraversal(schema?.schema, schema.path); +}; + +export const getFormatParams = (meta, form) => { + const newMeta = [...meta]; + newMeta?.map((param) => { + // 请求参数有body的情况,如果body里的参数有必填,则body必填 + if (param.schema?.name === 'body') { + const body = form?.schema?.current?.properties?.body || null; + if (body?.required?.length) { + param.schema.required = true; + } + } + param.schema.name = param.name; + // 参数分组 + // if (Object.keys(categoryObj || {})?.length) { + // Object.keys(categoryObj || {}).map((key, index) => { + // if (categoryObj[key].includes(param.name)) { + // param.schema.group = key; + // param.schema.groupIndex = index + 1; + // } + // }); + // } + schemaTraversal(param.schema); + }); + return newMeta; +}; + +export const addParamStruct = (schemaPath, schema) => { + emitter.emit('addStruct', { schemaPath, schema }); +}; + +export const addNewStruct = (parameters, schemaPath, struct) => { + // const _struct = {...struct} + const _parameters = [...parameters]; + const travelSchema = (schema) => { + if (!schema) return; + if (`properties.${schema?.path}` === schemaPath && schema.ref) { + Object.keys(struct)?.map((key) => { + if (key !== 'path') { + schema[key] = struct[key]; + } + }); + schema.path = schema?.path + '.ref'; + delete schema.ref; + return; + } + if (schema?.type && schema?.type === 'array' && schema?.items) { + travelSchema(schema?.items); + } + if (schema?.type && schema?.type === 'object' && schema?.properties) { + Object.keys(schema?.properties).map((key) => { + travelSchema(schema?.properties[key]); + }); + } + travelSchema(schema?.schema); + }; + + _parameters?.map((param) => { + travelSchema(param?.schema); + }); + return _parameters; +}; + +export const getFormatValues = (paramValues: any, apiParams, purpose?: string) => { + if (!apiParams?.length) { + return {}; + } + + const newAPIParamValues = _.cloneDeep(paramValues || {}); + + // 调试器校验前的valueformat不需要转换object(json) + if (purpose !== 'validate') { + Object.keys(newAPIParamValues || {})?.map((key) => { + if (key !== 'endpoint' && !apiParams?.find((item) => item.name === key)) { + delete newAPIParamValues?.[key]; + } + // 处理object类型,但需要往后端传json string类型的参数值(不能仅仅用 style 为 json 进行判断) + if ( + apiParams?.find( + (item) => + item.name === key && + item.schema?.type === 'object' && + !item.schema?.properties && + !item.schema?.additionalProperties, + ) + ) { + try { + newAPIParamValues[key] = JSON.stringify(newAPIParamValues[key]); + } catch (e) { + newAPIParamValues[key] = ''; + } + } + }); + } else { + // 兼容校验问题 + Object.keys(newAPIParamValues || {})?.map((key) => { + if (key !== 'endpoint' && !apiParams?.find((item) => item.name === key)) { + delete newAPIParamValues?.[key]; + } + // 处理object类型,但需要往后端传json string类型的参数值(不能仅仅用 style 为 json 进行判断) + if ( + apiParams?.find( + (item) => + item.name === key && + item.schema?.type === 'any' && + !item.schema?.properties && + !item.schema?.additionalProperties && + typeof newAPIParamValues[key] === 'string', + ) + ) { + try { + console.log(newAPIParamValues[key], JSON.parse(newAPIParamValues[key])); + newAPIParamValues[key] = JSON.parse(newAPIParamValues[key]); + } catch (e) { + newAPIParamValues[key] = {}; + } + } + }); + } + + const travelObj = (values) => { + // 不再过滤空字符串 + Object.keys(values || {})?.map((key) => { + if (values[key] === undefined) { + delete values?.[key]; + } + if (typeof values[key] === 'object' && !Array.isArray(values[key])) { + if (isEmptyObj(values[key])) { + delete values?.[key]; + } + travelObj(values[key]); + } + if (Array.isArray(values[key])) { + if (values[key]?.length === 0) { + delete values?.[key]; + } else { + values[key].map((item, index) => { + if (item === undefined) { + values[key].splice(index, 1); + } + if (typeof item === 'object') { + if (isEmptyObj(item)) { + values[key].splice(index, 1); + } + travelObj(item); + } + }); + } + } + }); + }; + + const isEmptyObj = (obj) => { + let isEmpty = true; + + Object.keys(obj || {}).forEach((key) => { + if (obj[key] === '') { + isEmpty = false; + } + if (key === '') { + isEmpty = true; + } + if (obj[key] || key !== '') { + if (Array.isArray(obj[key]) && obj[key].length) { + isEmpty = false; + } + if (['string'].includes(typeof obj[key]) && !!obj[key]) { + isEmpty = false; + } + if (['number', 'boolean'].includes(typeof obj[key])) { + isEmpty = false; + } + if (_.isObject(obj[key]) && !_.isEmpty(obj[key])) { + isEmpty = false; + } + } + }); + return isEmpty; + }; + + travelObj(newAPIParamValues); + return newAPIParamValues; +}; + +export const handleExample = (schema, example) => { + if (['array', 'object', 'map'].includes(schema.type)) { + return undefined; + } else if (schema.type === 'number') { + return Number(example); + } else if (schema.type === 'boolean') { + if (schema.example === 'true') { + return true; + } else { + return false; + } + } else { + return schema.example; + } +}; + +export const stringToType = (type: string, value: string) => { + switch (type) { + case 'number': + return Number(value); + case 'boolean': + if (value === 'true') { + return true; + } + return false; + default: + return value; + } +}; + +export const getErrorWarningTip = (errors: ErrorField[]) => { + let errTip = ''; + errors?.map((error) => { + const errorMsg = `${error.dataPath}${error.message}`; + errTip = errTip?.length ? errTip + ',' + errorMsg : errorMsg; + }); + return errTip; +}; diff --git a/media/src/pages/document/index.scss b/media/src/pages/document/index.scss index e178d9e..c842c2d 100644 --- a/media/src/pages/document/index.scss +++ b/media/src/pages/document/index.scss @@ -5,6 +5,11 @@ @import "../../styles/Markdown.scss"; @import "../../components/APIPage/API.module.css"; @import "../../components/APIPage/TryAPI/TryAPI.module.css"; +@import "../../components/APIPage/APIDebugger//widgets/xconsole/enum.module.scss"; +@import "../../components/APIPage/APIDebugger//widgets/xconsole/index.module.scss"; +@import "../../components/APIPage/APIDebugger//widgets/xconsole/list.module.scss"; +@import "../../components/APIPage/APIDebugger/APIGuide.module.scss"; +@import "../../components/APIPage/APIDebugger/APIDebugger.module.scss"; $primary-color: #448ef7; $nav-height: 36px; From ed01ef03a80506c48ef96a19e0cb9e5257274e72 Mon Sep 17 00:00:00 2001 From: yini-chen Date: Wed, 3 Jan 2024 15:11:35 +0800 Subject: [PATCH 05/32] feat: update debug widgets --- .../APIPage/APIDebugger/APIGuide.tsx | 12 +- .../APIPage/APIDebugger/widgets/emitter.tsx | 116 ------------------ .../APIDebugger/widgets/xconsole/Struct.tsx | 6 +- .../APIDebugger/widgets/xconsole/utils.tsx | 7 +- media/src/components/main.tsx | 2 +- media/src/pages/document/index.module.scss | 5 + 6 files changed, 19 insertions(+), 129 deletions(-) delete mode 100644 media/src/components/APIPage/APIDebugger/widgets/emitter.tsx diff --git a/media/src/components/APIPage/APIDebugger/APIGuide.tsx b/media/src/components/APIPage/APIDebugger/APIGuide.tsx index d4594f9..24b19e4 100644 --- a/media/src/components/APIPage/APIDebugger/APIGuide.tsx +++ b/media/src/components/APIPage/APIDebugger/APIGuide.tsx @@ -29,6 +29,8 @@ export class APIGuideProps { export const APIGuide = React.memo((props: APIGuideProps) => { const isNewHeader = props.schema?.temperary; + console.log(props) + const description = props.schema?.description || props.schema?.title; const getShortName = (des: string) => { @@ -53,7 +55,7 @@ export const APIGuide = React.memo((props: APIGuideProps) => { } return shortName; }; - const shortDesc = getShortName(description || ''); + const shortDesc = /[0-9]+$/.test(props.fieldName) ? "" : getShortName(description || ''); let hasCollapsedIcon = ['object', 'array'].includes(props.schema.type as any); if (props.schema?.type === 'array' && ['string', 'number', 'integer'].includes(props.schema?.items?.type as any)) { @@ -70,9 +72,9 @@ export const APIGuide = React.memo((props: APIGuideProps) => { props.schema?.format ); - const paramTitle = !props.schema?.isItems - ? props.fieldName - : props.fieldName.substring(props.fieldName.lastIndexOf('.') + 1, props.fieldName.length); + const paramTitle = /[0-9]+$/.test(props.fieldName) + ? props.fieldName.substring(props.fieldName.lastIndexOf('.') + 1, props.fieldName.length) + : props.fieldName if (!paramTitle?.length) { return null; @@ -99,7 +101,7 @@ export const APIGuide = React.memo((props: APIGuideProps) => { {props.isRequired ? : null} {paramTitle} - {hasHelpTip ? ( + {hasHelpTip && shortDesc?.length ? ( {shortDesc} AuditCommentDTO - openAuditComment: any; - - refreshApiPage: string; //刷新 API 页面 - - refreshUnRelatedApi: string; - - resetMetaMapOpenRows: string; //重置metaMap 展开的行 - - jumpToCode: ErrorObject; - - addStruct: any; // 获取到数据结构 - - /** - * 打开快捷调试 - */ - openNewQuickTest: string; - -} - -// =================== -// 以下内容无需关心 -// =================== -export type EventNames = keyof EventTypes; - -class AmpEventEmitter extends EventEmitter.EventEmitter { - on(eventName: K, listener: (params?: EventTypes[K]) => void): this { - return super.on.call(this, eventName, listener); - } - - once(eventName: K, listener: (params?: EventTypes[K]) => void): this { - return super.once.call(this, eventName, listener); - } - - emit(eventName: K, params?: EventTypes[K]): boolean { - return super.emit.call(this, eventName, params); - } - - removeListener(eventName: K, listener: (params?: EventTypes[K]) => void): this { - return super.removeListener.call(this, eventName, listener); - } - - removeAllListeners(eventName: K): this { - return super.removeAllListeners.call(this, eventName); - } -} - -const emitter = new AmpEventEmitter(); -emitter.setMaxListeners(0); - -export { emitter }; diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/Struct.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/Struct.tsx index f908215..0c99569 100644 --- a/media/src/components/APIPage/APIDebugger/widgets/xconsole/Struct.tsx +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/Struct.tsx @@ -7,7 +7,7 @@ // import { LinkButton } from '@alicloud/console-components-actions'; import React from 'react'; import { CommonWidgetProps } from '../types'; -import { addParamStruct } from './utils'; +// import { addParamStruct } from './utils'; // import { addParamStruct } from '../utils'; export class StructProps extends CommonWidgetProps { @@ -24,7 +24,7 @@ export const Struct: React.FC = (props) => { } catch {} props.onChange(value); } - addParamStruct(props.schemaPath, props.schema); + // addParamStruct(props.schemaPath, props.schema); } return (
@@ -33,7 +33,7 @@ export const Struct: React.FC = (props) => { style={{ textDecoration: 'none' }} className="op" onClick={() => { - addParamStruct(props.schemaPath, props.schema); + // addParamStruct(props.schemaPath, props.schema); }} >
diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/utils.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/utils.tsx index e7b0e74..efac0af 100644 --- a/media/src/components/APIPage/APIDebugger/widgets/xconsole/utils.tsx +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/utils.tsx @@ -37,7 +37,6 @@ export const simpleType: any[] = ["string", "boolean", "number", "array", "objec // import { emitter } from '@/utils/emitter'; // import { ErrorField } from '@ali/api-component'; import _ from 'lodash'; -import { emitter } from "../emitter"; import { ErrorField } from "../../../../SemixFormRender/context"; export const schemaTraversal = (schema, parentPath?: string) => { @@ -112,9 +111,9 @@ export const getFormatParams = (meta, form) => { return newMeta; }; -export const addParamStruct = (schemaPath, schema) => { - emitter.emit('addStruct', { schemaPath, schema }); -}; +// export const addParamStruct = (schemaPath, schema) => { +// emitter.emit('addStruct', { schemaPath, schema }); +// }; export const addNewStruct = (parameters, schemaPath, struct) => { // const _struct = {...struct} diff --git a/media/src/components/main.tsx b/media/src/components/main.tsx index 7ec2613..4c7b974 100644 --- a/media/src/components/main.tsx +++ b/media/src/components/main.tsx @@ -5,7 +5,7 @@ import { getVSCode } from "../utils/utils"; import { parseAPIMetaDescription } from "../utils/parseAPIMetaDescription"; import { ApiErrorCode } from "./APIPage/APIDocument/ApiErrorCode"; import { PontUIService } from "../service/UIService"; -import "../pages/document/index.module.scss"; +import "../pages/document/index.scss"; import { routerMeta } from "../mocks/routerMeta"; if (typeof window !== "undefined") { diff --git a/media/src/pages/document/index.module.scss b/media/src/pages/document/index.module.scss index e178d9e..c842c2d 100644 --- a/media/src/pages/document/index.module.scss +++ b/media/src/pages/document/index.module.scss @@ -5,6 +5,11 @@ @import "../../styles/Markdown.scss"; @import "../../components/APIPage/API.module.css"; @import "../../components/APIPage/TryAPI/TryAPI.module.css"; +@import "../../components/APIPage/APIDebugger//widgets/xconsole/enum.module.scss"; +@import "../../components/APIPage/APIDebugger//widgets/xconsole/index.module.scss"; +@import "../../components/APIPage/APIDebugger//widgets/xconsole/list.module.scss"; +@import "../../components/APIPage/APIDebugger/APIGuide.module.scss"; +@import "../../components/APIPage/APIDebugger/APIDebugger.module.scss"; $primary-color: #448ef7; $nav-height: 36px; From f96a07560dfb8d6702c84783faea00bf40ad172a Mon Sep 17 00:00:00 2001 From: yini-chen Date: Thu, 4 Jan 2024 17:35:28 +0800 Subject: [PATCH 06/32] feat: sdk demo --- media/src/components/APIPage/API.tsx | 4 +- .../APIPage/APIDebugger/APIDebugger.tsx | 14 +- .../src/components/APIPage/TryAPI/TryAPI.tsx | 2 +- .../TrySDK/LanguageSwitcher.module.scss | 153 ++++++++++++++++++ .../APIPage/TrySDK/LanguageSwitcher.tsx | 92 +++++++++++ .../APIPage/TrySDK/TrySDK.module.scss | 5 + .../src/components/APIPage/TrySDK/TrySDK.tsx | 96 +++++++++++ media/src/components/APIPage/context.ts | 27 +++- .../components/SemixFormRender/SemixForm.tsx | 2 +- media/src/components/define.ts | 58 +++++++ media/src/components/utils.ts | 99 ++++++++++++ media/src/main.css | 2 +- media/src/mocks/makeCode.ts | 23 +++ media/src/pages/document/index.module.scss | 2 + media/src/pages/document/index.scss | 2 + media/src/service/UIService.ts | 6 +- media/src/types/openAPI.ts | 25 ++- 17 files changed, 588 insertions(+), 24 deletions(-) create mode 100644 media/src/components/APIPage/TrySDK/LanguageSwitcher.module.scss create mode 100644 media/src/components/APIPage/TrySDK/LanguageSwitcher.tsx create mode 100644 media/src/components/APIPage/TrySDK/TrySDK.module.scss create mode 100644 media/src/components/APIPage/TrySDK/TrySDK.tsx create mode 100644 media/src/components/define.ts create mode 100644 media/src/mocks/makeCode.ts diff --git a/media/src/components/APIPage/API.tsx b/media/src/components/APIPage/API.tsx index 38a4df8..a95bc83 100644 --- a/media/src/components/APIPage/API.tsx +++ b/media/src/components/APIPage/API.tsx @@ -16,6 +16,7 @@ import { APIPageContext } from "./context"; import APIDebugger from "./APIDebugger/APIDebugger"; import { SemixForm } from "../SemixFormRender"; import { TryAPI } from "./TryAPI/TryAPI"; +import TrySDK from "./TrySDK/TrySDK"; export class APIProps { selectedApi?: PontSpec.PontAPI; @@ -156,7 +157,7 @@ export const API: React.FC = (props) => { {documentComp} -
敬请期待...
+
{selectedApi?.externalDocs ? ( @@ -166,7 +167,6 @@ export const API: React.FC = (props) => { component="a" href={selectedApi?.externalDocs?.url} target="_blank" - // onClick={() => { // window.open(selectedApi?.externalDocs?.url, "_blank"); // }} diff --git a/media/src/components/APIPage/APIDebugger/APIDebugger.tsx b/media/src/components/APIPage/APIDebugger/APIDebugger.tsx index d116468..8812630 100644 --- a/media/src/components/APIPage/APIDebugger/APIDebugger.tsx +++ b/media/src/components/APIPage/APIDebugger/APIDebugger.tsx @@ -19,19 +19,7 @@ import { APIGuide } from "./APIGuide"; export class APIDebuggerProps {} export const APIDebugger: React.FC = (props) => { - const { apiMeta, schemaForm, product, version, onDebug, changeMode } = APIPageContext.useContainer(); - const [regionId, setRegionId] = React.useState(""); - const [endpoints, setEndpoints] = React.useState([]); - - React.useEffect(() => { - if (endpoints.length === 0) { - // get endpoints list - PontUIService.requestEndpoints(product).then((res) => { - console.log(res); - setEndpoints(res?.length ? res : endpointsMocks); - }); - } - }, [product]); + const { apiMeta, schemaForm, product, version, onDebug, changeMode, endpoints, regionId, setRegionId } = APIPageContext.useContainer(); return React.useMemo(() => { return ( diff --git a/media/src/components/APIPage/TryAPI/TryAPI.tsx b/media/src/components/APIPage/TryAPI/TryAPI.tsx index 3f311ee..facc725 100644 --- a/media/src/components/APIPage/TryAPI/TryAPI.tsx +++ b/media/src/components/APIPage/TryAPI/TryAPI.tsx @@ -39,7 +39,7 @@ const TAB_PANES = [ export const TryAPI: React.FC = (props) => { const { openAPIResponses, isApiResultLoading,version,apiMeta, product } = APIPageContext.useContainer(); const doc = `${product}::${version}::${apiMeta.name}` - const apiResult = openAPIResponses?.[doc] || apiResponse; + const apiResult = openAPIResponses?.[doc]; const noShowMonacoEditor = ["byte"]; diff --git a/media/src/components/APIPage/TrySDK/LanguageSwitcher.module.scss b/media/src/components/APIPage/TrySDK/LanguageSwitcher.module.scss new file mode 100644 index 0000000..dff43a7 --- /dev/null +++ b/media/src/components/APIPage/TrySDK/LanguageSwitcher.module.scss @@ -0,0 +1,153 @@ +.language-switcher-comp { + margin:16px 16px; + + .ant-tabs-content-holder{ + margin-top:16px + } + .ant-tabs { + .ant-tabs-extra-content { + display: inline-block; + margin-right: 20px; + color: #666666; + font-weight: 500; + } + &>.ant-tabs-nav::before { + border: none; + } + } + .ant-tabs-nav { + height: 32px; + margin-bottom: 16px; + color: #303030; + line-height: 32px; + padding-left: 0; + border: none; + box-shadow: none; + + .ant-tabs-extra-content { + .label { + &::after { + content: ':'; + } + } + } + + .oa-iconfont.icon-dara { + margin-right: -15px; + margin-left: -15px; + font-size: 18px; + vertical-align: -0.1em; + } + .ant-tabs-nav-more { + border: 1px solid #CBCBCB; + .anticon { + vertical-align: 0.4em; + } + } + } + + .ant-tabs-nav-wrap { + margin-left:16px; + .ant-tabs-nav-list { + padding-left: 0px !important; + .ant-tabs-tab { + margin-right: -1px !important; + padding: 8px 16px !important; + font-size: 12px; + border: none; + border: 1px solid #CBCBCB; + border-radius: 0px; + background-color: #fff; + &:nth-last-child(2) { + margin-right: 0px !important; + } + } + .ant-tabs-tab-active { + border: 1px solid #FF6A00; + position: relative; + z-index: 999; + } + } + + .oa-iconfont { + margin-right: 2px; + font-size: 14px; + vertical-align: 0; + + &.icon-java { + font-size: 16px; + vertical-align: -0.25em; + } + + &.icon-typescript { + font-size: 16px; + vertical-align: -0.25em; + } + + &.icon-python { + font-size: 16px; + vertical-align: -0.2em; + } + + &.icon-php { + font-size: 26px; + vertical-align: -0.3em; + } + + &.icon-go { + font-size: 26px; + vertical-align: -0.3em; + } + + &.icon-csharp { + vertical-align: -0.15em; + } + + &.icon-node-js { + vertical-align: -0.15em; + } + + &.icon-ruby { + vertical-align: -0.2em; + } + &.icon-cpp { + vertical-align: -0.2em; + } + &.icon-swift { + font-size: 20px; + vertical-align: -0.2em; + } + } + } + + .right-ops { + .ant-btn { + margin-right: 12px; + } + + .infos { + vertical-align: middle; + .item { + margin-left: 12px; + color: #fb6220; + cursor: pointer; + } + + .sep-line { + display: inline-block; + width: 1px; + height: 22px; + margin: 0 10px; + line-height: 22px; + vertical-align: middle; + background-color: #d8d8d8; + } + } + } + + .empty-card { + margin-top: 16px; + } + } + + \ No newline at end of file diff --git a/media/src/components/APIPage/TrySDK/LanguageSwitcher.tsx b/media/src/components/APIPage/TrySDK/LanguageSwitcher.tsx new file mode 100644 index 0000000..4c5b16a --- /dev/null +++ b/media/src/components/APIPage/TrySDK/LanguageSwitcher.tsx @@ -0,0 +1,92 @@ +/** + * @author yini-chen + * @description 语言切换 + */ +import { Card, Empty, Tabs } from 'antd'; +import * as React from 'react'; +// import { WorkbenchIcon } from './Icon/Icon'; +// import { EditorLanguages } from './MonacoEditor/define'; +// import { I18N } from '../utils/I18N'; +import { EditorLanguages } from '../../define'; +import I18N from '../../../utils/I18N'; +import { WorkbenchIcon } from '../../../Icon/Icon'; + +export const OLD_SDK_LANGUAAGES = [ + { value: EditorLanguages.Java, text: EditorLanguages.Java, icon: 'java' }, + { value: EditorLanguages.Javascript, text: 'Node.js', icon: 'node-js' }, + { value: EditorLanguages.Go, text: EditorLanguages.Go, icon: 'go' }, + { value: EditorLanguages.PHP, text: EditorLanguages.PHP, icon: 'php' }, + { value: EditorLanguages.Python, text: EditorLanguages.Python, icon: 'python' }, + { value: EditorLanguages.CSharp, text: 'C#', icon: 'csharp' }, + { value: EditorLanguages.Ruby, text: EditorLanguages.Ruby, icon: 'ruby' }, + { value: EditorLanguages.CPP, text: 'C++', icon: 'cpp' }, +]; + +export const DARA_SDK_LANGUAGES = [ + { value: EditorLanguages.JavaAsync, text: EditorLanguages.JavaAsync, icon: 'java' }, + { value: EditorLanguages.Java, text: EditorLanguages.Java, icon: 'java' }, + { value: EditorLanguages.TypeScript, text: 'TypeScript', icon: 'typescript' }, + { value: EditorLanguages.Go, text: EditorLanguages.Go, icon: 'go' }, + { value: EditorLanguages.PHP, text: EditorLanguages.PHP, icon: 'php' }, + { value: EditorLanguages.Python, text: EditorLanguages.Python, icon: 'python' }, + { value: EditorLanguages.Python2, text: EditorLanguages.Python2, icon: 'python' }, + + { value: EditorLanguages.CSharp, text: 'C#', icon: 'csharp' }, + { value: EditorLanguages.CPP, text: 'C++', icon: 'cpp' }, + { value: EditorLanguages.Swift, text: 'Swift', icon: 'swift' }, +]; + +export class LanguageSwitcherProps { + languages? = DARA_SDK_LANGUAGES; + + language? :string; + + tabContent: React.ReactNode; + + onLanguageChange(language: EditorLanguages) {} + + extra = null; + languageStatus? = {}; + sdkDemos? = {}; +} + +export const LanguageSwitcher: React.FC = (props) => { + + return ( +
+ props.onLanguageChange(tab as EditorLanguages)} + // tabBarExtraContent={ + // { left:
{I18N.main.explorer.allLanguages}
} + // } + type={'card'} + tabBarGutter={0} + > + {props.languages.map((language) => { + return ( + + {/* */} + {language.text} + + } + key={language.value} + > + {props.tabContent} + + ); + })} +
+ {/* {props.languages && props.languages?.length ? null : ( + + + + )} */} +
+ ); +}; + +LanguageSwitcher.defaultProps = new LanguageSwitcherProps(); diff --git a/media/src/components/APIPage/TrySDK/TrySDK.module.scss b/media/src/components/APIPage/TrySDK/TrySDK.module.scss new file mode 100644 index 0000000..d788255 --- /dev/null +++ b/media/src/components/APIPage/TrySDK/TrySDK.module.scss @@ -0,0 +1,5 @@ +.sdk-demo-content{ + .head-info{ + margin:16px 0 0 36px + } +} \ No newline at end of file diff --git a/media/src/components/APIPage/TrySDK/TrySDK.tsx b/media/src/components/APIPage/TrySDK/TrySDK.tsx new file mode 100644 index 0000000..189e1ab --- /dev/null +++ b/media/src/components/APIPage/TrySDK/TrySDK.tsx @@ -0,0 +1,96 @@ +/** + * @author 念依 + * @description + */ +import React from "react"; +import { LanguageSwitcher } from "./LanguageSwitcher"; +import { getLanguageEditorByLang } from "../../utils"; +import { Editor } from "@monaco-editor/react"; +import { codes } from "../../../mocks/makeCode"; +import { Tag } from "antd"; +import { PontUIService } from "../../../service/UIService"; +import { APIPageContext } from "../context"; + +export class TrySDKProps {} + +export const TrySDK: React.FC = (props) => { + // const daraSdkLannguages = SDKPublishInfo.getDaraLanguages(props.sdkInfos || [], product, version); + const { apiMeta, schemaForm, product, version, onDebug, changeMode, endpoints, regionId } = APIPageContext.useContainer(); + const [languageTab, setLanguageTab] = React.useState("java-async"); + const [sdkDemos, setSdkDemos] = React.useState(codes.demoSdk as any); + + React.useEffect(() => { + PontUIService.makeCodeRequest({ + paramsValue: schemaForm.formData, + apiMeta: apiMeta, + product, + version, + endpoint: endpoints?.find((item) => item.regionId === regionId)?.public, + regionId:regionId + }).then((res) => { + setSdkDemos(res?.data?.demoSdk || codes.demoSdk); + }); + }, [schemaForm.formData, regionId, apiMeta?.name,product,version ]); + + const getEditorLanguage = (lang) => { + switch (lang) { + case "java-async": + return "java"; + case "Java": + return "java"; + case "TypeScript": + return "typescript"; + case "Go": + return "go"; + case "PHP": + return "php"; + case "Python": + return "python"; + case "Python2": + return "python"; + case "CSharp": + return "csharp"; + case "cpp": + return "cpp"; + case "swift": + return "swift"; + default: + return "javascript"; + } + }; + + const tabContent = React.useMemo(() => { + return sdkDemos ? ( + <> + + + ): null; + }, [languageTab, sdkDemos]); + + return React.useMemo(() => { + return ( +
+
+ sdk | v2.0 +
+ setLanguageTab(lang)} + tabContent={tabContent} + > +
+ ); + }, [languageTab, sdkDemos, tabContent]); +}; +TrySDK.defaultProps = new TrySDKProps(); +export default TrySDK; diff --git a/media/src/components/APIPage/context.ts b/media/src/components/APIPage/context.ts index aa7e1ef..57db624 100644 --- a/media/src/components/APIPage/context.ts +++ b/media/src/components/APIPage/context.ts @@ -5,7 +5,8 @@ import { createContainer } from 'unstated-next'; import { SemixFormProps } from '../SemixFormRender'; import React from 'react'; import { PontUIService } from '../../service/UIService'; -import { ExtensionResponse, OpenAPIRequestResult } from '../../types/openAPI'; +import { OpenAPIResponse, OpenAPIRequestResult } from '../../types/openAPI'; +import { endpointsMocks } from '../../mocks/endpoints'; export class APIPageState { /** @@ -31,11 +32,33 @@ export class APIPageState { */ isApiResultLoading? = false changeMode: (mode: 'debug' | 'doc' | 'sdk') => void; + /** + * 服务地址 + */ + endpoints?: any[]; + /** + * regionId + */ + regionId?: string; + setRegionId?: (regionId: string) => void; } export const useAPIPageContext = (initialState = {} as APIPageState): APIPageState => { const [openAPIResponses, setOpenAPIResponse] = React.useState(null); const [isApiResultLoading, setIsApiResultLoading] = React.useState(false); + const [endpoints, setEndpoints] = React.useState([]); + const [regionId, setRegionId] = React.useState(""); + + React.useEffect(() => { + if (endpoints.length === 0) { + // get endpoints list + PontUIService.requestEndpoints(initialState.product).then((res) => { + console.log(res); + setEndpoints(res?.length ? res : endpointsMocks); + }); + } + }, [initialState.product]); + const onDebug = (value) =>{ setIsApiResultLoading(true); PontUIService.openAPIRequest(value).then(res=>{ @@ -47,7 +70,7 @@ export const useAPIPageContext = (initialState = {} as APIPageState): APIPageSta setOpenAPIResponse(responses); }); } - return { ...initialState, openAPIResponses, onDebug, isApiResultLoading }; + return { ...initialState, openAPIResponses, onDebug, isApiResultLoading, endpoints, regionId, setRegionId }; }; export const APIPageContext = createContainer(useAPIPageContext); \ No newline at end of file diff --git a/media/src/components/SemixFormRender/SemixForm.tsx b/media/src/components/SemixFormRender/SemixForm.tsx index 8a1e1ae..08ca40f 100644 --- a/media/src/components/SemixFormRender/SemixForm.tsx +++ b/media/src/components/SemixFormRender/SemixForm.tsx @@ -9,7 +9,7 @@ import { getSchemaByRef, semixifySchema, usePrevious } from "./utils"; import { SemixUISchema } from "./type"; import { FormInstance, FormStoreContext, FormStoreIntialState, useForm } from "./context"; import { Field } from "./common/Field"; -import { useDeepCompareMemo } from "use-deep-compare"; +// import { useDeepCompareMemo } from "use-deep-compare"; import { FormItem } from "./common/FormItem"; import { getFieldClassName } from "./utils"; import classNames from "classnames"; diff --git a/media/src/components/define.ts b/media/src/components/define.ts new file mode 100644 index 0000000..502cec0 --- /dev/null +++ b/media/src/components/define.ts @@ -0,0 +1,58 @@ +/** + * @file SQL编辑器定义文件 + * @author 奇阳 + */ + +export enum EditorLanguages { + /** Darabonba */ + Darabonba = 'Darabonba', + + TypeScript = 'TypeScript', + + Javascript = 'javascript', + + PHP = 'PHP', + + Ruby = 'Ruby', + + CSharp = 'CSharp', + + Markdown = 'markdown', + + /** Go 语言 */ + Go = 'Go', + + /** Java 语言 */ + Java = 'Java', + + /** Python 语言 */ + Python = 'Python', + + Python2 = 'Python2', + + JSON = 'json', + + XML = 'xml', + + Shell = 'shell', + + CPP = 'cpp', + + JavaAsync = 'java-async', + + Swift = 'swift', + } + + export interface SDKLanguage { + typescript: string; + java: string; + php: string; + python: string; + python2: string; + go: string; + csharp: string; + swift: string; + "java-async": string; + dependencies: string; +} + \ No newline at end of file diff --git a/media/src/components/utils.ts b/media/src/components/utils.ts index b3c1570..970cb90 100644 --- a/media/src/components/utils.ts +++ b/media/src/components/utils.ts @@ -1,6 +1,7 @@ import _ from "lodash"; import { APIResponse } from "../types/WorkbenchAPI"; import { Parser } from 'xml2js'; +import { EditorLanguages } from "./define"; export const getRefSchema = (schemas: any) => ($ref: string) => { const schemaName = $ref.split("/").pop(); @@ -27,3 +28,101 @@ export const parseXml = (body: string): any => { } return result.output; }; + +export const getLanguageEditorByLang = ( + lang: + | 'JAVA' + | 'NODEJS' + | 'GO' + | 'PHP' + | 'PYTHON' + | 'PYTHON2' + | 'CSHARP' + | 'RUBY' + | 'TYPESCRIPT' + | 'JAVAASYNC' + | 'SWIFT', +) => { + switch (lang) { + case 'CSHARP': { + return EditorLanguages.CSharp; + } + case 'GO': { + return EditorLanguages.Go; + } + case 'JAVA': { + return EditorLanguages.Java; + } + case 'JAVAASYNC': { + return EditorLanguages.JavaAsync; + } + case 'NODEJS': { + return EditorLanguages.Javascript; + } + case 'PHP': { + return EditorLanguages.PHP; + } + case 'PYTHON': { + return EditorLanguages.Python; + } + case 'PYTHON2': { + return EditorLanguages.Python2; + } + case 'RUBY': { + return EditorLanguages.Ruby; + } + case 'TYPESCRIPT': { + return EditorLanguages.TypeScript; + } + case 'SWIFT': { + return EditorLanguages.Swift; + } + default: { + return EditorLanguages.Javascript; + } + } +}; + +export const getLangByLanguageEditor = (language: EditorLanguages) => { + switch (language) { + case EditorLanguages.TypeScript: { + return 'TYPESCRIPT'; + } + case EditorLanguages.CSharp: { + return 'CSHARP'; + } + case EditorLanguages.Go: { + return 'GO'; + } + case EditorLanguages.Java: { + return 'JAVA'; + } + case EditorLanguages.JavaAsync: { + return 'JAVAASYNC'; + } + case EditorLanguages.Javascript: { + return 'NODEJS'; + } + case EditorLanguages.PHP: { + return 'PHP'; + } + case EditorLanguages.Python: { + return 'PYTHON'; + } + case EditorLanguages.Python2: { + return 'PYTHON2'; + } + case EditorLanguages.Ruby: { + return 'RUBY'; + } + case EditorLanguages.CPP: { + return 'CPP'; + } + case EditorLanguages.Swift: { + return 'SWIFT'; + } + default: { + return language; + } + } +}; diff --git a/media/src/main.css b/media/src/main.css index 9fde540..818a6a4 100644 --- a/media/src/main.css +++ b/media/src/main.css @@ -46,7 +46,7 @@ html body { padding-left:16px; .content{ padding: 0px 20px 20px 0; - height: calc(100vh - 200px); + height: calc(100vh - 270px); overflow: auto; } } diff --git a/media/src/mocks/makeCode.ts b/media/src/mocks/makeCode.ts new file mode 100644 index 0000000..9e2ed4d --- /dev/null +++ b/media/src/mocks/makeCode.ts @@ -0,0 +1,23 @@ +export const codes = { + "demoSdk": { + "typescript": "// This file is auto-generated, don't edit it\n// 依赖的模块可通过下载工程中的模块依赖文件或右上角的获取 SDK 依赖信息查看\nimport Ecs20140526, * as $Ecs20140526 from '@alicloud/ecs20140526';\nimport OpenApi, * as $OpenApi from '@alicloud/openapi-client';\nimport Util, * as $Util from '@alicloud/tea-util';\nimport * as $tea from '@alicloud/tea-typescript';\n\n\nexport default class Client {\n\n /**\n * 使用AK&SK初始化账号Client\n * @param accessKeyId\n * @param accessKeySecret\n * @return Client\n * @throws Exception\n */\n static createClient(accessKeyId: string, accessKeySecret: string): Ecs20140526 {\n let config = new $OpenApi.Config({\n // 必填,您的 AccessKey ID\n accessKeyId: accessKeyId,\n // 必填,您的 AccessKey Secret\n accessKeySecret: accessKeySecret,\n });\n // Endpoint 请参考 https://api.aliyun.com/product/Ecs\n config.endpoint = `ecs.cn-qingdao.aliyuncs.com`;\n return new Ecs20140526(config);\n }\n\n static async main(args: string[]): Promise {\n // 请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID 和 ALIBABA_CLOUD_ACCESS_KEY_SECRET。\n // 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例使用环境变量获取 AccessKey 的方式进行调用,仅供参考,建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378664.html\n let client = Client.createClient(process.env['ALIBABA_CLOUD_ACCESS_KEY_ID'], process.env['ALIBABA_CLOUD_ACCESS_KEY_SECRET']);\n let runInstancesRequest = new $Ecs20140526.RunInstancesRequest({\n regionId: \"cn-qingdao\",\n });\n let runtime = new $Util.RuntimeOptions({ });\n try {\n // 复制代码运行请自行打印 API 的返回值\n await client.runInstancesWithOptions(runInstancesRequest, runtime);\n } catch (error) {\n // 错误 message\n console.log(error.message);\n // 诊断地址\n console.log(error.data[\"Recommend\"]);\n Util.assertAsString(error.message);\n } \n }\n\n}\n\nClient.main(process.argv.slice(2));", + "java": "// This file is auto-generated, don't edit it. Thanks.\npackage com.aliyun.sample;\n\nimport com.aliyun.tea.*;\n\npublic class Sample {\n\n /**\n * 使用AK&SK初始化账号Client\n * @param accessKeyId\n * @param accessKeySecret\n * @return Client\n * @throws Exception\n */\n public static com.aliyun.ecs20140526.Client createClient(String accessKeyId, String accessKeySecret) throws Exception {\n com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()\n // 必填,您的 AccessKey ID\n .setAccessKeyId(accessKeyId)\n // 必填,您的 AccessKey Secret\n .setAccessKeySecret(accessKeySecret);\n // Endpoint 请参考 https://api.aliyun.com/product/Ecs\n config.endpoint = \"ecs.cn-qingdao.aliyuncs.com\";\n return new com.aliyun.ecs20140526.Client(config);\n }\n\n public static void main(String[] args_) throws Exception {\n java.util.List args = java.util.Arrays.asList(args_);\n // 请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID 和 ALIBABA_CLOUD_ACCESS_KEY_SECRET。\n // 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例使用环境变量获取 AccessKey 的方式进行调用,仅供参考,建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378657.html\n com.aliyun.ecs20140526.Client client = Sample.createClient(System.getenv(\"ALIBABA_CLOUD_ACCESS_KEY_ID\"), System.getenv(\"ALIBABA_CLOUD_ACCESS_KEY_SECRET\"));\n com.aliyun.ecs20140526.models.RunInstancesRequest runInstancesRequest = new com.aliyun.ecs20140526.models.RunInstancesRequest()\n .setRegionId(\"cn-qingdao\");\n com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();\n try {\n // 复制代码运行请自行打印 API 的返回值\n client.runInstancesWithOptions(runInstancesRequest, runtime);\n } catch (TeaException error) {\n // 错误 message\n System.out.println(error.getMessage());\n // 诊断地址\n System.out.println(error.getData().get(\"Recommend\"));\n com.aliyun.teautil.Common.assertAsString(error.message);\n } catch (Exception _error) {\n TeaException error = new TeaException(_error.getMessage(), _error);\n // 错误 message\n System.out.println(error.getMessage());\n // 诊断地址\n System.out.println(error.getData().get(\"Recommend\"));\n com.aliyun.teautil.Common.assertAsString(error.message);\n } \n }\n}\n", + "php": " $accessKeyId,\n // 必填,您的 AccessKey Secret\n \"accessKeySecret\" => $accessKeySecret\n ]);\n // Endpoint 请参考 https://api.aliyun.com/product/Ecs\n $config->endpoint = \"ecs.cn-qingdao.aliyuncs.com\";\n return new Ecs($config);\n }\n\n /**\n * @param string[] $args\n * @return void\n */\n public static function main($args){\n // 请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID 和 ALIBABA_CLOUD_ACCESS_KEY_SECRET。\n // 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例使用环境变量获取 AccessKey 的方式进行调用,仅供参考,建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/311677.html\n $client = self::createClient(getenv(\"ALIBABA_CLOUD_ACCESS_KEY_ID\"), getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'));\n $runInstancesRequest = new RunInstancesRequest([\n \"regionId\" => \"cn-qingdao\"\n ]);\n $runtime = new RuntimeOptions([]);\n try {\n // 复制代码运行请自行打印 API 的返回值\n $client->runInstancesWithOptions($runInstancesRequest, $runtime);\n }\n catch (Exception $error) {\n if (!($error instanceof TeaError)) {\n $error = new TeaError([], $error->getMessage(), $error->getCode(), $error);\n }\n // 错误 message\n var_dump($error->message);\n // 诊断地址\n var_dump($error->data[\"Recommend\"]);\n Utils::assertAsString($error->message);\n }\n }\n}\n$path = __DIR__ . \\DIRECTORY_SEPARATOR . '..' . \\DIRECTORY_SEPARATOR . 'vendor' . \\DIRECTORY_SEPARATOR . 'autoload.php';\nif (file_exists($path)) {\n require_once $path;\n}\nSample::main(array_slice($argv, 1));\n", + "python": "# -*- coding: utf-8 -*-\n# This file is auto-generated, don't edit it. Thanks.\nimport os\nimport sys\n\nfrom typing import List\n\nfrom alibabacloud_ecs20140526.client import Client as Ecs20140526Client\nfrom alibabacloud_tea_openapi import models as open_api_models\nfrom alibabacloud_ecs20140526 import models as ecs_20140526_models\nfrom alibabacloud_tea_util import models as util_models\nfrom alibabacloud_tea_util.client import Client as UtilClient\n\n\nclass Sample:\n def __init__(self):\n pass\n\n @staticmethod\n def create_client(\n access_key_id: str,\n access_key_secret: str,\n ) -> Ecs20140526Client:\n \"\"\"\n 使用AK&SK初始化账号Client\n @param access_key_id:\n @param access_key_secret:\n @return: Client\n @throws Exception\n \"\"\"\n config = open_api_models.Config(\n # 必填,您的 AccessKey ID,\n access_key_id=access_key_id,\n # 必填,您的 AccessKey Secret,\n access_key_secret=access_key_secret\n )\n # Endpoint 请参考 https://api.aliyun.com/product/Ecs\n config.endpoint = f'ecs.cn-qingdao.aliyuncs.com'\n return Ecs20140526Client(config)\n\n @staticmethod\n def main(\n args: List[str],\n ) -> None:\n # 请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID 和 ALIBABA_CLOUD_ACCESS_KEY_SECRET。\n # 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例使用环境变量获取 AccessKey 的方式进行调用,仅供参考,建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378659.html\n client = Sample.create_client(os.environ['ALIBABA_CLOUD_ACCESS_KEY_ID'], os.environ['ALIBABA_CLOUD_ACCESS_KEY_SECRET'])\n run_instances_request = ecs_20140526_models.RunInstancesRequest(\n region_id='cn-qingdao'\n )\n runtime = util_models.RuntimeOptions()\n try:\n # 复制代码运行请自行打印 API 的返回值\n client.run_instances_with_options(run_instances_request, runtime)\n except Exception as error:\n # 错误 message\n print(error.message)\n # 诊断地址\n print(error.data.get(\"Recommend\"))\n UtilClient.assert_as_string(error.message)\n\n @staticmethod\n async def main_async(\n args: List[str],\n ) -> None:\n # 请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID 和 ALIBABA_CLOUD_ACCESS_KEY_SECRET。\n # 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例使用环境变量获取 AccessKey 的方式进行调用,仅供参考,建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378659.html\n client = Sample.create_client(os.environ['ALIBABA_CLOUD_ACCESS_KEY_ID'], os.environ['ALIBABA_CLOUD_ACCESS_KEY_SECRET'])\n run_instances_request = ecs_20140526_models.RunInstancesRequest(\n region_id='cn-qingdao'\n )\n runtime = util_models.RuntimeOptions()\n try:\n # 复制代码运行请自行打印 API 的返回值\n await client.run_instances_with_options_async(run_instances_request, runtime)\n except Exception as error:\n # 错误 message\n print(error.message)\n # 诊断地址\n print(error.data.get(\"Recommend\"))\n UtilClient.assert_as_string(error.message)\n\n\nif __name__ == '__main__':\n Sample.main(sys.argv[1:])\n", + "go": "// This file is auto-generated, don't edit it. Thanks.\npackage main\n\nimport (\n \"encoding/json\"\n \"strings\"\n \"fmt\"\n \"os\"\n ecs20140526 \"github.com/alibabacloud-go/ecs-20140526/v3/client\"\n openapi \"github.com/alibabacloud-go/darabonba-openapi/v2/client\"\n util \"github.com/alibabacloud-go/tea-utils/v2/service\"\n \"github.com/alibabacloud-go/tea/tea\"\n)\n\n\n/**\n * 使用AK&SK初始化账号Client\n * @param accessKeyId\n * @param accessKeySecret\n * @return Client\n * @throws Exception\n */\nfunc CreateClient (accessKeyId *string, accessKeySecret *string) (_result *ecs20140526.Client, _err error) {\n config := &openapi.Config{\n // 必填,您的 AccessKey ID\n AccessKeyId: accessKeyId,\n // 必填,您的 AccessKey Secret\n AccessKeySecret: accessKeySecret,\n }\n // Endpoint 请参考 https://api.aliyun.com/product/Ecs\n config.Endpoint = tea.String(\"ecs.cn-qingdao.aliyuncs.com\")\n _result = &ecs20140526.Client{}\n _result, _err = ecs20140526.NewClient(config)\n return _result, _err\n}\n\nfunc _main (args []*string) (_err error) {\n // 请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID 和 ALIBABA_CLOUD_ACCESS_KEY_SECRET。\n // 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例使用环境变量获取 AccessKey 的方式进行调用,仅供参考,建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378661.html\n client, _err := CreateClient(tea.String(os.Getenv(\"ALIBABA_CLOUD_ACCESS_KEY_ID\")), tea.String(os.Getenv(\"ALIBABA_CLOUD_ACCESS_KEY_SECRET\")))\n if _err != nil {\n return _err\n }\n\n runInstancesRequest := &ecs20140526.RunInstancesRequest{\n RegionId: tea.String(\"cn-qingdao\"),\n }\n runtime := &util.RuntimeOptions{}\n tryErr := func()(_e error) {\n defer func() {\n if r := tea.Recover(recover()); r != nil {\n _e = r\n }\n }()\n // 复制代码运行请自行打印 API 的返回值\n _, _err = client.RunInstancesWithOptions(runInstancesRequest, runtime)\n if _err != nil {\n return _err\n }\n\n return nil\n }()\n\n if tryErr != nil {\n var error = &tea.SDKError{}\n if _t, ok := tryErr.(*tea.SDKError); ok {\n error = _t\n } else {\n error.Message = tea.String(tryErr.Error())\n }\n // 错误 message\n fmt.Println(tea.StringValue(error.Message))\n // 诊断地址\n var data interface{}\n d := json.NewDecoder(strings.NewReader(tea.StringValue(error.Data)))\n d.Decode(&data)\n if m, ok := data.(map[string]interface{}); ok {\n recommend, _ := m[\"Recommend\"]\n fmt.Println(recommend)\n }\n _, _err = util.AssertAsString(error.Message)\n if _err != nil {\n return _err\n }\n }\n return _err\n}\n\n\nfunc main() {\n err := _main(tea.StringSlice(os.Args[1:]))\n if err != nil {\n panic(err)\n }\n}\n", + "python2": "# -*- coding: utf-8 -*-\n# This file is auto-generated, don't edit it. Thanks.\nfrom __future__ import unicode_literals\n\nimport os\nimport sys\n\nfrom alibabacloud_ecs20140526.client import Client as Ecs20140526Client\nfrom alibabacloud_tea_openapi import models as open_api_models\nfrom alibabacloud_ecs20140526 import models as ecs_20140526_models\nfrom alibabacloud_tea_util import models as util_models\nfrom alibabacloud_tea_util.client import Client as UtilClient\n\n\nclass Sample(object):\n def __init__(self):\n pass\n\n @staticmethod\n def create_client(access_key_id, access_key_secret):\n \"\"\"\n 使用AK&SK初始化账号Client\n\n @type access_key_id: str\n @param access_key_id:\n\n @type access_key_secret: str\n @param access_key_secret:\n\n @return: Client\n @throws Exception\n \"\"\"\n config = open_api_models.Config(\n # 必填,您的 AccessKey ID,\n access_key_id=access_key_id,\n # 必填,您的 AccessKey Secret,\n access_key_secret=access_key_secret\n )\n # Endpoint 请参考 https://api.aliyun.com/product/Ecs\n config.endpoint = 'ecs.cn-qingdao.aliyuncs.com'\n return Ecs20140526Client(config)\n\n @staticmethod\n def main(args):\n # 请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID 和 ALIBABA_CLOUD_ACCESS_KEY_SECRET。\n # 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例使用环境变量获取 AccessKey 的方式进行调用,仅供参考,建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378659.html\n client = Sample.create_client(os.environ['ALIBABA_CLOUD_ACCESS_KEY_ID'], os.environ['ALIBABA_CLOUD_ACCESS_KEY_SECRET'])\n run_instances_request = ecs_20140526_models.RunInstancesRequest(\n region_id='cn-qingdao'\n )\n runtime = util_models.RuntimeOptions()\n try:\n # 复制代码运行请自行打印 API 的返回值\n client.run_instances_with_options(run_instances_request, runtime)\n except Exception as error:\n # 错误 message\n print error.message\n # 诊断地址\n print error.data.get(\"Recommend\")\n UtilClient.assert_as_string(error.message)\n\n\nif __name__ == '__main__':\n Sample.main(sys.argv[1:])\n", + "csharp": "// This file is auto-generated, don't edit it. Thanks.\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Threading.Tasks;\n\nusing Tea;\nusing Tea.Utils;\n\n\nnamespace AlibabaCloud.SDK.Sample\n{\n public class Sample \n {\n\n /**\n * 使用AK&SK初始化账号Client\n * @param accessKeyId\n * @param accessKeySecret\n * @return Client\n * @throws Exception\n */\n public static AlibabaCloud.SDK.Ecs20140526.Client CreateClient(string accessKeyId, string accessKeySecret)\n {\n AlibabaCloud.OpenApiClient.Models.Config config = new AlibabaCloud.OpenApiClient.Models.Config\n {\n // 必填,您的 AccessKey ID\n AccessKeyId = accessKeyId,\n // 必填,您的 AccessKey Secret\n AccessKeySecret = accessKeySecret,\n };\n // Endpoint 请参考 https://api.aliyun.com/product/Ecs\n config.Endpoint = \"ecs.cn-qingdao.aliyuncs.com\";\n return new AlibabaCloud.SDK.Ecs20140526.Client(config);\n }\n\n public static void Main(string[] args)\n {\n // 请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID 和 ALIBABA_CLOUD_ACCESS_KEY_SECRET。\n // 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例使用环境变量获取 AccessKey 的方式进行调用,仅供参考,建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378671.html\n AlibabaCloud.SDK.Ecs20140526.Client client = CreateClient(Environment.GetEnvironmentVariable(\"ALIBABA_CLOUD_ACCESS_KEY_ID\"), Environment.GetEnvironmentVariable(\"ALIBABA_CLOUD_ACCESS_KEY_SECRET\"));\n AlibabaCloud.SDK.Ecs20140526.Models.RunInstancesRequest runInstancesRequest = new AlibabaCloud.SDK.Ecs20140526.Models.RunInstancesRequest\n {\n RegionId = \"cn-qingdao\",\n };\n AlibabaCloud.TeaUtil.Models.RuntimeOptions runtime = new AlibabaCloud.TeaUtil.Models.RuntimeOptions();\n try\n {\n // 复制代码运行请自行打印 API 的返回值\n client.RunInstancesWithOptions(runInstancesRequest, runtime);\n }\n catch (TeaException error)\n {\n // 错误 message\n Console.WriteLine(error.Message);\n // 诊断地址\n Console.WriteLine(error.Data[\"Recommend\"]);\n AlibabaCloud.TeaUtil.Common.AssertAsString(error.Message);\n }\n catch (Exception _error)\n {\n TeaException error = new TeaException(new Dictionary\n {\n { \"message\", _error.Message }\n });\n // 错误 message\n Console.WriteLine(error.Message);\n // 诊断地址\n Console.WriteLine(error.Data[\"Recommend\"]);\n AlibabaCloud.TeaUtil.Common.AssertAsString(error.Message);\n }\n }\n\n\n }\n}\n", + "swift": "#!/usr/bin/env xcrun swift\n\nimport Cocoa\nimport Foundation\nimport Tea\nimport AlibabacloudEcs20140526\nimport AlibabacloudOpenApi\nimport TeaUtils\n\nopen class Client {\n public static func createClient(_ accessKeyId: String?, _ accessKeySecret: String?) throws -> AlibabacloudEcs20140526.Client {\n var config: AlibabacloudOpenApi.Config = AlibabacloudOpenApi.Config([\n \"accessKeyId\": accessKeyId as! String,\n \"accessKeySecret\": accessKeySecret as! String\n ])\n config.endpoint = \"ecs.cn-qingdao.aliyuncs.com\"\n return AlibabacloudEcs20140526.Client(config)\n }\n\n @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)\n public static func main(_ args: [String]?) async throws -> Void {\n var client: AlibabacloudEcs20140526.Client = try Client.createClient(ProcessInfo.processInfo.environment[\"ALIBABA_CLOUD_ACCESS_KEY_ID\"], ProcessInfo.processInfo.environment[\"ALIBABA_CLOUD_ACCESS_KEY_SECRET\"])\n var runInstancesRequest: AlibabacloudEcs20140526.RunInstancesRequest = AlibabacloudEcs20140526.RunInstancesRequest([\n \"regionId\": \"cn-qingdao\"\n ])\n var runtime: TeaUtils.RuntimeOptions = TeaUtils.RuntimeOptions([:])\n do {\n try await client.runInstancesWithOptions(runInstancesRequest as! AlibabacloudEcs20140526.RunInstancesRequest, runtime as! TeaUtils.RuntimeOptions)\n }\n catch {\n if error is Tea.TeaError {\n try TeaUtils.Client.assertAsString(error.message)\n } else {\n throw error\n }\n }\n }\n}\n\nClient.main(CommandLine.arguments)\n", + "java-async": "// This file is auto-generated, don't edit it. Thanks.\npackage demo;\n\nimport com.aliyun.auth.credentials.Credential;\nimport com.aliyun.auth.credentials.provider.StaticCredentialProvider;\nimport com.aliyun.core.http.HttpClient;\nimport com.aliyun.core.http.HttpMethod;\nimport com.aliyun.core.http.ProxyOptions;\nimport com.aliyun.httpcomponent.httpclient.ApacheAsyncHttpClientBuilder;\nimport com.aliyun.sdk.service.ecs20140526.models.*;\nimport com.aliyun.sdk.service.ecs20140526.*;\nimport com.google.gson.Gson;\nimport darabonba.core.RequestConfiguration;\nimport darabonba.core.client.ClientOverrideConfiguration;\nimport darabonba.core.utils.CommonUtil;\nimport darabonba.core.TeaPair;\n\n//import javax.net.ssl.KeyManager;\n//import javax.net.ssl.X509TrustManager;\nimport java.net.InetSocketAddress;\nimport java.time.Duration;\nimport java.util.*;\nimport java.util.concurrent.CompletableFuture;\n\npublic class RunInstances {\n public static void main(String[] args) throws Exception {\n\n // HttpClient Configuration\n /*HttpClient httpClient = new ApacheAsyncHttpClientBuilder()\n .connectionTimeout(Duration.ofSeconds(10)) // Set the connection timeout time, the default is 10 seconds\n .responseTimeout(Duration.ofSeconds(10)) // Set the response timeout time, the default is 20 seconds\n .maxConnections(128) // Set the connection pool size\n .maxIdleTimeOut(Duration.ofSeconds(50)) // Set the connection pool timeout, the default is 30 seconds\n // Configure the proxy\n .proxy(new ProxyOptions(ProxyOptions.Type.HTTP, new InetSocketAddress(\"\", 9001))\n .setCredentials(\"\", \"\"))\n // If it is an https connection, you need to configure the certificate, or ignore the certificate(.ignoreSSL(true))\n .x509TrustManagers(new X509TrustManager[]{})\n .keyManagers(new KeyManager[]{})\n .ignoreSSL(false)\n .build();*/\n\n // Configure Credentials authentication information, including ak, secret, token\n StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()\n // Please ensure that the environment variables ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET are set.\n .accessKeyId(System.getenv(\"ALIBABA_CLOUD_ACCESS_KEY_ID\"))\n .accessKeySecret(System.getenv(\"ALIBABA_CLOUD_ACCESS_KEY_SECRET\"))\n //.securityToken(System.getenv(\"ALIBABA_CLOUD_SECURITY_TOKEN\")) // use STS token\n .build());\n\n // Configure the Client\n AsyncClient client = AsyncClient.builder()\n .region(\"cn-qingdao\") // Region ID\n //.httpClient(httpClient) // Use the configured HttpClient, otherwise use the default HttpClient (Apache HttpClient)\n .credentialsProvider(provider)\n //.serviceConfiguration(Configuration.create()) // Service-level configuration\n // Client-level configuration rewrite, can set Endpoint, Http request parameters, etc.\n .overrideConfiguration(\n ClientOverrideConfiguration.create()\n // Endpoint 请参考 https://api.aliyun.com/product/Ecs\n .setEndpointOverride(\"ecs.cn-qingdao.aliyuncs.com\")\n //.setConnectTimeout(Duration.ofSeconds(30))\n )\n .build();\n\n // Parameter settings for API request\n RunInstancesRequest runInstancesRequest = RunInstancesRequest.builder()\n .regionId(\"cn-qingdao\")\n // Request-level configuration rewrite, can set Http request parameters, etc.\n // .requestConfiguration(RequestConfiguration.create().setHttpHeaders(new HttpHeaders()))\n .build();\n\n // Asynchronously get the return value of the API request\n CompletableFuture response = client.runInstances(runInstancesRequest);\n // Synchronously get the return value of the API request\n RunInstancesResponse resp = response.get();\n System.out.println(new Gson().toJson(resp));\n // Asynchronous processing of return values\n /*response.thenAccept(resp -> {\n System.out.println(new Gson().toJson(resp));\n }).exceptionally(throwable -> { // Handling exceptions\n System.out.println(throwable.getMessage());\n return null;\n });*/\n\n // Finally, close the client\n client.close();\n }\n\n}\n", + "dependencies": "\n\n 4.0.0\n com.aliyun\n sample\n 1.0.0\n jar\n sample\n \n \n The Apache License, Version 2.0\n http://www.apache.org/licenses/LICENSE-2.0.txt\n \n \n \n \n aliyunproducts\n Aliyun SDK\n aliyunsdk@aliyun.com\n \n \n \n 1.8\n 8\n 8\n \n \n \n com.aliyun\n alibabacloud-ecs20140526\n 2.0.12\n \n \n com.aliyun\n aliyun-gateway-pop\n 0.1.5-beta\n \n \n com.aliyun\n aliyun-gateway-oss\n 0.1.5-beta\n \n \n com.aliyun\n aliyun-gateway-sls\n 0.1.5-beta\n \n \n \n \n \n org.apache.maven.plugins\n maven-compiler-plugin\n 3.6.1\n \n ${maven.compiler.source}\n ${maven.compiler.target}\n \n \n \n \n" + }, + "apiInfo": { + "apiStyle": "RPC", + "product": "Ecs", + "method": "POST,GET", + "apiVersion": "2014-05-26", + "apiName": "RunInstances", + "regionId": "cn-qingdao" + }, + "cost": 628 +} \ No newline at end of file diff --git a/media/src/pages/document/index.module.scss b/media/src/pages/document/index.module.scss index c842c2d..8197e0b 100644 --- a/media/src/pages/document/index.module.scss +++ b/media/src/pages/document/index.module.scss @@ -10,6 +10,8 @@ @import "../../components/APIPage/APIDebugger//widgets/xconsole/list.module.scss"; @import "../../components/APIPage/APIDebugger/APIGuide.module.scss"; @import "../../components/APIPage/APIDebugger/APIDebugger.module.scss"; +@import "../../components/APIPage/TrySDK/LanguageSwitcher.module.scss"; +@import "../../components/APIPage/TrySDK/TrySDK.module.scss"; $primary-color: #448ef7; $nav-height: 36px; diff --git a/media/src/pages/document/index.scss b/media/src/pages/document/index.scss index c842c2d..8197e0b 100644 --- a/media/src/pages/document/index.scss +++ b/media/src/pages/document/index.scss @@ -10,6 +10,8 @@ @import "../../components/APIPage/APIDebugger//widgets/xconsole/list.module.scss"; @import "../../components/APIPage/APIDebugger/APIGuide.module.scss"; @import "../../components/APIPage/APIDebugger/APIDebugger.module.scss"; +@import "../../components/APIPage/TrySDK/LanguageSwitcher.module.scss"; +@import "../../components/APIPage/TrySDK/TrySDK.module.scss"; $primary-color: #448ef7; $nav-height: 36px; diff --git a/media/src/service/UIService.ts b/media/src/service/UIService.ts index 6fbd085..f074e12 100644 --- a/media/src/service/UIService.ts +++ b/media/src/service/UIService.ts @@ -1,5 +1,5 @@ import { PontSpec } from "pontx-spec"; -import { ExtensionResponse } from "../types/openAPI"; +import { MakeCodeResponse, OpenAPIResponse } from "../types/openAPI"; /** 不同使用场景,各自注册服务来源 */ const defaultSpecs: any[] = []; @@ -51,10 +51,12 @@ export const PontUIService = { }): Promise => {}, /** request openapi */ - openAPIRequest: async (params = {}): Promise => new ExtensionResponse, + openAPIRequest: async (params = {}): Promise => new OpenAPIResponse, /** get endpoints list */ requestEndpoints: async (product: string) => { return [] as any; }, + /** get sdk demo */ + makeCodeRequest: async (params = {}): Promise => new MakeCodeResponse, }; \ No newline at end of file diff --git a/media/src/types/openAPI.ts b/media/src/types/openAPI.ts index 4f9a498..379972d 100644 --- a/media/src/types/openAPI.ts +++ b/media/src/types/openAPI.ts @@ -51,9 +51,30 @@ export class PontAPI { }; } -export class ExtensionResponse { +export class OpenAPIResponse { requestId : string; doc: string; response: OpenAPIRequestResult; type: string; -} \ No newline at end of file +} + +export class MakeCodeResponse { + code: number; + data: MakeCodeData; +} + +export class MakeCodeData { + demoSdk: any; + apiInfo: APIInfo; + cost: number; +} + +export class APIInfo { + apiStyle: string; + product: string; + method: string; + apiVersion: Date; + apiName: string; + regionId: string; +} + From fa0243e919e57744e7aaa78dcd389c49617f98ab Mon Sep 17 00:00:00 2001 From: yini-chen Date: Thu, 4 Jan 2024 17:35:40 +0800 Subject: [PATCH 07/32] feat: sdk demo --- src/Service.ts | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/Service.ts b/src/Service.ts index 9df3573..6149ef2 100644 --- a/src/Service.ts +++ b/src/Service.ts @@ -163,6 +163,45 @@ export class AlicloudAPIService { } } + + + async makeCodeRequest(requestData) { + const { apiMeta, paramsValue, product, version, endpoint, regionId } = requestData; + const newParamsValue = getFormatValues(paramsValue, apiMeta?.parameters); + const security = apiMeta?.ext?.security; + const defaultCredentialType = + security?.length > 0 + ? security.indexOf("AK") < 0 + ? security.indexOf("BearerToken") < 0 + ? "anonymous" + : "bearer" + : "ak" + : "ak"; + const body = { + "apiName": apiMeta?.name, + "apiStyle": "RPC", + "apiVersion": version, + "method": "POST,GET", + "product": product, + "bodyStyle": null, + "sdkType": "dara", + "params": newParamsValue || {}, + "regionId": regionId, + "endpoint": endpoint, + "credential": {tyep: defaultCredentialType}, + "runtimeOptions": {}, + "useCommon": false + } + const resStr = await fetch( + `https://api.aliyun.com/api/product/makeCode`, + {method: 'post', + body: JSON.stringify(body), + headers: {'Content-Type': 'application/json'}}, + ).then((res) => res.text()); + const res = JSON.parse(resStr); + return res; + } + async openAPIRequest(requestData) { const { apiMeta, paramsValue, product, version, endpoint } = requestData; const newParamsValue = getFormatValues(paramsValue, apiMeta?.parameters); From 3b6fb38941c1b7064ae9a1d6a95f97df4173ee71 Mon Sep 17 00:00:00 2001 From: yini-chen Date: Fri, 5 Jan 2024 16:01:41 +0800 Subject: [PATCH 08/32] feat: Stores the language selected by the user --- .../TrySDK/LanguageSwitcher.module.scss | 5 --- .../APIPage/TrySDK/LanguageSwitcher.tsx | 30 +++++++++------- .../APIPage/TrySDK/TrySDK.module.scss | 14 ++++++-- .../src/components/APIPage/TrySDK/TrySDK.tsx | 35 +++++++++++++++---- media/src/components/main.tsx | 2 +- media/src/service/UIService.ts | 4 +++ src/Service.ts | 9 +++-- 7 files changed, 70 insertions(+), 29 deletions(-) diff --git a/media/src/components/APIPage/TrySDK/LanguageSwitcher.module.scss b/media/src/components/APIPage/TrySDK/LanguageSwitcher.module.scss index dff43a7..bd92e68 100644 --- a/media/src/components/APIPage/TrySDK/LanguageSwitcher.module.scss +++ b/media/src/components/APIPage/TrySDK/LanguageSwitcher.module.scss @@ -1,9 +1,4 @@ .language-switcher-comp { - margin:16px 16px; - - .ant-tabs-content-holder{ - margin-top:16px - } .ant-tabs { .ant-tabs-extra-content { display: inline-block; diff --git a/media/src/components/APIPage/TrySDK/LanguageSwitcher.tsx b/media/src/components/APIPage/TrySDK/LanguageSwitcher.tsx index 4c5b16a..7fbbf86 100644 --- a/media/src/components/APIPage/TrySDK/LanguageSwitcher.tsx +++ b/media/src/components/APIPage/TrySDK/LanguageSwitcher.tsx @@ -2,7 +2,7 @@ * @author yini-chen * @description 语言切换 */ -import { Card, Empty, Tabs } from 'antd'; +import { Card, Empty, Select, Tabs } from 'antd'; import * as React from 'react'; // import { WorkbenchIcon } from './Icon/Icon'; // import { EditorLanguages } from './MonacoEditor/define'; @@ -54,12 +54,9 @@ export const LanguageSwitcher: React.FC = (props) => { return (
- props.onLanguageChange(tab as EditorLanguages)} - // tabBarExtraContent={ - // { left:
{I18N.main.explorer.allLanguages}
} - // } type={'card'} tabBarGutter={0} > @@ -69,7 +66,6 @@ export const LanguageSwitcher: React.FC = (props) => { disabled={!Object.keys(props.sdkDemos || {})?.includes(language.value?.toLocaleLowerCase())} tab={ - {/* */} {language.text} } @@ -79,12 +75,22 @@ export const LanguageSwitcher: React.FC = (props) => { ); })} -
- {/* {props.languages && props.languages?.length ? null : ( - - - - )} */} + */} + +
); }; diff --git a/media/src/components/APIPage/TrySDK/TrySDK.module.scss b/media/src/components/APIPage/TrySDK/TrySDK.module.scss index d788255..8695dac 100644 --- a/media/src/components/APIPage/TrySDK/TrySDK.module.scss +++ b/media/src/components/APIPage/TrySDK/TrySDK.module.scss @@ -1,5 +1,15 @@ .sdk-demo-content{ - .head-info{ - margin:16px 0 0 36px + .operations{ + display: flex; + margin:16px 16px; + width: 300px; + justify-content: space-around; + .head-info{ + margin: auto 0; + } } + .tab-content{ + position: relative; + } + } \ No newline at end of file diff --git a/media/src/components/APIPage/TrySDK/TrySDK.tsx b/media/src/components/APIPage/TrySDK/TrySDK.tsx index 189e1ab..5fb731a 100644 --- a/media/src/components/APIPage/TrySDK/TrySDK.tsx +++ b/media/src/components/APIPage/TrySDK/TrySDK.tsx @@ -4,21 +4,32 @@ */ import React from "react"; import { LanguageSwitcher } from "./LanguageSwitcher"; -import { getLanguageEditorByLang } from "../../utils"; import { Editor } from "@monaco-editor/react"; import { codes } from "../../../mocks/makeCode"; import { Tag } from "antd"; import { PontUIService } from "../../../service/UIService"; import { APIPageContext } from "../context"; +import { Button } from "@alicloud/console-components"; +import { getVSCode } from "../../../utils/utils"; export class TrySDKProps {} export const TrySDK: React.FC = (props) => { // const daraSdkLannguages = SDKPublishInfo.getDaraLanguages(props.sdkInfos || [], product, version); const { apiMeta, schemaForm, product, version, onDebug, changeMode, endpoints, regionId } = APIPageContext.useContainer(); - const [languageTab, setLanguageTab] = React.useState("java-async"); + const [languageTab, setLanguageTab] = React.useState("Java"); const [sdkDemos, setSdkDemos] = React.useState(codes.demoSdk as any); + React.useEffect(()=>{ + PontUIService.getLocalLanguage().then(res=>{ + if(res?.length){ + setLanguageTab(res) + }else{ + setLanguageTab('Java') + } + }) + }) + React.useEffect(() => { PontUIService.makeCodeRequest({ paramsValue: schemaForm.formData, @@ -61,23 +72,25 @@ export const TrySDK: React.FC = (props) => { const tabContent = React.useMemo(() => { return sdkDemos ? ( - <> +
- +
): null; }, [languageTab, sdkDemos]); return React.useMemo(() => { return (
+
sdk | v2.0
@@ -85,9 +98,19 @@ export const TrySDK: React.FC = (props) => { language={languageTab} sdkDemos={sdkDemos} extra={null} - onLanguageChange={(lang) => setLanguageTab(lang)} - tabContent={tabContent} + onLanguageChange={(lang) => { + setLanguageTab(lang); + PontUIService.updateLocalLanguage(lang) + }} + tabContent={null} > + +
+ {tabContent}
); }, [languageTab, sdkDemos, tabContent]); diff --git a/media/src/components/main.tsx b/media/src/components/main.tsx index 4c7b974..7ec2613 100644 --- a/media/src/components/main.tsx +++ b/media/src/components/main.tsx @@ -5,7 +5,7 @@ import { getVSCode } from "../utils/utils"; import { parseAPIMetaDescription } from "../utils/parseAPIMetaDescription"; import { ApiErrorCode } from "./APIPage/APIDocument/ApiErrorCode"; import { PontUIService } from "../service/UIService"; -import "../pages/document/index.scss"; +import "../pages/document/index.module.scss"; import { routerMeta } from "../mocks/routerMeta"; if (typeof window !== "undefined") { diff --git a/media/src/service/UIService.ts b/media/src/service/UIService.ts index f074e12..721203c 100644 --- a/media/src/service/UIService.ts +++ b/media/src/service/UIService.ts @@ -59,4 +59,8 @@ export const PontUIService = { }, /** get sdk demo */ makeCodeRequest: async (params = {}): Promise => new MakeCodeResponse, + /** get local language */ + getLocalLanguage : async () => "", + /** update local language */ + updateLocalLanguage : async (language:string) => "", }; \ No newline at end of file diff --git a/src/Service.ts b/src/Service.ts index 6149ef2..1ed97de 100644 --- a/src/Service.ts +++ b/src/Service.ts @@ -163,7 +163,13 @@ export class AlicloudAPIService { } } + async updateLocalLanguage(lang) { + this.context.globalState.update('defaultLanguage', lang); + } + async getLocalLanguage() { + return this.context.globalState.get('defaultLanguage') + } async makeCodeRequest(requestData) { const { apiMeta, paramsValue, product, version, endpoint, regionId } = requestData; @@ -179,11 +185,8 @@ export class AlicloudAPIService { : "ak"; const body = { "apiName": apiMeta?.name, - "apiStyle": "RPC", "apiVersion": version, - "method": "POST,GET", "product": product, - "bodyStyle": null, "sdkType": "dara", "params": newParamsValue || {}, "regionId": regionId, From 1fb2be3e1f19e8275916b76ac9a42f99720292c9 Mon Sep 17 00:00:00 2001 From: yini-chen Date: Tue, 9 Jan 2024 10:58:08 +0800 Subject: [PATCH 09/32] feat: open in code --- media/src/components/APIPage/API.tsx | 35 ++--- .../APIDebugger/APIDebugger.module.scss | 3 + .../APIPage/APIDebugger/APIDebugger.tsx | 86 +++++------ .../APIPage/APIDebugger/APIGuide.tsx | 72 ++++----- .../APIPage/TryAPI/TryAPI.module.css | 11 ++ .../src/components/APIPage/TryAPI/TryAPI.tsx | 109 ++++++++++--- .../APIPage/TrySDK/LanguageSwitcher.tsx | 5 +- .../APIPage/TrySDK/TrySDK.module.scss | 14 +- .../src/components/APIPage/TrySDK/TrySDK.tsx | 101 +++++++----- media/src/components/APIPage/context.ts | 5 + media/src/components/common/MonacoEditor.scss | 40 +++++ media/src/components/common/MonacoEditor.tsx | 145 ++++++++++++++++++ media/src/components/main.tsx | 4 +- media/src/components/utils.ts | 53 +++++++ media/src/main.css | 6 +- media/src/pages/document/index.module.scss | 1 + media/src/pages/document/index.scss | 1 + media/src/registerService.tsx | 2 +- media/src/service/UIService.ts | 4 + media/src/utils/utils.ts | 3 +- src/Service.ts | 25 ++- src/openApiService/request/request.ts | 6 +- 22 files changed, 541 insertions(+), 190 deletions(-) create mode 100644 media/src/components/common/MonacoEditor.scss create mode 100644 media/src/components/common/MonacoEditor.tsx diff --git a/media/src/components/APIPage/API.tsx b/media/src/components/APIPage/API.tsx index a95bc83..389d9a6 100644 --- a/media/src/components/APIPage/API.tsx +++ b/media/src/components/APIPage/API.tsx @@ -17,6 +17,9 @@ import APIDebugger from "./APIDebugger/APIDebugger"; import { SemixForm } from "../SemixFormRender"; import { TryAPI } from "./TryAPI/TryAPI"; import TrySDK from "./TrySDK/TrySDK"; +import { Dropdown, MenuProps } from "antd"; +import { PontUIService } from "../../service/UIService"; +import { getVSCode } from "../../utils/utils"; export class APIProps { selectedApi?: PontSpec.PontAPI; @@ -157,25 +160,14 @@ export const API: React.FC = (props) => { {documentComp}
-
+
+ +
- {selectedApi?.externalDocs ? ( -
- -
- ) : null} -
+
+ +
@@ -197,7 +189,14 @@ export const API: React.FC = (props) => {
{/* */} {selectedApi ? ( diff --git a/media/src/components/APIPage/APIDebugger/APIDebugger.module.scss b/media/src/components/APIPage/APIDebugger/APIDebugger.module.scss index d82e342..5d36800 100644 --- a/media/src/components/APIPage/APIDebugger/APIDebugger.module.scss +++ b/media/src/components/APIPage/APIDebugger/APIDebugger.module.scss @@ -25,4 +25,7 @@ bottom: 0; left: 0; } +} +.endpoint-selector-title{ + margin: 8px 0; } \ No newline at end of file diff --git a/media/src/components/APIPage/APIDebugger/APIDebugger.tsx b/media/src/components/APIPage/APIDebugger/APIDebugger.tsx index 8812630..7d55526 100644 --- a/media/src/components/APIPage/APIDebugger/APIDebugger.tsx +++ b/media/src/components/APIPage/APIDebugger/APIDebugger.tsx @@ -21,50 +21,50 @@ export class APIDebuggerProps {} export const APIDebugger: React.FC = (props) => { const { apiMeta, schemaForm, product, version, onDebug, changeMode, endpoints, regionId, setRegionId } = APIPageContext.useContainer(); - return React.useMemo(() => { - return ( -
-
- -
-
- { - return - }} - form={schemaForm as any} - // onChange={(value): void => { - // callVscode({ data: value, type: "openAPIDebug", requestId: 100 }, (res) => { - // console.log("callVscode callback", res); - // }); - // }} - > -
-
- -
- + const endpoint = regionId ? endpoints?.find((item) => item.regionId === regionId)?.public : endpoints?.find((item) => item.regionId === 'cn-hangzhou')?.public; + + return ( +
+
+ +
+
+ { + return + }} + form={schemaForm as any} + // onChange={(value): void => { + // callVscode({ data: value, type: "openAPIDebug", requestId: 100 }, (res) => { + // console.log("callVscode callback", res); + // }); + // }} + >
- ); - }, [schemaForm.formData, regionId, endpoints]); +
+ +
+ +
+ ); }; APIDebugger.defaultProps = new APIDebuggerProps(); export default APIDebugger; diff --git a/media/src/components/APIPage/APIDebugger/APIGuide.tsx b/media/src/components/APIPage/APIDebugger/APIGuide.tsx index 24b19e4..70652ab 100644 --- a/media/src/components/APIPage/APIDebugger/APIGuide.tsx +++ b/media/src/components/APIPage/APIDebugger/APIGuide.tsx @@ -2,15 +2,9 @@ * @author 念依 * @description 参数Title */ - -// import { AmpIcon } from '@ali/amp-base'; -import { Balloon, Icon, Tag } from '@alicloud/console-components'; -// import { LinkButton } from '@alicloud/console-components-actions'; -import { RightOutlined } from '@ant-design/icons'; -import React from 'react'; -import { stringToType } from './widgets/xconsole/utils'; -import { SemixMarkdown } from 'semix-schema-table'; -// import { stringToType } from './utils'; +import { Balloon, Icon, Tag } from "@alicloud/console-components"; +import React from "react"; +import { SemixMarkdown } from "semix-schema-table"; export class APIGuideProps { schema?: any; @@ -22,20 +16,17 @@ export class APIGuideProps { deleteHeader?: Function; changeValue?: (dataPath: string, value: string | number | boolean) => void; // titleOperator?: any; - product:string; - version:string; + product: string; + version: string; } export const APIGuide = React.memo((props: APIGuideProps) => { - const isNewHeader = props.schema?.temperary; - - console.log(props) const description = props.schema?.description || props.schema?.title; const getShortName = (des: string) => { const endIndex = Math.min( - ...[',', '\n', '。'].map((ch) => des.indexOf(ch)).map((num) => (num === -1 ? Number.MAX_VALUE : num)), + ...[",", "\n", "。"].map((ch) => des.indexOf(ch)).map((num) => (num === -1 ? Number.MAX_VALUE : num)), ); let shortName = des; @@ -43,10 +34,10 @@ export const APIGuide = React.memo((props: APIGuideProps) => { shortName = des.slice(0, endIndex); } - if (shortName.startsWith('/g) || []; repalceStrs?.map((str) => { - shortName = shortName.replace(str, ''); + shortName = shortName.replace(str, ""); }); } @@ -55,10 +46,10 @@ export const APIGuide = React.memo((props: APIGuideProps) => { } return shortName; }; - const shortDesc = /[0-9]+$/.test(props.fieldName) ? "" : getShortName(description || ''); + const shortDesc = /[0-9]+$/.test(props.fieldName) ? "" : getShortName(description || ""); - let hasCollapsedIcon = ['object', 'array'].includes(props.schema.type as any); - if (props.schema?.type === 'array' && ['string', 'number', 'integer'].includes(props.schema?.items?.type as any)) { + let hasCollapsedIcon = ["object", "array"].includes(props.schema.type as any); + if (props.schema?.type === "array" && ["string", "number", "integer"].includes(props.schema?.items?.type as any)) { hasCollapsedIcon = false; } @@ -73,8 +64,8 @@ export const APIGuide = React.memo((props: APIGuideProps) => { ); const paramTitle = /[0-9]+$/.test(props.fieldName) - ? props.fieldName.substring(props.fieldName.lastIndexOf('.') + 1, props.fieldName.length) - : props.fieldName + ? props.fieldName.substring(props.fieldName.lastIndexOf(".") + 1, props.fieldName.length) + : props.fieldName; if (!paramTitle?.length) { return null; @@ -83,24 +74,27 @@ export const APIGuide = React.memo((props: APIGuideProps) => { return (
{!props.schema?.isRoot ? ( -
+
{hasCollapsedIcon ? ( -
- { - props.setCollapsed(!props.collapsed); - }} - style={{ marginRight: 8, paddingTop: 5 }} - rotate={props.collapsed ? 0 : 90} - translate={undefined} - /> +
{ + props.setCollapsed(!props.collapsed); + }} + style={{ marginRight: 8, paddingTop: 2 }} + > + {props.collapsed ? ( +
+ ) : ( +
+ )}
) : null} -
+
{props.isRequired ? : null} {paramTitle} - + {hasHelpTip && shortDesc?.length ? ( {shortDesc} @@ -110,14 +104,16 @@ export const APIGuide = React.memo((props: APIGuideProps) => { style={{ width: 500 }} closable={false} trigger={ - -
+ +
} >
描述 -
+
+ +
{props.schema?.example ? ( <>
@@ -149,7 +145,7 @@ export const APIGuide = React.memo((props: APIGuideProps) => { <>
枚举值
{props.schema?.enum?.map((item) => ( - + {item.value || item} ))} diff --git a/media/src/components/APIPage/TryAPI/TryAPI.module.css b/media/src/components/APIPage/TryAPI/TryAPI.module.css index c345151..aa68525 100644 --- a/media/src/components/APIPage/TryAPI/TryAPI.module.css +++ b/media/src/components/APIPage/TryAPI/TryAPI.module.css @@ -2,6 +2,17 @@ position: relative; padding: 0 16px 16px; height: 100%; + .ant-btn { + margin-left: 8px; + padding: 8px; + background-color: #f8f9fa; + border: 1px #f8f9fa solid; + } + .ant-btn:hover{ + background-color: #fff; + border: 1px #2f84ae solid; + color: #2f84ae; + } .ant-alert { margin-bottom: 16px; diff --git a/media/src/components/APIPage/TryAPI/TryAPI.tsx b/media/src/components/APIPage/TryAPI/TryAPI.tsx index facc725..4c96702 100644 --- a/media/src/components/APIPage/TryAPI/TryAPI.tsx +++ b/media/src/components/APIPage/TryAPI/TryAPI.tsx @@ -4,11 +4,11 @@ */ import _ from "lodash"; -import { getIsUploadApi, parseXml } from "../../utils"; +import { getEditorMenuItems, getIsUploadApi, parseXml } from "../../utils"; import React from "react"; -import { Alert, Empty, Spin, message } from "antd"; +import { Alert, Button, Dropdown, Empty, Spin, message } from "antd"; import CopyToClipboard from "react-copy-to-clipboard"; -import { Button, Tab } from "@alicloud/console-components"; +import { Balloon, Tab } from "@alicloud/console-components"; import Editor from "@monaco-editor/react"; import I18N from "../../../utils/I18N"; import { APIResponse } from "../../../types/WorkbenchAPI"; @@ -17,9 +17,7 @@ import { OpenAPIRequestResult } from "../../../types/openAPI"; import { APIPageContext } from "../context"; import { apiResponse } from "../../../mocks/openApiResponse"; -export class TryAPIProps { - -} +export class TryAPIProps {} const TAB_PANES = [ { @@ -37,9 +35,10 @@ const TAB_PANES = [ ]; export const TryAPI: React.FC = (props) => { - const { openAPIResponses, isApiResultLoading,version,apiMeta, product } = APIPageContext.useContainer(); - const doc = `${product}::${version}::${apiMeta.name}` - const apiResult = openAPIResponses?.[doc]; + const { openAPIResponses, isApiResultLoading, version, apiMeta, product, mode } = APIPageContext.useContainer(); + const doc = `${product}::${version}::${apiMeta.name}`; + const [tab, setTab] = React.useState(TAB_PANES[0].value); + const apiResult = openAPIResponses?.[doc] || apiResponse; const noShowMonacoEditor = ["byte"]; @@ -132,16 +131,21 @@ export const TryAPI: React.FC = (props) => { return responseSchema[statusCode]?.schema || {}; }; + const items = [ + ...getEditorMenuItems(getTabValue(tab), "json"), + { + key: "gotoweb", + label: "去门户网页版调试", + codicon: "link-external", + onClick: () => { + window.open(apiMeta?.externalDocs?.url, "_blank"); + }, + }, + ]; + return (
- + {apiResult?.result || isApiResultLoading ? (
{isApiResultLoading ? ( @@ -155,18 +159,26 @@ export const TryAPI: React.FC = (props) => { {/* {apiResult?.result || props.isApiResultLoading ? ( */}
-
-
-
- {String(statusCode).startsWith("2") ? I18N.main.explorer.success : I18N.main.explorer.error} +
+
+
+ {String(statusCode).startsWith("2") ? I18N.main.explorer.success : I18N.main.explorer.error} +
-
{apiResult && statusCode ? (
{/* {httpStatusMessageMap[statusCode] || statusCode} */} {I18N.main.explorer.statusCode} - + {statusCode}
@@ -215,7 +227,56 @@ export const TryAPI: React.FC = (props) => { )}
- + + { + if (navigator.clipboard) { + navigator.clipboard.writeText(getTabValue(tab)); + message.success("复制成功"); + } + }} + > +
+ + } + > + 复制 + + {/* {items?.map((item) => { + return ( + +
+ + } + > + {item.label} + + ); + })} */} + + + + + } + onChange={(t: any) => setTab(t)} + > {TAB_PANES.map((tab) => { return ( diff --git a/media/src/components/APIPage/TrySDK/LanguageSwitcher.tsx b/media/src/components/APIPage/TrySDK/LanguageSwitcher.tsx index 7fbbf86..4659f50 100644 --- a/media/src/components/APIPage/TrySDK/LanguageSwitcher.tsx +++ b/media/src/components/APIPage/TrySDK/LanguageSwitcher.tsx @@ -34,7 +34,7 @@ export const DARA_SDK_LANGUAGES = [ { value: EditorLanguages.CSharp, text: 'C#', icon: 'csharp' }, { value: EditorLanguages.CPP, text: 'C++', icon: 'cpp' }, { value: EditorLanguages.Swift, text: 'Swift', icon: 'swift' }, -]; +] as Array<{value:string; text:string; icon:string; disabled:boolean}>; export class LanguageSwitcherProps { languages? = DARA_SDK_LANGUAGES; @@ -47,7 +47,6 @@ export class LanguageSwitcherProps { extra = null; languageStatus? = {}; - sdkDemos? = {}; } export const LanguageSwitcher: React.FC = (props) => { @@ -80,7 +79,7 @@ export const LanguageSwitcher: React.FC = (props) => { props.languages.map((language) => { return ( = (props) => { // const daraSdkLannguages = SDKPublishInfo.getDaraLanguages(props.sdkInfos || [], product, version); - const { apiMeta, schemaForm, product, version, onDebug, changeMode, endpoints, regionId } = APIPageContext.useContainer(); + const { apiMeta, schemaForm, product, version, onDebug, changeMode, endpoints, regionId, mode } = + APIPageContext.useContainer(); const [languageTab, setLanguageTab] = React.useState("Java"); const [sdkDemos, setSdkDemos] = React.useState(codes.demoSdk as any); - React.useEffect(()=>{ - PontUIService.getLocalLanguage().then(res=>{ - if(res?.length){ - setLanguageTab(res) - }else{ - setLanguageTab('Java') + React.useEffect(() => { + PontUIService.getLocalLanguage().then((res) => { + if (res?.length) { + setLanguageTab(res); + } else { + setLanguageTab("Java"); } - }) - }) + }); + }); React.useEffect(() => { PontUIService.makeCodeRequest({ - paramsValue: schemaForm.formData, - apiMeta: apiMeta, - product, - version, - endpoint: endpoints?.find((item) => item.regionId === regionId)?.public, - regionId:regionId - }).then((res) => { + paramsValue: schemaForm.formData, + apiMeta: apiMeta, + product, + version, + endpoint: endpoints?.find((item) => item.regionId === regionId)?.public, + regionId: regionId, + }).then((res) => { setSdkDemos(res?.data?.demoSdk || codes.demoSdk); }); - }, [schemaForm.formData, regionId, apiMeta?.name,product,version ]); + }, [schemaForm.formData, regionId, apiMeta?.name, product, version]); const getEditorLanguage = (lang) => { switch (lang) { @@ -70,6 +72,17 @@ export const TrySDK: React.FC = (props) => { } }; + React.useEffect(() => { + if (mode === "sdk") { + getVSCode()?.setState({ + ...getVSCode()?.getState(), + code: sdkDemos[languageTab?.toLocaleLowerCase()], + language: languageTab, + }); + } + }, [mode, languageTab, sdkDemos[languageTab?.toLocaleLowerCase()]]); + + const tabContent = React.useMemo(() => { return sdkDemos ? (
@@ -77,40 +90,44 @@ export const TrySDK: React.FC = (props) => { height={800} options={{ readOnly: true, - tabCompletion:true + tabCompletion: true, }} // theme='vs-dark' language={getEditorLanguage(languageTab)} value={sdkDemos[languageTab?.toLocaleLowerCase()]} />
- ): null; + ) : null; }, [languageTab, sdkDemos]); return React.useMemo(() => { return (
-
-
- sdk | v2.0 -
- { - setLanguageTab(lang); - PontUIService.updateLocalLanguage(lang) - }} - tabContent={null} - > - -
- {tabContent} + + sdk | v2.0 +
+ } + menuItems={[{ + key: "gotoweb", + label: "去门户网页版调试", + codicon:"link-external", + onClick: () => { + window.open(apiMeta?.externalDocs?.url, "_blank"); + }, + }]} + value={sdkDemos[languageTab?.toLocaleLowerCase()]} + languages={DARA_SDK_LANGUAGES?.map((lang) => { + if (Object.keys(sdkDemos || {})?.includes(lang?.value?.toLocaleLowerCase())) { + return lang; + } else { + return { ...lang, disabled: true }; + } + })} + >
); }, [languageTab, sdkDemos, tabContent]); diff --git a/media/src/components/APIPage/context.ts b/media/src/components/APIPage/context.ts index 57db624..c3247b3 100644 --- a/media/src/components/APIPage/context.ts +++ b/media/src/components/APIPage/context.ts @@ -7,6 +7,7 @@ import React from 'react'; import { PontUIService } from '../../service/UIService'; import { OpenAPIResponse, OpenAPIRequestResult } from '../../types/openAPI'; import { endpointsMocks } from '../../mocks/endpoints'; +import { getVSCode } from '../../utils/utils'; export class APIPageState { /** @@ -31,6 +32,10 @@ export class APIPageState { * 发起调用loading */ isApiResultLoading? = false + /** + * 模式选择 + */ + mode: 'debug' | 'doc' |'sdk' = 'debug'; changeMode: (mode: 'debug' | 'doc' | 'sdk') => void; /** * 服务地址 diff --git a/media/src/components/common/MonacoEditor.scss b/media/src/components/common/MonacoEditor.scss new file mode 100644 index 0000000..2829b69 --- /dev/null +++ b/media/src/components/common/MonacoEditor.scss @@ -0,0 +1,40 @@ +.editor-content { + .operations { + display: flex; + margin: 0 0 16px 0px; + // box-shadow: 1px 2px 2px 0px rgba(0, 0, 0, 0.2); + padding: 8px 16px; + // width: 300px; + justify-content: space-between; + .right-area { + display: flex; + .ant-btn { + margin-left: 8px; + padding: 0px; + background-color: #f8f9fa; + border: 1px #f8f9fa solid; + } + .ant-btn:hover{ + background-color: #fff; + border: 1px #2f84ae solid; + color: #2f84ae; + } + + } + .left-area { + display: flex; + margin: auto 0; + width: 200px; + .head-info { + margin: auto 8px; + } + } + .codicon { + padding: 8px; + cursor: pointer; + } + } + .tab-content { + position: relative; + } +} diff --git a/media/src/components/common/MonacoEditor.tsx b/media/src/components/common/MonacoEditor.tsx new file mode 100644 index 0000000..c7e882b --- /dev/null +++ b/media/src/components/common/MonacoEditor.tsx @@ -0,0 +1,145 @@ +/** + * @author 念依 + * @description 编辑器 + */ +import { Editor } from "@monaco-editor/react"; +import React from "react"; +import { DARA_SDK_LANGUAGES, LanguageSwitcher } from "../APIPage/TrySDK/LanguageSwitcher"; +import { Button, Dropdown, MenuProps, Tooltip, message } from "antd"; +import { PontUIService } from "../../service/UIService"; +import { Balloon } from "@alicloud/console-components"; +import { getEditorLanguage, getEditorMenuItems } from "../utils"; + +export class MonacoEditorProps { + languageTab?: string; + setLanguageTab?: (lang) => void; + languageSelector? = true; + copyable? = true; + header?: React.ReactNode; + value? = ""; + readOnly? = true; + height? = 800; + languages? = DARA_SDK_LANGUAGES; + menuItems?: Array<{ key: string; label: string; codicon?: string; onClick: () => void }> = []; +} + +export const MonacoEditor: React.FC = (props) => { + const { + languageTab, + setLanguageTab, + languageSelector, + languages, + copyable, + header, + value, + readOnly, + height, + menuItems, + } = props; + + const items: Array<{ key: string; label: string; codicon?: string; onClick: () => void }> = [ + ...getEditorMenuItems(value, getEditorLanguage(languageTab)), + ...menuItems, + ]; + + const tabContent = React.useMemo(() => { + return value?.length ? ( +
+ +
+ ) : null; + }, [languageTab, value]); + + return ( +
+
+
+ {languageSelector ? ( + { + setLanguageTab(lang); + PontUIService.updateLocalLanguage(lang); + }} + tabContent={null} + > + ) : null} + {header || null} +
+
+ {copyable ? ( + { + if (navigator.clipboard) { + navigator.clipboard.writeText(value); + message.success("复制成功"); + } + }} + > +
+ + } + > + 复制 + + ) : null} + {items?.length ? ( +
+ {/* + + */} + {items?.map((item) => { + return ( + +
+ + } + > + {item.label} + + ); + })} + {/* {(window as any).vscode ? ( + + e.preventDefault()}> + fff +
+ + + ) : null} */} +
+ ) : null} +
+
+ {tabContent} +
+ ); +}; +MonacoEditor.defaultProps = new MonacoEditorProps(); +export default MonacoEditor; diff --git a/media/src/components/main.tsx b/media/src/components/main.tsx index 7ec2613..db5788d 100644 --- a/media/src/components/main.tsx +++ b/media/src/components/main.tsx @@ -5,7 +5,7 @@ import { getVSCode } from "../utils/utils"; import { parseAPIMetaDescription } from "../utils/parseAPIMetaDescription"; import { ApiErrorCode } from "./APIPage/APIDocument/ApiErrorCode"; import { PontUIService } from "../service/UIService"; -import "../pages/document/index.module.scss"; +import "../pages/document/index.scss"; import { routerMeta } from "../mocks/routerMeta"; if (typeof window !== "undefined") { @@ -23,7 +23,7 @@ const getRouterMeta = (): any => { value = JSON.parse(decodeURI(routerMetaStr)); } catch (e) {} - getVSCode().setState(value); + getVSCode()?.setState(value); return value; }; diff --git a/media/src/components/utils.ts b/media/src/components/utils.ts index 970cb90..2ad5fba 100644 --- a/media/src/components/utils.ts +++ b/media/src/components/utils.ts @@ -2,6 +2,8 @@ import _ from "lodash"; import { APIResponse } from "../types/WorkbenchAPI"; import { Parser } from 'xml2js'; import { EditorLanguages } from "./define"; +import { PontUIService } from "../service/UIService"; +import { getVSCode } from "../utils/utils"; export const getRefSchema = (schemas: any) => ($ref: string) => { const schemaName = $ref.split("/").pop(); @@ -126,3 +128,54 @@ export const getLangByLanguageEditor = (language: EditorLanguages) => { } } }; + +export const getEditorMenuItems = (code, language):Array<{ key: string; label: string; codicon?: string; onClick: () => void }> => { + return [ + { + key: "openInCode", + label: "在 IDE 中打开", + codicon: "file-code", + onClick: () => { + PontUIService.openInCode({ + code: code, + language: language, + }); + }, + }, + { + key: "saveToFile", + label: "另存为...", + codicon: "save-as", + onClick: () => { + PontUIService.saveToFile(code || ""); + }, + }, + ]; +} + +export const getEditorLanguage = (lang) => { + switch (lang) { + case "java-async": + return "java"; + case "Java": + return "java"; + case "TypeScript": + return "typescript"; + case "Go": + return "go"; + case "PHP": + return "php"; + case "Python": + return "python"; + case "Python2": + return "python"; + case "CSharp": + return "csharp"; + case "cpp": + return "cpp"; + case "swift": + return "swift"; + default: + return "javascript"; + } +}; diff --git a/media/src/main.css b/media/src/main.css index 818a6a4..d16d52f 100644 --- a/media/src/main.css +++ b/media/src/main.css @@ -44,8 +44,12 @@ html body { width: 75%; border-left: 1px #ccc solid; padding-left:16px; + .right-ops{ + padding: 0 16px; + font-weight: 500; + } .content{ - padding: 0px 20px 20px 0; + padding: 16px 20px 20px 0; height: calc(100vh - 270px); overflow: auto; } diff --git a/media/src/pages/document/index.module.scss b/media/src/pages/document/index.module.scss index 8197e0b..8d08317 100644 --- a/media/src/pages/document/index.module.scss +++ b/media/src/pages/document/index.module.scss @@ -12,6 +12,7 @@ @import "../../components/APIPage/APIDebugger/APIDebugger.module.scss"; @import "../../components/APIPage/TrySDK/LanguageSwitcher.module.scss"; @import "../../components/APIPage/TrySDK/TrySDK.module.scss"; +@import "../../components/common/MonacoEditor.scss"; $primary-color: #448ef7; $nav-height: 36px; diff --git a/media/src/pages/document/index.scss b/media/src/pages/document/index.scss index 8197e0b..8d08317 100644 --- a/media/src/pages/document/index.scss +++ b/media/src/pages/document/index.scss @@ -12,6 +12,7 @@ @import "../../components/APIPage/APIDebugger/APIDebugger.module.scss"; @import "../../components/APIPage/TrySDK/LanguageSwitcher.module.scss"; @import "../../components/APIPage/TrySDK/TrySDK.module.scss"; +@import "../../components/common/MonacoEditor.scss"; $primary-color: #448ef7; $nav-height: 36px; diff --git a/media/src/registerService.tsx b/media/src/registerService.tsx index 90186fc..0b2e800 100644 --- a/media/src/registerService.tsx +++ b/media/src/registerService.tsx @@ -5,7 +5,7 @@ import { PontUIService } from "./service/UIService"; if (import.meta.env.PROD) { const requestPostMessage = (message: { type: string; value?: any; requestId:string }): Promise => { // const requestId = _.uniqueId(); - getVSCode().postMessage({ ...message }); + getVSCode()?.postMessage({ ...message }); return new Promise((resove, reject) => { window.addEventListener("message", (event) => { diff --git a/media/src/service/UIService.ts b/media/src/service/UIService.ts index 721203c..62862e3 100644 --- a/media/src/service/UIService.ts +++ b/media/src/service/UIService.ts @@ -63,4 +63,8 @@ export const PontUIService = { getLocalLanguage : async () => "", /** update local language */ updateLocalLanguage : async (language:string) => "", + /** open in ide */ + openInCode: async (codeInfo:{code:string,language:string}): Promise => {}, + /** save to file */ + saveToFile: async (code:string): Promise => {}, }; \ No newline at end of file diff --git a/media/src/utils/utils.ts b/media/src/utils/utils.ts index ba377c0..9cc67e9 100644 --- a/media/src/utils/utils.ts +++ b/media/src/utils/utils.ts @@ -9,8 +9,9 @@ export const getVSCode = () => { if (((window as any).vscode)) { return (window as any).vscode; } + // return null - const vscode: WebviewApi = acquireVsCodeApi?.(); + const vscode: WebviewApi = acquireVsCodeApi ? acquireVsCodeApi?.() : null; (window as any).vscode = vscode; return vscode; diff --git a/src/Service.ts b/src/Service.ts index 1ed97de..3c5aab5 100644 --- a/src/Service.ts +++ b/src/Service.ts @@ -149,6 +149,29 @@ export class AlicloudAPIService { return res?.data?.endpoints || []; } + async openInCode(codeInfo:{code:string,language:string}){ + const {language, code} = codeInfo + // 创建新的文件 + vscode.workspace.openTextDocument({ + content: code, + language: language?.toLocaleLowerCase(), + }).then(newDocument => { + vscode.window.showTextDocument(newDocument,{ + viewColumn: vscode.ViewColumn.Beside, + }); + }); + return {} + } + + async saveToFile(code:string){ + const uri = await vscode.window.showSaveDialog(); + if (uri) { + const buf = Buffer.from(code, 'utf8'); + await vscode.workspace.fs.writeFile(uri, buf); + } + return {} + } + async loadProfiles() { const configFilePath = path.join(os.homedir(), ".aliyun/config.json"); @@ -233,7 +256,7 @@ export class AlicloudAPIService { apiVersion: version, params: newParamsValue || {}, productName: product, - meta: this.pontManager.localPontSpecs[0], + meta: apiMeta, bodyStyle: undefined, credential: {tyep: defaultCredentialType}, }); diff --git a/src/openApiService/request/request.ts b/src/openApiService/request/request.ts index f07d55c..fbdc573 100644 --- a/src/openApiService/request/request.ts +++ b/src/openApiService/request/request.ts @@ -98,11 +98,11 @@ export const request = async function (options: OpenAPIOptions) { bodyStyle, credential } = options; - let method = meta?.apis[action]?.method?.toUpperCase(); + let method = meta?.method?.toUpperCase(); let protocol = 'https'; endpoint = endpoint ? endpoint.replace('http://', '').replace('https://', '') : `${productName.toLowerCase()}.cn-hangzhou.aliyuncs.com`; let pathname = '/'; - const schema = meta.apis[action].responses['200'] && meta.apis[action].responses['200'].schema; + const schema = meta?.responses['200'] && meta?.responses['200'].schema; const requestType = bodyStyle === 'json' ? 'json' : 'formData'; let responseType; if (!schema) { @@ -128,7 +128,7 @@ export const request = async function (options: OpenAPIOptions) { // }); // paramObject.params = newParams; const parameters = {}; - meta.apis[action]?.parameters?.map(param=>{ + meta?.parameters?.map(param=>{ parameters[param.name] = param; }) // paramObject.params = params; From bd873cd395547c2103369c4abfecd2db68bfa0e3 Mon Sep 17 00:00:00 2001 From: yini-chen Date: Tue, 9 Jan 2024 14:17:16 +0800 Subject: [PATCH 10/32] fix: loose state when hidding webview tab --- src/webview.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/webview.ts b/src/webview.ts index f3b373b..f4bb43a 100644 --- a/src/webview.ts +++ b/src/webview.ts @@ -63,6 +63,7 @@ export class AlicloudAPIWebview { { // Enable javascript in the webview enableScripts: true, + retainContextWhenHidden: true }, ); webview = AlicloudAPIWebview.webviewPanels[panelKey]; @@ -112,6 +113,7 @@ export class AlicloudAPIWebview { { // Enable javascript in the webview enableScripts: true, + retainContextWhenHidden: true }, ); const filewatcher = vscode.workspace.createFileSystemWatcher(filePath, true, false, true); From d576f4293cf437b7d9032c0d92a731ec9eb54640 Mon Sep 17 00:00:00 2001 From: yini-chen Date: Tue, 9 Jan 2024 14:18:40 +0800 Subject: [PATCH 11/32] feat: api params struct --- media/src/components/APIPage/API.tsx | 3 + .../components/APIPage/APIDebugger/utils.ts | 1 - .../APIDebugger/widgets/xconsole/Object.tsx | 1 - .../APIDebugger/widgets/xconsole/enum.tsx | 2 +- .../src/components/APIPage/TryAPI/TryAPI.tsx | 2 +- .../components/SemixFormRender/context.tsx | 4 +- .../src/components/SemixFormRender/utils.tsx | 2 +- media/src/mocks/definitions.ts | 3852 +++++++++++++++++ media/src/mocks/routerMeta.ts | 3060 +------------ media/src/utils/utils.ts | 2 +- 10 files changed, 3976 insertions(+), 2953 deletions(-) create mode 100644 media/src/mocks/definitions.ts diff --git a/media/src/components/APIPage/API.tsx b/media/src/components/APIPage/API.tsx index 389d9a6..074d666 100644 --- a/media/src/components/APIPage/API.tsx +++ b/media/src/components/APIPage/API.tsx @@ -82,6 +82,9 @@ export const API: React.FC = (props) => { const apiNameEle = selectedApi?.name ?
{selectedApi?.name}
: null; const paramsSchema = selectedApi?.parameters?.reduce( (result, param) => { + if(param.schema?.$ref){ + param.schema = getSchema(param.schema?.$ref) + } return { ...result, properties: { diff --git a/media/src/components/APIPage/APIDebugger/utils.ts b/media/src/components/APIPage/APIDebugger/utils.ts index 47d37b4..3faf23a 100644 --- a/media/src/components/APIPage/APIDebugger/utils.ts +++ b/media/src/components/APIPage/APIDebugger/utils.ts @@ -1,6 +1,5 @@ export const getCustomWidget = (schema): string => { if (schema?.type === 'array') { - console.log("array") // if (schema.items?.type === 'string' && schema?.widget === 'stringList') { // return 'stringList'; // } diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/Object.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/Object.tsx index b156a67..91efb38 100644 --- a/media/src/components/APIPage/APIDebugger/widgets/xconsole/Object.tsx +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/Object.tsx @@ -19,7 +19,6 @@ export class RenderObjectProps { } export const RenderObject: React.FC = (props) => { - console.log(props.dataPath) if (props.value && typeof props.value === 'string') { let value = {}; try { diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/enum.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/enum.tsx index 94d1be5..77973d5 100644 --- a/media/src/components/APIPage/APIDebugger/widgets/xconsole/enum.tsx +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/enum.tsx @@ -74,7 +74,7 @@ export const EnumSelect: React.FC = (props) => { changeValue={changeCurValue} param={props.schema as any}> */} props.onLanguageChange(tab as EditorLanguages)} children={ - props.languages.map((language) => { - return ( - - {props.tabContent} - - ); - }) - }> - + options={options} + >
); }; diff --git a/media/src/components/APIPage/TrySDK/TrySDK.tsx b/media/src/components/APIPage/TrySDK/TrySDK.tsx index 20ae1f3..d65d9a0 100644 --- a/media/src/components/APIPage/TrySDK/TrySDK.tsx +++ b/media/src/components/APIPage/TrySDK/TrySDK.tsx @@ -100,6 +100,13 @@ export const TrySDK: React.FC = (props) => { ) : null; }, [languageTab, sdkDemos]); + const getCode = React.useCallback(()=>{ + if(!sdkDemos[languageTab?.toLocaleLowerCase()]){ + return "// API 暂未支持该语言的 SDK" + } + return sdkDemos[languageTab?.toLocaleLowerCase()] + },[sdkDemos, languageTab]) + return React.useMemo(() => { return (
@@ -120,7 +127,7 @@ export const TrySDK: React.FC = (props) => { // window.open(apiMeta?.externalDocs?.url, "_blank"); }, }]} - value={sdkDemos[languageTab?.toLocaleLowerCase()]} + value={getCode()} languages={DARA_SDK_LANGUAGES?.map((lang) => { if (Object.keys(sdkDemos || {})?.includes(lang?.value?.toLocaleLowerCase())) { return lang; From 135d7f9819409c2cfbf416340e37f87673b8758b Mon Sep 17 00:00:00 2001 From: yini-chen Date: Thu, 11 Jan 2024 10:31:28 +0800 Subject: [PATCH 23/32] fix:code --- media/src/components/APIPage/API.module.css | 2 +- media/src/langs/zh_CN/main.ts | 2 +- src/openApiService/request/request.ts | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/media/src/components/APIPage/API.module.css b/media/src/components/APIPage/API.module.css index 55b2086..636c361 100644 --- a/media/src/components/APIPage/API.module.css +++ b/media/src/components/APIPage/API.module.css @@ -21,7 +21,7 @@ /* border: 1px #ccc solid; border-top: 0px; */ padding: 20px; - height: calc(100vh - 330px); + height: calc(100vh - 370px); overflow: auto; } .footer-content{ diff --git a/media/src/langs/zh_CN/main.ts b/media/src/langs/zh_CN/main.ts index d11ca4e..28c0099 100644 --- a/media/src/langs/zh_CN/main.ts +++ b/media/src/langs/zh_CN/main.ts @@ -120,7 +120,7 @@ export default { contributionDemo: '贡献示例', seeResults: '查看结果', AKTip: - '已通过您的配置信息获取当前账号临时Access Keys、发起调用可能对当前账号发起线上资源操作,请小心操作', + '已通过您的配置信息获取 Access Keys、发起调用可能对当前账号发起线上资源操作,请小心操作', APICallResult: 'API 调用结果', overview: '概览', success: '调用成功', diff --git a/src/openApiService/request/request.ts b/src/openApiService/request/request.ts index fbdc573..fd17870 100644 --- a/src/openApiService/request/request.ts +++ b/src/openApiService/request/request.ts @@ -224,7 +224,6 @@ export const request = async function (options: OpenAPIOptions) { readTimeout: 50000, connectTimeout: 50000 }); - request.query = {}; const data = { version: apiVersion, method, From 2578fd1abfee9ecb525680e1bbab3373e70451f1 Mon Sep 17 00:00:00 2001 From: yini-chen Date: Thu, 11 Jan 2024 13:59:44 +0800 Subject: [PATCH 24/32] fix:code --- media/src/components/APIPage/API.tsx | 19 +++++++++---------- .../src/components/APIPage/TryAPI/TryAPI.tsx | 1 - media/src/components/main.tsx | 3 ++- src/Service.ts | 8 ++++---- src/openApiService/request/request.ts | 19 ++++++++++++++----- 5 files changed, 29 insertions(+), 21 deletions(-) diff --git a/media/src/components/APIPage/API.tsx b/media/src/components/APIPage/API.tsx index 7da45e9..48eb700 100644 --- a/media/src/components/APIPage/API.tsx +++ b/media/src/components/APIPage/API.tsx @@ -82,14 +82,16 @@ export const API: React.FC = (props) => { }, [definitions, getSchema]); const mapSchema = (schema) => { + return SemixJsonSchema.mapSchema(schema as any, (schema)=>{ + if(schema?.properties){ + Object.keys(schema.properties)?.map((item=>{ + schema.properties[item] = mapSchema(schema.properties[item]) + })) + } if(schema?.$ref){ schema = getSchema(schema?.$ref) - if(schema?.properties){ - Object.keys(schema.properties)?.map((item=>{ - schema.properties[item] = mapSchema(schema.properties[item]) - })) - } + schema = mapSchema(schema) return schema; } return schema @@ -98,13 +100,10 @@ export const API: React.FC = (props) => { const pathEle = selectedApi?.path ?
{selectedApi.path}
: null; const apiNameEle = selectedApi?.name ?
{selectedApi?.name}
: null; - let paramsSchema = _.cloneDeep(selectedApi?.parameters); + let paramsSchema = _.cloneDeep(selectedApi?.parameters) const newParamsSchema = paramsSchema?.reduce( (result, param) => { - if(param.schema?.$ref){ - param.schema = getSchema(param.schema?.$ref) - param.schema = mapSchema(param.schema) - } + param.schema = mapSchema(param.schema); return { ...result, properties: { diff --git a/media/src/components/APIPage/TryAPI/TryAPI.tsx b/media/src/components/APIPage/TryAPI/TryAPI.tsx index e23d2dc..002ca8f 100644 --- a/media/src/components/APIPage/TryAPI/TryAPI.tsx +++ b/media/src/components/APIPage/TryAPI/TryAPI.tsx @@ -205,7 +205,6 @@ export const TryAPI: React.FC = (props) => { } - style={{ marginTop: "16px", marginBottom: "-8px" }} > ) : null}
diff --git a/media/src/components/main.tsx b/media/src/components/main.tsx index 6d437a7..f4fbc20 100644 --- a/media/src/components/main.tsx +++ b/media/src/components/main.tsx @@ -5,7 +5,8 @@ import { getVSCode } from "../utils/utils"; import { parseAPIMetaDescription } from "../utils/parseAPIMetaDescription"; import { ApiErrorCode } from "./APIPage/APIDocument/ApiErrorCode"; import { PontUIService } from "../service/UIService"; -import "../pages/document/index.scss"; +import "../pages/document/index.module.scss"; +import { definitions } from "../mocks/definitions"; // import { definitions } from "../mocks/definitions"; // import { routerMeta } from "../mocks/routerMeta"; // import { definitions } from "../mocks/definitions"; diff --git a/src/Service.ts b/src/Service.ts index 983b18c..87bfa22 100644 --- a/src/Service.ts +++ b/src/Service.ts @@ -214,7 +214,7 @@ export class AlicloudAPIService { "params": newParamsValue || {}, "regionId": regionId, "endpoint": endpoint, - "credential": {tyep: defaultCredentialType}, + "credential": {type: defaultCredentialType}, "runtimeOptions": {}, "useCommon": false } @@ -230,7 +230,7 @@ export class AlicloudAPIService { async openAPIRequest(requestData) { const { apiMeta, paramsValue, product, version, endpoint } = requestData; - const newParamsValue = getFormatValues(paramsValue, apiMeta?.parameters); + // const newParamsValue = getFormatValues(paramsValue, apiMeta?.parameters); let response = {} as any; let data; const profilesInfo = await this.loadProfiles(); @@ -255,11 +255,11 @@ export class AlicloudAPIService { endpoint: endpoint, action: apiMeta?.name, apiVersion: version, - params: newParamsValue || {}, + params: paramsValue || {}, productName: product, meta: apiMeta, bodyStyle: undefined, - credential: {tyep: defaultCredentialType}, + credential: {type: defaultCredentialType}, }); response = data; // 设置状态码 diff --git a/src/openApiService/request/request.ts b/src/openApiService/request/request.ts index fd17870..18f77cf 100644 --- a/src/openApiService/request/request.ts +++ b/src/openApiService/request/request.ts @@ -103,15 +103,23 @@ export const request = async function (options: OpenAPIOptions) { endpoint = endpoint ? endpoint.replace('http://', '').replace('https://', '') : `${productName.toLowerCase()}.cn-hangzhou.aliyuncs.com`; let pathname = '/'; const schema = meta?.responses['200'] && meta?.responses['200'].schema; - const requestType = bodyStyle === 'json' ? 'json' : 'formData'; + let requestType; + if(meta?.consumes){ + requestType = _bodyType(meta?.consumes) + }else{ + requestType = bodyStyle === 'json' ? 'json' : 'formData'; + } let responseType; + if (!schema) { responseType = _bodyType(meta.apis[action] && meta.apis[action].produces); } else if (schema.xml) { responseType = 'xml'; } else if (schema.type && schema.type !== 'object') { responseType = schema.format || schema.type; - } else { + } else if (meta?.ext?.produces){ + responseType = _bodyType(meta.ext.produces); + }else { responseType = 'json'; } @@ -177,7 +185,8 @@ export const request = async function (options: OpenAPIOptions) { } request.query[name] = value; break; - case 'Body': + case 'body': + case 'formData': if (!request.body) { request.body = {}; } @@ -193,7 +202,7 @@ export const request = async function (options: OpenAPIOptions) { // request.stream = await ossUtil.getStream(`tmpFile/${params[name]}`); } break; - case 'Header': + case 'header': request.headers[name] = value; break; } @@ -231,7 +240,7 @@ export const request = async function (options: OpenAPIOptions) { action, reqBodyType: requestType, bodyType: responseType, - authType:'AK', + authType: credential && credential.type === 'anonymous' ? 'Anonymous' : 'AK', }; return await client.doRequest(data, request, {}); }; \ No newline at end of file From 56a41ffdde6439fb25699960641ae5915d5ad2ff Mon Sep 17 00:00:00 2001 From: yini-chen Date: Thu, 11 Jan 2024 14:02:14 +0800 Subject: [PATCH 25/32] fix:code --- media/src/components/main.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/media/src/components/main.tsx b/media/src/components/main.tsx index f4fbc20..6d437a7 100644 --- a/media/src/components/main.tsx +++ b/media/src/components/main.tsx @@ -5,8 +5,7 @@ import { getVSCode } from "../utils/utils"; import { parseAPIMetaDescription } from "../utils/parseAPIMetaDescription"; import { ApiErrorCode } from "./APIPage/APIDocument/ApiErrorCode"; import { PontUIService } from "../service/UIService"; -import "../pages/document/index.module.scss"; -import { definitions } from "../mocks/definitions"; +import "../pages/document/index.scss"; // import { definitions } from "../mocks/definitions"; // import { routerMeta } from "../mocks/routerMeta"; // import { definitions } from "../mocks/definitions"; From fb00dafd404b2cdb16c74d723cab9e30cae02c3b Mon Sep 17 00:00:00 2001 From: yini-chen Date: Thu, 11 Jan 2024 14:45:56 +0800 Subject: [PATCH 26/32] fix:code --- src/openApiService/request/request.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openApiService/request/request.ts b/src/openApiService/request/request.ts index 18f77cf..5b98adf 100644 --- a/src/openApiService/request/request.ts +++ b/src/openApiService/request/request.ts @@ -240,7 +240,7 @@ export const request = async function (options: OpenAPIOptions) { action, reqBodyType: requestType, bodyType: responseType, - authType: credential && credential.type === 'anonymous' ? 'Anonymous' : 'AK', + authType: 'AK', }; return await client.doRequest(data, request, {}); }; \ No newline at end of file From d9fe7c92dd77d861d783c73110d969faa440aeb3 Mon Sep 17 00:00:00 2001 From: yini-chen Date: Thu, 11 Jan 2024 15:29:30 +0800 Subject: [PATCH 27/32] fix:code --- .../src/components/APIPage/APIDebugger/widgets/xconsole/map.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/map.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/map.tsx index e8796b1..17fe875 100644 --- a/media/src/components/APIPage/APIDebugger/widgets/xconsole/map.tsx +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/map.tsx @@ -46,7 +46,7 @@ export const SimpleMap: React.FC = (props) => { props.copyItem(index); }} > -
+ copy )} Date: Thu, 11 Jan 2024 15:33:13 +0800 Subject: [PATCH 28/32] fix: update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 72b37bc..9d3879c 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Alibaba Cloud API Toolkit 是一个轻量化的阿里云 API 工具,支持在 * **API 文档预览:** 点击 API 可以打开一个新的标签页,并显示对应的 API 文档,包括描述、请求参数、响应参数和错误码。在文档中点击调试按钮,可以链接到阿里云 OpenAPI 门户进行在线 API 试用。 -* **API 调试:** 你可以在插件中使用图形表单的方式调试阿里云 API,并查看结果。 +* **API 调试:** 你可以在插件中使用表单的方式调试阿里云 API,并查看结果。 * **SDK 代码示例:** 你可以在插件中获得 SDK 代码示例,并在编辑器中快速打开对应的 SDK 代码。 @@ -36,7 +36,7 @@ Alibaba Cloud API Toolkit 是一个轻量化的阿里云 API 工具,支持在 ![API Serching](https://img.alicdn.com/imgextra/i1/O1CN01KaWkBF1UfCUkY0N3v_!!6000000002544-0-tps-1286-518.jpg) * API 调试 -调试功能需要配置你的 AK/SK信息,请先安装 [Alibaba Cloud CLI Tools](vscode:extension/alibabacloud-openapi.aliyuncli) 插件,然后在插件中配置你的 AK/SK 信息。更多信息请参考 [Alibaba Cloud CLI Documentation](https://github.com/aliyun/aliyun-cli?tab=readme-ov-file#configure)。 +调试功能需要配置你的 AK/SK 信息,请先安装 [Alibaba Cloud CLI Tools](vscode:extension/alibabacloud-openapi.aliyuncli) 插件,然后在插件中配置你的 AK/SK 信息。更多信息请参考 [Alibaba Cloud CLI Documentation](https://github.com/aliyun/aliyun-cli?tab=readme-ov-file#configure)。 ![API debug](https://img.alicdn.com/imgextra/i4/O1CN01F1qI7S1BunIFJPiAt_!!6000000000006-0-tps-2618-2050.jpg) From 7305fce8adde3be146bcebec084565f0e4100758 Mon Sep 17 00:00:00 2001 From: yini-chen Date: Thu, 11 Jan 2024 15:34:57 +0800 Subject: [PATCH 29/32] fix: code --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4c13d18..5c87eff 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "Alibaba Cloud API Toolkit", "description": "The Alibaba Cloud API Toolkit for VSCode makes it easier to access Alibaba Cloud services.", "author": "Alibaba Cloud SDK Team", - "version": "0.0.1", + "version": "0.0.3", "private": true, "publisher": "alibabacloud-openapi", "license": "Apache-2.0", From 6f45931f1ab465577d3ffdd98b92a7dd430f79c6 Mon Sep 17 00:00:00 2001 From: yini-chen Date: Thu, 11 Jan 2024 20:52:03 +0800 Subject: [PATCH 30/32] fix:helper --- README.en-US.md | 8 +++++++- README.md | 8 +++++++- media/src/components/APIPage/TryAPI/TryAPI.tsx | 18 +++++++++++++++++- media/src/langs/zh_CN/main.ts | 2 +- src/Service.ts | 7 +++---- 5 files changed, 35 insertions(+), 8 deletions(-) diff --git a/README.en-US.md b/README.en-US.md index dcc643a..854068e 100644 --- a/README.en-US.md +++ b/README.en-US.md @@ -35,7 +35,13 @@ Click to debug, you can link to Aliyun OpenAPI portal for online API trial. ![API Serching](https://img.alicdn.com/imgextra/i1/O1CN01KaWkBF1UfCUkY0N3v_!!6000000002544-0-tps-1286-518.jpg) * Call the API -To call the API, you need to configure your AK/SK information. Please install the [Alibaba Cloud CLI Tools](vscode:extension/alibabacloud-openapi.aliyuncli) first, and then configure your AK/SK information. For more information, please refer to [Alibaba Cloud CLI Documentation](https://github.com/aliyun/aliyun-cli?tab=readme-ov-file#configure). + +The feature requires you to configure your AK/SK information as follows: +1. Install [Alibaba Cloud CLI Tools](https://marketplace.visualstudio.com/items?itemName=alibabacloud-openapi.aliyuncli) extentions. +2. Open the command line to install aliyun-cli `brew install aliyun-cli`, +3. Enter `aliyun configure` and follow the prompts to configure it, +4. Click the Alibaba Cloud icon in VS Code status bar to manage your profiles, +5. More information please refer to the [Alibaba Cloud CLI Documentation] (https://github.com/aliyun/aliyun-cli?tab=readme-ov-file#configure). ![API debug](https://img.alicdn.com/imgextra/i4/O1CN01F1qI7S1BunIFJPiAt_!!6000000000006-0-tps-2618-2050.jpg) diff --git a/README.md b/README.md index 9d3879c..710f159 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,13 @@ Alibaba Cloud API Toolkit 是一个轻量化的阿里云 API 工具,支持在 ![API Serching](https://img.alicdn.com/imgextra/i1/O1CN01KaWkBF1UfCUkY0N3v_!!6000000002544-0-tps-1286-518.jpg) * API 调试 -调试功能需要配置你的 AK/SK 信息,请先安装 [Alibaba Cloud CLI Tools](vscode:extension/alibabacloud-openapi.aliyuncli) 插件,然后在插件中配置你的 AK/SK 信息。更多信息请参考 [Alibaba Cloud CLI Documentation](https://github.com/aliyun/aliyun-cli?tab=readme-ov-file#configure)。 + +调试功能需要配置你的 AK/SK 信息,配置方法如下: +1. 安装 [Alibaba Cloud CLI Tools](https://marketplace.visualstudio.com/items?itemName=alibabacloud-openapi.aliyuncli) 插件, +2. 打开命令行安装 aliyun-cli `brew install aliyun-cli`, +3. 输入 `aliyun configure` 命令,按照提示进行配置, +4. 点击 VS Code 状态栏中的阿里云图标,管理你的 profiles, +5. 更多信息请参考 [Alibaba Cloud CLI Documentation](https://github.com/aliyun/aliyun-cli?tab=readme-ov-file#configure)。 ![API debug](https://img.alicdn.com/imgextra/i4/O1CN01F1qI7S1BunIFJPiAt_!!6000000000006-0-tps-2618-2050.jpg) diff --git a/media/src/components/APIPage/TryAPI/TryAPI.tsx b/media/src/components/APIPage/TryAPI/TryAPI.tsx index 002ca8f..bf56220 100644 --- a/media/src/components/APIPage/TryAPI/TryAPI.tsx +++ b/media/src/components/APIPage/TryAPI/TryAPI.tsx @@ -135,7 +135,11 @@ export const TryAPI: React.FC = (props) => { ...getEditorMenuItems(getTabValue(tab), "json"), { key: "gotoweb", - label: 去门户网页版调试, + label: ( + + 去门户网页版调试 + + ), codicon: "link-external", onClick: () => { // window.open(apiMeta?.externalDocs?.url, "_blank"); @@ -145,6 +149,18 @@ export const TryAPI: React.FC = (props) => { return (
+ + 请利用 aliyun-cli 配置您的 AK/SK 信息:1. 安装 aliyun-cli: brew install aliyun-cli; 2. 命令行输入 aliyun + configure。 + 点击查看更多信息。 +
+ } + type="warning" + showIcon + closable + /> {apiResult?.result || isApiResultLoading ? (
diff --git a/media/src/langs/zh_CN/main.ts b/media/src/langs/zh_CN/main.ts index 28c0099..e1b514a 100644 --- a/media/src/langs/zh_CN/main.ts +++ b/media/src/langs/zh_CN/main.ts @@ -120,7 +120,7 @@ export default { contributionDemo: '贡献示例', seeResults: '查看结果', AKTip: - '已通过您的配置信息获取 Access Keys、发起调用可能对当前账号发起线上资源操作,请小心操作', + '将通过您的配置信息获取 Access Keys、发起调用可能对当前账号发起线上资源操作,请小心操作', APICallResult: 'API 调用结果', overview: '概览', success: '调用成功', diff --git a/src/Service.ts b/src/Service.ts index 87bfa22..9b8b506 100644 --- a/src/Service.ts +++ b/src/Service.ts @@ -304,11 +304,10 @@ export class AlicloudAPIService { response }; }else{ - let result = await vscode.window.showErrorMessage("请先安装阿里云 CLI 插件,并完成AK/SK配置后,再发起调用", "install","cancel"); - if (result === "install") { - vscode.env.openExternal(vscode.Uri.parse('vscode:extension/alibabacloud-openapi.aliyuncli')); + let result = await vscode.window.showErrorMessage("请完成AK/SK配置后,再发起调用", "查看配置方法","取消"); + if (result === "查看配置方法") { + vscode.env.openExternal(vscode.Uri.parse('https://github.com/aliyun/aliyun-cli?tab=readme-ov-file#configure')); } - } } From cb432ce510caef422a2651a87a2dc489b8ed1a73 Mon Sep 17 00:00:00 2001 From: yini-chen Date: Thu, 11 Jan 2024 20:56:35 +0800 Subject: [PATCH 31/32] fix:helper --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5c87eff..0339229 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "Alibaba Cloud API Toolkit", "description": "The Alibaba Cloud API Toolkit for VSCode makes it easier to access Alibaba Cloud services.", "author": "Alibaba Cloud SDK Team", - "version": "0.0.3", + "version": "0.0.4", "private": true, "publisher": "alibabacloud-openapi", "license": "Apache-2.0", From 4d93308777af76037b9c9947c3a632d50b0f5fc0 Mon Sep 17 00:00:00 2001 From: yini-chen Date: Fri, 19 Jan 2024 11:10:47 +0800 Subject: [PATCH 32/32] fix style --- media/src/components/APIPage/API.module.css | 3 +- .../APIDebugger/widgets/xconsole/map.tsx | 5 +- .../APIDebugger/widgets/xconsole/string.tsx | 35 +++++------ .../APIPage/TryAPI/TryAPI.module.css | 3 +- .../src/components/APIPage/TryAPI/TryAPI.tsx | 59 ------------------- .../APIPage/TrySDK/TrySDK.module.scss | 3 - .../src/components/APIPage/TrySDK/TrySDK.tsx | 1 + media/src/components/common/MonacoEditor.scss | 8 ++- media/src/main.css | 4 +- media/src/utils/utils.ts | 1 - 10 files changed, 31 insertions(+), 91 deletions(-) diff --git a/media/src/components/APIPage/API.module.css b/media/src/components/APIPage/API.module.css index 636c361..265eed5 100644 --- a/media/src/components/APIPage/API.module.css +++ b/media/src/components/APIPage/API.module.css @@ -46,13 +46,12 @@ .right-panel { min-width: 540px; border-left: 1px #ccc solid; - padding-left:16px; .right-ops{ padding: 0 16px; font-weight: 500; } .content{ - padding: 16px 20px 20px 0; + padding: 0 0 16px 20px; height: calc(100vh - 270px); overflow: auto; .api-params-doc{ diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/map.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/map.tsx index 17fe875..e80cd58 100644 --- a/media/src/components/APIPage/APIDebugger/widgets/xconsole/map.tsx +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/map.tsx @@ -26,7 +26,7 @@ export const SimpleMap: React.FC = (props) => { const keyItem = ( <> = (props) => { props.addItem(); }} > -
- add + add
diff --git a/media/src/components/APIPage/APIDebugger/widgets/xconsole/string.tsx b/media/src/components/APIPage/APIDebugger/widgets/xconsole/string.tsx index d4350a5..9142eaa 100644 --- a/media/src/components/APIPage/APIDebugger/widgets/xconsole/string.tsx +++ b/media/src/components/APIPage/APIDebugger/widgets/xconsole/string.tsx @@ -103,9 +103,9 @@ export const String: React.FC = (props) => { switch (inputType) { case 'textarea': formItem = ( -
+
= (props) => { { setCurvalue((curvalue || '').replace(/\n/gi, '')); setIsToTextArea(false); }} > +
{/* */} - transfer
@@ -166,20 +167,20 @@ export const String: React.FC = (props) => { }); } }} - onKeyDown={(e) => { - if (e.key === 'Enter') { - setIsToTextArea(true); - e.preventDefault(); - const { value: newValue, position } = getNewTextareaValueAndPosition('\n'); - setCurvalue(newValue); - Promise.resolve().then(() => { - // @ts-ignore - textareaRef?.current?.resizableTextArea?.textArea?.focus(); - // @ts-ignore - textareaRef?.current?.resizableTextArea?.textArea?.setSelectionRange(position, position); - }); - } - }} + // onKeyDown={(e) => { + // if (e.key === 'Enter') { + // setIsToTextArea(true); + // e.preventDefault(); + // const { value: newValue, position } = getNewTextareaValueAndPosition('\n'); + // setCurvalue(newValue); + // Promise.resolve().then(() => { + // // @ts-ignore + // textareaRef?.current?.resizableTextArea?.textArea?.focus(); + // // @ts-ignore + // textareaRef?.current?.resizableTextArea?.textArea?.setSelectionRange(position, position); + // }); + // } + // }} >
); diff --git a/media/src/components/APIPage/TryAPI/TryAPI.module.css b/media/src/components/APIPage/TryAPI/TryAPI.module.css index 6568a1e..64ec9be 100644 --- a/media/src/components/APIPage/TryAPI/TryAPI.module.css +++ b/media/src/components/APIPage/TryAPI/TryAPI.module.css @@ -121,6 +121,7 @@ .content { padding-top: 1px; + height: 425px; .title { margin-bottom: 8px; font-family: PingFangSC; @@ -176,7 +177,7 @@ } .ant-empty { - height: calc(100% - 37px); + height: 400px; display: flex; flex-direction: column; align-items: center; diff --git a/media/src/components/APIPage/TryAPI/TryAPI.tsx b/media/src/components/APIPage/TryAPI/TryAPI.tsx index bf56220..f90ebfb 100644 --- a/media/src/components/APIPage/TryAPI/TryAPI.tsx +++ b/media/src/components/APIPage/TryAPI/TryAPI.tsx @@ -111,15 +111,6 @@ export const TryAPI: React.FC = (props) => { return res; }; - const copyBtn = (tab) => ( - message.success(I18N.main.explorer.copySuccess)} - > - - - ); const getResponseSchema = (statusCode, responseSchema) => { if (!statusCode || _.isEmpty(responseSchema)) { @@ -264,25 +255,6 @@ export const TryAPI: React.FC = (props) => { > 复制 - {/* {items?.map((item) => { - return ( - -
- - } - > - {item.label} - - ); - })} */} - // ) : ( - // - // )} - // {copyBtn(tab)} - // - // ) : ( - // { - // message.success(I18N.main.explorer.copySucc); - // }} - // > - // - // - // ) - // } /> ) : (
{I18N.main.explorer.specialresponsetip}
- // )}
diff --git a/media/src/components/APIPage/TrySDK/TrySDK.module.scss b/media/src/components/APIPage/TrySDK/TrySDK.module.scss index 9789ff8..e69de29 100644 --- a/media/src/components/APIPage/TrySDK/TrySDK.module.scss +++ b/media/src/components/APIPage/TrySDK/TrySDK.module.scss @@ -1,3 +0,0 @@ -.sdk-demo-content{ - -} \ No newline at end of file diff --git a/media/src/components/APIPage/TrySDK/TrySDK.tsx b/media/src/components/APIPage/TrySDK/TrySDK.tsx index d65d9a0..e08c401 100644 --- a/media/src/components/APIPage/TrySDK/TrySDK.tsx +++ b/media/src/components/APIPage/TrySDK/TrySDK.tsx @@ -112,6 +112,7 @@ export const TrySDK: React.FC = (props) => {
diff --git a/media/src/components/common/MonacoEditor.scss b/media/src/components/common/MonacoEditor.scss index 2829b69..0dc3660 100644 --- a/media/src/components/common/MonacoEditor.scss +++ b/media/src/components/common/MonacoEditor.scss @@ -1,10 +1,8 @@ .editor-content { .operations { display: flex; - margin: 0 0 16px 0px; - // box-shadow: 1px 2px 2px 0px rgba(0, 0, 0, 0.2); + margin: 16px 0 16px 0px; padding: 8px 16px; - // width: 300px; justify-content: space-between; .right-area { display: flex; @@ -32,6 +30,10 @@ .codicon { padding: 8px; cursor: pointer; + color:#6e6e6e; + } + .codicon:hover{ + color: #2f84ae; } } .tab-content { diff --git a/media/src/main.css b/media/src/main.css index 689994b..1efa71a 100644 --- a/media/src/main.css +++ b/media/src/main.css @@ -6,7 +6,7 @@ html body { border-left: 1px solid #d7d7d7; } .vscode-page { - padding: 20px; + padding: 20px 20px 0 20px; width: 100%; .api-page-content { position: relative; @@ -15,7 +15,7 @@ html body { width: 100%; .content{ width: 100%; - padding:0 0 20px + padding:0 0 16px 20px } } } diff --git a/media/src/utils/utils.ts b/media/src/utils/utils.ts index 0980bd3..ba377c0 100644 --- a/media/src/utils/utils.ts +++ b/media/src/utils/utils.ts @@ -9,7 +9,6 @@ export const getVSCode = () => { if (((window as any).vscode)) { return (window as any).vscode; } - // return null const vscode: WebviewApi = acquireVsCodeApi?.(); (window as any).vscode = vscode;