1
0
Fork 0

[BC BREAK] DataApi config lookup

This was a bit involved because the system had to be made async all
the way up the stack.  No attempt was made to clean up the mess up the
stack---no time.

* src/dapi/DataApiFactory.js
  (fromType): [BC BREAK] Fix docblock.  Add `api_name' param.  Call
    `#descLookup' and return promise.
  (descLookup): Add method.  Return promise resolving to provided
    descriptor.  Intended to be overridden by subtype.
* src/dapi/DataApiManager.js
  (_dataApis): Update docblock to indicate that it now stores
    promises.
  (getApiData): Expect promise for `DataApiFactory#fromType' call.
* src/server/DocumentServer.js: (create): [BC BREAK] Accept
    configuration.  Look up dapi conf and pass to
    `ServerDataApiFactory' ctor.  Return promise.
* src/server/daemon/Daemon.js (_initRouters): Provide configuration.
* src/server/daemon/controller.js
  (init): Accept configuration.  Handle return of promise from
    `_createDocumentServer'.
  (_createDocumentServer): Accept configuration, providing to
    `DocumentServer#create'.  Because of aforementioned change to
    `#create', returns promise.
* src/server/request/ServerDataApiFactory.js: Add StoreMissError
    import.
  (_conf): Add property.
  (constructor): [BC BREAK] Accept configuration.
  (descLookup): Add override.  Look up configuration for provided
    dapi.
master
Mike Gerwitz 2017-08-29 15:00:16 -04:00
parent 5f8fb2fcc5
commit 445783c256
7 changed files with 162 additions and 94 deletions

View File

@ -43,23 +43,44 @@ module.exports = Class( 'DataApiFactory',
* The source and method have type-specific meaning; that is, "source" * 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. * may be a URL and "method" may be get/post for a RESTful service.
* *
* @param {string} type service type (e.g. "rest") * @param {string} type service type (e.g. "rest")
* @param {Object} desc API description * @param {Object} desc API description
* @param {Bucket} bucket active bucket
* @param {string} api_name dapi name
* *
* @return {DataApi} appropriate DataApi instance * @return {DataApi} appropriate DataApi instance
*/ */
'public fromType': function( type, desc, bucket ) 'public fromType': function( type, desc, bucket, api_name )
{ {
const static_data = ( desc['static'] || [] ); return this.descLookup( api_name, desc ).then( descl =>
const nonempty = !!desc.static_nonempty; {
const multiple = !!desc.static_multiple; 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( return RestrictedDataApi(
StaticAdditionDataApi( api, nonempty, multiple, static_data ), StaticAdditionDataApi( api, nonempty, multiple, static_data ),
desc 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 );
}, },

View File

@ -38,7 +38,7 @@ module.exports = Class( 'DataApiManager' )
'private _dataApiFactory': null, 'private _dataApiFactory': null,
/** /**
* DataApi instances, indexed by API id * DataApi instance promises, indexed by API id
* @type {Object} * @type {Object}
*/ */
'private _dataApis': {}, 'private _dataApis': {},
@ -157,18 +157,18 @@ module.exports = Class( 'DataApiManager' )
} }
// create the API if necessary (lazy-load); otherwise, use the existing // create the API if necessary (lazy-load); otherwise, use the existing
// instance // instance (well, a promise for one)
var api = this._dataApis[ api ] || ( function() var apip = this._dataApis[ api ] || ( function()
{ {
var apidesc = _self._apis[ api ]; var apidesc = _self._apis[ api ];
// create a new instance of the API // create a new instance of the API
return _self._dataApis[ api ] = _self._dataApiFactory.fromType( return _self._dataApis[ api ] = _self._dataApiFactory.fromType(
apidesc.type, apidesc, bucket apidesc.type, apidesc, bucket, api
).on( 'error', function( e ) )
{ .then( api =>
_self.emit( 'error', e ); api.on( 'error', e => _self.emit( 'error', e ) )
} ); );
} )(); } )();
// this has the effect of wiping out previous requests of the same id, // 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 // 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 // forward to the caller
// still be considered pending callback.apply( this, arguments );
var curuid = ( _self._pendingApiCall[ id ] || {} ).uid;
if ( curuid === uid )
{
// forward to the caller
callback.apply( this, arguments );
// clear the pending flag // clear the pending flag
_self._pendingApiCall[ id ] = undefined; _self._pendingApiCall[ id ] = undefined;
_self.emit( 'fieldLoaded', name, +index ); _self.emit( 'fieldLoaded', name, +index );
} }
} ); } ) )
} .catch( e => fc( e ) );
catch ( e )
{
fc( e );
}
}; };
// field is about to be re-loaded // field is about to be re-loaded

View File

@ -53,23 +53,27 @@ const {
*/ */
module.exports = Class( 'DocumentServer', module.exports = Class( 'DocumentServer',
{ {
'public create': ( dao, logger, enc_service, origin_url ) => Server( 'public create': ( dao, logger, enc_service, origin_url, conf ) =>
new JsonServerResponse.create(), Promise.all( [
dao, conf.get( 'dapi' ),
logger, ] ).then( ([ dapi_conf ]) => Server(
enc_service, new JsonServerResponse.create(),
dao,
logger,
enc_service,
DataProcessor( DataProcessor(
bucket_filter, bucket_filter,
( apis, request ) => DataApiManager( ( apis, request ) => DataApiManager(
ServerDataApiFactory( ServerDataApiFactory(
origin_url || request.getOrigin(), origin_url || request.getOrigin(),
request request,
dapi_conf
),
apis
), ),
apis DapiMetaSource( QuoteDataBucket ),
), StagingBucket
DapiMetaSource( QuoteDataBucket ), )
StagingBucket ) )
)
),
} ); } );

View File

@ -491,7 +491,7 @@ module.exports = AbstractClass( 'Daemon',
{ {
if ( router.init instanceof Function ) if ( router.init instanceof Function )
{ {
router.init( _self._debugLog, _self._encService ); router.init( _self._debugLog, _self._encService, _self._conf );
} }
}); });
}, },

View File

@ -96,7 +96,7 @@ var sflag = {};
exports.rater = {}; exports.rater = {};
exports.init = function( logger, enc_service ) exports.init = function( logger, enc_service, conf )
{ {
var db = new MongoDb( var db = new MongoDb(
'program', 'program',
@ -109,46 +109,50 @@ exports.init = function( logger, enc_service )
); );
const dao = MongoServerDao( db ); const dao = MongoServerDao( db );
server = _createDocumentServer( dao, logger, enc_service );
server_cache = _createCache( server ); _createDocumentServer( dao, logger, enc_service, conf ).then( srv =>
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 )
{ {
c1_export_service = service; server = srv;
} );
server.on( 'quotePverUpdate', function( quote, program, event ) server_cache = _createCache( server );
{ server.init( server_cache, exports.rater );
// let them know that we're going to be a moment
var c = event.wait();
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 c1_export_service = service;
if ( err ) } );
{
event.bad( err );
}
else
{
event.good();
}
// we're done server.on( 'quotePverUpdate', function( quote, program, event )
c(); {
// 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 || ''; 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 );
} }

View File

@ -21,12 +21,17 @@
const { Class } = require( 'easejs' ); const { Class } = require( 'easejs' );
const { const {
DataApiFactory, dapi: {
http: { DataApiFactory,
NodeHttpImpl, http: {
SpoofedNodeHttpImpl, NodeHttpImpl,
SpoofedNodeHttpImpl,
},
}, },
} = require( '../..' ).dapi; store: {
StoreMissError,
},
} = require( '../..' );
/** /**
@ -47,11 +52,50 @@ module.exports = Class( 'ServerDataApiFactory' )
*/ */
'private _session': null, 'private _session': null,
/**
* Dapi configuration
* @type {Store}
*/
'private _conf': null,
constructor( origin, session )
constructor( origin, session, conf )
{ {
this._origin = ''+origin; this._origin = ''+origin;
this._session = session; 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 )
);
}, },

View File

@ -116,7 +116,7 @@ function createStubDapiFactory( dapis )
return { return {
fromType( type ) fromType( type )
{ {
return dapis[ type ]; return Promise.resolve( dapis[ type ] );
}, },
}; };
} }