1 | import { sdkVersion } from "./sdk_version"
|
2 | import { ClientProfile, Credential, ClientConfig } from "./interface"
|
3 | import Sign from "./sign"
|
4 | import { HttpConnection } from "./http/http_connection"
|
5 | import TencentCloudSDKHttpException from "./exception/tencent_cloud_sdk_exception"
|
6 | import { Response } from "node-fetch"
|
7 |
|
8 | type ResponseCallback = (error: string, rep: any) => void
|
9 | interface RequestOptions {
|
10 | multipart: boolean
|
11 | }
|
12 | interface RequestData {
|
13 | Action: string
|
14 | RequestClient: string
|
15 | Nonce: number
|
16 | Timestamp: number
|
17 | Version: string
|
18 | Signature: string
|
19 | SecretId?: string
|
20 | region?: string
|
21 | Token?: string
|
22 | SinatureMethod?: string
|
23 | [key: string]: any
|
24 | }
|
25 | type ResponseData = any
|
26 |
|
27 |
|
28 |
|
29 |
|
30 | export class AbstractClient {
|
31 | sdkVersion: string
|
32 | path: string
|
33 | credential: Credential
|
34 | region: string
|
35 | apiVersion: string
|
36 | endpoint: string
|
37 | profile: ClientProfile
|
38 | |
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 | constructor(endpoint: string, version: string, { credential, region, profile }: ClientConfig) {
|
47 | this.path = "/"
|
48 |
|
49 | |
50 |
|
51 |
|
52 | this.credential = Object.assign(
|
53 | {
|
54 | secretId: null,
|
55 | secretKey: null,
|
56 | token: null,
|
57 | },
|
58 | credential
|
59 | )
|
60 |
|
61 | |
62 |
|
63 |
|
64 | this.region = region || null
|
65 | this.sdkVersion = "SDK_NODEJS_" + sdkVersion
|
66 | this.apiVersion = version
|
67 | this.endpoint = (profile && profile.httpProfile && profile.httpProfile.endpoint) || endpoint
|
68 |
|
69 | |
70 |
|
71 |
|
72 |
|
73 | this.profile = {
|
74 | signMethod: (profile && profile.signMethod) || "TC3-HMAC-SHA256",
|
75 | httpProfile: Object.assign(
|
76 | {
|
77 | reqMethod: "POST",
|
78 | endpoint: null,
|
79 | protocol: "https://",
|
80 | reqTimeout: 60,
|
81 | },
|
82 | profile && profile.httpProfile
|
83 | ),
|
84 | }
|
85 | }
|
86 |
|
87 | |
88 |
|
89 |
|
90 | async request(
|
91 | action: string,
|
92 | req: any,
|
93 | options?: ResponseCallback | RequestOptions,
|
94 | cb?: ResponseCallback
|
95 | ): Promise<ResponseData> {
|
96 | if (typeof options === "function") {
|
97 | cb = options
|
98 | options = {} as RequestOptions
|
99 | }
|
100 | try {
|
101 | const result = await this.doRequest(action, req, options as RequestOptions)
|
102 | cb && cb(null, result)
|
103 | return result
|
104 | } catch (e) {
|
105 | cb && cb(e, null)
|
106 | throw e
|
107 | }
|
108 | }
|
109 |
|
110 | |
111 |
|
112 |
|
113 | private async doRequest(
|
114 | action: string,
|
115 | req: any,
|
116 | options?: RequestOptions
|
117 | ): Promise<ResponseData> {
|
118 | if (this.profile.signMethod === "TC3-HMAC-SHA256") {
|
119 | return this.doRequestWithSign3(action, req, options)
|
120 | }
|
121 | let params = this.mergeData(req)
|
122 | params = this.formatRequestData(action, params)
|
123 | let res
|
124 | try {
|
125 | res = await HttpConnection.doRequest({
|
126 | method: this.profile.httpProfile.reqMethod,
|
127 | url: this.profile.httpProfile.protocol + this.endpoint + this.path,
|
128 | data: params,
|
129 | timeout: this.profile.httpProfile.reqTimeout * 1000,
|
130 | })
|
131 | } catch (error) {
|
132 | throw new TencentCloudSDKHttpException(error.message)
|
133 | }
|
134 | return this.parseResponse(res)
|
135 | }
|
136 |
|
137 | |
138 |
|
139 |
|
140 | private async doRequestWithSign3(
|
141 | action: string,
|
142 | params: any,
|
143 | options?: RequestOptions
|
144 | ): Promise<ResponseData> {
|
145 | let res
|
146 | try {
|
147 | res = await HttpConnection.doRequestWithSign3({
|
148 | method: this.profile.httpProfile.reqMethod,
|
149 | url: this.profile.httpProfile.protocol + this.endpoint + this.path,
|
150 | secretId: this.credential.secretId,
|
151 | secretKey: this.credential.secretKey,
|
152 | region: this.region,
|
153 | data: params || "",
|
154 | service: this.endpoint.split(".")[0],
|
155 | action: action,
|
156 | version: this.apiVersion,
|
157 | multipart: options && options.multipart,
|
158 | timeout: this.profile.httpProfile.reqTimeout * 1000,
|
159 | token: this.credential.token,
|
160 | requestClient: this.sdkVersion,
|
161 | })
|
162 | } catch (e) {
|
163 | throw new TencentCloudSDKHttpException(e.message)
|
164 | }
|
165 | return this.parseResponse(res)
|
166 | }
|
167 |
|
168 | private async parseResponse(res: Response): Promise<ResponseData> {
|
169 | if (res.status !== 200) {
|
170 | const tcError = new TencentCloudSDKHttpException(res.statusText)
|
171 | tcError.httpCode = res.status
|
172 | throw tcError
|
173 | } else {
|
174 | const data = await res.json()
|
175 | if (data.Response.Error) {
|
176 | const tcError = new TencentCloudSDKHttpException(
|
177 | data.Response.Error.Message,
|
178 | data.Response.RequestId
|
179 | )
|
180 | tcError.code = data.Response.Error.Code
|
181 | throw tcError
|
182 | } else {
|
183 | return data.Response
|
184 | }
|
185 | }
|
186 | }
|
187 |
|
188 | |
189 |
|
190 |
|
191 | private mergeData(data: any, prefix = "") {
|
192 | const ret: any = {}
|
193 | for (const k in data) {
|
194 | if (data[k] === null) {
|
195 | continue
|
196 | }
|
197 | if (data[k] instanceof Array || data[k] instanceof Object) {
|
198 | Object.assign(ret, this.mergeData(data[k], prefix + k + "."))
|
199 | } else {
|
200 | ret[prefix + k] = data[k]
|
201 | }
|
202 | }
|
203 | return ret
|
204 | }
|
205 |
|
206 | |
207 |
|
208 |
|
209 | private formatRequestData(action: string, params: RequestData): RequestData {
|
210 | params.Action = action
|
211 | params.RequestClient = this.sdkVersion
|
212 | params.Nonce = Math.round(Math.random() * 65535)
|
213 | params.Timestamp = Math.round(Date.now() / 1000)
|
214 | params.Version = this.apiVersion
|
215 |
|
216 | if (this.credential.secretId) {
|
217 | params.SecretId = this.credential.secretId
|
218 | }
|
219 |
|
220 | if (this.region) {
|
221 | params.Region = this.region
|
222 | }
|
223 |
|
224 | if (this.credential.token) {
|
225 | params.Token = this.credential.token
|
226 | }
|
227 |
|
228 | if (this.profile.signMethod) {
|
229 | params.SignatureMethod = this.profile.signMethod
|
230 | }
|
231 | const signStr = this.formatSignString(params)
|
232 |
|
233 | params.Signature = Sign.sign(this.credential.secretKey, signStr, this.profile.signMethod)
|
234 | return params
|
235 | }
|
236 |
|
237 | |
238 |
|
239 |
|
240 | private formatSignString(params: RequestData): string {
|
241 | let strParam = ""
|
242 | const keys = Object.keys(params)
|
243 | keys.sort()
|
244 | for (const k in keys) {
|
245 |
|
246 | strParam += "&" + keys[k] + "=" + params[keys[k]]
|
247 | }
|
248 | const strSign =
|
249 | this.profile.httpProfile.reqMethod.toLocaleUpperCase() +
|
250 | this.endpoint +
|
251 | this.path +
|
252 | "?" +
|
253 | strParam.slice(1)
|
254 | return strSign
|
255 | }
|
256 | }
|