diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..67c3fc113 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ +# 3.0.1-beta 2020-10-16 +## First Release + - ### Supported Services + - DevStar diff --git a/CHANGELOG_CN.md b/CHANGELOG_CN.md new file mode 100644 index 000000000..4ae8478bf --- /dev/null +++ b/CHANGELOG_CN.md @@ -0,0 +1,4 @@ +# 3.0.1-beta 2020-10-16 +## 首次发布 + - ### 已支持服务 + - 模板引擎(DevStar) diff --git a/LICENSE b/LICENSE index 261eeb9e9..808e3296c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,13 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +Copyright (c) Huawei Technologies Co., Ltd. 2020-present. All rights reserved. - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - 1. Definitions. + http://www.apache.org/licenses/LICENSE-2.0 - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md index 069ff89e4..d60abfc98 100644 --- a/README.md +++ b/README.md @@ -1 +1,282 @@ -# huaweicloud-sdk-nodejs-v3 \ No newline at end of file +English | [简体中文](README_CN.md) + +# HuaweiCloud NodeJs Software Development Kit (NodeJs SDK) + +The HuaweiCloud NodeJs SDK allows you to easily work with Huawei Cloud services such as Elastic Compute Service (ECS) and Virtual Private Cloud (VPC) without the need to handle API related tasks. + +This document introduces how to obtain and use HuaweiCloud NodeJs SDK. + +## Getting Started + +- To use HuaweiCloud NodeJs SDK, you must have Huawei Cloud account as well as the Access Key and Secret key of the HuaweiCloud account. + + The accessKey is required when initializing `{Service}Client`. You can create an AccessKey in the Huawei Cloud console. For more information, see [My Credentials](https://support.huaweicloud.com/usermanual-ca/zh-cn_topic_0046606340.html). + +- HuaweiCloud NodeJs SDK requires **Node 10.16.1** or later. + +## Install NodeJs SDK + +You could obtain and install NodeJs SDK through following methods: + +- Installing Dependencies Using NPM (Recommended) + + Using npm to install project dependencies is the recommended method to use NodeJs SDK: + + You must install `@huaweicloud/huaweicloud-sdk-core` library no matter which product/service development kit you need to use. Take using Devstar SDK for example, you need to install `@huaweicloud/huaweicloud-sdk-core` library and `@huaweicloud/huaweicloud-sdk-devstar` library: + ``` xml + npm install @huaweicloud/huaweicloud-sdk-core @huaweicloud/huaweicloud-sdk-devstar + ``` + +## Use NodeJs SDK + +1. Import the required modules as follows + + ``` nodejs + // User identity authentication + import { BasicCredentials } from "@huaweicloud/huaweicloud-sdk-core/auth/BasicCredentials"; + import { GlobalCredentials } from "@huaweicloud/huaweicloud-sdk-core/auth/GlobalCredentials"; + // {Service}Client, replace Devstar to your specified service, take DevstarClient for example + import { DevstarClient } from "@huaweicloud/huaweicloud-sdk-devstar/v1/DevstarClient"; + // Import the corresponding product model + import { ShowJobDetailRequest } from '@huaweicloud/huaweicloud-sdk-devstar/v1/model/ShowJobDetailRequest'; + ``` + +2. Config the `{Service}Client` configurations + + 2.1 Use default configuration + + ``` nodejs + // Use default configuration + const client = DevstarClient.newBuilder() + ``` + + 2.2 Proxy(Optional) + + ``` nodejs + // Use proxy if needed + client.withProxyAgent("http://proxy.huaweicloud.com") + ``` + 2.3 SSL Certification(Optional) + + ``` nodejs + // Skip ssl certification checking while using https protocol if needed + process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0" + ``` + +3. Initialize Credentials + + 3.1 Use permanent AK/SK + + ``` nodejs + // Regional Services + const basicCredentials = new BasicCredentials() + .withAk('ak') + .withSk('sk') + .withProjectId('projectId') + + // Global Services + const globalCredentials = new GlobalCredentials() + .withAk('ak') + .withSk('sk') + .withDomainId('domainId') + ``` + + **where:** + + For project services, you only need to provide projectId, domainId is optional. While using global services, projectId must be null, and domainId should be filled in according to the actual situation. Global services only contains IAM(Identity and Access Management) currently. + + - `ak` is the access key ID for your account. + - `sk` is the secret access key for your account. + - `projectId` is the ID of your project depending on your region which you want to operate. + - `domainId` is the account ID of huawei cloud. + + 3.2 Use temporary AK/SK + + It's preferred to obtain temporary access key, security key and security token first, which could be obtained through permanent access key and security key or through an agency. + + Obtaining a temporary access key token through permanent access key and security key, you could refer to document: https://support.huaweicloud.com/en-us/api-iam/iam_04_0002.html . The API mentioned in the document above corresponds to the method of createTemporaryAccessKeyByToken in IAM SDK. + + Obtaining a temporary access key and security token through an agency, you could refer to document: https://support.huaweicloud.com/en-us/api-iam/iam_04_0101.html . The API mentioned in the document above corresponds to the method of createTemporaryAccessKeyByAgency in IAM SDK. + + ``` nodejs + // Region级服务 + const basicCredentials = new BasicCredentials() + .withAk('ak') + .withSk('sk') + .withSecurityToken('securityToken') + .withProjectId('projectId') + + // Global级服务 + const globalCredentials = new GlobalCredentials() + .withAk('ak') + .withSk('sk') + .withSecurityToken('securityToken') + .withDomainId('domainId') + +4. Initialize the {Service}Client instance + + ``` nodejs + const client = DevstarClient.newBuilder() + .withCredential(basicCredentials) + .withEndpoint(endpoint) + .withProxyAgent(proxy) + .build() + ``` + + **where:** + - `endpoint` is the service specific endpoints, see [Regions and Endpoints](https://developer.huaweicloud.com/intl/en-us/endpoint). + +5. Send a request and print response + + ``` nodejs + const result = client.showJobDetail(new ShowJobDetailRequest("id")); + result.then(result => { + res.send("JSON.stringify(result)::" + JSON.stringify(result)) + }).catch(ex => { + res.send("exception:" + JSON.stringify(ex)) + }); + ``` + +6. Exceptions + + | Level 1 | Notice | Level 2 | Notice | + | :---- | :---- | :---- | :---- | + | ConnectionException | Connection error | HostUnreachableException | Host is not reachable | + | | | SslHandShakeException | SSL certification error | + | RequestTimeoutException | Request timeout | CallTimeoutException | timeout for single request | + | | | RetryOutageException | no response after retrying | + | ServiceResponseException | service response error | ServerResponseException | server inner error, http status code: [500,] | + | | | ClientRequestException | invalid request, http status code: [400? 500) | + + ``` nodejs + if (httpStatusCode >= 400 && httpStatusCode < 500) { + return new ClientRequestException(httpStatusCode, errorData); + } else if (httpStatusCode >= 500 && httpStatusCode < 600) { + return new ServerResponseException(httpStatusCode, errorData); + } else { + return new ServiceResponseException(httpStatusCode, errorData); + } + ``` + +7. Access Log + + The SDK will print the access log by default, every request will be recorded in console like: + + ``` shell + 16:53:04.905 [main] INFO HuaweiCloud-SDK-Access - "GET https://ecs.cn-southwest-2.myhuaweicloud.com/v1/077d6a6c19000fdd2f3bc00150080291/cloudservers/detail?offset=1&limit=25" 200 2251 deabe20c14f997a0291fc451a4da16a4 + 16:53:06.212 [main] INFO HuaweiCloud-SDK-Access - "PUT https://ecs.cn-southwest-2.myhuaweicloud.com/v1/077d6a6c19000fdd2f3bc00150080291/cloudservers/1aeac6fb-a2f2-48dc-9052-36637d119dd3" 200 880 f16f70e3fe245c11ab741760f8689a01 + 17:02:37.734 [main] INFO HuaweiCloud-SDK-Access - "GET https://ecs.cn-southwest-2.myhuaweicloud.com/v1/077d6a6c19000fdd2f3bc00150080291/cloudservers/detail?offset=1&limit=-1" 400 165 8c3c8b6fed4482d28e1929a78dc93f04 + ``` + + SDK access log name is "HuaweiCloud-SDK-Access", and out log format is: + + ``` text + "{httpMethod} {uri}" {httpStatusCode} {responseContentLength} {requestId} + ``` + + **Where:** + - `requestId` is the ID returned by HuaweiCloud API Gateway, which can be used for user guarantee or issue tracking. + + You can shield access log in the log configuration depending on your project, or print access log to an independent file. + +8. Original HTTP Listener + + In some situation, you may need to debug your http requests, original http request and response information will be needed. The SDK provides a listener function to obtain the original encrypted http request and response information. + + > :warning: Warning: The original http log information are used in debugging stage only, please do not print the original http header or body in the production environment. These log information are not encrypted and contain sensitive data such as the password of your ECS virtual machine or the password of your IAM user account, etc. When the response body is binary content, the body will be printed as "***" without detailed information. + + Set the environment variable process.env.NODE_DEBUG or process.env.DEBUG to enable debug log printing. + ``` nodejs + if (process.env.NODE_DEBUG === 'axios' || process.env.DEBUG) { + this.axiosInstance.interceptors.request.use((config: any) => { + logger.debug('Request:'); + try { + logger.debug(JSON.stringify(config, null, 2)); + } catch { + logger.error(config) + } + + return config; + }, (error: any) => { + logger.error('Error: '); + try { + logger.error(JSON.stringify(error, null, 2)); + } catch { + logger.error(error); + } + // @ts-ignore + return Promise.reject(error); + }); + + this.axiosInstance.interceptors.response.use((response: any) => { + logger.debug('Response:'); + try { + logger.debug(JSON.stringify(response, null, 2)); + } catch { + logger.error(response); + } + + return response; + }, (error: any) => { + logger.error('Error: '); + try { + logger.error(JSON.stringify(error, null, 2)); + } catch { + logger.error(error); + } + // @ts-ignore + return Promise.reject(error); + }); + } + ``` + + +## Code Example + +- The following example shows how to query job detail, you need to substitute your real `{Service}Client` for `DevstarClient` in actual use. +- Substitute the values for `{your ak string}`, `{your sk string}`, `{your endpoint string}`, `{your project id}`and `{job id}`. + + ``` nodejs + import express = require('express'); + import { DevstarClient } from "@huaweicloud/huaweicloud-sdk-devstar/v1/DevstarClient"; + import { BasicCredentials } from "@huaweicloud/huaweicloud-sdk-core/auth/BasicCredentials"; + import { ShowJobDetailRequest } from '@huaweicloud/huaweicloud-sdk-devstar/v1/model/ShowJobDetailRequest'; + + // Create a new express application instance + const app: express.Application = express(); + process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; + app.get('/', function (req: any, res: { send: (arg0: string) => void; }) { + const client = DevstarClient.newBuilder() + .withCredential(new BasicCredentials() + .withAk("{your ak string}") + .withSk("{your sk string}") + .withProjectId("{your project id}")) + .withEndpoint("{your endpoint string}") + .withProxyAgent("") + .build(); + + + const result = client.showJobDetail(new ShowJobDetailRequest("{job id}")); + result.then(result => { + res.send("JSON.stringify(result)::" + JSON.stringify(result)) + }).catch(ex => { + res.send("exception:" + JSON.stringify(ex)) + }); + }); + app.listen(3000, function () { + console.log('Example app listening on port 3000!'); + }); + ``` +- Debugging Procedure + + ``` nodejs + Run command: + npm install ts-node-dev + npm install express + npm install typescript + Add scripts: + "dev": "ts-node-dev ./index.ts" + Run command: + npm run dev + Local browser access: http://localhost:3000/ + ``` \ No newline at end of file diff --git a/README_CN.md b/README_CN.md new file mode 100644 index 000000000..8897df2df --- /dev/null +++ b/README_CN.md @@ -0,0 +1,286 @@ +[English](./README.md) | 简体中文 + +# 华为云 NodeJs 软件开发工具包(NodeJs SDK) + +欢迎使用华为云 NodeJs SDK。 + +华为云 NodeJs SDK 让您无需关心请求细节即可快速使用云服务器、虚拟私有云等多个华为云服务。 + +这里将向您介绍如何获取并使用华为云 NodeJs SDK。 + +## 在线示例 + +[API Explorer](https://apiexplorer.developer.huaweicloud.com/apiexplorer/overview) 提供API检索及平台调试,支持全量快速检索、可视化调试、帮助文档查看、在线咨询。 + +## 现在开始 + +- 要使用华为云 NodeJs SDK,您需要拥有华为云账号以及该账号对应的 Access Key(AK)和 Secret Access Key(SK)。请在华为云控制台“我的凭证-访问密钥”页面上创建和查看您的 AKSK。更多信息请查看[访问密钥]( https://support.huaweicloud.com/usermanual-ca/zh-cn_topic_0046606340.html )。 +- 华为云 NodeJs SDK 支持 **Node 10.16.1** 及其以上版本。 + +## SDK 获取和安装 + +您可以通过如下方式获取和安装 SDK: + +- 通过 npm 安装依赖(推荐) + + 通过 npm 安装项目依赖是使用 NodeJs SDK 的推荐方法 + + 无论您要使用哪个产品/服务的开发工具包,都必须安装@huaweicloud/huaweicloud-sdk-core。以使用虚拟私有云Devstar SDK为例,您需要安装@huaweicloud/huaweicloud-sdk-core和@huaweicloud/huaweicloud-sdk-devstar: + ``` xml + npm install @huaweicloud/huaweicloud-sdk-core @huaweicloud/huaweicloud-sdk-devstar + ``` + +### 开始使用 + +1. 导入依赖模块: + + ``` nodejs + // 用户身份认证 + import { BasicCredentials } from "@huaweicloud/huaweicloud-sdk-core/auth/BasicCredentials"; + import { GlobalCredentials } from "@huaweicloud/huaweicloud-sdk-core/auth/GlobalCredentials"; + // 导入指定云服务的 {Service}Client,此处以 DevstarClient 为例 + import { DevstarClient } from "@huaweicloud/huaweicloud-sdk-devstar/v1/DevstarClient"; + // 导入相应产品的 model + import { ShowJobDetailRequest } from '@huaweicloud/huaweicloud-sdk-devstar/v1/model/ShowJobDetailRequest'; + ``` + +2. 配置客户端属性 + + 2.1 默认配置 + + ``` nodejs + // 使用默认配置 + const client = DevstarClient.newBuilder() + ``` + + 2.2 代理配置(可选) + + ``` nodejs + // 使用代理服务器(可选) + client.withProxyAgent("http://proxy.huaweicloud.com") + ``` + + 2.3 SSL配置(可选) + + ``` nodejs + // 配置跳过服务端证书验证(可选) + process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0" + ``` + +3. 初始化认证信息 + + 3.1 使用永久AK/SK + + ``` nodejs + // Region级服务 + const basicCredentials = new BasicCredentials() + .withAk('ak') + .withSk('sk') + .withProjectId('projectId') + + // Global级服务 + const globalCredentials = new GlobalCredentials() + .withAk('ak') + .withSk('sk') + .withDomainId('domainId') + ``` + + **说明**: + + 非全局服务仅需要提供 projectId , domainId 无需提供。全局服务 projectId 必须为 null , domainId 请按照实际情况填写。全局服务当前仅支持 IAM 。 + + - `ak` 华为云账号 Access Key 。 + - `sk` 华为云账号 Secret Access Key 。 + - `projectId` 云服务所在项目 ID ,根据你想操作的项目所属区域选择对应的项目 ID 。 + - `domainId` 华为云账号ID 。 + + 3.2 使用临时AK/SK + + 首选需要获得临时AK、SK和SecurityToken,获取可以从永久AK/SK获得,或者通过委托授权首选获得 + + 通过永久AK/SK获得可以参考文档:https://support.huaweicloud.com/api-iam/iam_04_0002.html, 对应IAM SDK 中的createTemporaryAccessKeyByToken方法。 + + 通过委托授权获得可以参考文档:https://support.huaweicloud.com/api-iam/iam_04_0101.html, 对应IAM SDK 中的createTemporaryAccessKeyByAgency方法。 + + ``` nodejs + // Region级服务 + const basicCredentials = new BasicCredentials() + .withAk('ak') + .withSk('sk') + .withSecurityToken('securityToken') + .withProjectId('projectId') + + // Global级服务 + const globalCredentials = new GlobalCredentials() + .withAk('ak') + .withSk('sk') + .withSecurityToken('securityToken') + .withDomainId('domainId'); + ``` + +4. 初始化客户端 + + ``` nodejs + // 初始化指定云服务的客户端 {Service}Client ,以初始化 DevstarClient 为例 + const client = DevstarClient.newBuilder() + .withCredential(basicCredentials) + .withEndpoint(endpoint) + .withProxyAgent(proxy) + .build() + ``` + + **说明:** + - `endpoint` 华为云各服务应用区域和各服务的终端节点,详情请查看[地区和终端节点](https://developer.huaweicloud.com/endpoint)。 + +5. 发送请求并查看响应 + + ``` nodejs + const result = client.showJobDetail(new ShowJobDetailRequest("id")); + result.then(result => { + res.send("JSON.stringify(result)::" + JSON.stringify(result)) + }).catch(ex => { + res.send("exception:" + JSON.stringify(ex)) + }); + ``` + +6. 异常处理 + + | 一级分类 | 一级分类说明 | 二级分类 | 二级分类说明 | + | :---- | :---- | :---- | :---- | + | ConnectionException | 连接类异常 | HostUnreachableException | 网络不可达、被拒绝 | + | | | SslHandShakeException | SSL认证异常 | + | RequestTimeoutException | 响应超时异常 | CallTimeoutException | 单次请求,服务器处理超时未返回 | + | | | RetryOutageException | 在重试策略消耗完成已后,仍无有效的响应 | + | ServiceResponseException | 服务器响应异常 | ServerResponseException | 服务端内部错误,Http响应码:[500,] | + | | | ClientRequestException | 请求参数不合法,Http响应码:[400, 500) | + + ``` nodejs + // 异常处理 + if (httpStatusCode >= 400 && httpStatusCode < 500) { + return new ClientRequestException(httpStatusCode, errorData); + } else if (httpStatusCode >= 500 && httpStatusCode < 600) { + return new ServerResponseException(httpStatusCode, errorData); + } else { + return new ServiceResponseException(httpStatusCode, errorData); + } + ``` + +7. 访问日志 + SDK默认会打印访问日志,每次请求都会有一条记录: + + ``` shell + 16:53:04.905 [main] INFO HuaweiCloud-SDK-Access - "GET https://ecs.cn-southwest-2.myhuaweicloud.com/v1/077d6a6c19000fdd2f3bc00150080291/cloudservers/detail?offset=1&limit=25" 200 2251 deabe20c14f997a0291fc451a4da16a4 + 16:53:06.212 [main] INFO HuaweiCloud-SDK-Access - "PUT https://ecs.cn-southwest-2.myhuaweicloud.com/v1/077d6a6c19000fdd2f3bc00150080291/cloudservers/1aeac6fb-a2f2-48dc-9052-36637d119dd3" 200 880 f16f70e3fe245c11ab741760f8689a01 + 17:02:37.734 [main] INFO HuaweiCloud-SDK-Access - "GET https://ecs.cn-southwest-2.myhuaweicloud.com/v1/077d6a6c19000fdd2f3bc00150080291/cloudservers/detail?offset=1&limit=-1" 400 165 8c3c8b6fed4482d28e1929a78dc93f04 + ``` + + 日志名称为"HuaweiCloud-SDK-Access", 日志格式为: + + ``` text + "{httpMethod} {uri}" {httpStatusCode} {responseContentLength} {requestId} + ``` + + 其中requestId是华为云APIG返回的请求ID,可以用户保障或者问题跟踪。 + + 可以根据项目情况在对应的日志配置文件中对访问日志进行屏蔽,或者单独打印到独立文件中。 + +8. 原始Http侦听器 + + 在某些场景下可能对业务发出的Http请求进行Debug,需要看到原始的Http请求和返回信息,SDK提供侦听器功能来获取原始的为加密的Http请求和返回信息。 + + > :warning: Warning: 原始信息打印仅在debug阶段使用,请不要在生产系统中将原始的Http头和Body信息打印到日志,这些信息并未加密且其中包含敏感数据,例如所创建虚拟机的密码,IAM用户的密码等; + 当Body体为二进制内容,即Content-Type标识为二进制时 body为"***",详细内容不输出。 + + 设置环境变量 process.env.NODE_DEBUG 或 process.env.DEBUG,开启Debug日志打印 + ``` nodejs + if (process.env.NODE_DEBUG === 'axios' || process.env.DEBUG) { + this.axiosInstance.interceptors.request.use((config: any) => { + logger.debug('Request:'); + try { + logger.debug(JSON.stringify(config, null, 2)); + } catch { + logger.error(config) + } + + return config; + }, (error: any) => { + logger.error('Error: '); + try { + logger.error(JSON.stringify(error, null, 2)); + } catch { + logger.error(error); + } + // @ts-ignore + return Promise.reject(error); + }); + + this.axiosInstance.interceptors.response.use((response: any) => { + logger.debug('Response:'); + try { + logger.debug(JSON.stringify(response, null, 2)); + } catch { + logger.error(response); + } + + return response; + }, (error: any) => { + logger.error('Error: '); + try { + logger.error(JSON.stringify(error, null, 2)); + } catch { + logger.error(error); + } + // @ts-ignore + return Promise.reject(error); + }); + } + ``` + +## 代码实例 + +- 使用如下代码同步查询任务详情,调用前请根据实际情况替换如下变量: `{your ak string}`、`{your sk string}`、`{your endpoint string}`、`{your project id}`以及`{job id}` + + ``` nodejs + import express = require('express'); + import { DevstarClient } from "@huaweicloud/huaweicloud-sdk-devstar/v1/DevstarClient"; + import { BasicCredentials } from "@huaweicloud/huaweicloud-sdk-core/auth/BasicCredentials"; + import { ShowJobDetailRequest } from '@huaweicloud/huaweicloud-sdk-devstar/v1/model/ShowJobDetailRequest'; + + // Create a new express application instance + const app: express.Application = express(); + process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; + app.get('/', function (req: any, res: { send: (arg0: string) => void; }) { + const client = DevstarClient.newBuilder() + .withCredential(new BasicCredentials() + .withAk("{your ak string}") + .withSk("{your sk string}") + .withProjectId("{your project id}")) + .withEndpoint("{your endpoint string}") + .withProxyAgent("") + .build(); + + + const result = client.showJobDetail(new ShowJobDetailRequest("{job id}")); + result.then(result => { + res.send("JSON.stringify(result)::" + JSON.stringify(result)) + }).catch(ex => { + res.send("exception:" + JSON.stringify(ex)) + }); + }); + app.listen(3000, function () { + console.log('Example app listening on port 3000!'); + }); + ``` + - 调试步骤 + + ``` nodejs + 执行命令: + npm install ts-node-dev + npm install express + npm install typescript + 添加scripts: + "dev": "ts-node-dev ./index.ts" + 执行命令: + npm run dev + 本地浏览器访问:http://localhost:3000/ + ``` \ No newline at end of file diff --git a/core/ClientBuilder.ts b/core/ClientBuilder.ts new file mode 100644 index 000000000..618c18f48 --- /dev/null +++ b/core/ClientBuilder.ts @@ -0,0 +1,158 @@ +/* + * Copyright 2020 Huawei Technologies Co.,Ltd. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {HcClient} from "./HcClient"; +import {ICredential} from "./auth/ICredential"; +import {DefaultHttpClient} from "./http/DefaultHttpClient"; +import extend from 'extend'; +import { RequiredError } from "./auth/AKSKSigner"; +import { BasicCredentials } from "./auth/BasicCredentials"; +import { GlobalCredentials } from "./auth/GlobalCredentials"; +const path = require('path'); + +interface CredParams { + ak?: string; + sk?: string, + project_id?: string, + domain_id?: string, +} +export class ClientBuilder { + private init: Function; + private endpoint: string | undefined; + private credential: ICredential | undefined; + private proxyAgent: string | undefined; + public credentialType: string[] = ["BasicCredentials","GlobalCredentials"]; + public envParams: CredParams = process.env; + + public constructor(init: (hcClient: HcClient) => T, credentialType?: string) { + this.init = init; + if (credentialType !== null && credentialType !== undefined) { + this.credentialType = credentialType.split(","); + } + } + + public withEndpoint(endpoint: string): ClientBuilder { + this.endpoint = endpoint; + return this; + } + + public withCredential(credential?: ICredential): ClientBuilder { + this.credential = credential; + return this; + } + + public withProxyAgent(proxyAgent: string): ClientBuilder { + this.proxyAgent = proxyAgent; + return this; + } + + public build(): T { + const axiosOptions = { + disableSslVerification: true + }; + if (this.proxyAgent) { + extend(axiosOptions, { proxyAgent: this.proxyAgent }); + } + + if (this.credential === null || this.credential === undefined) { + this.credential = this.getCredentialFromEnvironment(); + } + + if (this.credential !== null && this.credential !== undefined) { + if (this.credentialType.indexOf(this.credential.constructor.name) < 0) { + throw new RequiredError('credentialType', "This client need input " + this.credentialType.toString() + " credential object"); + } + } + + const client = new DefaultHttpClient(axiosOptions); + const hcClient = new HcClient(client); + hcClient.withEndpoint(this.endpoint).withCredential(this.credential); + return this.init(hcClient); + } + + public getCredentialFromEnvironment(): ICredential { + const sdkType: any = process.env.HUAWEICLOUD_SDK_TYPE; + const credentialTYPE = this.whichCredential(sdkType) + return this.getInputParamCredential(credentialTYPE, this.envParams); + // 从环境变量获取 HUAWEICLOUD_SDK_TYPE + // 环境变量里没有则使用 credentialType[0] + // 生成credential实体 + // 从环境变量获取 AK SK projectId/domainId 进行赋值, 如果环境变量是GlobalCredentials,则赋值domainId + // 返回Credentials + } + + public whichCredential(sdkType: string) { + let credentialTYPE; + if (sdkType) { + switch (sdkType) { + case 'BasicCredentials': + credentialTYPE = new BasicCredentials(); + break; + case 'GlobalCredentials': + credentialTYPE = new GlobalCredentials(); + break; + default: + const obj = {}; + const definedCredPath = path.join(this.init().getPath(), `${sdkType}`); + if (!obj[sdkType]) { + credentialTYPE = require(definedCredPath); + // 多加一层 + obj[sdkType] = credentialTYPE[sdkType]; + } + credentialTYPE = new obj[sdkType](); + break; + } + } else { + // 默认是basic + credentialTYPE = new BasicCredentials(); + } + return credentialTYPE; + } + + public getInputParamCredential(CredentialsType: any, credential: CredParams) { + // 判断是否有_ + let hash = {}; + for (let key in credential) { + const value = credential[key] + if (key.indexOf('_') == -1) { + key = key.toLowerCase(); + key = 'with' + key.charAt(0).toUpperCase() + key.slice(1); + } else { + const arr = key.split('_').map(item => { + item = item.toLowerCase(); + return item.charAt(0).toUpperCase() + item.slice(1); + }) + if (Array.isArray(arr)) { + key = 'with' + arr.join(""); + } + } + hash[key] = value + } + credential = hash; + for (const key in credential) { + if (CredentialsType[key]) { + CredentialsType[key](credential[key]); + } + } + return CredentialsType; +} + +} \ No newline at end of file diff --git a/core/HcClient.ts b/core/HcClient.ts new file mode 100644 index 000000000..0a8917fcb --- /dev/null +++ b/core/HcClient.ts @@ -0,0 +1,104 @@ +/* + * Copyright 2020 Huawei Technologies Co.,Ltd. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {HttpClient} from "./http/HttpClient"; +import {ICredential} from "./auth/ICredential"; +import {IHttpRequest} from "./http/IHttpRequest"; +import {HttpRequestBuilder} from "./http/IHttpRequestBuilder"; +import {SdkResponse} from "./SdkResponse"; +import {ExceptionUtil} from "./exception/ExceptionUtil"; + +export class HcClient { + private httpClient: HttpClient; + private endpoint: string | undefined; + private credential: ICredential | undefined; + private proxyAgent: string = ''; + + public constructor(client: HttpClient) { + this.httpClient = client; + } + + public withEndpoint(endpoint: string | undefined): HcClient { + this.endpoint = endpoint; + return this; + } + + public withCredential(credential: ICredential | undefined): HcClient { + this.credential = credential; + return this; + } + + public withHttpsAgent(proxyAgent: string) { + this.proxyAgent = proxyAgent; + return this; + } + + public sendRequest(options: any): Promise { + const request = this.buildRequest(options); + // @ts-ignore + return new Promise((resolve: any, reject: any) => { + this.httpClient.sendRequest(request).then(res => { + return resolve(HcClient.extractResponse(res)); + }, err => { + let error = err; + let statusCode = error.status; + return reject(ExceptionUtil.generalException(statusCode, error.body)); + }); + }); + } + + private buildRequest(options: any): IHttpRequest { + let url = this.endpoint + options.url; + const pathParams = options.pathParams; + Object.keys(pathParams).forEach(x => { + url = url.replace("{" + x + "}", pathParams[x]); + }); + + const builder = new HttpRequestBuilder(); + let httpRequest = builder + .withEndpoint(url) + .withHeaders(options.headers) + .withMethod(options.method) + .withPathParams(options.pathParams) + .withData(options.data) + .withQueryParams(options.queryParams) + .build(); + + // @ts-ignore + httpRequest = this.credential.processAuthRequest(httpRequest); + + return httpRequest; + } + + private static extractResponse(result?: any) { + const headers = result.headers; + let contentType = headers['content-type']; + contentType = contentType.toLowerCase(); + if (contentType && contentType == 'application/octet-stream') { + return result.result; + } else { + let response = new SdkResponse(); + response.setStatusCode(result.status); + response.setResult(result.result); + return response; + } + } +} \ No newline at end of file diff --git a/core/SdkResponse.ts b/core/SdkResponse.ts new file mode 100644 index 000000000..f05c330c8 --- /dev/null +++ b/core/SdkResponse.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2020 Huawei Technologies Co.,Ltd. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export class SdkResponse { + private httpStatusCode: number | undefined; + public result?: any; + public constructor(statusCode?: number) { + this.httpStatusCode = statusCode; + } + + public getStatusCode(): number { + return this.httpStatusCode; + } + public setStatusCode(code: number) { + this.httpStatusCode = code; + } + public setResult(result: any) { + this.result = result; + } + public getResult() { + return this.result; + } +} \ No newline at end of file diff --git a/core/auth/AKSKSigner.ts b/core/auth/AKSKSigner.ts new file mode 100644 index 000000000..4591d6bce --- /dev/null +++ b/core/auth/AKSKSigner.ts @@ -0,0 +1,258 @@ +/* + * Copyright 2020 Huawei Technologies Co.,Ltd. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as crypto from "crypto"; +import { BasicCredentials } from "./BasicCredentials"; +import moment = require('moment'); +import extend = require("extend"); +import url = require('url') +import querystring = require("querystring"); +import { IHttpRequest } from "../http/IHttpRequest"; +import * as _ from "lodash"; +import {ICredential} from "./ICredential"; + +export class AKSKSigner { + private static EMPTY_BODY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; + private static SDK_SIGNING_ALGORITHM = "SDK-HMAC-SHA256"; + private static BasicDateFormat = "YYYYMMDDTHHmmss"; + private static HeaderXDate = "X-Sdk-Date"; + private static HeaderHost = "host"; + private static HeaderAuthorization = "Authorization"; + private static HeaderContentSha256 = "X-Sdk-Content-Sha256"; + + private static hex: string[] = []; + private static hexTable(): string[]{ + if (this.hex.length <= 0) { + for (var i = 0; i < 256; ++i) { + this.hex[i] = '%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase(); + } + } + return this.hex; + }; + private static noEscape: number[] = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, // 32 - 47 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 80 - 95 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0 // 112 - 127 + ]; + + public static sign(request: IHttpRequest, credential: ICredential) { + let authenticationHeaders = {}; + let dateTimeStamp = request.headers[this.HeaderXDate]; + if (dateTimeStamp) { + dateTimeStamp = moment(dateTimeStamp).utcOffset(0).format(this.BasicDateFormat); + dateTimeStamp = dateTimeStamp + "Z"; + } + else { + dateTimeStamp = moment().utcOffset(0).format(this.BasicDateFormat); + dateTimeStamp = dateTimeStamp + "Z"; + extend(true, authenticationHeaders, { "X-Sdk-Date": dateTimeStamp }); + } + // @ts-ignore + const parsedUrl = url.parse(request.endpoint, true); + + let host = parsedUrl.host; + let reqUrlHostAndPort = request.headers[this.HeaderHost]; + if (reqUrlHostAndPort) { + host = reqUrlHostAndPort; + } + extend(true, authenticationHeaders, { "host": host }); + + let allHeaders = {}; + extend(allHeaders, request.headers, authenticationHeaders); + const canonicalURI = parsedUrl.pathname + "/"; + let query = parsedUrl.query; + let queryParams = request.queryParams; + const canonicalQueryString = this.CanonicalQueryString(request); + + let sortedKeys = _.sortBy(Object.keys(allHeaders), (x: string) => { + return x.toLocaleLowerCase(); + }); + const signedHeaderNames = sortedKeys.join(";").toLocaleLowerCase(); + const canonicalHeaders = this.buildCanonicalHeaders(allHeaders); + const payloadHash = this.buildPayloadHash(request); + + const canonicalRequest = this.buildCanonicalRequest(request.method, canonicalURI, canonicalQueryString, canonicalHeaders, signedHeaderNames, payloadHash); + + const canonicalRequestHash = this.Hex(canonicalRequest); + const stringToSign = this.getStringToSign(this.SDK_SIGNING_ALGORITHM, dateTimeStamp, canonicalRequestHash); + const signatureString = this.hmacSHA256(credential.getSk(), stringToSign); + + const authorization = { + Authorization: `${this.SDK_SIGNING_ALGORITHM} Access=${credential.getAk()}, SignedHeaders=${signedHeaderNames}, Signature=${signatureString}` + }; + + extend(allHeaders, authorization); + return allHeaders; + } + + private static Hex = (str: string) => { + return crypto + .createHash("sha256") + .update(str) + .digest("hex"); + }; + + + private static hmacSHA256 = (secretKey: string | undefined, str: string) => { + return crypto + // @ts-ignore + .createHmac("sha256", secretKey) + .update(str) + .digest("hex"); + }; + + private static getStringToSign(sdkSigningHash: string, dateTimeStamp: string, canonicalRequestHash: string) { + const arr = [sdkSigningHash, dateTimeStamp, canonicalRequestHash]; + + return arr.join("\n"); + } + + // eslint-disable-next-line max-params + private static buildCanonicalRequest(method: string | undefined, canonicalURI: string | null, canonicalQueryString: string, canonicalHeaders: string, signedHeaderNames: string, payloadHash: string | undefined) { + const arr = [method, canonicalURI, canonicalQueryString, canonicalHeaders, signedHeaderNames, payloadHash]; + + return arr.join("\n"); + } + + private static buildPayloadHash(request: IHttpRequest) { + if (request.headers[this.HeaderContentSha256]) { + return request.headers[this.HeaderContentSha256]; + } + if (request.data) { + return this.Hex(JSON.stringify(request.data)); + } + return this.EMPTY_BODY_SHA256; + } + + private static buildCanonicalHeaders(allHeaders: any) { + let sortedKeys = _.sortBy(Object.keys(allHeaders), (x: string) => { + return x.toLocaleLowerCase(); + }); + let canonicalHeaders = ""; + for (const key of sortedKeys) { + const lowerKey = key.toLocaleLowerCase(); + canonicalHeaders += `${lowerKey}:${allHeaders[key]}\n`; + } + return canonicalHeaders; + } + + private static CanonicalQueryString(r: any) { + const keys = []; + for (let key in r.queryParams) { + keys.push(key) + } + keys.sort(); + const a = []; + for (let i in keys) { + const key = this.urlEncode(keys[i]); + const value = r.queryParams[keys[i]]; + if (Array.isArray(value)) { + value.sort(); + for (let iv in value) { + a.push(key + '=' + this.urlEncode(value[iv])) + } + } else { + a.push(key + '=' + this.urlEncode(value)) + } + } + return a.join('&'); + } + + private static urlEncode(str: any) { + if (typeof str !== 'string') { + if (typeof str === 'object') + str = String(str); + else + str += ''; + } + let out = ''; + let lastPos = 0; + + for (var i = 0; i < str.length; ++i) { + var c = str.charCodeAt(i); + + // ASCII + if (c < 0x80) { + if (this.noEscape[c] === 1) + continue; + if (lastPos < i) + out += str.slice(lastPos, i); + lastPos = i + 1; + out += this.hexTable()[c]; + continue; + } + + if (lastPos < i) + out += str.slice(lastPos, i); + + // Multi-byte characters ... + if (c < 0x800) { + lastPos = i + 1; + out += this.hexTable()[0xC0 | (c >> 6)] + this.hexTable()[0x80 | (c & 0x3F)]; + continue; + } + if (c < 0xD800 || c >= 0xE000) { + lastPos = i + 1; + out += this.hexTable()[0xE0 | (c >> 12)] + + this.hexTable()[0x80 | ((c >> 6) & 0x3F)] + + this.hexTable()[0x80 | (c & 0x3F)]; + continue; + } + // Surrogate pair + ++i; + + if (i >= str.length) + throw new RequiredError('ERR_INVALID_URI'); + + var c2 = str.charCodeAt(i) & 0x3FF; + + lastPos = i + 1; + c = 0x10000 + (((c & 0x3FF) << 10) | c2); + out += this.hexTable()[0xF0 | (c >> 18)] + + this.hexTable()[0x80 | ((c >> 12) & 0x3F)] + + this.hexTable()[0x80 | ((c >> 6) & 0x3F)] + + this.hexTable()[0x80 | (c & 0x3F)]; + } + if (lastPos === 0) + return str; + if (lastPos < str.length) + return out + str.slice(lastPos); + return out; + } +} + +/** + * + * @export + * @class RequiredError + * @extends {Error} + */ +export class RequiredError extends Error { + name: "RequiredError" = "RequiredError"; + constructor(public field: string, msg?: string) { + super(msg); + } +} \ No newline at end of file diff --git a/core/auth/BasicCredentials.ts b/core/auth/BasicCredentials.ts new file mode 100644 index 000000000..ac301ed89 --- /dev/null +++ b/core/auth/BasicCredentials.ts @@ -0,0 +1,115 @@ +/* + * Copyright 2020 Huawei Technologies Co.,Ltd. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ICredential } from "./ICredential"; +import { IHttpRequest } from "../http/IHttpRequest"; +import { AKSKSigner } from "./AKSKSigner"; +import { request } from "express"; +import { HttpRequestBuilder } from "../http/IHttpRequestBuilder"; +import extend from "extend"; +import { RequiredError } from "./AKSKSigner"; + +export class BasicCredentials implements ICredential { + private ak: string | undefined; + private sk: string | undefined; + private securityToken: string | undefined; + private projectId: string | undefined; + + public withAk(ak: string | undefined): BasicCredentials { + this.ak = ak; + return this; + } + + public withSk(sk: string | undefined): BasicCredentials { + this.sk = sk; + return this; + } + + public withProjectId(projectId: string | undefined): BasicCredentials { + this.projectId = projectId; + return this; + } + + public withSecurityToken(securityToken: string | undefined): BasicCredentials { + this.securityToken = securityToken; + return this; + } + + public getAk(): string | undefined { + return this.ak; + } + public getSk(): string | undefined { + return this.sk; + } + + public getPathParams() { + const pathParams = {}; + if (this.projectId) { + extend(pathParams, { project_id: this.projectId }); + } + return pathParams; + } + public processAuthRequest(httpRequest: IHttpRequest): IHttpRequest { + + if (this.ak === null || this.ak === undefined) { + throw new RequiredError('AK cannot be empty or undefined.'); + } + if (this.sk === null || this.sk === undefined) { + throw new RequiredError('SK cannot be empty or undefined.'); + } + + const builder = new HttpRequestBuilder(); + builder.addPathParams(this.getPathParams()); + + // 替换所有的path参数 + if (this.projectId) { + let url = parsePath(httpRequest.endpoint, this.getPathParams()); + builder.withEndpoint(url); + } + + if (this.projectId) { + builder.addHeaders("X-Project-Id", this.projectId); + } + + if (this.securityToken) { + builder.addHeaders("X-Security-Token", this.securityToken); + } + + builder.addHeaders("Content-Type", "application/json"); + builder.addAllHeaders(httpRequest.headers); + extend(httpRequest, builder.build()); + const headers = AKSKSigner.sign(httpRequest, this); + + builder.addAllHeaders(headers); + + return extend(httpRequest, builder.build()); + } +} + +function parsePath(path: string | undefined, params: any): string { + if (!path || !params) { + return path; + } + return Object.keys(params).reduce((parsedPath, param) => { + const value = encodeURIComponent(params[param]); + return parsedPath.replace(new RegExp(`{${param}}`), value); + }, path); +} \ No newline at end of file diff --git a/core/auth/GlobalCredentials.ts b/core/auth/GlobalCredentials.ts new file mode 100644 index 000000000..284fa2f34 --- /dev/null +++ b/core/auth/GlobalCredentials.ts @@ -0,0 +1,115 @@ +/* + * Copyright 2020 Huawei Technologies Co.,Ltd. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ICredential } from "./ICredential"; +import { IHttpRequest } from "../http/IHttpRequest"; +import { AKSKSigner } from "./AKSKSigner"; +import { request } from "express"; +import { HttpRequestBuilder } from "../http/IHttpRequestBuilder"; +import extend from "extend"; +import { RequiredError } from "./AKSKSigner"; + +export class GlobalCredentials implements ICredential { + private ak: string | undefined; + private sk: string | undefined; + private securityToken: string | undefined; + private domainId: string | undefined; + + public withAk(ak: string | undefined): GlobalCredentials { + this.ak = ak; + return this; + } + + public withSk(sk: string | undefined): GlobalCredentials { + this.sk = sk; + return this; + } + + public withDomainId(domainId: string | undefined): GlobalCredentials { + this.domainId = domainId; + return this; + } + + public withSecurityToken(securityToken: string | undefined): GlobalCredentials { + this.securityToken = securityToken; + return this; + } + + public getAk(): string | undefined { + return this.ak; + } + public getSk(): string | undefined { + return this.sk; + } + + public getPathParams() { + const pathParams = {}; + if (this.domainId) { + extend(pathParams, { domain_id: this.domainId }); + } + return pathParams; + } + public processAuthRequest(httpRequest: IHttpRequest): IHttpRequest { + + if (this.ak === null || this.ak === undefined) { + throw new RequiredError('AK cannot be empty or undefined.'); + } + if (this.sk === null || this.sk === undefined) { + throw new RequiredError('SK cannot be empty or undefined.'); + } + + const builder = new HttpRequestBuilder(); + builder.addPathParams(this.getPathParams()); + + // 替换所有的path参数 + if (this.domainId) { + let url = parsePath(httpRequest.endpoint, this.getPathParams()); + builder.withEndpoint(url); + } + + if (this.domainId) { + builder.addHeaders("X-Domain-Id", this.domainId); + } + + if (this.securityToken) { + builder.addHeaders("X-Security-Token", this.securityToken); + } + + builder.addHeaders("Content-Type", "application/json"); + builder.addAllHeaders(httpRequest.headers); + extend(httpRequest, builder.build()); + const headers = AKSKSigner.sign(httpRequest, this); + + builder.addAllHeaders(headers); + + return extend(httpRequest, builder.build()); + } +} + +function parsePath(path: string | undefined, params: any): string { + if (!path || !params) { + return path; + } + return Object.keys(params).reduce((parsedPath, param) => { + const value = encodeURIComponent(params[param]); + return parsedPath.replace(new RegExp(`{${param}}`), value); + }, path); +} \ No newline at end of file diff --git a/core/auth/ICredential.ts b/core/auth/ICredential.ts new file mode 100644 index 000000000..ad30a7543 --- /dev/null +++ b/core/auth/ICredential.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2020 Huawei Technologies Co.,Ltd. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { IHttpRequest } from "../http/IHttpRequest"; + +export interface ICredential { + getAk: () => string | undefined; + getSk: () => string | undefined; + processAuthRequest: (httpRequest: IHttpRequest) => IHttpRequest; +} \ No newline at end of file diff --git a/core/exception/ClientRequestException.ts b/core/exception/ClientRequestException.ts new file mode 100644 index 000000000..2678c17b2 --- /dev/null +++ b/core/exception/ClientRequestException.ts @@ -0,0 +1,30 @@ +/* + * Copyright 2020 Huawei Technologies Co.,Ltd. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ServiceResponseException } from "./ServiceResponseException"; + +export class ClientRequestException extends ServiceResponseException { + + constructor(httpStatusCode: string | number, errorMsg: string, errorCode: string, requestId: string) { + super(httpStatusCode, errorMsg, errorCode, requestId); + this.name = 'ClientRequestException'; + } +} \ No newline at end of file diff --git a/core/exception/ExceptionUtil.ts b/core/exception/ExceptionUtil.ts new file mode 100644 index 000000000..883b4691e --- /dev/null +++ b/core/exception/ExceptionUtil.ts @@ -0,0 +1,52 @@ +/* + * Copyright 2020 Huawei Technologies Co.,Ltd. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ClientRequestException } from "./ClientRequestException"; +import { ServerResponseException } from "./ServerResponseException"; +import { ServiceResponseException } from "./ServiceResponseException"; + +export class ExceptionUtil { + static generalException(httpStatusCode: string | number, errorData: { error_code: any; error_msg: any; request_id: any; code: any; message: any; } | null | undefined) { + let errorCode; + let errorMsg; + let requestId; + if (errorData !== null && errorData !== undefined) { + errorCode = errorData.error_code; + errorMsg = errorData.error_msg; + requestId = errorData.request_id; + if (errorCode === null || errorCode === undefined) { + errorCode = errorData.code; + errorMsg = errorData.message; + if (errorCode === null || errorCode === undefined) { + errorMsg = errorData; + } + } + } + if (httpStatusCode) { + if (httpStatusCode >= 400 && httpStatusCode < 500) { + return new ClientRequestException(httpStatusCode, errorMsg, errorCode, requestId); + } else if (httpStatusCode >= 500 && httpStatusCode < 600) { + return new ServerResponseException(httpStatusCode, errorMsg, errorCode, requestId); + } + } + return new ServiceResponseException(httpStatusCode, errorMsg, errorCode, requestId); + } +} \ No newline at end of file diff --git a/core/exception/SdkException.ts b/core/exception/SdkException.ts new file mode 100644 index 000000000..76fcf7fdb --- /dev/null +++ b/core/exception/SdkException.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2020 Huawei Technologies Co.,Ltd. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export class SdkException extends Error { + constructor() { + super(); + this.name = 'SdkException'; + } +} \ No newline at end of file diff --git a/core/exception/ServerResponseException.ts b/core/exception/ServerResponseException.ts new file mode 100644 index 000000000..22f9ab7c2 --- /dev/null +++ b/core/exception/ServerResponseException.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2020 Huawei Technologies Co.,Ltd. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ServiceResponseException } from "./ServiceResponseException"; + +export class ServerResponseException extends ServiceResponseException { + constructor(httpStatusCode: string | number, errorMsg: string, errorCode: string, requestId: string) { + super(httpStatusCode, errorMsg, errorCode, requestId); + this.name = 'ServerResponseException'; + } +} \ No newline at end of file diff --git a/core/exception/ServiceResponseException.ts b/core/exception/ServiceResponseException.ts new file mode 100644 index 000000000..bfb0a7f8f --- /dev/null +++ b/core/exception/ServiceResponseException.ts @@ -0,0 +1,38 @@ +/* + * Copyright 2020 Huawei Technologies Co.,Ltd. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SdkException } from "./SdkException"; + +export class ServiceResponseException extends SdkException { + private httpStatusCode: string | number; + private errorMsg: string; + private errorCode: string; + private requestId: string; + + constructor(httpStatusCode: string | number, errorMsg: string, errorCode: string, requestId: string) { + super(); + this.name = 'ServiceResponseException'; + this.httpStatusCode = httpStatusCode; + this.errorMsg = errorMsg; + this.errorCode = errorCode; + this.requestId = requestId; + } +} \ No newline at end of file diff --git a/core/http/DefaultHttpClient.ts b/core/http/DefaultHttpClient.ts new file mode 100644 index 000000000..fe9a207da --- /dev/null +++ b/core/http/DefaultHttpClient.ts @@ -0,0 +1,359 @@ +/* + * Copyright 2020 Huawei Technologies Co.,Ltd. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import axios from 'axios'; +import extend from 'extend'; +import https = require('https'); +import logger from './logger'; +import { IHttpRequest } from './IHttpRequest'; +import querystring = require('querystring'); +import { HttpClient } from './HttpClient'; +import HttpsProxyAgent = require('https-proxy-agent'); +import { log4jsLogger } from './log4js'; + +export class DefaultHttpClient implements HttpClient { + private axiosInstance: any; + public static httpReqParam: any; + public static httpResponse: any; + + public constructor(axiosOptions?: any) { + axiosOptions = axiosOptions || {}; + let proxyAgent; + + if (axiosOptions.proxyAgent) { + proxyAgent = HttpsProxyAgent(axiosOptions.proxyAgent); + } + + // override several axios defaults + // axios sets the default Content-Type for `post`, `put`, and `patch` operations + // to 'application/x-www-form-urlencoded'. This causes problems, so overriding the + // defaults here + + this.axiosInstance = axios.create({ + maxContentLength: Infinity, + headers: { + post: { + 'Content-Type': 'application/json', + 'accept-encoding': '*' + }, + put: { + 'Content-Type': 'application/json', + 'accept-encoding': '*' + }, + patch: { + 'Content-Type': 'application/json', + 'accept-encoding': '*' + }, + get: { + 'Content-Type': 'application/json', + 'accept-encoding': '*' + }, + }, + // proxy: { host: '10.78.74.174', port: 3128 } + proxy: false, + httpsAgent: proxyAgent + }); + + if (process.env.NODE_DEBUG === 'axios' || process.env.DEBUG) { + this.axiosInstance.interceptors.request.use((config: any) => { + logger.debug('Request:'); + try { + logger.debug(JSON.stringify(config, null, 2)); + } catch { + logger.error(config) + } + + return config; + }, (error: any) => { + logger.error('Error: '); + try { + logger.error(JSON.stringify(error, null, 2)); + } catch { + logger.error(error); + } + // @ts-ignore + return Promise.reject(error); + }); + + this.axiosInstance.interceptors.response.use((response: any) => { + logger.debug('Response:'); + try { + logger.debug(JSON.stringify(response, null, 2)); + } catch { + logger.error(response); + } + + return response; + }, (error: any) => { + logger.error('Error: '); + try { + logger.error(JSON.stringify(error, null, 2)); + } catch { + logger.error(error); + } + // @ts-ignore + return Promise.reject(error); + }); + } + } + + public sendRequest(httpRequest: IHttpRequest): Promise { + const { endpoint, queryParams, pathParams, method, data, headers, contentType } = httpRequest; + + // Path params + let url = endpoint;//parsePath(endpoint, pathParams); + + if (queryParams && Object.keys(queryParams).length > 0) { + Object.keys(queryParams).forEach( + key => Array.isArray(queryParams[key]) && (queryParams[key] = queryParams[key].join(',')) + ); + } + url = stripTrailingSlash(url); + + const requestParams = { + url, + method, + headers, + params: queryParams, + data, + responseType: 'json', + paramsSerializer: (params: any) => { + return querystring.stringify(params); + }, + }; + DefaultHttpClient.httpReqParam = requestParams; + return this.axiosInstance(requestParams).then( + (res: { config: any; request: any; result: any; data: any; status: string; headers: any}) => { + // sometimes error responses will still trigger the `then` block - escape that behavior here + DefaultHttpClient.httpResponse = res; + if (!res) { return }; + + // these objects contain circular json structures and are not always relevant to the user + // if the user wants them, they can be accessed through the debug properties + delete res.config; + delete res.request; + + // the other sdks use the interface `result` for the body + res.result = res.data; + delete res.data; + + let requestId = res.headers['x-request-id']; + let reponseLength = res.result ? JSON.stringify(res.result).length : 1; + log4jsLogger.info('"' + requestParams.method + ' ' + requestParams.url + '" ' + res.status + ' ' + reponseLength + ' ' + requestId); + if (process.env.DEBUG) { + log4jsLogger.debug('request: ' + JSON.stringify(requestParams) + ". response: " + JSON.stringify(res)); + } + + // return another promise that resolves with 'res' to be handled in generated code + return res; + }, + (err: any) => { + let response = err.response; + DefaultHttpClient.httpResponse = err; + + let requestId = response.headers['x-request-id']; + let reponseLength = response.data ? JSON.stringify(response.data).length : 1; + log4jsLogger.info('"' + requestParams.method + ' ' + requestParams.url + '" ' + response.status + ' ' + reponseLength + ' ' + requestId); + if (process.env.DEBUG) { + log4jsLogger.debug('request: ' + JSON.stringify(requestParams) + ". response: " + JSON.stringify(response.data)); + } + + // return another promise that rejects with 'err' to be handled in generated code + throw this.formatError(err); + } + ); + } + + /** + * Format error returned by axios + * @private + * @returns {Error} + * @param axiosError + */ + public formatError(axiosError: any): Error { + // return an actual error object, + // but make it flexible so we can add properties like 'body' + const error: any = new Error(); + + // axios specific handling + // this branch is for an error received from the service + if (axiosError.response) { + axiosError = axiosError.response; + // The request was made and the server responded with a status code + // that falls out of the range of 2xx + delete axiosError.config; + delete axiosError.request; + + error.statusText = axiosError.statusText; + error.name = axiosError.statusText; // ** deprecated ** + + error.status = axiosError.status; + error.code = axiosError.status; // ** deprecated ** + + error.message = parseServiceErrorMessage(axiosError.data) || axiosError.statusText; + + // some services bury the useful error message within 'data' + // adding it to the error under the key 'body' as a string or object + let errorBody; + try { + // try/catch to handle objects with circular references + // errorBody = JSON.stringify(axiosError.data); + errorBody = axiosError.data; + } catch (e) { + // ignore the error, use the object, and tack on a warning + errorBody = axiosError.data; + errorBody.warning = 'Body contains circular reference'; + logger.error(`Failed to stringify axiosError: ${e}`); + } + + error.body = errorBody; + + // attach headers to error object + error.headers = axiosError.headers; + + // print a more descriptive error message for auth issues + if (isAuthenticationError(axiosError)) { + error.message = 'Access is denied due to invalid credentials.'; + } + } else if (axiosError.request) { + // The request was made but no response was received + // `error.request` is an instance of XMLHttpRequest in the browser and an instance of + // http.ClientRequest in node.js + error.message = axiosError.message; + error.statusText = axiosError.code; + error.body = 'Response not received - no connection was made to the service.'; + + // when a request to a private cloud instance has an ssl problem, it never connects and follows this branch of the error handling + if (isSelfSignedCertificateError(axiosError)) { + error.message = `The connection failed because the SSL certificate is not valid. ` + + `To use a self-signed certificate, set the \`disableSslVerification\` parameter in the constructor options.`; + } + } else { + // Something happened in setting up the request that triggered an Error + error.message = axiosError.message; + } + + return error; + } +} + + + +/** + * @private + * @param {string} path + * @param {Object} params + * @returns {string} + */ +function parsePath(path: string, params: any): string { + if (!path || !params) { + return path; + } + return Object.keys(params).reduce((parsedPath, param) => { + const value = encodeURIComponent(params[param]); + return parsedPath.replace(new RegExp(`{${param}}`), value); + }, path); +} + +function stripTrailingSlash(url: string | undefined): string { + // Match a forward slash / at the end of the string ($) + // @ts-ignore + return url.replace(/\/$/, ''); +} +/** + * Determine if the error is due to bad credentials + * @private + * @param {Object} error - error object returned from axios + * @returns {boolean} true if error is due to authentication + */ +function isAuthenticationError(error: any): boolean { + let isAuthErr = false; + const code: number = error.status || null; + const body: any = error.data || {}; + + // handle specific error from iam service, should be relevant across platforms + const isIamServiceError: boolean = body.context && + body.context.url && + body.context.url.indexOf('iam') > -1; + + if (code === 401 || code === 403 || isIamServiceError) { + isAuthErr = true; + } + + return isAuthErr; +} +/** + * Look for service error message in common places, by priority + * first look in `errors[0].message`, then in `error`, then in + * `message`, then in `errorMessage` + * @private + * @param {Object} response - error response body received from service + * @returns {string | undefined} the error message if is was found, undefined otherwise + */ +function parseServiceErrorMessage(response: any): string | undefined { + let message; + + if (Array.isArray(response.errors) && response.errors.length > 0 && hasStringProperty(response.errors[0], 'message')) { + message = response.errors[0].message; + } else if (hasStringProperty(response, 'error')) { + message = response.error; + } else if (hasStringProperty(response, 'message')) { + message = response.message; + } else if (hasStringProperty(response, 'errorMessage')) { + message = response.errorMessage; + } + + logger.info(`Parsing service error message: ${message}`); + return message; +} + +/** + * Return true if object has a specified property that is a string + * @private + * @param {Object} obj - object to look for property in + * @param {string} property - name of the property to look for + * @returns {boolean} true if property exists and is string + */ +function hasStringProperty(obj: any, property: string): boolean { + return Boolean(obj[property] && typeof obj[property] === 'string'); +} + +/** +* Determine if the error is due to a bad self signed certificate +* @private +* @param {Object} error - error object returned from axios +* @returns {boolean} true if error is due to an SSL error +*/ +function isSelfSignedCertificateError(error: any): boolean { + let result = false; + + const sslCode = 'DEPTH_ZERO_SELF_SIGNED_CERT'; + const sslMessage = 'self signed certificate'; + + const hasSslCode = error.code === sslCode; + const hasSslMessage = hasStringProperty(error, 'message') && error.message.includes(sslMessage); + + if (hasSslCode || hasSslMessage) { + result = true; + } + + return result; +} \ No newline at end of file diff --git a/core/http/HttpClient.ts b/core/http/HttpClient.ts new file mode 100644 index 000000000..d1e32897f --- /dev/null +++ b/core/http/HttpClient.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2020 Huawei Technologies Co.,Ltd. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { IHttpRequest } from "./IHttpRequest"; + +export interface HttpClient { + sendRequest(httpRequest: IHttpRequest): Promise; +} \ No newline at end of file diff --git a/core/http/IHttpRequest.ts b/core/http/IHttpRequest.ts new file mode 100644 index 000000000..74c935af7 --- /dev/null +++ b/core/http/IHttpRequest.ts @@ -0,0 +1,71 @@ +/* + * Copyright 2020 Huawei Technologies Co.,Ltd. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +export interface IHttpRequest { + params: any; + endpoint: string | undefined; + contentType: string | undefined; + method: string | undefined; + queryParams: any; + pathParams: any; + headers: any; + data: any; + + getPathParams: () => any; +} + +export class HttpRequestImpl implements IHttpRequest { + public params: any; + public endpoint: string | undefined; + public contentType: string | undefined; + public method: string | undefined; + public queryParams: any; + public pathParams: any; + public headers: any; + public data: any; + + public getPathParams(): any { + if (!this.pathParams) { + this.pathParams = {}; + } + return this.pathParams; + } + + public getQueryParams(): any { + if (!this.queryParams) { + this.queryParams = {}; + } + return this.queryParams; + } + public getData(): any { + if (!this.data) { + this.data = {}; + } + return this.pathParams; + } + public getHeaders(): any { + if (!this.headers) { + this.headers = {}; + } + return this.headers; + } +} \ No newline at end of file diff --git a/core/http/IHttpRequestBuilder.ts b/core/http/IHttpRequestBuilder.ts new file mode 100644 index 000000000..085e548fd --- /dev/null +++ b/core/http/IHttpRequestBuilder.ts @@ -0,0 +1,89 @@ +/* + * Copyright 2020 Huawei Technologies Co.,Ltd. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { IHttpRequest, HttpRequestImpl } from "./IHttpRequest"; +import extend from "extend"; + +export class HttpRequestBuilder { + private httpRequest: HttpRequestImpl; + + constructor(httpRequest?: HttpRequestImpl) { + this.httpRequest = httpRequest || new HttpRequestImpl(); + } + + public build(): IHttpRequest { + return this.httpRequest; + } + + public withEndpoint(value: string) { + this.httpRequest.endpoint = value; + return this; + } + + public withContentType(value: string) { + this.httpRequest.contentType = value; + return this; + } + + public withMethod(value: string) { + this.httpRequest.method = value; + return this; + } + + public withQueryParams(value: any) { + this.httpRequest.queryParams = value; + return this; + } + public withPathParams(value: any) { + this.httpRequest.pathParams = value; + return this; + } + + public withHeaders(value: any) { + this.httpRequest.headers = value; + return this; + } + + public withData(value: any) { + this.httpRequest.data = value; + return this; + } + public addPathParams(pathParams: {}) { + extend(this.httpRequest.getPathParams(), pathParams); + return this; + } + public addQueryParams(queryParams: {}) { + extend(this.httpRequest.getQueryParams, queryParams); + return this; + } + + public addHeaders(key: string, value: string) { + let headers = this.httpRequest.getHeaders(); + if (!headers![key]) { + this.httpRequest.headers[key] = value; + } + return this; + } + public addAllHeaders(header: any) { + extend(this.httpRequest.getHeaders(), header); + return this; + } +} diff --git a/core/http/log4js.ts b/core/http/log4js.ts new file mode 100644 index 000000000..20dfbc6ab --- /dev/null +++ b/core/http/log4js.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2020 Huawei Technologies Co.,Ltd. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +const log4js = require('log4js'); +log4js.configure({ + appenders: { + everything: { type: 'dateFile', filename: 'HuaweiCloud-SDK-Access.log', daysToKeep: 7} + }, + categories: { + default: { appenders: [ 'everything' ], level: 'debug' } + } + }); + + +export const log4jsLogger = log4js.getLogger('HuaweiCloud-SDK-Access'); \ No newline at end of file diff --git a/core/http/logger.ts b/core/http/logger.ts new file mode 100644 index 000000000..d6e114000 --- /dev/null +++ b/core/http/logger.ts @@ -0,0 +1,52 @@ +/* + * Copyright 2020 Huawei Technologies Co.,Ltd. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import logger = require('debug'); +const debug = logger('hw-node-sdk-core:debug'); +const error = logger('hw-node-sdk-core:error'); +const info = logger('hw-node-sdk-core:info'); +const verbose = logger('hw-node-sdk-core:verbose'); +const warn = logger('hw-node-sdk-core:warning'); + +// enable loggers if axios flag is set & mimic log levels severity +if (process.env.NODE_DEBUG === 'axios') { + debug.enabled = true; +} +if (debug.enabled) { + verbose.enabled = true; +} +if (verbose.enabled) { + info.enabled = true; +} +if (info.enabled) { + warn.enabled = true; +} +if (warn.enabled) { + error.enabled = true; +} +// export loggers; +export default { + debug, + error, + info, + verbose, + warn +} diff --git a/core/utils/SignerUtil.ts b/core/utils/SignerUtil.ts new file mode 100644 index 000000000..cc4cbd7f1 --- /dev/null +++ b/core/utils/SignerUtil.ts @@ -0,0 +1,81 @@ +/* + * Copyright 2020 Huawei Technologies Co.,Ltd. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export class SignerUtil { + constructor() { } + // 返回请求的绝对路径 + static checkUrl(method: string, url: string) { + // if (method === "GET") { + // return this.getUrl(url) + "/"; + // } else { + // return this.getUrl(url) + "/"; + // } + return this.getUrl(url) + "/"; + } + static getUrl(url: string) { + var arrUrl = url.split("//"); + + var start = arrUrl[1].indexOf("/"); + var relUrl = arrUrl[1].substring(start); //stop省略,截取从start开始到结尾的所有字符 + + if (relUrl.indexOf("?") != -1) { + relUrl = relUrl.split("?")[0]; + } + return relUrl; + } + static getErr(obj: any) { + return { + getHttpCode: () => { + return obj.status + }, + getErrorCode: () => { + let code = ''; + if (obj.error_code) { + code = obj.error_code + } else if (obj.code) { + code = obj.code + } else { + for (let key in obj) { + if (obj[key].code) { + code = obj[key].code + } + } + } + return code + }, + getErrorMessage: () => { + let mes = ''; + if (obj.error_msg) { + mes = obj.error_msg + } else if (obj.message) { + mes = obj.message + } else { + for (let key in obj) { + if (obj[key].message) { + mes = obj[key].message + } + } + } + return mes + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..be17e0900 --- /dev/null +++ b/package.json @@ -0,0 +1,47 @@ +{ + "name": "huaweicloud-sdk-nodejs-v3", + "version": "3.0.1-beta", + "description": "Core code for Huaweicloud SDK for NodeJs", + "main": "index.js", + "scripts": { + "tsc": "tsc", + "dev": " ts-node-dev ./example/index.ts", + "prod": "tsc && node ./build/index.js", + "test": "mocha -r ts-node/register test/*test.ts" + }, + "repository": { + "type": "git", + "url": "https://github.com/huaweicloud/huaweicloud-sdk-nodejs-v3" + }, + "author": "HuaweiCloud_SDK", + "license": "Apache-2.0", + "dependencies": { + "@types/express": "^4.17.6", + "@types/extend": "^3.0.1", + "@types/node": "^14.0.14", + "axios": "^0.19.2", + "express": "^4.17.1", + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "lodash": "^4.17.15", + "log4js": "^6.3.0", + "moment": "^2.27.0", + "moment-timezone": "^0.5.31" + }, + "devDependencies": { + "@types/debug": "^4.1.5", + "@types/lodash": "^4.14.157", + "@types/mocha": "^8.0.0", + "@types/should": "^13.0.0", + "@typescript-eslint/eslint-plugin": "^3.5.0", + "@typescript-eslint/parser": "^3.5.0", + "eslint": "^7.3.1", + "eslint-config-alloy": "^3.7.3", + "mocha": "^8.0.1", + "prettier": "^2.0.5", + "should": "^13.2.3", + "typescript": "^3.9.6", + "ts-node-dev": "^1.0.0-pre.49", + "debug": "^4.1.1" + } +} diff --git a/services/devstar/v1/DevStarClient.ts b/services/devstar/v1/DevStarClient.ts new file mode 100644 index 000000000..2e5cd1a24 --- /dev/null +++ b/services/devstar/v1/DevStarClient.ts @@ -0,0 +1,382 @@ +import { HcClient } from "@huaweicloud/huaweicloud-sdk-core/HcClient"; +import { ClientBuilder } from "@huaweicloud/huaweicloud-sdk-core/ClientBuilder"; +import { SdkResponse } from "@huaweicloud/huaweicloud-sdk-core/SdkResponse"; + +import { DownloadApplicationCodeRequest } from './model/DownloadApplicationCodeRequest'; +import { DownloadApplicationCodeResponse } from './model/DownloadApplicationCodeResponse'; +import { ListPublishedTemplatesRequest } from './model/ListPublishedTemplatesRequest'; +import { ListPublishedTemplatesResponse } from './model/ListPublishedTemplatesResponse'; +import { PropertiesInfo } from './model/PropertiesInfo'; +import { RepositoryInfo } from './model/RepositoryInfo'; +import { RunDevstarTemplateJobRequest } from './model/RunDevstarTemplateJobRequest'; +import { RunDevstarTemplateJobResponse } from './model/RunDevstarTemplateJobResponse'; +import { ShowJobDetailRequest } from './model/ShowJobDetailRequest'; +import { ShowJobDetailResponse } from './model/ShowJobDetailResponse'; +import { ShowTemplateDetailRequest } from './model/ShowTemplateDetailRequest'; +import { ShowTemplateDetailResponse } from './model/ShowTemplateDetailResponse'; +import { TemplateJobInfo } from './model/TemplateJobInfo'; +import { TemplateSimpleInfo } from './model/TemplateSimpleInfo'; + +export class DevStarClient { + public static newBuilder(): ClientBuilder { + return new ClientBuilder(newClient); + } + + private hcClient: HcClient; + public constructor(client: HcClient) { + this.hcClient = client; + } + + public getPath() { + return __dirname; + } + + /** + * 下载模板产物 + * @summary 下载模板产物 + * @param {string} jobId 任务id + * @param {'zh-cn' | 'en-us'} [xLanguage] 语言类型 中文:zh-cn 英文:en-us + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public downloadApplicationCode(downloadApplicationCodeRequest?: DownloadApplicationCodeRequest): Promise { + // @ts-ignore + return new Promise((resolve: (arg0: any) => any, reject: (arg0: any) => any) => { + const options = ParamCreater().downloadApplicationCode(downloadApplicationCodeRequest); + + return this.hcClient.sendRequest(options).then( + (res: any) => { + return resolve(res); + }, + (err: any) => { + return reject(err); + }); + }); + } + /** + * 通过DevStar的模板进行应用代码创建 通过 DevStar 模板创建生成应用代码的任务,并将应用代码存储于指定的 CodeHub 仓库中,可以通过返回的任务 ID 查询相关任务状态 - 接口鉴权方式 通过华为云服务获取的用户token - 代码生成位置 应用代码生成后的地址,目前支持codehub地址和压缩包下载地址。 + * @summary Devstar 模板生成代码 + * @param {TemplateJobInfo} runDevstarTemplateJobRequestBody 创建任务参数 + * @param {'zh-cn' | 'en-us'} [xLanguage] 语言类型 中文:zh-cn 英文:en-us + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public runDevstarTemplateJob(runDevstarTemplateJobRequest?: RunDevstarTemplateJobRequest): Promise { + // @ts-ignore + return new Promise((resolve: (arg0: any) => any, reject: (arg0: any) => any) => { + const options = ParamCreater().runDevstarTemplateJob(runDevstarTemplateJobRequest); + + return this.hcClient.sendRequest(options).then( + (res: any) => { + return resolve(res); + }, + (err: any) => { + return reject(err); + }); + }); + } + /** + * 查询任务的详情 通过任务ID可以查看任务的状态 当任务结束时返回应用代码存放的位置 - 接口鉴权方式 通过华为云服务获取的用户token - 代码生成位置 应用代码生成后的地址,目前支持codehub地址和压缩包下载地址 + * @summary 查询任务详情 + * @param {string} jobId 任务id + * @param {'zh-cn' | 'en-us'} [xLanguage] 语言类型 中文:zh-cn 英文:en-us + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public showJobDetail(showJobDetailRequest?: ShowJobDetailRequest): Promise { + // @ts-ignore + return new Promise((resolve: (arg0: any) => any, reject: (arg0: any) => any) => { + const options = ParamCreater().showJobDetail(showJobDetailRequest); + + return this.hcClient.sendRequest(options).then( + (res: any) => { + return resolve(res); + }, + (err: any) => { + return reject(err); + }); + }); + } + /** + * 查询模板列表 + * @summary 查询模板列表 + * @param {'zh-cn' | 'en-us'} [xLanguage] 语言类型 中文:zh-cn 英文:en-us + * @param {string} [keyword] 搜索关键字,支持按名称和描述搜索,默认null + * @param {number} [offset] 偏移量, 表示从此偏移量开始查询, offset大于等于0 + * @param {number} [limit] 每页的模板条数, 默认10 + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public listPublishedTemplates(listPublishedTemplatesRequest?: ListPublishedTemplatesRequest): Promise { + // @ts-ignore + return new Promise((resolve: (arg0: any) => any, reject: (arg0: any) => any) => { + const options = ParamCreater().listPublishedTemplates(listPublishedTemplatesRequest); + + return this.hcClient.sendRequest(options).then( + (res: any) => { + return resolve(res); + }, + (err: any) => { + return reject(err); + }); + }); + } + /** + * 查询模板详情 + * @summary 查询模板详情 + * @param {string} templateId 模板id 说明:通过查询模板列表接口可获取相应模板 ID + * @param {'zh-cn' | 'en-us'} [xLanguage] 语言类型 中文:zh-cn 英文:en-us + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public showTemplateDetail(showTemplateDetailRequest?: ShowTemplateDetailRequest): Promise { + // @ts-ignore + return new Promise((resolve: (arg0: any) => any, reject: (arg0: any) => any) => { + const options = ParamCreater().showTemplateDetail(showTemplateDetailRequest); + + return this.hcClient.sendRequest(options).then( + (res: any) => { + return resolve(res); + }, + (err: any) => { + return reject(err); + }); + }); + } +} + +export const ParamCreater = function () { + return { + + /** + * 下载模板产物 + */ + downloadApplicationCode(downloadApplicationCodeRequest?: DownloadApplicationCodeRequest) { + const options = { + method: "GET", + url: "/v1/application-codes", + contentType: "", + queryParams: {}, + pathParams: {}, + headers: {}, + data: {} + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + let jobId; + let xLanguage; + + if (downloadApplicationCodeRequest !== null && downloadApplicationCodeRequest !== undefined) { + if (downloadApplicationCodeRequest instanceof DownloadApplicationCodeRequest) { + jobId = downloadApplicationCodeRequest.jobId; + xLanguage = downloadApplicationCodeRequest.xLanguage; + } else { + jobId = downloadApplicationCodeRequest['job_id']; + xLanguage = downloadApplicationCodeRequest['X-Language']; + } + } + + if (jobId === null || jobId === undefined) { + throw new RequiredError('jobId','Required parameter jobId was null or undefined when calling downloadApplicationCode.'); + } + if (jobId !== null && jobId !== undefined) { + localVarQueryParameter['job_id'] = jobId; + } + if (xLanguage !== undefined && xLanguage !== null) { + localVarHeaderParameter['X-Language'] = String(xLanguage); + } + + options.queryParams = localVarQueryParameter; + options.headers = localVarHeaderParameter; + return options; + }, + + /** + * 通过DevStar的模板进行应用代码创建 通过 DevStar 模板创建生成应用代码的任务,并将应用代码存储于指定的 CodeHub 仓库中,可以通过返回的任务 ID 查询相关任务状态 - 接口鉴权方式 通过华为云服务获取的用户token - 代码生成位置 应用代码生成后的地址,目前支持codehub地址和压缩包下载地址。 + */ + runDevstarTemplateJob(runDevstarTemplateJobRequest?: RunDevstarTemplateJobRequest) { + const options = { + method: "POST", + url: "/v1/jobs/template", + contentType: "application/json;charset=UTF-8", + queryParams: {}, + pathParams: {}, + headers: {}, + data: {} + }; + const localVarHeaderParameter = {} as any; + var body: any; + let xLanguage; + + if (runDevstarTemplateJobRequest !== null && runDevstarTemplateJobRequest !== undefined) { + if (runDevstarTemplateJobRequest instanceof RunDevstarTemplateJobRequest) { + body = runDevstarTemplateJobRequest.body !== undefined ? runDevstarTemplateJobRequest.body.getParseParam() : runDevstarTemplateJobRequest.body; + xLanguage = runDevstarTemplateJobRequest.xLanguage; + } else { + body = runDevstarTemplateJobRequest['body']; + xLanguage = runDevstarTemplateJobRequest['X-Language']; + } + } + + if (body === null || body === undefined) { + throw new RequiredError('body','Required parameter body was null or undefined when calling body.'); + } + if (xLanguage !== undefined && xLanguage !== null) { + localVarHeaderParameter['X-Language'] = String(xLanguage); + } + localVarHeaderParameter['Content-Type'] = 'application/json;charset=UTF-8'; + + options.data = body !== undefined ? body : {}; + options.headers = localVarHeaderParameter; + return options; + }, + + /** + * 查询任务的详情 通过任务ID可以查看任务的状态 当任务结束时返回应用代码存放的位置 - 接口鉴权方式 通过华为云服务获取的用户token - 代码生成位置 应用代码生成后的地址,目前支持codehub地址和压缩包下载地址 + */ + showJobDetail(showJobDetailRequest?: ShowJobDetailRequest) { + const options = { + method: "GET", + url: "/v1/jobs/{job_id}", + contentType: "", + queryParams: {}, + pathParams: {}, + headers: {}, + data: {} + }; + const localVarHeaderParameter = {} as any; + let jobId; + let xLanguage; + + if (showJobDetailRequest !== null && showJobDetailRequest !== undefined) { + if (showJobDetailRequest instanceof ShowJobDetailRequest) { + jobId = showJobDetailRequest.jobId; + xLanguage = showJobDetailRequest.xLanguage; + } else { + jobId = showJobDetailRequest['job_id']; + xLanguage = showJobDetailRequest['X-Language']; + } + } + + if (jobId === null || jobId === undefined) { + throw new RequiredError('jobId','Required parameter jobId was null or undefined when calling showJobDetail.'); + } + if (xLanguage !== undefined && xLanguage !== null) { + localVarHeaderParameter['X-Language'] = String(xLanguage); + } + + options.pathParams = { 'job_id': jobId, }; + options.headers = localVarHeaderParameter; + return options; + }, + + /** + * 查询模板列表 + */ + listPublishedTemplates(listPublishedTemplatesRequest?: ListPublishedTemplatesRequest) { + const options = { + method: "GET", + url: "/v1/templates", + contentType: "", + queryParams: {}, + pathParams: {}, + headers: {}, + data: {} + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + let xLanguage; + let keyword; + let offset; + let limit; + + if (listPublishedTemplatesRequest !== null && listPublishedTemplatesRequest !== undefined) { + if (listPublishedTemplatesRequest instanceof ListPublishedTemplatesRequest) { + xLanguage = listPublishedTemplatesRequest.xLanguage; + keyword = listPublishedTemplatesRequest.keyword; + offset = listPublishedTemplatesRequest.offset; + limit = listPublishedTemplatesRequest.limit; + } else { + xLanguage = listPublishedTemplatesRequest['X-Language']; + keyword = listPublishedTemplatesRequest['keyword']; + offset = listPublishedTemplatesRequest['offset']; + limit = listPublishedTemplatesRequest['limit']; + } + } + + if (keyword !== null && keyword !== undefined) { + localVarQueryParameter['keyword'] = keyword; + } + if (offset !== null && offset !== undefined) { + localVarQueryParameter['offset'] = offset; + } + if (limit !== null && limit !== undefined) { + localVarQueryParameter['limit'] = limit; + } + if (xLanguage !== undefined && xLanguage !== null) { + localVarHeaderParameter['X-Language'] = String(xLanguage); + } + + options.queryParams = localVarQueryParameter; + options.headers = localVarHeaderParameter; + return options; + }, + + /** + * 查询模板详情 + */ + showTemplateDetail(showTemplateDetailRequest?: ShowTemplateDetailRequest) { + const options = { + method: "GET", + url: "/v1/templates/{template_id}", + contentType: "", + queryParams: {}, + pathParams: {}, + headers: {}, + data: {} + }; + const localVarHeaderParameter = {} as any; + let templateId; + let xLanguage; + + if (showTemplateDetailRequest !== null && showTemplateDetailRequest !== undefined) { + if (showTemplateDetailRequest instanceof ShowTemplateDetailRequest) { + templateId = showTemplateDetailRequest.templateId; + xLanguage = showTemplateDetailRequest.xLanguage; + } else { + templateId = showTemplateDetailRequest['template_id']; + xLanguage = showTemplateDetailRequest['X-Language']; + } + } + + if (templateId === null || templateId === undefined) { + throw new RequiredError('templateId','Required parameter templateId was null or undefined when calling showTemplateDetail.'); + } + if (xLanguage !== undefined && xLanguage !== null) { + localVarHeaderParameter['X-Language'] = String(xLanguage); + } + + options.pathParams = { 'template_id': templateId, }; + options.headers = localVarHeaderParameter; + return options; + }, + + } +}; + +function newClient(client: HcClient): DevStarClient { + return new DevStarClient(client); +} + +/** + * + * @export + * @class RequiredError + * @extends {Error} + */ +export class RequiredError extends Error { + name: "RequiredError" = "RequiredError"; + constructor(public field: string, msg?: string) { + super(msg); + } +} \ No newline at end of file diff --git a/services/devstar/v1/model/DownloadApplicationCodeRequest.ts b/services/devstar/v1/model/DownloadApplicationCodeRequest.ts new file mode 100644 index 000000000..110d4f937 --- /dev/null +++ b/services/devstar/v1/model/DownloadApplicationCodeRequest.ts @@ -0,0 +1,26 @@ + + +export class DownloadApplicationCodeRequest { + public xLanguage?: DownloadApplicationCodeRequestXLanguageEnum; + public jobId: string; + public constructor(jobId: any) { + this.jobId = jobId; + } + public withXLanguage(xLanguage?: DownloadApplicationCodeRequestXLanguageEnum): DownloadApplicationCodeRequest { + this.xLanguage = xLanguage; + return this; + } + public withJobId(jobId: string): DownloadApplicationCodeRequest { + this.jobId = jobId; + return this; + } +} + +/** + * @export + * @enum {string} + */ +export enum DownloadApplicationCodeRequestXLanguageEnum { + ZH_CN = 'zh-cn', + EN_US = 'en-us' +} diff --git a/services/devstar/v1/model/DownloadApplicationCodeResponse.ts b/services/devstar/v1/model/DownloadApplicationCodeResponse.ts new file mode 100644 index 000000000..c02f73c3a --- /dev/null +++ b/services/devstar/v1/model/DownloadApplicationCodeResponse.ts @@ -0,0 +1,8 @@ + +import { SdkResponse } from "@huaweicloud/huaweicloud-sdk-core/SdkResponse"; + +export class DownloadApplicationCodeResponse extends SdkResponse { + public constructor() { + super(); + } +} \ No newline at end of file diff --git a/services/devstar/v1/model/ListPublishedTemplatesRequest.ts b/services/devstar/v1/model/ListPublishedTemplatesRequest.ts new file mode 100644 index 000000000..c2e690ca5 --- /dev/null +++ b/services/devstar/v1/model/ListPublishedTemplatesRequest.ts @@ -0,0 +1,35 @@ + + +export class ListPublishedTemplatesRequest { + public xLanguage?: ListPublishedTemplatesRequestXLanguageEnum; + public keyword?: string; + public offset?: number; + public limit?: number; + public constructor() { + } + public withXLanguage(xLanguage?: ListPublishedTemplatesRequestXLanguageEnum): ListPublishedTemplatesRequest { + this.xLanguage = xLanguage; + return this; + } + public withKeyword(keyword?: string): ListPublishedTemplatesRequest { + this.keyword = keyword; + return this; + } + public withOffset(offset?: number): ListPublishedTemplatesRequest { + this.offset = offset; + return this; + } + public withLimit(limit?: number): ListPublishedTemplatesRequest { + this.limit = limit; + return this; + } +} + +/** + * @export + * @enum {string} + */ +export enum ListPublishedTemplatesRequestXLanguageEnum { + ZH_CN = 'zh-cn', + EN_US = 'en-us' +} diff --git a/services/devstar/v1/model/ListPublishedTemplatesResponse.ts b/services/devstar/v1/model/ListPublishedTemplatesResponse.ts new file mode 100644 index 000000000..3cf907b4c --- /dev/null +++ b/services/devstar/v1/model/ListPublishedTemplatesResponse.ts @@ -0,0 +1,19 @@ +import { TemplateSimpleInfo } from './TemplateSimpleInfo'; + +import { SdkResponse } from "@huaweicloud/huaweicloud-sdk-core/SdkResponse"; + +export class ListPublishedTemplatesResponse extends SdkResponse { + public count?: number; + public templates?: Array; + public constructor() { + super(); + } + public withCount(count?: number): ListPublishedTemplatesResponse { + this.count = count; + return this; + } + public withTemplates(templates?: Array): ListPublishedTemplatesResponse { + this.templates = templates; + return this; + } +} \ No newline at end of file diff --git a/services/devstar/v1/model/PropertiesInfo.ts b/services/devstar/v1/model/PropertiesInfo.ts new file mode 100644 index 000000000..03e5a8a21 --- /dev/null +++ b/services/devstar/v1/model/PropertiesInfo.ts @@ -0,0 +1,146 @@ + + +export class PropertiesInfo { + public key?: string; + public defaultValue?: string; + public label?: string; + public type?: string; + public helpText?: string; + public readOnly?: boolean; + public required?: boolean; + public regType?: string; + public regPattern?: string; + public regTip?: string; + public isShow?: boolean; + public constructor() { + } + public withKey(key?: string): PropertiesInfo { + this.key = key; + return this; + } + public withDefaultValue(defaultValue?: string): PropertiesInfo { + this.defaultValue = defaultValue; + return this; + } + public withLabel(label?: string): PropertiesInfo { + this.label = label; + return this; + } + public withType(type?: string): PropertiesInfo { + this.type = type; + return this; + } + public withHelpText(helpText?: string): PropertiesInfo { + this.helpText = helpText; + return this; + } + public withReadOnly(readOnly?: boolean): PropertiesInfo { + this.readOnly = readOnly; + return this; + } + public withRequired(required?: boolean): PropertiesInfo { + this.required = required; + return this; + } + public withRegType(regType?: string): PropertiesInfo { + this.regType = regType; + return this; + } + public withRegPattern(regPattern?: string): PropertiesInfo { + this.regPattern = regPattern; + return this; + } + public withRegTip(regTip?: string): PropertiesInfo { + this.regTip = regTip; + return this; + } + public withIsShow(isShow?: boolean): PropertiesInfo { + this.isShow = isShow; + return this; + } + public getParseParam() { + const parseParam = new PropertiesInfoParse(); + if(this.key !== null && this.key !== undefined) { + parseParam.setKey(this.key); + } + if(this.defaultValue !== null && this.defaultValue !== undefined) { + parseParam.setDefaultValue(this.defaultValue); + } + if(this.label !== null && this.label !== undefined) { + parseParam.setLabel(this.label); + } + if(this.type !== null && this.type !== undefined) { + parseParam.setType(this.type); + } + if(this.helpText !== null && this.helpText !== undefined) { + parseParam.setHelpText(this.helpText); + } + if(this.readOnly !== null && this.readOnly !== undefined) { + parseParam.setReadOnly(this.readOnly); + } + if(this.required !== null && this.required !== undefined) { + parseParam.setRequired(this.required); + } + if(this.regType !== null && this.regType !== undefined) { + parseParam.setRegType(this.regType); + } + if(this.regPattern !== null && this.regPattern !== undefined) { + parseParam.setRegPattern(this.regPattern); + } + if(this.regTip !== null && this.regTip !== undefined) { + parseParam.setRegTip(this.regTip); + } + if(this.isShow !== null && this.isShow !== undefined) { + parseParam.setIsShow(this.isShow); + } + return parseParam; + } +} + +export class PropertiesInfoParse { + private 'key'?: any; + private 'defaultValue'?: any; + private 'label'?: any; + private 'type'?: any; + private 'helpText'?: any; + private 'readOnly'?: any; + private 'required'?: any; + private 'regType'?: any; + private 'regPattern'?: any; + private 'regTip'?: any; + private 'isShow'?: any; + + public setKey(key?: any) { + this['key'] = key; + } + public setDefaultValue(defaultValue?: any) { + this['defaultValue'] = defaultValue; + } + public setLabel(label?: any) { + this['label'] = label; + } + public setType(type?: any) { + this['type'] = type; + } + public setHelpText(helpText?: any) { + this['helpText'] = helpText; + } + public setReadOnly(readOnly?: any) { + this['readOnly'] = readOnly; + } + public setRequired(required?: any) { + this['required'] = required; + } + public setRegType(regType?: any) { + this['regType'] = regType; + } + public setRegPattern(regPattern?: any) { + this['regPattern'] = regPattern; + } + public setRegTip(regTip?: any) { + this['regTip'] = regTip; + } + public setIsShow(isShow?: any) { + this['isShow'] = isShow; + } +} \ No newline at end of file diff --git a/services/devstar/v1/model/RepositoryInfo.ts b/services/devstar/v1/model/RepositoryInfo.ts new file mode 100644 index 000000000..06745d17f --- /dev/null +++ b/services/devstar/v1/model/RepositoryInfo.ts @@ -0,0 +1,50 @@ + + +export class RepositoryInfo { + public name?: string; + public projectId?: string; + public regionId?: string; + public constructor() { + } + public withName(name?: string): RepositoryInfo { + this.name = name; + return this; + } + public withProjectId(projectId?: string): RepositoryInfo { + this.projectId = projectId; + return this; + } + public withRegionId(regionId?: string): RepositoryInfo { + this.regionId = regionId; + return this; + } + public getParseParam() { + const parseParam = new RepositoryInfoParse(); + if(this.name !== null && this.name !== undefined) { + parseParam.setName(this.name); + } + if(this.projectId !== null && this.projectId !== undefined) { + parseParam.setProjectId(this.projectId); + } + if(this.regionId !== null && this.regionId !== undefined) { + parseParam.setRegionId(this.regionId); + } + return parseParam; + } +} + +export class RepositoryInfoParse { + private 'name'?: any; + private 'project_id'?: any; + private 'region_id'?: any; + + public setName(name?: any) { + this['name'] = name; + } + public setProjectId(projectId?: any) { + this['project_id'] = projectId; + } + public setRegionId(regionId?: any) { + this['region_id'] = regionId; + } +} \ No newline at end of file diff --git a/services/devstar/v1/model/RunDevstarTemplateJobRequest.ts b/services/devstar/v1/model/RunDevstarTemplateJobRequest.ts new file mode 100644 index 000000000..d488440f9 --- /dev/null +++ b/services/devstar/v1/model/RunDevstarTemplateJobRequest.ts @@ -0,0 +1,29 @@ +import { TemplateJobInfo } from './TemplateJobInfo'; + + +export class RunDevstarTemplateJobRequest { + public xLanguage?: RunDevstarTemplateJobRequestXLanguageEnum; + public body?: TemplateJobInfo; + public constructor() { + } + public withXLanguage(xLanguage?: RunDevstarTemplateJobRequestXLanguageEnum): RunDevstarTemplateJobRequest { + this.xLanguage = xLanguage; + return this; + } + public withBody(body?: TemplateJobInfo): RunDevstarTemplateJobRequest { + this.body = body; + return this; + } + public getParseParam() { + return this.body; + } +} + +/** + * @export + * @enum {string} + */ +export enum RunDevstarTemplateJobRequestXLanguageEnum { + ZH_CN = 'zh-cn', + EN_US = 'en-us' +} diff --git a/services/devstar/v1/model/RunDevstarTemplateJobResponse.ts b/services/devstar/v1/model/RunDevstarTemplateJobResponse.ts new file mode 100644 index 000000000..c1863c9d9 --- /dev/null +++ b/services/devstar/v1/model/RunDevstarTemplateJobResponse.ts @@ -0,0 +1,13 @@ + +import { SdkResponse } from "@huaweicloud/huaweicloud-sdk-core/SdkResponse"; + +export class RunDevstarTemplateJobResponse extends SdkResponse { + public jobId?: string; + public constructor() { + super(); + } + public withJobId(jobId?: string): RunDevstarTemplateJobResponse { + this.jobId = jobId; + return this; + } +} \ No newline at end of file diff --git a/services/devstar/v1/model/ShowJobDetailRequest.ts b/services/devstar/v1/model/ShowJobDetailRequest.ts new file mode 100644 index 000000000..ba1971145 --- /dev/null +++ b/services/devstar/v1/model/ShowJobDetailRequest.ts @@ -0,0 +1,26 @@ + + +export class ShowJobDetailRequest { + public xLanguage?: ShowJobDetailRequestXLanguageEnum; + public jobId: string; + public constructor(jobId: any) { + this.jobId = jobId; + } + public withXLanguage(xLanguage?: ShowJobDetailRequestXLanguageEnum): ShowJobDetailRequest { + this.xLanguage = xLanguage; + return this; + } + public withJobId(jobId: string): ShowJobDetailRequest { + this.jobId = jobId; + return this; + } +} + +/** + * @export + * @enum {string} + */ +export enum ShowJobDetailRequestXLanguageEnum { + ZH_CN = 'zh-cn', + EN_US = 'en-us' +} diff --git a/services/devstar/v1/model/ShowJobDetailResponse.ts b/services/devstar/v1/model/ShowJobDetailResponse.ts new file mode 100644 index 000000000..b156f273f --- /dev/null +++ b/services/devstar/v1/model/ShowJobDetailResponse.ts @@ -0,0 +1,28 @@ + +import { SdkResponse } from "@huaweicloud/huaweicloud-sdk-core/SdkResponse"; + +export class ShowJobDetailResponse extends SdkResponse { + public id?: string; + public name?: string; + public jobStatus?: object; + public jobResult?: string; + public constructor() { + super(); + } + public withId(id?: string): ShowJobDetailResponse { + this.id = id; + return this; + } + public withName(name?: string): ShowJobDetailResponse { + this.name = name; + return this; + } + public withJobStatus(jobStatus?: object): ShowJobDetailResponse { + this.jobStatus = jobStatus; + return this; + } + public withJobResult(jobResult?: string): ShowJobDetailResponse { + this.jobResult = jobResult; + return this; + } +} \ No newline at end of file diff --git a/services/devstar/v1/model/ShowTemplateDetailRequest.ts b/services/devstar/v1/model/ShowTemplateDetailRequest.ts new file mode 100644 index 000000000..b7fbdd110 --- /dev/null +++ b/services/devstar/v1/model/ShowTemplateDetailRequest.ts @@ -0,0 +1,26 @@ + + +export class ShowTemplateDetailRequest { + public xLanguage?: ShowTemplateDetailRequestXLanguageEnum; + public templateId: string; + public constructor(templateId: any) { + this.templateId = templateId; + } + public withXLanguage(xLanguage?: ShowTemplateDetailRequestXLanguageEnum): ShowTemplateDetailRequest { + this.xLanguage = xLanguage; + return this; + } + public withTemplateId(templateId: string): ShowTemplateDetailRequest { + this.templateId = templateId; + return this; + } +} + +/** + * @export + * @enum {string} + */ +export enum ShowTemplateDetailRequestXLanguageEnum { + ZH_CN = 'zh-cn', + EN_US = 'en-us' +} diff --git a/services/devstar/v1/model/ShowTemplateDetailResponse.ts b/services/devstar/v1/model/ShowTemplateDetailResponse.ts new file mode 100644 index 000000000..d452f8b34 --- /dev/null +++ b/services/devstar/v1/model/ShowTemplateDetailResponse.ts @@ -0,0 +1,59 @@ +import { PropertiesInfo } from './PropertiesInfo'; + +import { SdkResponse } from "@huaweicloud/huaweicloud-sdk-core/SdkResponse"; + +export class ShowTemplateDetailResponse extends SdkResponse { + public id?: string; + public title?: string; + public description?: string; + public regionId?: string; + public repostoryId?: string; + public codeUrl?: string; + public sshUrl?: string; + public projectUuid?: string; + public status?: number; + public properties?: Array; + public constructor() { + super(); + } + public withId(id?: string): ShowTemplateDetailResponse { + this.id = id; + return this; + } + public withTitle(title?: string): ShowTemplateDetailResponse { + this.title = title; + return this; + } + public withDescription(description?: string): ShowTemplateDetailResponse { + this.description = description; + return this; + } + public withRegionId(regionId?: string): ShowTemplateDetailResponse { + this.regionId = regionId; + return this; + } + public withRepostoryId(repostoryId?: string): ShowTemplateDetailResponse { + this.repostoryId = repostoryId; + return this; + } + public withCodeUrl(codeUrl?: string): ShowTemplateDetailResponse { + this.codeUrl = codeUrl; + return this; + } + public withSshUrl(sshUrl?: string): ShowTemplateDetailResponse { + this.sshUrl = sshUrl; + return this; + } + public withProjectUuid(projectUuid?: string): ShowTemplateDetailResponse { + this.projectUuid = projectUuid; + return this; + } + public withStatus(status?: number): ShowTemplateDetailResponse { + this.status = status; + return this; + } + public withProperties(properties?: Array): ShowTemplateDetailResponse { + this.properties = properties; + return this; + } +} \ No newline at end of file diff --git a/services/devstar/v1/model/TemplateJobInfo.ts b/services/devstar/v1/model/TemplateJobInfo.ts new file mode 100644 index 000000000..a728dc0ca --- /dev/null +++ b/services/devstar/v1/model/TemplateJobInfo.ts @@ -0,0 +1,76 @@ +import { RepositoryInfo } from './RepositoryInfo'; + + +export class TemplateJobInfo { + public applicationName?: string; + public templateId: string; + public repoType?: number; + public properties?: object; + public repoInfo?: RepositoryInfo; + public constructor(templateId: any) { + this.templateId = templateId; + } + public withApplicationName(applicationName?: string): TemplateJobInfo { + this.applicationName = applicationName; + return this; + } + public withTemplateId(templateId: string): TemplateJobInfo { + this.templateId = templateId; + return this; + } + public withRepoType(repoType?: number): TemplateJobInfo { + this.repoType = repoType; + return this; + } + public withProperties(properties?: object): TemplateJobInfo { + this.properties = properties; + return this; + } + public withRepoInfo(repoInfo?: RepositoryInfo): TemplateJobInfo { + this.repoInfo = repoInfo; + return this; + } + public getParseParam() { + const parseParam = new TemplateJobInfoParse(); + if(this.applicationName !== null && this.applicationName !== undefined) { + parseParam.setApplicationName(this.applicationName); + } + if(this.templateId !== null && this.templateId !== undefined) { + parseParam.setTemplateId(this.templateId); + } + if(this.repoType !== null && this.repoType !== undefined) { + parseParam.setRepoType(this.repoType); + } + if(this.properties !== null && this.properties !== undefined) { + parseParam.setProperties(this.properties); + } + if(this.repoInfo !== null && this.repoInfo !== undefined) { + parseParam.setRepoInfo(this.repoInfo.getParseParam()); + } + return parseParam; + } +} + +export class TemplateJobInfoParse { + private 'application_name'?: any; + private 'template_id'?: any; + private 'repo_type'?: any; + private 'properties'?: any; + private 'repo_info'?: any; + + public setApplicationName(applicationName?: any) { + this['application_name'] = applicationName; + } + public setTemplateId(templateId?: any) { + this['template_id'] = templateId; + } + public setRepoType(repoType?: any) { + this['repo_type'] = repoType; + } + public setProperties(properties?: any) { + this['properties'] = properties; + } + public setRepoInfo(repoInfo?: any) { + this['repo_info'] = repoInfo; + } +} \ No newline at end of file diff --git a/services/devstar/v1/model/TemplateSimpleInfo.ts b/services/devstar/v1/model/TemplateSimpleInfo.ts new file mode 100644 index 000000000..83b07cf5c --- /dev/null +++ b/services/devstar/v1/model/TemplateSimpleInfo.ts @@ -0,0 +1,50 @@ + + +export class TemplateSimpleInfo { + public id?: string; + public title?: string; + public description?: string; + public constructor() { + } + public withId(id?: string): TemplateSimpleInfo { + this.id = id; + return this; + } + public withTitle(title?: string): TemplateSimpleInfo { + this.title = title; + return this; + } + public withDescription(description?: string): TemplateSimpleInfo { + this.description = description; + return this; + } + public getParseParam() { + const parseParam = new TemplateSimpleInfoParse(); + if(this.id !== null && this.id !== undefined) { + parseParam.setId(this.id); + } + if(this.title !== null && this.title !== undefined) { + parseParam.setTitle(this.title); + } + if(this.description !== null && this.description !== undefined) { + parseParam.setDescription(this.description); + } + return parseParam; + } +} + +export class TemplateSimpleInfoParse { + private 'id'?: any; + private 'title'?: any; + private 'description'?: any; + + public setId(id?: any) { + this['id'] = id; + } + public setTitle(title?: any) { + this['title'] = title; + } + public setDescription(description?: any) { + this['description'] = description; + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..4b2cfc92d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + "declaration": true, /* Generates corresponding '.d.ts' file. */ + "outDir": "./build", /* Redirect output structure to the directory. */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + "suppressImplicitAnyIndexErrors":true, + + /* Module Resolution Options */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + }, + "exclude": ["example/index.ts"] +} diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 000000000..abb9bb41e --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,46 @@ +const path = require('path'); +const CleanWebpackPlugin = require('clean-webpack-plugin'); + +// the path(s) that should be cleaned +let pathsToClean = ['dist']; + +// the clean options to use +let cleanOptions = { + root: path.resolve(__dirname), + // exclude: ['shared.js'], + verbose: true, + dry: false, +}; + +module.exports = { + resolve: { + extensions: ['.js', '.ts', '.json'], + }, + entry: './core/httpClient/index.ts', + target:'async-node', + devtool: 'source-map',// 打包出的js文件是否生成map文件(方便浏览器调试) + mode: 'production', + output: { + filename: 'main.js', + path: path.resolve(__dirname, 'dist'), + libraryTarget: 'commonjs' + }, + module: { + rules: [ + { + test: /\.ts?$/, + use: [ + { + loader: 'ts-loader', + options: { + // 指定特定的ts编译配置,为了区分脚本的ts配置 + configFile: path.resolve(__dirname, './tsconfig.json'), + }, + }, + ], + exclude: /node_modules/, + }, + ], + }, + plugins: [new CleanWebpackPlugin(pathsToClean, cleanOptions)] +}; \ No newline at end of file