import { RequestOptions as HttpRequestOptions } from 'http' ; import { RequestOptions as HttpsRequestOptions } from 'https' ; import * as url from 'url' ; export interface export interface ApiClientMessage { headers : Array < { key : string ; value : string ; } > ; body ? : string ; } export interface extends ApiClientMessage export interface ApiClientRequest extends ApiClientMessage { url : string ; method : string ; } export interface extends ApiClientMessage export interface ApiClientResponse extends ApiClientMessage { /**
     * Result code of the attempt to satisfy the request. Normally this
     * corresponds to the HTTP status code returned by the server.
     */ statusCode : number ; } export interface export interface ApiResponse { headers : Array < { key : string ; value : string ; } > ; body ? : any ; statusCode : number ; } export interface export interface ApiClient { param { ApiClientRequest } request returns { Promise < ApiClientResponse > } memberof invoke ( request ) : Promise < ApiClientResponse > ; } /**
 * Default implementation of  ApiClient which uses the native HTTP/HTTPS library of Node.JS.
  export class DefaultApiClient implements ApiClient { param { ApiClientRequest } request returns { Promise < ApiClientResponse > } public invoke ( request ) { const urlObj = url . parse ( request . url ) ; const clientRequestOptions = { hostname : urlObj . hostname , path : urlObj . path , port : urlObj . port , protocol : urlObj . protocol , auth : urlObj . auth , headers : this . arrayToObjectHeader ( request . headers ) , method : request . method , } ; const client = clientRequestOptions . protocol === 'https:' ? require ( 'https' ) : require ( 'http' ) ; return new Promise < ApiClientResponse > ( ( resolve , reject ) => { const clientRequest = client . request ( clientRequestOptions , ( response ) => { const chunks = [ ] ; response . on ( 'data' , ( chunk ) => { chunks . push ( chunk ) ; } ) ; response . on ( 'end' , ( ) => { const responseStr = Buffer . concat ( chunks ) . toString ( ) ; const responseObj = { statusCode : response . statusCode , body : responseStr , headers : this . objectToArrayHeader ( response . headers ) , } ; resolve ( responseObj ) ; } ) ; } ) ; clientRequest . on ( 'error' , ( err ) => { reject ( this . createAskSdkModelRuntimeError ( this . constructor . name , err . message ) ) ; } ) ; if ( request . body ) { clientRequest . write ( request . body ) ; } clientRequest . end ( ) ; } ) ; } /**
     * Converts the header array in  ApiClientRequest to compatible JSON object.
     *  private param { { key : string , value : string } [ ] } header returns { Object . < string , string [ ] > } private arrayToObjectHeader ( header ) { const reducer = ( obj , item ) : { [ key ] : string [ ] ; } => { if ( obj [ item . key ] ) { obj [ item . key ] . push ( item . value ) ; } else { obj [ item . key ] = [ item . value ] ; } return obj ; } ; return header . reduce ( reducer , { } ) ; } private param { Object . < string , ( string | string [ ] ) > } header returns { { key : string , value : string } [ ] } private objectToArrayHeader ( header ) { const arrayHeader = < Array < { key : string ; value : string ; } > > [ ] ; Object . keys ( header ) . forEach ( ( key ) => { const headerArray = Array . isArray ( header [ key ] ) ? header [ key ] : [ header [ key ] ] ; for ( const value of < string [ ] > headerArray ) { arrayHeader . push ( { key , value , } ) ; } } ) ; return arrayHeader ; } param { string } errorScope param { string } errorMessage returns { Error } private createAskSdkModelRuntimeError ( errorScope , errorMessage ) { const error = new Error ( errorMessage ) ; error . name = `AskSdkModelRuntime.${ errorScope } Error` ; return error ; } } interface export interface ApiConfiguration { /**
     * Configured ApiClient implementation
     */ apiClient : ApiClient ; /**
     * Authorization value to be used on any calls of the service client instance
     */ authorizationValue : string ; /**
     * Endpoint to hit by the service client instance
     */ apiEndpoint : string ; } /**
 * Class to be used as the base class for the generated service clients.
 */ export abstract class BaseServiceClient { private static isCodeSuccessful ( responseCode ) { return responseCode >= 200 && responseCode < 300 ; } private static buildUrl ( endpoint , path , queryParameters , pathParameters ) { const processedEndpoint = endpoint . endsWith ( '/' ) ? endpoint . substr ( 0 , endpoint . length - 1 ) : endpoint ; const pathWithParams = this . interpolateParams ( path , pathParameters ) ; const isConstantQueryPresent = pathWithParams . includes ( '?' ) ; const queryString = this . buildQueryString ( queryParameters , isConstantQueryPresent ) ; return processedEndpoint + pathWithParams + queryString ; } private static interpolateParams ( path , params ) { if ( ! params ) { return path ; } let result = path ; params . forEach ( ( paramValue , paramName ) => { result = result . replace ( '{' + paramName + '}' , encodeURIComponent ( paramValue ) ) ; } ) ; return result ; } private static buildQueryString ( params , isQueryStart ) { if ( ! params ) { return '' ; } const sb = [ ] ; if ( isQueryStart ) { sb . push ( '&' ) ; } else { sb . push ( '?' ) ; } params . forEach ( ( obj ) => { sb . push ( encodeURIComponent ( obj . key ) ) ; sb . push ( '=' ) ; sb . push ( encodeURIComponent ( obj . value ) ) ; sb . push ( '&' ) ; } ) ; sb . pop ( ) ; return sb . join ( '' ) ; } /**
     * ApiConfiguration instance to provide dependencies for this service client
     */ protected apiConfiguration : ApiConfiguration ; private requestInterceptors : Array < ( request : ApiClientRequest ) => void | Promise < void > > = [ ] ; private responseInterceptors : Array < ( response : ApiClientResponse ) => void | Promise < void > > = [ ] ; param { ApiConfiguration } apiConfiguration protected constructor ( apiConfiguration ) { this . apiConfiguration = apiConfiguration ; } param { Function } requestInterceptor returns { BaseServiceClient } public withRequestInterceptors ( ... requestInterceptors ) { for ( const interceptor of requestInterceptors ) { this . requestInterceptors . push ( interceptor ) ; } return this ; } param { Function } responseInterceptor returns { BaseServiceClient } public withResponseInterceptors ( ... responseInterceptors ) { for ( const interceptor of responseInterceptors ) { this . responseInterceptors . push ( interceptor ) ; } return this ; } param method param endpoint param path param pathParams param queryParams param headerParams param bodyParam param errors param nonJsonBody protected async invoke ( method , endpoint , path , pathParams , queryParams , headerParams , bodyParam , errors , nonJsonBody ? ) { const request = { url : BaseServiceClient . buildUrl ( endpoint , path , queryParams , pathParams ) , method , headers : headerParams , } ; if ( bodyParam != null ) { const contentType = headerParams . find ( ( header ) => header . key . toLowerCase ( ) === 'content-type' ) ; const contentTypeNonJson = contentType && ! contentType . value . includes ( 'application/json' ) ; request . body = nonJsonBody || contentTypeNonJson ? bodyParam : JSON . stringify ( bodyParam ) ; } const apiClient = this . apiConfiguration . apiClient ; let response ; try { for ( const requestInterceptor of this . requestInterceptors ) { await requestInterceptor ( request ) ; } response = await apiClient . invoke ( request ) ; for ( const responseInterceptor of this . responseInterceptors ) { await responseInterceptor ( response ) ; } } catch ( err ) { err . message = `Call to service failed: ${ err . message }` ; throw err ; } let body ; 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 ) ) { const apiResponse = { headers : response . headers , body , statusCode : response . statusCode , } ; return apiResponse ; } const err = new Error ( 'Unknown error' ) ; err . name = 'ServiceError' ; err [ 'statusCode' ] = response . statusCode ; err [ 'headers' ] = response . headers ; err [ 'response' ] = body ; if ( errors && errors . has ( response . statusCode ) ) { err . message = errors . get ( response . statusCode ) ; } throw err ; } } /**
 * Represents a Login With Amazon(LWA) access token
 */ export interface AccessToken { token : string ; expiry : Number ; } /**
 * Represents a request for retrieving a Login With Amazon(LWA) access token
 */ export interface AccessTokenRequest { clientId : string ; clientSecret : string ; scope ? : string ; refreshToken ? : string ; } /**
 * Represents a response returned by LWA containing a Login With Amazon(LWA) access token
 */ export interface AccessTokenResponse { access_token : string ; expires_in : number ; scope : string ; token_type : string ; } /**
 * Represents the authentication configuration for a client ID and client secret
 */ export interface AuthenticationConfiguration { clientId : string ; clientSecret : string ; accessToken ? : string ; refreshToken ? : string ; authEndpoint ? : string ; } /**
 * Class to be used to call Amazon LWA to retrieve access tokens.
 */ export class LwaServiceClient extends BaseServiceClient { protected static EXPIRY_OFFSET_MILLIS : number = 60000 ; protected static REFRESH_ACCESS_TOKEN : string = 'refresh_access_token' ; protected static CLIENT_CREDENTIALS_GRANT_TYPE : string = 'client_credentials' ; protected static LWA_CREDENTIALS_GRANT_TYPE : string = 'refresh_token' ; protected static AUTH_ENDPOINT : string = 'https://api.amazon.com' ; protected authenticationConfiguration : AuthenticationConfiguration ; protected tokenStore : { [ cacheKey ] : AccessToken ; } ; protected grantType : string ; constructor ( options ) { super ( options . apiConfiguration ) ; 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 = { } ; } public async getAccessTokenForScope ( scope ) { if ( scope == null ) { throw new Error ( 'Scope cannot be null or undefined.' ) ; } return this . getAccessToken ( scope ) ; } public async getAccessToken ( scope ? ) { if ( this . authenticationConfiguration . accessToken ) { return this . authenticationConfiguration . accessToken ; } const cacheKey = scope ? scope : LwaServiceClient . REFRESH_ACCESS_TOKEN ; const accessToken = this . tokenStore [ cacheKey ] ; if ( accessToken && accessToken . expiry > Date . now ( ) + LwaServiceClient . EXPIRY_OFFSET_MILLIS ) { return accessToken . token ; } const 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 ; } const accessTokenResponse = await this . generateAccessToken ( accessTokenRequest ) ; this . tokenStore [ cacheKey ] = { token : accessTokenResponse . access_token , expiry : Date . now ( ) + accessTokenResponse . expires_in * 1000 , } ; return accessTokenResponse . access_token ; } protected async generateAccessToken ( accessTokenRequest ) { const authEndpoint = this . authenticationConfiguration . authEndpoint || LwaServiceClient . AUTH_ENDPOINT ; if ( ! accessTokenRequest . clientId || ! accessTokenRequest . clientSecret ) { throw new Error ( `Required parameter accessTokenRequest didn't specify clientId or clientSecret` ) ; } const queryParams = [ ] ; const headerParams = [ ] ; headerParams . push ( { key : 'Content-type' , value : 'application/x-www-form-urlencoded' } ) ; const pathParams = new Map < string , string > ( ) ; const paramInfo = this . grantType === LwaServiceClient . LWA_CREDENTIALS_GRANT_TYPE ? `&refresh_token=${ accessTokenRequest . refreshToken }` : `&scope=${ accessTokenRequest . scope }` ; const bodyParams = `grant_type=${ this . grantType }&client_secret=${ accessTokenRequest . clientSecret }&client_id=${ accessTokenRequest . clientId }` + paramInfo ; const errorDefinitions = new Map < number , string > ( ) ; errorDefinitions . set ( 200 , 'Token request sent.' ) ; errorDefinitions . set ( 400 , 'Bad Request' ) ; errorDefinitions . set ( 401 , 'Authentication Failed' ) ; errorDefinitions . set ( 500 , 'Internal Server Error' ) ; const apiResponse = await this . invoke ( 'POST' , authEndpoint , '/auth/O2/token' , pathParams , queryParams , headerParams , bodyParams , errorDefinitions , true ) ; const response = { access_token : apiResponse . body [ `access_token` ] , expires_in : apiResponse . body [ `expires_in` ] , scope : apiResponse . body [ `scope` ] , token_type : apiResponse . body [ `token_type` ] , } ; return response ; } } param packageVersion param customUserAgent export function createUserAgent ( packageVersion , customUserAgent ) { const customUserAgentString = customUserAgent ? ( ' ' + customUserAgent ) : '' ; return `ask-node-model/${ packageVersion } Node/${ process . version }` + customUserAgentString ; }