diff --git a/src/dapi/DataApiFactory.js b/src/dapi/DataApiFactory.js index 9c7dfb4..741dc7c 100644 --- a/src/dapi/DataApiFactory.js +++ b/src/dapi/DataApiFactory.js @@ -43,23 +43,44 @@ module.exports = Class( 'DataApiFactory', * The source and method have type-specific meaning; that is, "source" * may be a URL and "method" may be get/post for a RESTful service. * - * @param {string} type service type (e.g. "rest") - * @param {Object} desc API description + * @param {string} type service type (e.g. "rest") + * @param {Object} desc API description + * @param {Bucket} bucket active bucket + * @param {string} api_name dapi name * * @return {DataApi} appropriate DataApi instance */ - 'public fromType': function( type, desc, bucket ) + 'public fromType': function( type, desc, bucket, api_name ) { - const static_data = ( desc['static'] || [] ); - const nonempty = !!desc.static_nonempty; - const multiple = !!desc.static_multiple; + return this.descLookup( api_name, desc ).then( descl => + { + const static_data = ( descl['static'] || [] ); + const nonempty = !!descl.static_nonempty; + const multiple = !!descl.static_multiple; - const api = this._createDataApi( type, desc, bucket ); + const api = this._createDataApi( type, descl, bucket ); - return RestrictedDataApi( - StaticAdditionDataApi( api, nonempty, multiple, static_data ), - desc - ); + return RestrictedDataApi( + StaticAdditionDataApi( api, nonempty, multiple, static_data ), + descl + ); + } ); + }, + + + /** + * Look up dapi descriptor from configuration + * + * The default implementation just echoes back the given descriptor. + * + * @param {string} api_name dapi identifier + * @param {Object} desc given descriptor + * + * @return {Object} looked up descriptor + */ + 'virtual protected descLookup'( api_name, desc ) + { + return Promise.resolve( desc ); }, diff --git a/src/dapi/DataApiManager.js b/src/dapi/DataApiManager.js index 35d137c..9679058 100644 --- a/src/dapi/DataApiManager.js +++ b/src/dapi/DataApiManager.js @@ -38,7 +38,7 @@ module.exports = Class( 'DataApiManager' ) 'private _dataApiFactory': null, /** - * DataApi instances, indexed by API id + * DataApi instance promises, indexed by API id * @type {Object} */ 'private _dataApis': {}, @@ -157,18 +157,18 @@ module.exports = Class( 'DataApiManager' ) } // create the API if necessary (lazy-load); otherwise, use the existing - // instance - var api = this._dataApis[ api ] || ( function() + // instance (well, a promise for one) + var apip = this._dataApis[ api ] || ( function() { var apidesc = _self._apis[ api ]; // create a new instance of the API return _self._dataApis[ api ] = _self._dataApiFactory.fromType( - apidesc.type, apidesc, bucket - ).on( 'error', function( e ) - { - _self.emit( 'error', e ); - } ); + apidesc.type, apidesc, bucket, api + ) + .then( api => + api.on( 'error', e => _self.emit( 'error', e ) ) + ); } )(); // this has the effect of wiping out previous requests of the same id, @@ -187,28 +187,22 @@ module.exports = Class( 'DataApiManager' ) }; // process the request; we'll let them know when it comes back - try + apip.then( api => api.request( data, function() { - api.request( data, function() + // we only wish to populate the field if the request should + // still be considered pending + var curuid = ( _self._pendingApiCall[ id ] || {} ).uid; + if ( curuid === uid ) { - // we only wish to populate the field if the request should - // still be considered pending - var curuid = ( _self._pendingApiCall[ id ] || {} ).uid; - if ( curuid === uid ) - { - // forward to the caller - callback.apply( this, arguments ); + // forward to the caller + callback.apply( this, arguments ); - // clear the pending flag - _self._pendingApiCall[ id ] = undefined; - _self.emit( 'fieldLoaded', name, +index ); - } - } ); - } - catch ( e ) - { - fc( e ); - } + // clear the pending flag + _self._pendingApiCall[ id ] = undefined; + _self.emit( 'fieldLoaded', name, +index ); + } + } ) ) + .catch( e => fc( e ) ); }; // field is about to be re-loaded diff --git a/src/server/DocumentServer.js b/src/server/DocumentServer.js index adf0eb7..7d450c9 100644 --- a/src/server/DocumentServer.js +++ b/src/server/DocumentServer.js @@ -53,23 +53,27 @@ const { */ module.exports = Class( 'DocumentServer', { - 'public create': ( dao, logger, enc_service, origin_url ) => Server( - new JsonServerResponse.create(), - dao, - logger, - enc_service, + 'public create': ( dao, logger, enc_service, origin_url, conf ) => + Promise.all( [ + conf.get( 'dapi' ), + ] ).then( ([ dapi_conf ]) => Server( + new JsonServerResponse.create(), + dao, + logger, + enc_service, - DataProcessor( - bucket_filter, - ( apis, request ) => DataApiManager( - ServerDataApiFactory( - origin_url || request.getOrigin(), - request + DataProcessor( + bucket_filter, + ( apis, request ) => DataApiManager( + ServerDataApiFactory( + origin_url || request.getOrigin(), + request, + dapi_conf + ), + apis ), - apis - ), - DapiMetaSource( QuoteDataBucket ), - StagingBucket - ) - ), + DapiMetaSource( QuoteDataBucket ), + StagingBucket + ) + ) ) } ); diff --git a/src/server/daemon/Daemon.js b/src/server/daemon/Daemon.js index 700af97..87060fa 100644 --- a/src/server/daemon/Daemon.js +++ b/src/server/daemon/Daemon.js @@ -491,7 +491,7 @@ module.exports = AbstractClass( 'Daemon', { if ( router.init instanceof Function ) { - router.init( _self._debugLog, _self._encService ); + router.init( _self._debugLog, _self._encService, _self._conf ); } }); }, diff --git a/src/server/daemon/controller.js b/src/server/daemon/controller.js index 7062670..1cca831 100644 --- a/src/server/daemon/controller.js +++ b/src/server/daemon/controller.js @@ -96,7 +96,7 @@ var sflag = {}; exports.rater = {}; -exports.init = function( logger, enc_service ) +exports.init = function( logger, enc_service, conf ) { var db = new MongoDb( 'program', @@ -109,46 +109,50 @@ exports.init = function( logger, enc_service ) ); const dao = MongoServerDao( db ); - server = _createDocumentServer( dao, logger, enc_service ); - server_cache = _createCache( server ); - server.init( server_cache, exports.rater ); - - rating_service = RatingService( logger, dao, server, exports.rater ); - - // TODO: exports.init needs to support callbacks; this will work, but - // only because it's unlikely that we'll get a request within - // milliseconds of coming online - _initExportService( db, function( service ) + _createDocumentServer( dao, logger, enc_service, conf ).then( srv => { - c1_export_service = service; - } ); + server = srv; - server.on( 'quotePverUpdate', function( quote, program, event ) - { - // let them know that we're going to be a moment - var c = event.wait(); + server_cache = _createCache( server ); + server.init( server_cache, exports.rater ); - getCleaner( program ).clean( quote, function( err ) + rating_service = RatingService( logger, dao, server, exports.rater ); + + // TODO: exports.init needs to support callbacks; this will work, but + // only because it's unlikely that we'll get a request within + // milliseconds of coming online + _initExportService( db, function( service ) { - // report on our success/failure - if ( err ) - { - event.bad( err ); - } - else - { - event.good(); - } + c1_export_service = service; + } ); - // we're done - c(); + server.on( 'quotePverUpdate', function( quote, program, event ) + { + // let them know that we're going to be a moment + var c = event.wait(); + + getCleaner( program ).clean( quote, function( err ) + { + // report on our success/failure + if ( err ) + { + event.bad( err ); + } + else + { + event.good(); + } + + // we're done + c(); + } ); } ); } ); } -function _createDocumentServer( dao, logger, enc_service ) +function _createDocumentServer( dao, logger, enc_service, conf ) { const origin_url = process.env.HTTP_ORIGIN_URL || ''; @@ -163,7 +167,8 @@ function _createDocumentServer( dao, logger, enc_service ) ); } - return DocumentServer().create( dao, logger, enc_service, origin_url ); + return DocumentServer() + .create( dao, logger, enc_service, origin_url, conf ); } diff --git a/src/server/request/ServerDataApiFactory.js b/src/server/request/ServerDataApiFactory.js index 830b926..15495eb 100644 --- a/src/server/request/ServerDataApiFactory.js +++ b/src/server/request/ServerDataApiFactory.js @@ -21,12 +21,17 @@ const { Class } = require( 'easejs' ); const { - DataApiFactory, - http: { - NodeHttpImpl, - SpoofedNodeHttpImpl, + dapi: { + DataApiFactory, + http: { + NodeHttpImpl, + SpoofedNodeHttpImpl, + }, }, -} = require( '../..' ).dapi; + store: { + StoreMissError, + }, +} = require( '../..' ); /** @@ -47,11 +52,50 @@ module.exports = Class( 'ServerDataApiFactory' ) */ 'private _session': null, + /** + * Dapi configuration + * @type {Store} + */ + 'private _conf': null, - constructor( origin, session ) + + constructor( origin, session, conf ) { this._origin = ''+origin; this._session = session; + this._conf = conf; + }, + + + /** + * Look up dapi descriptor from configuration + * + * If no configuration is found for `api_name`, the original `desc` will + * be returned. Otherwise, they will be merged, with the lookup taking + * precedence. + * + * @param {string} api_name dapi identifier + * @param {Object} desc given descriptor + * + * @return {Object} looked up descriptor + */ + 'override protected descLookup'( api_name, desc ) + { + return this._conf.get( 'aliases' ) + .then( aliases => aliases.get( api_name ) ) + .then( desc_lookup => desc_lookup.reduce( + ( ret, value, key ) => + { + // merges the two, with lookup taking precedence + ret[ key ] = value; + return ret; + }, + Object.create( desc ) + ) ) + .catch( e => ( Class.isA( StoreMissError, e ) ) + ? desc + : Promise.reject( e ) + ); }, diff --git a/test/dapi/DataApiManagerTest.js b/test/dapi/DataApiManagerTest.js index 5b06707..49b3c30 100644 --- a/test/dapi/DataApiManagerTest.js +++ b/test/dapi/DataApiManagerTest.js @@ -116,7 +116,7 @@ function createStubDapiFactory( dapis ) return { fromType( type ) { - return dapis[ type ]; + return Promise.resolve( dapis[ type ] ); }, }; }