import Resource , { ListResponse } from './index' ; import axios , { AxiosPromise , AxiosRequestConfig , AxiosResponse , AxiosError , AxiosInstance } from 'axios' ; export * from 'axios' ; export interface RequestConfig extends AxiosRequestConfig { useCache ? : boolean ; query ? : any ; } export interface ResourceResponse < T extends Resource , U extends any = any > extends Record < string , any > { response : AxiosResponse < U > ; resources : T [ ] ; count ? : ( ) => number ; pages ? : ( ) => number ; currentPage ? : ( ) => number ; perPage ? : ( ) => number ; next ? : ( ) => Promise < ResourceResponse < T , U > > ; previous ? : ( ) => Promise < ResourceResponse < T , U > > ; } export type ExtractorFunction < T extends Resource , U extends any = any > = ( result : ResourceResponse < T , U > [ 'response' ] ) => ResourceResponse < T , U > ; export class BaseClient { axios : AxiosInstance ; config : AxiosRequestConfig = { } ; constructor ( baseURL , config = { } ) { this . config = Object . assign ( { baseURL } , config ) ; this . axios = axios . create ( this . config ) ; } get hostname ( ) { return this . config . baseURL ; } set hostname ( value ) { this . config . baseURL = value ; this . axios = axios . create ( this . config ) ; } static extend < T , U > ( this , classProps ) { return Object . assign ( class extends this { } , classProps ) ; } negotiateContent < T extends typeof Resource > ( ResourceClass ) { return ( response ) => { let objects = [ ] ; if ( Array . isArray ( response . data ) ) { response . data . forEach ( ( attributes ) => objects . push ( new ResourceClass ( attributes ) as InstanceType < T > ) ) ; } else { objects . push ( new ResourceClass ( response . data ) as InstanceType < T > ) ; } return { response , resources : objects , count : ( ) => response . headers [ 'Pagination-Count' ] , pages : ( ) => Math . ceil ( response . headers [ 'Pagination-Count' ] / response . headers [ 'Pagination-Limit' ] ) , currentPage : ( ) => response . headers [ 'Pagination-Page' ] , perPage : ( ) => response . headers [ 'Pagination-Limit' ] , } as ResourceResponse < InstanceType < T > > ; } ; } param ResourceClass param options list < T extends typeof Resource , U = any > ( ResourceClass , options = { } ) { return this . get < U > ( ResourceClass . getListRoutePath ( options . query ) , options ) . then ( this . negotiateContent ( ResourceClass ) ) ; } param ResourceClass param options detail < T extends typeof Resource , U = any > ( ResourceClass , id , options = { } ) { return this . get < U > ( ResourceClass . getDetailRoutePath ( id , options . query ) , options ) . then ( this . negotiateContent ( ResourceClass ) ) ; } get < T = any > ( path , options = { } ) { return this . axios . get < T > ( path , options ) . catch ( ( e ) => this . onError ( e ) ) ; } put < T = any > ( path , body = { } , options = { } ) { return this . axios . put < T > ( path , body , options ) . catch ( ( e ) => this . onError ( e ) ) ; } post < T = any > ( path , body = { } , options = { } ) { return this . axios . post < T > ( path , body , options ) . catch ( ( e ) => this . onError ( e ) ) ; } patch < T = any > ( path , body = { } , options = { } ) { return this . axios . patch < T > ( path , body , options ) . catch ( ( e ) => this . onError ( e ) ) ; } delete < T = any > ( path , options = { } ) { return this . axios . delete ( path , options ) . catch ( ( e ) => this . onError ( e ) ) ; } head < T = any > ( path , options = { } ) { return this . axios . head ( path , options ) . catch ( ( e ) => this . onError ( e ) ) ; } options < T = any > ( path , options = { } ) { return this . axios . options ( path , options ) . catch ( ( e ) => this . onError ( e ) ) ; } bindMethodsToPath ( relativePath ) { return { get : this . get . bind ( this , relativePath ) , post : this . post . bind ( this , relativePath ) , put : this . put . bind ( this , relativePath ) , patch : this . patch . bind ( this , relativePath ) , head : this . head . bind ( this , relativePath ) , options : this . options . bind ( this , relativePath ) , delete : this . delete . bind ( this , relativePath ) , } ; } onError ( exception ) { throw exception ; } } export class DefaultClient extends BaseClient { } export class JWTBearerClient extends BaseClient { token : string ; constructor ( baseURL , token = '' , options = { } ) { let headers = Object . assign ( { Authorization : `Bearer ${ token }` , } , options . headers ) ; options . headers = headers ; super ( baseURL , options ) ; this . token = token ; } getTokenPayload ( ) { try { let jwtPieces = this . token . split ( '.' ) ; let payloadBase64 = jwtPieces [ 1 ] ; let payloadBuffer = Buffer . from ( payloadBase64 , 'base64' ) . toString ( ) ; return JSON . parse ( payloadBuffer . toString ( ) ) ; } catch ( e ) { return undefined ; } } tokenIsExpired ( ) { try { let payload = this . getTokenPayload ( ) ; let nowInSeconds = Math . floor ( Date . now ( ) / 1000 ) ; return payload . exp < nowInSeconds ; } catch ( e ) { return true ; } } tokenIsValid ( ) { return this . token && ! this . tokenIsExpired ( ) ; } }