· 4 years ago · Nov 22, 2020, 02:50 PM
1/*
2 * Copyright 2017-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with
5 * the License. A copy of the License is located at
6 *
7 * http://aws.amazon.com/apache2.0/
8 *
9 * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
10 * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
11 * and limitations under the License.
12 */
13var __assign = (this && this.__assign) || function () {
14 __assign = Object.assign || function(t) {
15 for (var s, i = 1, n = arguments.length; i < n; i++) {
16 s = arguments[i];
17 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
18 t[p] = s[p];
19 }
20 return t;
21 };
22 return __assign.apply(this, arguments);
23};
24var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
25 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
26 return new (P || (P = Promise))(function (resolve, reject) {
27 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
28 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
29 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
30 step((generator = generator.apply(thisArg, _arguments || [])).next());
31 });
32};
33var __generator = (this && this.__generator) || function (thisArg, body) {
34 var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
35 return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
36 function verb(n) { return function (v) { return step([n, v]); }; }
37 function step(op) {
38 if (f) throw new TypeError("Generator is already executing.");
39 while (_) try {
40 if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
41 if (y = 0, t) op = [op[0] & 2, t.value];
42 switch (op[0]) {
43 case 0: case 1: t = op; break;
44 case 4: _.label++; return { value: op[1], done: false };
45 case 5: _.label++; y = op[1]; op = [0]; continue;
46 case 7: op = _.ops.pop(); _.trys.pop(); continue;
47 default:
48 if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
49 if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
50 if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
51 if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
52 if (t[2]) _.ops.pop();
53 _.trys.pop(); continue;
54 }
55 op = body.call(thisArg, _);
56 } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
57 if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
58 }
59};
60var __rest = (this && this.__rest) || function (s, e) {
61 var t = {};
62 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
63 t[p] = s[p];
64 if (s != null && typeof Object.getOwnPropertySymbols === "function")
65 for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
66 if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
67 t[p[i]] = s[p[i]];
68 }
69 return t;
70};
71import { ConsoleLogger as Logger, Credentials, DateUtils, Signer, Platform, } from '@aws-amplify/core';
72import axios from 'axios';
73import { parse, format } from 'url';
74var logger = new Logger('RestClient');
75/**
76* HTTP Client for REST requests. Send and receive JSON data.
77* Sign request with AWS credentials if available
78* Usage:
79<pre>
80const restClient = new RestClient();
81restClient.get('...')
82 .then(function(data) {
83 console.log(data);
84 })
85 .catch(err => console.log(err));
86</pre>
87*/
88var RestClient = /** @class */ (function () {
89 /**
90 * @param {RestClientOptions} [options] - Instance options
91 */
92 function RestClient(options) {
93 this._region = 'us-east-1'; // this will be updated by endpoint function
94 this._service = 'execute-api'; // this can be updated by endpoint function
95 this._custom_header = undefined; // this can be updated by endpoint function
96 /**
97 * This weak map provides functionality to let clients cancel
98 * in-flight axios requests. https://github.com/axios/axios#cancellation
99 *
100 * 1. For every axios request, a unique cancel token is generated and added in the request.
101 * 2. Promise for fulfilling the request is then mapped to that unique cancel token.
102 * 3. The promise is returned to the client.
103 * 4. Clients can either wait for the promise to fulfill or call `API.cancel(promise)` to cancel the request.
104 * 5. If `API.cancel(promise)` is called, then the corresponding cancel token is retrieved from the map below.
105 * 6. Promise returned to the client will be in rejected state with the error provided during cancel.
106 * 7. Clients can check if the error is because of cancelling by calling `API.isCancel(error)`.
107 *
108 * For more details, see https://github.com/aws-amplify/amplify-js/pull/3769#issuecomment-552660025
109 */
110 this._cancelTokenMap = null;
111 this.Credentials = Credentials;
112 this._options = options;
113 logger.debug('API Options', this._options);
114 if (this._cancelTokenMap == null) {
115 this._cancelTokenMap = new WeakMap();
116 }
117 }
118 /**
119 * Update AWS credentials
120 * @param {AWSCredentials} credentials - AWS credentials
121 *
122 updateCredentials(credentials: AWSCredentials) {
123 this.options.credentials = credentials;
124 }
125*/
126 /**
127 * Basic HTTP request. Customizable
128 * @param {string | ApiInfo } urlOrApiInfo - Full request URL or Api information
129 * @param {string} method - Request HTTP method
130 * @param {json} [init] - Request extra params
131 * @return {Promise} - A promise that resolves to an object with response status and JSON data, if successful.
132 */
133 RestClient.prototype.ajax = function (urlOrApiInfo, method, init) {
134 return __awaiter(this, void 0, void 0, function () {
135 var parsed_url, url, region, service, custom_header, params, libraryHeaders, userAgent, initParams, isAllResponse, custom_header_obj, _a, _b, search, parsedUrl;
136 var _this = this;
137 return __generator(this, function (_c) {
138 switch (_c.label) {
139 case 0:
140 logger.debug(method, urlOrApiInfo);
141 region = 'us-east-1';
142 service = 'execute-api';
143 custom_header = undefined;
144 if (typeof urlOrApiInfo === 'string') {
145 parsed_url = this._parseUrl(urlOrApiInfo);
146 url = urlOrApiInfo;
147 }
148 else {
149 (url = urlOrApiInfo.endpoint, custom_header = urlOrApiInfo.custom_header, region = urlOrApiInfo.region, service = urlOrApiInfo.service);
150 parsed_url = this._parseUrl(urlOrApiInfo.endpoint);
151 }
152 params = {
153 method: method,
154 url: url,
155 host: parsed_url.host,
156 path: parsed_url.path,
157 headers: {},
158 data: null,
159 responseType: 'json',
160 timeout: 0,
161 cancelToken: null,
162 };
163 libraryHeaders = {};
164 if (Platform.isReactNative) {
165 userAgent = Platform.userAgent || 'aws-amplify/0.1.x';
166 libraryHeaders = {
167 'User-Agent': userAgent,
168 };
169 }
170 initParams = Object.assign({}, init);
171 isAllResponse = initParams.response;
172 if (initParams.body) {
173 if (typeof FormData === 'function' &&
174 initParams.body instanceof FormData) {
175 libraryHeaders['Content-Type'] = 'multipart/form-data';
176 params.data = initParams.body;
177 }
178 else {
179 libraryHeaders['Content-Type'] = 'application/json; charset=UTF-8';
180 params.data = JSON.stringify(initParams.body);
181 }
182 }
183 if (initParams.responseType) {
184 params.responseType = initParams.responseType;
185 }
186 if (initParams.withCredentials) {
187 params['withCredentials'] = initParams.withCredentials;
188 }
189 if (initParams.timeout) {
190 params.timeout = initParams.timeout;
191 }
192 if (initParams.cancellableToken) {
193 params.cancelToken = initParams.cancellableToken.token;
194 }
195 params['signerServiceInfo'] = initParams.signerServiceInfo;
196 if (!(typeof custom_header === 'function')) return [3 /*break*/, 2];
197 return [4 /*yield*/, custom_header()];
198 case 1:
199 _a = _c.sent();
200 return [3 /*break*/, 3];
201 case 2:
202 _a = undefined;
203 _c.label = 3;
204 case 3:
205 custom_header_obj = _a;
206 params.headers = __assign(__assign(__assign({}, libraryHeaders), custom_header_obj), initParams.headers);
207 _b = parse(url, true, true), search = _b.search, parsedUrl = __rest(_b, ["search"]);
208 params.url = format(__assign(__assign({}, parsedUrl), { query: __assign(__assign({}, parsedUrl.query), (initParams.queryStringParameters || {})) }));
209 // Do not sign the request if client has added 'Authorization' header,
210 // which means custom authorizer.
211 if (typeof params.headers['Authorization'] !== 'undefined') {
212 params.headers = Object.keys(params.headers).reduce(function (acc, k) {
213 if (params.headers[k]) {
214 acc[k] = params.headers[k];
215 }
216 return acc;
217 // tslint:disable-next-line:align
218 }, {});
219 return [2 /*return*/, this._request(params, isAllResponse)];
220 }
221 // Signing the request in case there credentials are available
222 return [2 /*return*/, this.Credentials.get().then(function (credentials) {
223 return _this._signed(__assign({}, params), credentials, isAllResponse, {
224 region: region,
225 service: service,
226 }).catch(function (error) {
227 if (DateUtils.isClockSkewError(error)) {
228 var headers = error.response.headers;
229 var dateHeader = headers && (headers.date || headers.Date);
230 var responseDate = new Date(dateHeader);
231 var requestDate = DateUtils.getDateFromHeaderString(params.headers['x-amz-date']);
232 if (DateUtils.isClockSkewed(requestDate, responseDate)) {
233 DateUtils.setClockOffset(responseDate.getTime() - requestDate.getTime());
234 return _this.ajax(urlOrApiInfo, method, init);
235 }
236 }
237 throw error;
238 });
239 }, function (err) {
240 logger.debug('No credentials available, the request will be unsigned');
241 return _this._request(params, isAllResponse);
242 })];
243 }
244 });
245 });
246 };
247 /**
248 * GET HTTP request
249 * @param {string | ApiInfo } urlOrApiInfo - Full request URL or Api information
250 * @param {JSON} init - Request extra params
251 * @return {Promise} - A promise that resolves to an object with response status and JSON data, if successful.
252 */
253 RestClient.prototype.get = function (urlOrApiInfo, init) {
254 return this.ajax(urlOrApiInfo, 'GET', init);
255 };
256 /**
257 * PUT HTTP request
258 * @param {string | ApiInfo } urlOrApiInfo - Full request URL or Api information
259 * @param {json} init - Request extra params
260 * @return {Promise} - A promise that resolves to an object with response status and JSON data, if successful.
261 */
262 RestClient.prototype.put = function (urlOrApiInfo, init) {
263 return this.ajax(urlOrApiInfo, 'PUT', init);
264 };
265 /**
266 * PATCH HTTP request
267 * @param {string | ApiInfo } urlOrApiInfo - Full request URL or Api information
268 * @param {json} init - Request extra params
269 * @return {Promise} - A promise that resolves to an object with response status and JSON data, if successful.
270 */
271 RestClient.prototype.patch = function (urlOrApiInfo, init) {
272 return this.ajax(urlOrApiInfo, 'PATCH', init);
273 };
274 /**
275 * POST HTTP request
276 * @param {string | ApiInfo } urlOrApiInfo - Full request URL or Api information
277 * @param {json} init - Request extra params
278 * @return {Promise} - A promise that resolves to an object with response status and JSON data, if successful.
279 */
280 RestClient.prototype.post = function (urlOrApiInfo, init) {
281 return this.ajax(urlOrApiInfo, 'POST', init);
282 };
283 /**
284 * DELETE HTTP request
285 * @param {string | ApiInfo } urlOrApiInfo - Full request URL or Api information
286 * @param {json} init - Request extra params
287 * @return {Promise} - A promise that resolves to an object with response status and JSON data, if successful.
288 */
289 RestClient.prototype.del = function (urlOrApiInfo, init) {
290 return this.ajax(urlOrApiInfo, 'DELETE', init);
291 };
292 /**
293 * HEAD HTTP request
294 * @param {string | ApiInfo } urlOrApiInfo - Full request URL or Api information
295 * @param {json} init - Request extra params
296 * @return {Promise} - A promise that resolves to an object with response status and JSON data, if successful.
297 */
298 RestClient.prototype.head = function (urlOrApiInfo, init) {
299 return this.ajax(urlOrApiInfo, 'HEAD', init);
300 };
301 /**
302 * Cancel an inflight API request
303 * @param {Promise<any>} request - The request promise to cancel
304 * @param {string} [message] - A message to include in the cancelation exception
305 */
306 RestClient.prototype.cancel = function (request, message) {
307 var source = this._cancelTokenMap.get(request);
308 if (source) {
309 source.cancel(message);
310 }
311 return true;
312 };
313 /**
314 * Checks to see if an error thrown is from an api request cancellation
315 * @param {any} error - Any error
316 * @return {boolean} - A boolean indicating if the error was from an api request cancellation
317 */
318 RestClient.prototype.isCancel = function (error) {
319 return axios.isCancel(error);
320 };
321 /**
322 * Retrieves a new and unique cancel token which can be
323 * provided in an axios request to be cancelled later.
324 */
325 RestClient.prototype.getCancellableToken = function () {
326 return axios.CancelToken.source();
327 };
328 /**
329 * Updates the weakmap with a response promise and its
330 * cancel token such that the cancel token can be easily
331 * retrieved (and used for cancelling the request)
332 */
333 RestClient.prototype.updateRequestToBeCancellable = function (promise, cancelTokenSource) {
334 this._cancelTokenMap.set(promise, cancelTokenSource);
335 };
336 /**
337 * Getting endpoint for API
338 * @param {string} apiName - The name of the api
339 * @return {string} - The endpoint of the api
340 */
341 RestClient.prototype.endpoint = function (apiName) {
342 var _this = this;
343 var cloud_logic_array = this._options.endpoints;
344 var response = '';
345 if (!Array.isArray(cloud_logic_array)) {
346 return response;
347 }
348 cloud_logic_array.forEach(function (v) {
349 if (v.name === apiName) {
350 response = v.endpoint;
351 if (typeof v.region === 'string') {
352 _this._region = v.region;
353 }
354 else if (typeof _this._options.region === 'string') {
355 _this._region = _this._options.region;
356 }
357 if (typeof v.service === 'string') {
358 _this._service = v.service || 'execute-api';
359 }
360 else {
361 _this._service = 'execute-api';
362 }
363 if (typeof v.custom_header === 'function') {
364 _this._custom_header = v.custom_header;
365 }
366 else {
367 _this._custom_header = undefined;
368 }
369 }
370 });
371 return response;
372 };
373 /** private methods **/
374 RestClient.prototype._signed = function (params, credentials, isAllResponse, _a) {
375 var service = _a.service, region = _a.region;
376 var signerServiceInfoParams = params.signerServiceInfo, otherParams = __rest(params, ["signerServiceInfo"]);
377 var endpoint_region = region || this._region || this._options.region;
378 var endpoint_service = service || this._service || this._options.service;
379 var creds = {
380 secret_key: credentials.secretAccessKey,
381 access_key: credentials.accessKeyId,
382 session_token: credentials.sessionToken,
383 };
384 var endpointInfo = {
385 region: endpoint_region,
386 service: endpoint_service,
387 };
388 var signerServiceInfo = Object.assign(endpointInfo, signerServiceInfoParams);
389 var signed_params = Signer.sign(otherParams, creds, signerServiceInfo);
390 if (signed_params.data) {
391 signed_params.body = signed_params.data;
392 }
393 logger.debug('Signed Request: ', signed_params);
394 delete signed_params.headers['host'];
395 return axios(signed_params)
396 .then(function (response) { return (isAllResponse ? response : response.data); })
397 .catch(function (error) {
398 logger.debug(error);
399 throw error;
400 });
401 };
402 RestClient.prototype._request = function (params, isAllResponse) {
403 if (isAllResponse === void 0) { isAllResponse = false; }
404 return axios(params)
405 .then(function (response) { return (isAllResponse ? response : response.data); })
406 .catch(function (error) {
407 logger.debug(error);
408 throw error;
409 });
410 };
411 RestClient.prototype._parseUrl = function (url) {
412 var parts = url.split('/');
413 return {
414 host: parts[2],
415 path: '/' + parts.slice(3).join('/'),
416 };
417 };
418 return RestClient;
419}());
420export { RestClient };
421//# sourceMappingURL=RestClient.js.map