import { createLogger , ErrorWithCause , propertyExists } from '@sap-cloud-sdk/util' ; import CircuitBreaker from 'opossum' ; import axios , { AxiosRequestConfig , AxiosResponse , AxiosError } from 'axios' ; import { decodeJwt , wrapJwtInHeader } from '../jwt' ; import { circuitBreakerDefaultOptions , ResilienceOptions } from '../resilience-options' ; import { CachingOptions } from '../cache' ; import { urlAndAgent } from '../../http-agent' ; import { DestinationConfiguration , DestinationJson , parseDestination } from './destination' ; import { Destination , DestinationType } from './destination-service-types' ; import { destinationServiceCache } from './destination-service-cache' ; const logger = createLogger ( { package : 'connectivity' , messageContext : 'destination-service' } ) ; type DestinationCircuitBreaker < ResponseType > = CircuitBreaker < [ requestConfig : AxiosRequestConfig ] , AxiosResponse < ResponseType > > ; let circuitBreaker ; param destinationServiceUri param jwt param options returns internal export function fetchInstanceDestinations ( destinationServiceUri , jwt , options ? ) { return fetchDestinations ( destinationServiceUri , jwt , DestinationType . Instance , options ) ; } param destinationServiceUri param jwt param options returns internal export function fetchSubaccountDestinations ( destinationServiceUri , jwt , options ? ) { return fetchDestinations ( destinationServiceUri , jwt , DestinationType . Subaccount , options ) ; } async function fetchDestinations ( destinationServiceUri , jwt , type , options ? ) { const targetUri = `${ destinationServiceUri . replace ( /\/$/ , '' ) }/destination-configuration/v1/${ type }Destinations` ; if ( options ?. useCache ) { const destinationsFromCache = destinationServiceCache . retrieveDestinationsFromCache ( targetUri , decodeJwt ( jwt ) , options . isolationStrategy ) ; if ( destinationsFromCache ) { return destinationsFromCache ; } } const headers = wrapJwtInHeader ( jwt ) . headers ; return callDestinationService ( targetUri , headers , options ) . then ( response => { const destinations = response . data . map ( d => parseDestination ( d ) ) ; if ( options ?. useCache ) { destinationServiceCache . cacheRetrievedDestinations ( targetUri , decodeJwt ( jwt ) , destinations , options . isolationStrategy ) ; } return destinations ; } ) . catch ( error => { throw new ErrorWithCause ( `Failed to fetch ${ type } destinations.${ errorMessageFromResponse ( error ) }` , error ) ; } ) ; } internal export interface AuthAndExchangeTokens { authHeaderJwt : string ; exchangeHeaderJwt ? : string ; exchangeTenant ? : string ; } param destinationServiceUri param token param destinationName param options returns internal export async function fetchDestination ( destinationServiceUri , token , destinationName , options ? ) { return fetchDestinationByTokens ( destinationServiceUri , typeof token === 'string' ? { authHeaderJwt : token } : token , destinationName , options ) ; } async function fetchDestinationByTokens ( destinationServiceUri , tokens , destinationName , options ? ) { const targetUri = `${ destinationServiceUri . replace ( /\/$/ , '' ) }/destination-configuration/v1/destinations/${ destinationName }` ; if ( options ?. useCache ) { const destinationsFromCache = destinationServiceCache . retrieveDestinationsFromCache ( targetUri , decodeJwt ( tokens . authHeaderJwt ) , options . isolationStrategy ) ; if ( destinationsFromCache ) { if ( destinationsFromCache . length > 1 ) { logger . warn ( 'More than one destination found in the cache. This should not happen. First element used.' ) ; } return destinationsFromCache [ 0 ] ; } } let authHeader = wrapJwtInHeader ( tokens . authHeaderJwt ) . headers ; authHeader = tokens . exchangeHeaderJwt ? { ... authHeader , 'X-user-token' : tokens . exchangeHeaderJwt } : authHeader ; authHeader = tokens . exchangeTenant ? { ... authHeader , 'X-tenant' : tokens . exchangeTenant } : authHeader ; return callDestinationService ( targetUri , authHeader , options ) . then ( response => { const destination = parseDestination ( response . data ) ; if ( options ?. useCache ) { destinationServiceCache . cacheRetrievedDestinations ( targetUri , decodeJwt ( tokens . authHeaderJwt ) , [ destination ] , options . isolationStrategy ) ; } return destination ; } ) . catch ( error => { { throw new ErrorWithCause ( `Failed to fetch destination ${ destinationName }.${ errorMessageFromResponse ( error ) }` , error ) ; } } ) ; } function errorMessageFromResponse ( error ) { return propertyExists ( error , 'response' , 'data' , 'ErrorMessage' ) ? ` ${ error . response ! . data . ErrorMessage }` : '' ; } function callDestinationService ( uri , headers , options = { enableCircuitBreaker : true } ) { const config = { ... urlAndAgent ( uri ) , proxy : false , method : 'get' , headers } ; if ( options . enableCircuitBreaker ) { return getCircuitBreaker ( ) . fire ( config ) ; } return axios . request ( config ) ; } function getCircuitBreaker ( ) { const request = axios . request ; if ( ! circuitBreaker ) { circuitBreaker = new CircuitBreaker ( request , circuitBreakerDefaultOptions ) ; } return circuitBreaker ; }