ask-sdk-model-runtime
Version:
The base components for ASK SDK Models
471 lines • 23.5 kB
JavaScript
"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