UNPKG

ask-sdk-model-runtime

Version:
471 lines 23.5 kB
"use strict"; /* * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * http://www.apache.org/licenses/LICENSE-2.0 * * or in the "license" file accompanying this file. This file 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. */ var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { 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; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createUserAgent = exports.LwaServiceClient = exports.BaseServiceClient = exports.DefaultApiClient = void 0; var url = require("url"); /** * Default implementation of {@link ApiClient} which uses the native HTTP/HTTPS library of Node.JS. */ var DefaultApiClient = /** @class */ (function () { function DefaultApiClient() { } /** * Dispatches a request to an API endpoint described in the request. * An ApiClient is expected to resolve the Promise in the case an API returns a non-200 HTTP * status code. The responsibility of translating a particular response code to an error lies with the * caller to invoke. * @param {ApiClientRequest} request request to dispatch to the ApiClient * @returns {Promise<ApiClientResponse>} response from the ApiClient */ DefaultApiClient.prototype.invoke = function (request) { var _this = this; var urlObj = url.parse(request.url); var clientRequestOptions = { // tslint:disable:object-literal-sort-keys hostname: urlObj.hostname, path: urlObj.path, port: urlObj.port, protocol: urlObj.protocol, auth: urlObj.auth, headers: this.arrayToObjectHeader(request.headers), method: request.method, }; var client = clientRequestOptions.protocol === 'https:' ? require('https') : require('http'); return new Promise(function (resolve, reject) { var clientRequest = client.request(clientRequestOptions, function (response) { var chunks = []; response.on('data', function (chunk) { chunks.push(chunk); }); response.on('end', function () { var responseStr = Buffer.concat(chunks).toString(); var responseObj = { statusCode: response.statusCode, body: responseStr, headers: _this.objectToArrayHeader(response.headers), }; resolve(responseObj); }); }); clientRequest.on('error', function (err) { reject(_this.createAskSdkModelRuntimeError(_this.constructor.name, err.message)); }); if (request.body) { clientRequest.write(request.body); } clientRequest.end(); }); }; /** * Converts the header array in {@link ApiClientRequest} to compatible JSON object. * @private * @param {{key : string, value : string}[]} header header array from ApiClientRequest} * @returns {Object.<string, string[]>} header object to pass into HTTP client */ DefaultApiClient.prototype.arrayToObjectHeader = function (header) { var reducer = function (obj, item) { if (obj[item.key]) { obj[item.key].push(item.value); } else { obj[item.key] = [item.value]; } return obj; }; return header.reduce(reducer, {}); }; /** * Converts JSON header object to header array required for {ApiClientResponse} * @private * @param {Object.<string, (string|string[])>} header JSON header object returned by HTTP client * @returns {{key : string, value : string}[]} */ DefaultApiClient.prototype.objectToArrayHeader = function (header) { var arrayHeader = []; Object.keys(header).forEach(function (key) { var headerArray = Array.isArray(header[key]) ? header[key] : [header[key]]; for (var _i = 0, _a = headerArray; _i < _a.length; _i++) { var value = _a[_i]; arrayHeader.push({ key: key, value: value, }); } }); return arrayHeader; }; /** * function creating an AskSdk error. * @param {string} errorScope * @param {string} errorMessage * @returns {Error} */ DefaultApiClient.prototype.createAskSdkModelRuntimeError = function (errorScope, errorMessage) { var error = new Error(errorMessage); error.name = "AskSdkModelRuntime." + errorScope + " Error"; return error; }; return DefaultApiClient; }()); exports.DefaultApiClient = DefaultApiClient; /** * Class to be used as the base class for the generated service clients. */ var BaseServiceClient = /** @class */ (function () { /** * Creates new instance of the BaseServiceClient * @param {ApiConfiguration} apiConfiguration configuration parameter to provide dependencies to service client instance */ function BaseServiceClient(apiConfiguration) { this.requestInterceptors = []; this.responseInterceptors = []; this.apiConfiguration = apiConfiguration; } BaseServiceClient.isCodeSuccessful = function (responseCode) { return responseCode >= 200 && responseCode < 300; }; BaseServiceClient.buildUrl = function (endpoint, path, queryParameters, pathParameters) { var processedEndpoint = endpoint.endsWith('/') ? endpoint.substr(0, endpoint.length - 1) : endpoint; var pathWithParams = this.interpolateParams(path, pathParameters); var isConstantQueryPresent = pathWithParams.includes('?'); var queryString = this.buildQueryString(queryParameters, isConstantQueryPresent); return processedEndpoint + pathWithParams + queryString; }; BaseServiceClient.interpolateParams = function (path, params) { if (!params) { return path; } var result = path; params.forEach(function (paramValue, paramName) { result = result.replace('{' + paramName + '}', encodeURIComponent(paramValue)); }); return result; }; BaseServiceClient.buildQueryString = function (params, isQueryStart) { if (!params) { return ''; } var sb = []; if (isQueryStart) { sb.push('&'); } else { sb.push('?'); } params.forEach(function (obj) { sb.push(encodeURIComponent(obj.key)); sb.push('='); sb.push(encodeURIComponent(obj.value)); sb.push('&'); }); sb.pop(); return sb.join(''); }; /** * Sets array of functions that is going to be executed before the request is send * @param {Function} requestInterceptor request interceptor function * @returns {BaseServiceClient} */ BaseServiceClient.prototype.withRequestInterceptors = function () { var requestInterceptors = []; for (var _i = 0; _i < arguments.length; _i++) { requestInterceptors[_i] = arguments[_i]; } for (var _a = 0, requestInterceptors_1 = requestInterceptors; _a < requestInterceptors_1.length; _a++) { var interceptor = requestInterceptors_1[_a]; this.requestInterceptors.push(interceptor); } return this; }; /** * Sets array of functions that is going to be executed after the request is send * @param {Function} responseInterceptor response interceptor function * @returns {BaseServiceClient} */ BaseServiceClient.prototype.withResponseInterceptors = function () { var responseInterceptors = []; for (var _i = 0; _i < arguments.length; _i++) { responseInterceptors[_i] = arguments[_i]; } for (var _a = 0, responseInterceptors_1 = responseInterceptors; _a < responseInterceptors_1.length; _a++) { var interceptor = responseInterceptors_1[_a]; this.responseInterceptors.push(interceptor); } return this; }; /** * Invocation wrapper to implement service operations in generated classes * @param method HTTP method, such as 'POST', 'GET', 'DELETE', etc. * @param endpoint base API url * @param path the path pattern with possible placeholders for path parameters in form {paramName} * @param pathParams path parameters collection * @param queryParams query parameters collection * @param headerParams headers collection * @param bodyParam if body parameter is present it is provided here, otherwise null or undefined * @param errors maps recognized status codes to messages * @param nonJsonBody if the body is in JSON format */ BaseServiceClient.prototype.invoke = function (method, endpoint, path, pathParams, queryParams, headerParams, bodyParam, errors, nonJsonBody) { return __awaiter(this, void 0, void 0, function () { var request, contentType, contentTypeNonJson, apiClient, response, _i, _a, requestInterceptor, _b, _c, responseInterceptor, err_1, body, apiResponse, err; return __generator(this, function (_d) { switch (_d.label) { case 0: request = { url: BaseServiceClient.buildUrl(endpoint, path, queryParams, pathParams), method: method, headers: headerParams, }; if (bodyParam != null) { contentType = headerParams.find(function (header) { return header.key.toLowerCase() === 'content-type'; }); contentTypeNonJson = contentType && !contentType.value.includes('application/json'); request.body = nonJsonBody || contentTypeNonJson ? bodyParam : JSON.stringify(bodyParam); } apiClient = this.apiConfiguration.apiClient; _d.label = 1; case 1: _d.trys.push([1, 11, , 12]); _i = 0, _a = this.requestInterceptors; _d.label = 2; case 2: if (!(_i < _a.length)) return [3 /*break*/, 5]; requestInterceptor = _a[_i]; return [4 /*yield*/, requestInterceptor(request)]; case 3: _d.sent(); _d.label = 4; case 4: _i++; return [3 /*break*/, 2]; case 5: return [4 /*yield*/, apiClient.invoke(request)]; case 6: response = _d.sent(); _b = 0, _c = this.responseInterceptors; _d.label = 7; case 7: if (!(_b < _c.length)) return [3 /*break*/, 10]; responseInterceptor = _c[_b]; return [4 /*yield*/, responseInterceptor(response)]; case 8: _d.sent(); _d.label = 9; case 9: _b++; return [3 /*break*/, 7]; case 10: return [3 /*break*/, 12]; case 11: err_1 = _d.sent(); err_1.message = "Call to service failed: " + err_1.message; throw err_1; case 12: try { body = response.body ? JSON.parse(response.body) : undefined; } catch (err) { throw new SyntaxError("Failed trying to parse the response body: " + response.body); } if (BaseServiceClient.isCodeSuccessful(response.statusCode)) { apiResponse = { headers: response.headers, body: body, statusCode: response.statusCode, }; return [2 /*return*/, apiResponse]; } err = new Error('Unknown error'); err.name = 'ServiceError'; err['statusCode'] = response.statusCode; // tslint:disable-line:no-string-literal err['headers'] = response.headers; // tslint:disable-line:no-string-literal err['response'] = body; // tslint:disable-line:no-string-literal if (errors && errors.has(response.statusCode)) { err.message = errors.get(response.statusCode); } throw err; } }); }); }; return BaseServiceClient; }()); exports.BaseServiceClient = BaseServiceClient; /** * Class to be used to call Amazon LWA to retrieve access tokens. */ var LwaServiceClient = /** @class */ (function (_super) { __extends(LwaServiceClient, _super); function LwaServiceClient(options) { var _this = _super.call(this, options.apiConfiguration) || this; if (options.authenticationConfiguration == null) { throw new Error('AuthenticationConfiguration cannot be null or undefined.'); } _this.grantType = options.grantType ? options.grantType : LwaServiceClient.CLIENT_CREDENTIALS_GRANT_TYPE; _this.authenticationConfiguration = options.authenticationConfiguration; _this.tokenStore = {}; return _this; } LwaServiceClient.prototype.getAccessTokenForScope = function (scope) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { if (scope == null) { throw new Error('Scope cannot be null or undefined.'); } return [2 /*return*/, this.getAccessToken(scope)]; }); }); }; LwaServiceClient.prototype.getAccessToken = function (scope) { return __awaiter(this, void 0, void 0, function () { var cacheKey, accessToken, accessTokenRequest, accessTokenResponse; return __generator(this, function (_a) { switch (_a.label) { case 0: if (this.authenticationConfiguration.accessToken) { return [2 /*return*/, this.authenticationConfiguration.accessToken]; } cacheKey = scope ? scope : LwaServiceClient.REFRESH_ACCESS_TOKEN; accessToken = this.tokenStore[cacheKey]; if (accessToken && accessToken.expiry > Date.now() + LwaServiceClient.EXPIRY_OFFSET_MILLIS) { return [2 /*return*/, accessToken.token]; } accessTokenRequest = { clientId: this.authenticationConfiguration.clientId, clientSecret: this.authenticationConfiguration.clientSecret, }; if (scope && this.authenticationConfiguration.refreshToken) { throw new Error('Cannot support both refreshToken and scope.'); } else if (scope == null && this.authenticationConfiguration.refreshToken == null) { throw new Error('Either refreshToken or scope must be specified.'); } else if (scope == null) { accessTokenRequest.refreshToken = this.authenticationConfiguration.refreshToken; } else { accessTokenRequest.scope = scope; } return [4 /*yield*/, this.generateAccessToken(accessTokenRequest)]; case 1: accessTokenResponse = _a.sent(); this.tokenStore[cacheKey] = { token: accessTokenResponse.access_token, expiry: Date.now() + accessTokenResponse.expires_in * 1000, }; return [2 /*return*/, accessTokenResponse.access_token]; } }); }); }; LwaServiceClient.prototype.generateAccessToken = function (accessTokenRequest) { return __awaiter(this, void 0, void 0, function () { var authEndpoint, queryParams, headerParams, pathParams, paramInfo, bodyParams, errorDefinitions, apiResponse, response; return __generator(this, function (_a) { switch (_a.label) { case 0: authEndpoint = this.authenticationConfiguration.authEndpoint || LwaServiceClient.AUTH_ENDPOINT; if (!accessTokenRequest.clientId || !accessTokenRequest.clientSecret) { throw new Error("Required parameter accessTokenRequest didn't specify clientId or clientSecret"); } queryParams = []; headerParams = []; headerParams.push({ key: 'Content-type', value: 'application/x-www-form-urlencoded' }); pathParams = new Map(); paramInfo = this.grantType === LwaServiceClient.LWA_CREDENTIALS_GRANT_TYPE ? "&refresh_token=" + accessTokenRequest.refreshToken : "&scope=" + accessTokenRequest.scope; bodyParams = "grant_type=" + this.grantType + "&client_secret=" + accessTokenRequest.clientSecret + "&client_id=" + accessTokenRequest.clientId + paramInfo; errorDefinitions = new Map(); errorDefinitions.set(200, 'Token request sent.'); errorDefinitions.set(400, 'Bad Request'); errorDefinitions.set(401, 'Authentication Failed'); errorDefinitions.set(500, 'Internal Server Error'); return [4 /*yield*/, this.invoke('POST', authEndpoint, '/auth/O2/token', pathParams, queryParams, headerParams, bodyParams, errorDefinitions, true)]; case 1: apiResponse = _a.sent(); response = { access_token: apiResponse.body["access_token"], expires_in: apiResponse.body["expires_in"], scope: apiResponse.body["scope"], token_type: apiResponse.body["token_type"], }; return [2 /*return*/, response]; } }); }); }; LwaServiceClient.EXPIRY_OFFSET_MILLIS = 60000; LwaServiceClient.REFRESH_ACCESS_TOKEN = 'refresh_access_token'; LwaServiceClient.CLIENT_CREDENTIALS_GRANT_TYPE = 'client_credentials'; LwaServiceClient.LWA_CREDENTIALS_GRANT_TYPE = 'refresh_token'; LwaServiceClient.AUTH_ENDPOINT = 'https://api.amazon.com'; return LwaServiceClient; }(BaseServiceClient)); exports.LwaServiceClient = LwaServiceClient; /** * function creating an AskSdk user agent. * @param packageVersion * @param customUserAgent */ function createUserAgent(packageVersion, customUserAgent) { var customUserAgentString = customUserAgent ? (' ' + customUserAgent) : ''; return "ask-node-model/" + packageVersion + " Node/" + process.version + customUserAgentString; } exports.createUserAgent = createUserAgent; //# sourceMappingURL=index.js.map