import { Int64 , ProtocolType } from '@creditkarma/thrift-server-core' ; import { createThriftServer } from '@creditkarma/thrift-server-hapi' ; import { ZipkinTracingHapi } from '@creditkarma/zipkin-tracing-hapi' ; import { createHttpClient , IRequest } from '@creditkarma/thrift-client' ; import { ThriftClientZipkinFilter } from '@creditkarma/thrift-client-zipkin-filter' ; import { ThriftClientTimingFilter } from '@creditkarma/thrift-client-timing-filter' ; import * as Hapi from '@hapi/hapi' ; import { IMappedStruct , ISharedStruct , SharedUnion , SharedUnionCodec , } from './generated-strict/shared' ; import { ADD_SERVER_CONFIG , HAPI_CALC_SERVER_STRICT_CONFIG } from './config' ; import { Calculator , Choice , ChoiceType , ICommonStruct , Operation , Work , } from './generated-strict/calculator-service' ; import { CommonUnion , CommonUnionCodec } from './generated-strict/common' ; import { AddService } from './generated-strict/add-service' ; export async function createServer ( sampleRate = 0 , protocolType = 'binary' ) { const addServiceClient = createHttpClient ( AddService . Client , { hostName : ADD_SERVER_CONFIG . hostName , port : ADD_SERVER_CONFIG . port , register : sampleRate > 0 ? [ ThriftClientZipkinFilter ( { localServiceName : 'calculator-service' , remoteServiceName : 'add-service' , tracerConfig : { endpoint : process . env . ZIPKIN_ENDPOINT , zipkinVersion : process . env . ZIPKIN_VERSION === 'v2' ? 'v2' : 'v1' , sampleRate , httpInterval : 0 , } , } ) , ThriftClientTimingFilter ( { remoteServiceName : 'add-service' , } ) , ] : [ ThriftClientTimingFilter ( { remoteServiceName : 'add-service' , } ) , ] , } ) ; /**
     * Implementation of our thrift service.
     *
     * Notice the second parameter, "context" - this is the Hapi request object,
     * passed along to our service by the Hapi thrift plugin. Thus, you have access to
     * all HTTP request data from within your service implementation.
     */ const impl = new Calculator . Processor < Hapi . Request > ( { ping ( ) { return ; } , add ( a , b , context ) { return addServiceClient . add ( a , b , { headers : context . headers } ) ; } , addInt64 ( a , b , context ) { return addServiceClient . addInt64 ( a , b , context ) ; } , addWithContext ( a , b , context ) { if ( context !== undefined && context . headers [ 'x-fake-token' ] === 'fake-token' ) { return a + b ; } else { throw new Error ( 'Unauthorized' ) ; } } , calculate ( logId , work , context ) { switch ( work . op ) { case Operation . ADD : return addServiceClient . add ( work . num1 , work . num2 , { headers : context . headers , } ) ; case Operation . SUBTRACT : return work . num1 - work . num2 ; case Operation . DIVIDE : return work . num1 / work . num2 ; case Operation . MULTIPLY : return work . num1 * work . num2 ; } } , zip ( ) { return ; } , getStruct ( ) { return { code : { status : new Int64 ( 0 ) , } , value : 'test' , } ; } , getUnion ( index ) { if ( index === 1 ) { return SharedUnionCodec . create ( { option1 : 'foo' } ) ; } else { return SharedUnionCodec . create ( { option2 : 'bar' } ) ; } } , getMappedStruct ( index ) { const map = new Map ( ) ; map . set ( 'one' , { code : { status : 5 , } , value : 'test' , } ) ; return { data : map , } ; } , echoBinary ( word ) { return word . toString ( 'utf-8' ) ; } , echoString ( word ) { return word ; } , checkName ( choice ) { switch ( choice . __type ) { case ChoiceType . ChoiceWithFirstName : return `FirstName: ${ choice . firstName . name }` ; case ChoiceType . ChoiceWithLastName : return `LastName: ${ choice . lastName . name }` ; default : const msg = choice ; throw new Error ( `Unknown choice: ${ msg }` ) ; } } , checkOptional ( type ? ) { if ( type === undefined ) { return 'undefined' ; } else { return type ; } } , mapOneList ( list ) { return list . map ( ( next ) => next + 1 ) ; } , mapValues ( map ) { return Array . from ( map . values ( ) ) ; } , listToMap ( list ) { return list . reduce ( ( acc , next ) => { acc . set ( next [ 0 ] , next [ 1 ] ) ; return acc ; } , new Map ( ) ) ; } , fetchThing ( ) { return { code : { status : new Int64 ( 0 ) , } , value : 'test' , } ; } , fetchUnion ( ) { return CommonUnionCodec . create ( { option1 : 'test' } ) ; } , broken ( ) { throw new Error ( `Yeah, this didn't work` ) ; } , } ) ; /**
     * Creates Hapi server with thrift endpoint.
     */ const server = await createThriftServer ( { port : HAPI_CALC_SERVER_STRICT_CONFIG . port , path : HAPI_CALC_SERVER_STRICT_CONFIG . path , thriftOptions : { serviceName : 'calculator-service' , handler : impl , protocol : protocolType , } , } ) ; if ( sampleRate > 0 ) { await server . register ( { plugin : ZipkinTracingHapi ( { localServiceName : 'calculator-service' , tracerConfig : { endpoint : process . env . ZIPKIN_ENDPOINT , zipkinVersion : process . env . ZIPKIN_VERSION === 'v2' ? 'v2' : 'v1' , sampleRate , httpInterval : 0 , } , } ) , } ) ; } const client = createHttpClient ( Calculator . Client , { serviceName : 'calculator-service' , hostName : HAPI_CALC_SERVER_STRICT_CONFIG . hostName , port : HAPI_CALC_SERVER_STRICT_CONFIG . port , path : HAPI_CALC_SERVER_STRICT_CONFIG . path , register : sampleRate > 0 ? [ ThriftClientZipkinFilter ( { localServiceName : 'calculator-client' , remoteServiceName : 'calculator-service' , tracerConfig : { endpoint : process . env . ZIPKIN_ENDPOINT , zipkinVersion : process . env . ZIPKIN_VERSION === 'v2' ? 'v2' : 'v1' , sampleRate , httpInterval : 0 , } , } ) , ThriftClientTimingFilter ( { remoteServiceName : 'calculator-service' , } ) , ] : [ ThriftClientTimingFilter ( { remoteServiceName : 'calculator-service' , } ) , ] , } ) ; server . route ( { method : 'GET' , path : '/add' , async handler ( request , reply ) { if ( typeof request . query === 'object' ) { const left = Number ( request . query [ 'left' ] ) ; const right = Number ( request . query [ 'right' ] ) ; return client . add ( left , right ) . then ( ( response ) => { return reply . response ( `${ response }` ) ; } ) . catch ( ( err ) => { return reply . response ( err ) . code ( 500 ) ; } ) ; } else { return reply . response ( new Error ( `No arguments` ) ) . code ( 400 ) ; } } , } ) ; /**
     * The Hapi server can process requests that are not targeted to the thrift
     * service
     */ server . route ( { method : 'GET' , path : '/control' , handler ( request , reply ) { return reply . response ( 'PASS' ) ; } , } ) ; server . route ( { method : 'POST' , path : '/return500' , handler ( request , reply ) { return reply . response ( 'NOPE' ) . code ( 500 ) ; } , } ) ; server . route ( { method : 'POST' , path : '/return400' , handler ( request , reply ) { return reply . response ( 'NOPE' ) . code ( 400 ) ; } , } ) ; return server ; }