import * as http from 'http' ; import * as fs from 'fs-extra' ; import * as url from 'url' ; import Mock from '../tasks/mock' ; import { httpHeaders } from './http' ; import Registry from './registry' ; import Handler from './handler' ; /** Abstract Handler for a request. */ abstract class NgApimockHandler implements Handler { MAX_RECORDINGS_PER_MOCK = 2 ; param registry param identifier param ngApimockId return abstract getSelection ( registry , identifier , ngApimockId ) ; param registry param ngApimockId return abstract getVariables ( registry , ngApimockId ? ) ; param registry param identifier param ngApimockId return abstract getEcho ( registry , identifier , ngApimockId ) ; param registry param identifier param ngApimockId return abstract getDelay ( registry , identifier , ngApimockId ) ; inheritDoc handleRequest ( request , response , next , registry , ngApimockId ) { const requestDataChunks = [ ] ; request . on ( 'data' , ( rawData ) => requestDataChunks . push ( rawData ) ) ; request . on ( 'end' , ( ) => { const payload = Buffer . concat ( requestDataChunks ) . toString ( ) ; const match = this . getMatchingMock ( registry . mocks , request . url , request . method , payload ) ; if ( match ) { const selection = this . getSelection ( registry , match . identifier , ngApimockId ) ; const variables = this . getVariables ( registry , ngApimockId ) ; const mockResponse = match . responses [ selection ] ; if ( mockResponse !== undefined ) { if ( this . getEcho ( registry , match . identifier , ngApimockId ) ) { console . log ( match . method + ' request made on \'' + match . expression + '\' with payload: ' , payload ) ; } const statusCode = mockResponse . status || 200 ; const jsonCallbackName = this . getJsonCallbackName ( request . url ) ; let headers ; let chunk ; if ( this . isBinaryResponse ( mockResponse ) ) { headers = mockResponse . headers || httpHeaders . CONTENT_TYPE_BINARY ; chunk = fs . readFileSync ( mockResponse . file ) ; } else { headers = mockResponse . headers || httpHeaders . CONTENT_TYPE_APPLICATION_JSON ; chunk = this . updateData ( mockResponse . data , variables , ( match . isArray ? [ ] : { } ) ) ; } if ( jsonCallbackName !== false ) { chunk = jsonCallbackName + '(' + chunk + ')' ; } const mockDelay = this . getDelay ( registry , match . identifier , ngApimockId ) ; const _delay = mockDelay === null || mockDelay === undefined ? mockResponse . delay : mockDelay ; const sendResponse = ( ) => { response . writeHead ( statusCode , headers ) ; response . end ( chunk ) ; if ( registry . record ) { this . storeRecording ( payload , chunk , request , statusCode , registry , match . identifier ) ; } } ; if ( _delay ) { setTimeout ( sendResponse , _delay ) ; } else { sendResponse ( ) ; } } else { if ( registry . record && ! request . headers . record ) { const headers = request . headers ; const host = < string > headers . host ; const options = { host : host . split ( ':' ) [ 0 ] , port : Number ( host . split ( ':' ) [ 1 ] ) , path : request . url , method : request . method , headers : headers } ; headers . record = 'off' ; const req = http . request ( options , ( res ) => { res . setEncoding ( 'utf8' ) ; res . on ( 'data' , ( chunk ) => { this . storeRecording ( payload , chunk . toString ( 'utf8' ) , request , res . statusCode , registry , match . identifier ) ; response . end ( chunk ) ; } ) ; } ) ; req . on ( 'error' , function ( e ) { response . end ( e ) ; } ) ; req . end ( ) ; } else { next ( ) ; } } } else { next ( ) ; } } ) ; } param mocks param requestUrl param method param payload returns getMatchingMock ( mocks , requestUrl , method , payload ) { const allMatches = mocks . filter ( _mock => { const expressionMatches = ! ! ( new RegExp ( _mock . expression ) . exec ( decodeURI ( requestUrl ) ) ) ; const methodMatches = _mock . method === method ; const bodyMatches = _mock . body ? ! ! ( new RegExp ( _mock . body ) . exec ( payload ) ) : true ; return expressionMatches && methodMatches && bodyMatches ; } ) ; return allMatches . filter ( _mock => _mock . body ) [ 0 ] || allMatches [ 0 ] ; } param response return { boolean } private isBinaryResponse ( response ) { return response . file !== undefined ; } param data param variables param defaults return private updateData ( data , variables , defaults ) { let _data ; if ( data !== undefined ) { _data = JSON . stringify ( data ) ; Object . keys ( variables ) . forEach ( function ( key ) { if ( variables . hasOwnProperty ( key ) ) { _data = _data . replace ( new RegExp ( '%%' + key + '%%' , 'g' ) , variables [ key ] ) ; } } ) ; } else { _data = JSON . stringify ( defaults ) ; } return _data ; } param requestUrl returns { string | boolean } private getJsonCallbackName ( requestUrl ) { const url_parts = url . parse ( requestUrl , true ) ; if ( ! url_parts . query || ! url_parts . query . callback ) { return false ; } return url_parts . query . callback ; } param payload param chunk param request param statusCode param registry param identifier private storeRecording ( payload , chunk , request , statusCode , registry , identifier ) { const result = { data : typeof chunk === 'string' ? chunk : chunk . toString ( 'utf8' ) , payload : payload , datetime : new Date ( ) . getTime ( ) , method : request . method , url : request . url , statusCode : statusCode } ; if ( registry . recordings [ identifier ] === undefined ) { registry . recordings [ identifier ] = [ ] ; } else if ( registry . recordings [ identifier ] . length > ( this . MAX_RECORDINGS_PER_MOCK - 1 ) ) { registry . recordings [ identifier ] . shift ( ) ; } registry . recordings [ identifier ] . push ( result ) ; } } export default NgApimockHandler ;