Better server-side change detection, QuoteDataApi
commit
b7139bdc6a
|
@ -80,6 +80,12 @@ module.exports = Class( 'StagingBucket' )
|
|||
*/
|
||||
'private _dirty': false,
|
||||
|
||||
/**
|
||||
* Prevent setCommittedValues from bypassing staging
|
||||
* @type {boolean}
|
||||
*/
|
||||
'private _noStagingBypass': false,
|
||||
|
||||
|
||||
/**
|
||||
* Initializes staging bucket with the provided data bucket
|
||||
|
@ -155,6 +161,11 @@ module.exports = Class( 'StagingBucket' )
|
|||
*/
|
||||
'public setCommittedValues': function( data /*, ...*/ )
|
||||
{
|
||||
if ( this._noStagingBypass )
|
||||
{
|
||||
return this.setValues.apply( this, arguments );
|
||||
}
|
||||
|
||||
this._bucket.setValues.apply( this._bucket, arguments );
|
||||
|
||||
// no use in triggering a pre-update, since these values are
|
||||
|
@ -165,6 +176,20 @@ module.exports = Class( 'StagingBucket' )
|
|||
},
|
||||
|
||||
|
||||
/**
|
||||
* Prevent #setCommittedValues from bypassing staging
|
||||
*
|
||||
* When set, #setCommittedValues will act as an alias of #setValues.
|
||||
*
|
||||
* @return {StagingBucket} self
|
||||
*/
|
||||
'public forbidBypass'()
|
||||
{
|
||||
this._noStagingBypass = true;
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether values have changed
|
||||
*
|
||||
|
@ -179,31 +204,108 @@ module.exports = Class( 'StagingBucket' )
|
|||
*/
|
||||
'private _hasChanged': function( data, merge_index )
|
||||
{
|
||||
let changed = false;
|
||||
|
||||
for ( let name in data )
|
||||
{
|
||||
let values = data[ name ];
|
||||
let cur = this._curdata[ name ] || [];
|
||||
let len = this._length( values );
|
||||
let has_null = ( len !== values.length );
|
||||
|
||||
if ( !merge_index && ( values.length !== cur.length ) )
|
||||
let merge_len_change = (
|
||||
merge_index && has_null && ( len < cur.length )
|
||||
);
|
||||
|
||||
let replace_len_change = (
|
||||
!merge_index && ( len !== cur.length )
|
||||
);
|
||||
|
||||
// quick change check (index removal if merge_index, or index
|
||||
// count change if not merge_index)
|
||||
if ( merge_len_change || replace_len_change )
|
||||
{
|
||||
return true;
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
for ( let index in values )
|
||||
for ( let index = 0; index < len; index++ )
|
||||
{
|
||||
if ( merge_index && ( values[ index ] === undefined ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( values[ index ] !== cur[ index ] )
|
||||
if ( !this._deepEqual( values[ index ], cur[ index ] ) )
|
||||
{
|
||||
return true;
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// unchanged
|
||||
values[ index ] = undefined;
|
||||
}
|
||||
|
||||
// if nothing is left, remove entirely
|
||||
if ( !values.some( x => x !== undefined ) )
|
||||
{
|
||||
delete data[ name ];
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Get actual length of vector
|
||||
*
|
||||
* This considers when the last element of the vector is a null value,
|
||||
* which is a truncation indicator.
|
||||
*
|
||||
* @param {Array} values value vector
|
||||
*
|
||||
* @return {number} length of vector considering truncation
|
||||
*/
|
||||
'private _length'( values )
|
||||
{
|
||||
if ( values[ values.length - 1 ] === null )
|
||||
{
|
||||
return values.length - 1;
|
||||
}
|
||||
|
||||
return values.length;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Recursively check for equality of two vavlues
|
||||
*
|
||||
* This only recognizes nested arrays (vectors). They are not
|
||||
* traditionally encountered in the bucket, but may exist.
|
||||
*
|
||||
* The final comparison is by string equality, since bucket values are
|
||||
* traditionally strings.
|
||||
*
|
||||
* @param {*} a first vector or scalar
|
||||
* @param {*} b second vector or scalar
|
||||
*
|
||||
* @return {boolean} whether `a` and `b` are equal
|
||||
*/
|
||||
'private _deepEqual'( a, b )
|
||||
{
|
||||
if ( Array.isArray( a ) )
|
||||
{
|
||||
if ( !Array.isArray( b ) || ( a.length !== b.length ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return a.map( ( item, i ) => this._deepEqual( item, b[ i ] ) )
|
||||
.every( res => res === true );
|
||||
}
|
||||
|
||||
return ''+a === ''+b;
|
||||
},
|
||||
|
||||
|
||||
|
|
|
@ -27,7 +27,14 @@ function _each( data, value, callback )
|
|||
|
||||
for ( var i = 0; i < data_len; i++ )
|
||||
{
|
||||
// index removals are null
|
||||
if ( data[ i ] === null )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
cur_val = ( value[ i ] !== undefined ) ? value[ i ] : cur_val;
|
||||
|
||||
result.push( callback( data[ i ], cur_val, i ) );
|
||||
}
|
||||
|
||||
|
@ -51,6 +58,11 @@ exports.join = function( data, value )
|
|||
{
|
||||
return _each( data, value, function( arr, delimiter )
|
||||
{
|
||||
if ( !Array.isArray( arr ) )
|
||||
{
|
||||
arr = [];
|
||||
}
|
||||
|
||||
return arr.join( delimiter );
|
||||
});
|
||||
};
|
||||
|
@ -121,7 +133,11 @@ exports.length = function( data )
|
|||
break;
|
||||
}
|
||||
|
||||
result.push( item.length );
|
||||
var len = ( item[ item.length - 1 ] === null )
|
||||
? item.length - 1
|
||||
: item.length;
|
||||
|
||||
result.push( len );
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -214,8 +230,8 @@ exports.date = function()
|
|||
// formatted as
|
||||
return [
|
||||
now.getFullYear() + '-'
|
||||
+ ( now.getMonth() + 1 ) + '-'
|
||||
+ now.getDate()
|
||||
+ ( '0' + ( now.getMonth() + 1 ) ).substr( -2 ) + '-'
|
||||
+ ( '0' + now.getDate() ).substr( -2 )
|
||||
];
|
||||
};
|
||||
|
||||
|
@ -277,8 +293,8 @@ exports.relativeDate = function( data, value )
|
|||
// return in the YYYY-MM-DD format, since that's what our fields are
|
||||
// formatted as
|
||||
return date_new.getFullYear() + '-'
|
||||
+ ( date_new.getMonth() + 1 ) + '-'
|
||||
+ date_new.getDate();
|
||||
+ ( '0' + ( date_new.getMonth() + 1 ) ).substr( -2 ) + '-'
|
||||
+ ( '0' + date_new.getDate() ).substr( -2 );
|
||||
} );
|
||||
};
|
||||
|
||||
|
@ -647,8 +663,43 @@ exports.value = function( data, indexes )
|
|||
};
|
||||
|
||||
|
||||
exports.repeat = function( data, value )
|
||||
{
|
||||
var times = value[ 0 ] || 0;
|
||||
var result = [];
|
||||
|
||||
while ( times-- > 0 )
|
||||
{
|
||||
result.push( data );
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
exports.repeatConcat = function( data, value )
|
||||
{
|
||||
var times = value[ 0 ] || 0;
|
||||
var result = [];
|
||||
|
||||
while ( times-- > 0 )
|
||||
{
|
||||
result = result.concat( data );
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
exports.index = function( data, value )
|
||||
{
|
||||
var index = value[ 0 ] || 0;
|
||||
|
||||
return data[ index ] || [];
|
||||
};
|
||||
|
||||
|
||||
exports[ 'void' ] = function()
|
||||
{
|
||||
return [];
|
||||
};
|
||||
|
||||
|
|
|
@ -19,7 +19,9 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var Interface = require( 'easejs' ).Interface;
|
||||
'use strict';
|
||||
|
||||
const { Interface } = require( 'easejs' );
|
||||
|
||||
|
||||
/**
|
||||
|
@ -42,8 +44,8 @@ module.exports = Interface( 'DataApi',
|
|||
* The first parameter of the callback shall contain an Error in the event
|
||||
* of a failure; otherwise, it shall be null.
|
||||
*
|
||||
* @param {Object=} data request params
|
||||
* @param {function(?Error,*)} callback continuation upon reply
|
||||
* @param {?Object<string,string>|string} data params or post data
|
||||
* @param {function(?Error,*):string} callback continuation upon reply
|
||||
*
|
||||
* @return {DataApi} self
|
||||
*/
|
||||
|
|
|
@ -19,13 +19,17 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Class = require( 'easejs' ).Class;
|
||||
const HttpDataApi = require( './http/HttpDataApi' );
|
||||
const XhrHttpImpl = require( './http/XhrHttpImpl' );
|
||||
const JsonResponse = require( './format/JsonResponse' );
|
||||
const ResponseApply = require( './format/ResponseApply' );
|
||||
const RestrictedDataApi = require( './RestrictedDataApi' );
|
||||
const StaticAdditionDataApi = require( './StaticAdditionDataApi' );
|
||||
const BucketDataApi = require( './BucketDataApi' );
|
||||
const QuoteDataApi = require( './QuoteDataApi' );
|
||||
|
||||
|
||||
/**
|
||||
|
@ -36,8 +40,8 @@ module.exports = Class( 'DataApiFactory',
|
|||
/**
|
||||
* Return a DataApi instance for the requested service type
|
||||
*
|
||||
* 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.
|
||||
* 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
|
||||
|
@ -46,39 +50,11 @@ module.exports = Class( 'DataApiFactory',
|
|||
*/
|
||||
'public fromType': function( type, desc, bucket )
|
||||
{
|
||||
var api = null,
|
||||
source = ( desc.source || '' ),
|
||||
method = ( desc.method || '' ),
|
||||
const static_data = ( desc['static'] || [] );
|
||||
const nonempty = !!desc.static_nonempty;
|
||||
const multiple = !!desc.static_multiple;
|
||||
|
||||
static_data = ( desc['static'] || [] ),
|
||||
nonempty = !!desc.static_nonempty,
|
||||
multiple = !!desc.static_multiple;
|
||||
|
||||
switch ( type )
|
||||
{
|
||||
case 'rest':
|
||||
const impl = this.createHttpImpl();
|
||||
|
||||
api = HttpDataApi.use( JsonResponse )(
|
||||
source,
|
||||
method.toUpperCase(),
|
||||
impl
|
||||
);
|
||||
break;
|
||||
|
||||
case 'local':
|
||||
// currently, only local bucket data sources are supported
|
||||
if ( source !== 'bucket' )
|
||||
{
|
||||
throw Error( "Unknown local data API source: " + source );
|
||||
}
|
||||
|
||||
api = BucketDataApi( bucket, desc.retvals );
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error( 'Unknown data API type: ' + type );
|
||||
};
|
||||
const api = this._createDataApi( type, desc, bucket );
|
||||
|
||||
return RestrictedDataApi(
|
||||
StaticAdditionDataApi( api, nonempty, multiple, static_data ),
|
||||
|
@ -87,6 +63,90 @@ module.exports = Class( 'DataApiFactory',
|
|||
},
|
||||
|
||||
|
||||
/**
|
||||
* Create DataApi instance
|
||||
*
|
||||
* @param {string} type API type
|
||||
* @param {Object} desc API descriptor
|
||||
* @param {Bucket} bucket data bucket
|
||||
*
|
||||
* @return {DataApi}
|
||||
*/
|
||||
'private _createDataApi'( type, desc, bucket )
|
||||
{
|
||||
const source = ( desc.source || '' );
|
||||
const method = ( desc.method || '' );
|
||||
const enctype = ( desc.enctype || '' );
|
||||
|
||||
switch ( type )
|
||||
{
|
||||
case 'rest':
|
||||
return this._createHttp(
|
||||
HttpDataApi.use( JsonResponse ),
|
||||
source,
|
||||
method,
|
||||
enctype
|
||||
);
|
||||
|
||||
case 'local':
|
||||
// currently, only local bucket data sources are supported
|
||||
if ( source !== 'bucket' )
|
||||
{
|
||||
throw Error( "Unknown local data API source: " + source );
|
||||
}
|
||||
|
||||
return BucketDataApi( bucket, desc.retvals );
|
||||
|
||||
case 'quote':
|
||||
return QuoteDataApi(
|
||||
this._createHttp(
|
||||
HttpDataApi
|
||||
.use( JsonResponse )
|
||||
.use( ResponseApply( data => [ data ] ) ),
|
||||
source,
|
||||
method,
|
||||
enctype
|
||||
)
|
||||
);
|
||||
|
||||
default:
|
||||
throw Error( 'Unknown data API type: ' + type );
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Create HttpDataApi instance
|
||||
*
|
||||
* The `Base` is intended to allow for the caller to mix traits in.
|
||||
*
|
||||
* @param {HttpDataApi} Base HttpDataApi type
|
||||
* @param {string} source URL
|
||||
* @param {string} method HTTP method
|
||||
* @param {string} enctype MIME media type (for POST)
|
||||
*
|
||||
* @return {HttpDataApi}
|
||||
*/
|
||||
'private _createHttp'( Base, source, method, enctype )
|
||||
{
|
||||
const impl = this.createHttpImpl();
|
||||
|
||||
return Base(
|
||||
source,
|
||||
method.toUpperCase(),
|
||||
impl,
|
||||
enctype
|
||||
);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Create HttpImpl
|
||||
*
|
||||
* This is simply intended to allow subtypes to override the type.
|
||||
*
|
||||
* @return {XhrHttpImpl}
|
||||
*/
|
||||
'virtual protected createHttpImpl'()
|
||||
{
|
||||
return XhrHttpImpl( XMLHttpRequest );
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
/**
|
||||
* Transform key/value data into standard quote request
|
||||
*
|
||||
* Copyright (C) 2017 R-T Specialty, LLC.
|
||||
*
|
||||
* This file is part of the Liza Data Collection Framework
|
||||
*
|
||||
* liza is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This is insurance-specific using a standardized request format for
|
||||
* producing insurance quote data.
|
||||
*/
|
||||
|
||||
|
||||
const { Class } = require( 'easejs' );
|
||||
const DataApi = require( './DataApi' );
|
||||
const EventEmitter = require( 'events' ).EventEmitter;
|
||||
|
||||
|
||||
/**
|
||||
* Structure flat key/value data for quote request
|
||||
*
|
||||
* The request structure can be seen in #mapData. No fields are required to
|
||||
* be present---they all have defaults; the philosophy is to allow the
|
||||
* server to fail if necessary. Basic validations (e.g. ensuring correct
|
||||
* data type and format) may be added in the future.
|
||||
*
|
||||
* This DataApi is responsible only for data transformation---it is expected
|
||||
* to decorate a DataApi capable of performing an actual data transfer.
|
||||
*/
|
||||
module.exports = Class( 'QuoteDataApi' )
|
||||
.implement( DataApi )
|
||||
.extend(
|
||||
{
|
||||
/**
|
||||
* Decorated DataApi
|
||||
*
|
||||
* @type {DataApi}
|
||||
*/
|
||||
'private _dapi': null,
|
||||
|
||||
|
||||
/**
|
||||
* Initialize with DataApi to decorate
|
||||
*
|
||||
* @param {DataApi} dapi subject to decorate
|
||||
*/
|
||||
constructor( dapi )
|
||||
{
|
||||
if ( !( Class.isA( DataApi, dapi ) ) )
|
||||
{
|
||||
throw TypeError(
|
||||
'Expected object of type DataApi; given: ' + data_api
|
||||
);
|
||||
}
|
||||
|
||||
this._dapi = dapi;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Request data from the service
|
||||
*
|
||||
* @param {Object=} data request params
|
||||
* @param {function(?Error,Object)=} callback server response callback
|
||||
*
|
||||
* @return {DataApi} self
|
||||
*/
|
||||
'public request'( data, callback )
|
||||
{
|
||||
this._dapi.request( this.mapData( data ), callback );
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Map key/value data into quote request
|
||||
*
|
||||
* @param {Object} data key/value data
|
||||
*
|
||||
* @return {Object} mapped request data
|
||||
*/
|
||||
'protected mapData'( data )
|
||||
{
|
||||
const rate_date = data.rate_date || data.effective_date || "";
|
||||
|
||||
return {
|
||||
"effective_date": this._formatDate( data.effective_date || "" ),
|
||||
"rate_date": this._formatDate( rate_date ),
|
||||
"insured": {
|
||||
"location": {
|
||||
"city": data.insured_city || "",
|
||||
"state": data.insured_state || "",
|
||||
"zip": data.insured_zip || "",
|
||||
"county": data.insured_county || "",
|
||||
},
|
||||
"business_year_count": +data.business_year_count || 0,
|
||||
},
|
||||
"coverages": ( data.classes || [] ).map(
|
||||
( class_code, i ) => ( {
|
||||
"class": class_code,
|
||||
"limit": {
|
||||
"occurrence": +( data.limit_occurrence || 0 ),
|
||||
"aggregate": +( data.limit_aggregate || 0 ),
|
||||
},
|
||||
"exposure": +( data.exposure || [] )[ i ] || 0,
|
||||
} )
|
||||
),
|
||||
"losses": ( data.loss_type || [] ).map(
|
||||
loss_type => ( {
|
||||
type: loss_type,
|
||||
} )
|
||||
),
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Append time to ISO 8601 date+time format
|
||||
*
|
||||
* This is required by some services.
|
||||
*
|
||||
* @type {string} date ISO 8601 date (without time)
|
||||
*
|
||||
* @return {string} ISO 8601 combined date and time
|
||||
*/
|
||||
'private _formatDate'( date )
|
||||
{
|
||||
return ( date === "" )
|
||||
? ""
|
||||
: ( date + "T00:00:00" );
|
||||
},
|
||||
} );
|
|
@ -85,7 +85,7 @@ module.exports = Class( 'RestrictedDataApi' )
|
|||
*
|
||||
* @return {DataApi} self
|
||||
*/
|
||||
'public request': function( data, callback )
|
||||
'virtual public request': function( data, callback )
|
||||
{
|
||||
data = data || {};
|
||||
callback = callback || function() {};
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* Applies arbitrary function to response data
|
||||
*
|
||||
* Copyright (C) 2017 R-T Specialty, LLC.
|
||||
*
|
||||
* This file is part of the Liza Data Collection Framework
|
||||
*
|
||||
* Liza is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var Trait = require( 'easejs' ).Trait,
|
||||
DataApi = require( '../DataApi' );
|
||||
|
||||
|
||||
module.exports = Trait( 'ResponseApply' )
|
||||
.implement( DataApi )
|
||||
.extend(
|
||||
{
|
||||
/**
|
||||
* Function to apply to data
|
||||
*
|
||||
* @type {function(*)}
|
||||
*/
|
||||
'private _dataf': () => {},
|
||||
|
||||
|
||||
/**
|
||||
* Initialize with function to apply to return data
|
||||
*
|
||||
* @param {function(*)} req_callback return data function
|
||||
*/
|
||||
__mixin( dataf )
|
||||
{
|
||||
if ( typeof dataf !== 'function' )
|
||||
{
|
||||
throw TypeError( 'expected function for #request callback' );
|
||||
}
|
||||
|
||||
this._dataf = dataf;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Apply function to response
|
||||
*
|
||||
* The function provided during mixin will be applied to the response
|
||||
* data to produce a new response.
|
||||
*
|
||||
* It is not recommended to use this trait for complex transformations;
|
||||
* a new trait should be created instead.
|
||||
*
|
||||
* @param {string} data binary data to transmit
|
||||
* @param {function(?Error,*)} callback continuation upon reply
|
||||
*
|
||||
* @return {DataApi} self
|
||||
*/
|
||||
'virtual abstract override public request'( data, callback )
|
||||
{
|
||||
this.__super( data, ( e, retdata ) =>
|
||||
{
|
||||
callback( e, this._dataf( retdata ) );
|
||||
} );
|
||||
|
||||
return this;
|
||||
},
|
||||
} );
|
|
@ -19,12 +19,14 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var Class = require( 'easejs' ).Class,
|
||||
DataApi = require( '../DataApi' ),
|
||||
HttpImpl = require( './HttpImpl' ),
|
||||
'use strict';
|
||||
|
||||
const { Class } = require( 'easejs' );
|
||||
const DataApi = require( '../DataApi' );
|
||||
const HttpImpl = require( './HttpImpl' );
|
||||
|
||||
// RFC 2616 methods
|
||||
rfcmethods = {
|
||||
const rfcmethods = {
|
||||
DELETE: true,
|
||||
GET: true,
|
||||
HEAD: true,
|
||||
|
@ -61,6 +63,12 @@ module.exports = Class( 'HttpDataApi' )
|
|||
*/
|
||||
'private _impl': null,
|
||||
|
||||
/**
|
||||
* MIME media type
|
||||
* @type {string}
|
||||
*/
|
||||
'private _enctype': '',
|
||||
|
||||
|
||||
/**
|
||||
* Initialize Data API with destination and HTTP implementation
|
||||
|
@ -69,15 +77,18 @@ module.exports = Class( 'HttpDataApi' )
|
|||
* requests, which permits the user to use whatever implementation works
|
||||
* well with their existing system.
|
||||
*
|
||||
* Default `enctype` is `application/x-www-form-urlencoded`.
|
||||
*
|
||||
* TODO: Accept URI encoder.
|
||||
*
|
||||
* @param {string} url destination URL
|
||||
* @param {string} method RFC-2616-compliant HTTP method
|
||||
* @param {HttpImpl} impl HTTP implementation
|
||||
* @param {string=} enctype MIME media type
|
||||
*
|
||||
* @throws {TypeError} when non-HttpImpl is provided
|
||||
*/
|
||||
__construct: function( url, method, impl )
|
||||
__construct: function( url, method, impl, enctype )
|
||||
{
|
||||
if ( !( Class.isA( HttpImpl, impl ) ) )
|
||||
{
|
||||
|
@ -87,6 +98,10 @@ module.exports = Class( 'HttpDataApi' )
|
|||
this._url = ''+url;
|
||||
this._method = this._validateMethod( method );
|
||||
this._impl = impl;
|
||||
|
||||
this._enctype = ( enctype )
|
||||
? ''+enctype
|
||||
: 'application/x-www-form-urlencoded';
|
||||
},
|
||||
|
||||
|
||||
|
@ -127,7 +142,7 @@ module.exports = Class( 'HttpDataApi' )
|
|||
this._impl.requestData(
|
||||
this._url,
|
||||
this._method,
|
||||
this._encodeData( data ),
|
||||
this.encodeData( data ),
|
||||
callback
|
||||
);
|
||||
|
||||
|
@ -164,7 +179,7 @@ module.exports = Class( 'HttpDataApi' )
|
|||
*/
|
||||
'private _validateDataType': function( data )
|
||||
{
|
||||
var type = typeof data;
|
||||
const type = typeof data;
|
||||
|
||||
if( !( ( type === 'string' ) || ( type === 'object' ) ) )
|
||||
{
|
||||
|
@ -177,51 +192,57 @@ module.exports = Class( 'HttpDataApi' )
|
|||
|
||||
|
||||
/**
|
||||
* If the data are an object, it's converted to an encoded key-value
|
||||
* URI; otherwise, the original string datum is returned.
|
||||
* Generate params for URI from key-value `data`
|
||||
*
|
||||
* @param {?Object<string,string>|string=} data raw data or key-value
|
||||
* Conversion depends on the MIME type (enctype) with which this instance
|
||||
* was initialized. For example, `application/x-www-form-urlencoded`
|
||||
* will result in a urlencoded string, whereas `application/json` will
|
||||
* simply be serialized.
|
||||
*
|
||||
* @return {string} encoded data
|
||||
* If `data` is not an object, it will be returned as a string datum.
|
||||
*
|
||||
* @param {Object<string,string>|string} data key-value request params
|
||||
*
|
||||
* @return {string} generated URI, or empty if no keys
|
||||
*/
|
||||
'private _encodeData': function( data )
|
||||
'protected encodeData': function( data )
|
||||
{
|
||||
if ( typeof data !== 'object' )
|
||||
{
|
||||
return ''+data;
|
||||
}
|
||||
|
||||
return this._encodeKeys( data );
|
||||
if ( this._method !== 'POST' )
|
||||
{
|
||||
return this._urlEncode( data );
|
||||
}
|
||||
|
||||
switch ( this._enctype )
|
||||
{
|
||||
case 'application/x-www-form-urlencoded':
|
||||
return this._urlEncode( data );
|
||||
|
||||
case 'application/json':
|
||||
return JSON.stringify( data );
|
||||
|
||||
default:
|
||||
throw Error( 'Unknown enctype for POST: ' + this._enctype );
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Generate params for URI from key-value DATA
|
||||
* urlencode each key of provided object
|
||||
*
|
||||
* @param {Object<string,string>} data key-value request params
|
||||
* @param {Object} obj key/value
|
||||
*
|
||||
* @return {string} generated URI, or empty if no keys
|
||||
* @return {string} urlencoded string, joined with '&'
|
||||
*/
|
||||
'private _encodeKeys': function( obj )
|
||||
'private _urlEncode'( obj )
|
||||
{
|
||||
var uri = '';
|
||||
|
||||
// ES3 support
|
||||
for ( var key in obj )
|
||||
{
|
||||
if ( !Object.prototype.hasOwnProperty.call( obj, key ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
uri += ( uri )
|
||||
? '&'
|
||||
: '';
|
||||
|
||||
uri += encodeURIComponent( key ) + '=' +
|
||||
encodeURIComponent( obj[ key ] );
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
return Object.keys( obj ).map( key =>
|
||||
encodeURIComponent( key ) + '=' +
|
||||
encodeURIComponent( obj[ key ] )
|
||||
).join( '&' );
|
||||
},
|
||||
} );
|
||||
|
|
|
@ -25,6 +25,7 @@ const {
|
|||
bucket: {
|
||||
bucket_filter,
|
||||
QuoteDataBucket,
|
||||
StagingBucket,
|
||||
},
|
||||
|
||||
dapi: {
|
||||
|
@ -67,7 +68,8 @@ module.exports = Class( 'DocumentServer',
|
|||
),
|
||||
apis
|
||||
),
|
||||
DapiMetaSource( QuoteDataBucket )
|
||||
DapiMetaSource( QuoteDataBucket ),
|
||||
StagingBucket
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
|
|
@ -1140,12 +1140,7 @@ module.exports = Class( 'Server' )
|
|||
parsed_data, request, program, bucket
|
||||
);
|
||||
|
||||
quote.setData( filtered );
|
||||
|
||||
server._monitorMetadataPromise( quote, dapis );
|
||||
|
||||
// calculated values (store only)
|
||||
program.initQuote( bucket, true );
|
||||
}
|
||||
catch ( err )
|
||||
{
|
||||
|
@ -1185,11 +1180,10 @@ module.exports = Class( 'Server' )
|
|||
)
|
||||
)
|
||||
.catch( e =>
|
||||
server.logger.log(
|
||||
server.logger.PRIORITY_ERROR,
|
||||
"Failed to save field %s[%s] metadata: %s",
|
||||
field,
|
||||
index,
|
||||
this.logger.log(
|
||||
this.logger.PRIORITY_ERROR,
|
||||
"Failed to save metadata (quote id %d): %s",
|
||||
quote.getId(),
|
||||
e.message
|
||||
)
|
||||
)
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
const { Class } = require( 'easejs' );
|
||||
|
||||
const { QuoteDataBucket } = require( '../../' ).bucket;
|
||||
const { QuoteDataBucket, StagingBucket } = require( '../../' ).bucket;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -56,15 +56,20 @@ module.exports = Class( 'DataProcessor',
|
|||
/**
|
||||
* Initialize processor
|
||||
*
|
||||
* The staging bucket constructor will be used to wrap the bucket for
|
||||
* diff-related operations.
|
||||
*
|
||||
* @param {Object} filter bucket filter
|
||||
* @param {function()} dapif data API constructor
|
||||
* @param {DapiMetaSource} meta_source metadata source
|
||||
* @param {function(Bucket)} staging_ctor staging bucket constructor
|
||||
*/
|
||||
constructor( filter, dapif, meta_source )
|
||||
constructor( filter, dapif, meta_source, staging_ctor )
|
||||
{
|
||||
this._filter = filter;
|
||||
this._dapif = dapif;
|
||||
this._metaSource = meta_source;
|
||||
this._stagingCtor = staging_ctor;
|
||||
},
|
||||
|
||||
|
||||
|
@ -86,12 +91,21 @@ module.exports = Class( 'DataProcessor',
|
|||
{
|
||||
const filtered = this.sanitizeDiff( data, request, program, false );
|
||||
const dapi_manager = this._dapif( program.apis, request );
|
||||
const staging = this._stagingCtor( bucket );
|
||||
|
||||
// forbidBypass will force diff generation on initQuote
|
||||
staging.setValues( filtered, true );
|
||||
staging.forbidBypass();
|
||||
|
||||
program.initQuote( staging, true );
|
||||
|
||||
// array of promises for any dapi requests
|
||||
const dapis = this._triggerDapis(
|
||||
dapi_manager, program, data, bucket
|
||||
dapi_manager, program, staging.getDiff(), staging
|
||||
);
|
||||
|
||||
staging.commit();
|
||||
|
||||
return {
|
||||
filtered: filtered,
|
||||
dapis: dapis,
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
const { Class } = require( 'easejs' );
|
||||
const root = require( '../../' );
|
||||
const expect = require( 'chai' ).expect;
|
||||
const sinon = require( 'sinon' );
|
||||
|
||||
const {
|
||||
Bucket,
|
||||
|
@ -111,6 +112,30 @@ describe( 'StagingBucket', () =>
|
|||
merge_index: true,
|
||||
is_change: true,
|
||||
},
|
||||
{
|
||||
initial: { foo: [ 'bar', 'baz' ] },
|
||||
update: { foo: [ 'bar', 'baz', null ] },
|
||||
merge_index: true,
|
||||
is_change: false,
|
||||
},
|
||||
{
|
||||
initial: { foo: [ 'bar', 'baz' ] },
|
||||
update: { foo: [ 'bar', 'baz', null ] },
|
||||
merge_index: false,
|
||||
is_change: false,
|
||||
},
|
||||
{
|
||||
initial: { foo: [ 'bar', 'baz' ] },
|
||||
update: { foo: [ 'bar', 'baz', 'quux' ] },
|
||||
merge_index: true,
|
||||
is_change: true,
|
||||
},
|
||||
{
|
||||
initial: { foo: [ 'bar', 'baz' ] },
|
||||
update: { foo: [ 'bar', 'baz', 'quux' ] },
|
||||
merge_index: false,
|
||||
is_change: true,
|
||||
},
|
||||
{
|
||||
initial: { foo: [ 'bar', 'baz' ] },
|
||||
update: { foo: [] },
|
||||
|
@ -161,6 +186,48 @@ describe( 'StagingBucket', () =>
|
|||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
|
||||
describe( "#setCommittedValues", () =>
|
||||
{
|
||||
it( "bypasses staging bucket without no bypass flag", () =>
|
||||
{
|
||||
const b = createStubBucket();
|
||||
const bmock = sinon.mock( b );
|
||||
const data = { foo: [ "bar" ] };
|
||||
const sut = Sut( b );
|
||||
|
||||
bmock.expects( 'setValues' )
|
||||
.once()
|
||||
.withExactArgs( data );
|
||||
|
||||
sut.setCommittedValues( data );
|
||||
|
||||
// no diff if bypassed
|
||||
expect( sut.getDiff() ).to.deep.equal( {} );
|
||||
|
||||
bmock.verify();
|
||||
} );
|
||||
|
||||
|
||||
it( "does not bypasses staging bucket with no bypass flag", () =>
|
||||
{
|
||||
const b = createStubBucket();
|
||||
const bmock = sinon.mock( b );
|
||||
const data = { foo: [ "bar" ] };
|
||||
const sut = Sut( b );
|
||||
|
||||
bmock.expects( 'setValues' ).never();
|
||||
|
||||
sut.forbidBypass();
|
||||
sut.setCommittedValues( data );
|
||||
|
||||
// should have been staged
|
||||
expect( sut.getDiff() ).to.deep.equal( data );
|
||||
|
||||
bmock.verify();
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* Applies arbitrary function to response data
|
||||
*
|
||||
* Copyright (C) 2017 R-T Specialty, LLC.
|
||||
*
|
||||
* This file is part of the Liza Data Collection Framework
|
||||
*
|
||||
* Liza is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const { Class } = require( 'easejs' );
|
||||
const { DataApi } = require( '../../' ).dapi;
|
||||
|
||||
|
||||
/**
|
||||
* Dummy DataApi implementation for testing
|
||||
*
|
||||
* This should not be used in production.
|
||||
*/
|
||||
module.exports = Class( 'DummyDataApi' )
|
||||
.implement( DataApi )
|
||||
.extend(
|
||||
{
|
||||
/**
|
||||
* #request callback
|
||||
*
|
||||
* @type {Function} #request method callback
|
||||
*/
|
||||
'private _reqCallback': () => {},
|
||||
|
||||
|
||||
/**
|
||||
* Initialize with `#request` method callback
|
||||
*
|
||||
* @param {Function} req_callback #request method callback
|
||||
*/
|
||||
constructor( req_callback )
|
||||
{
|
||||
this._reqCallback = req_callback;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Dummy method that invokes the callback provided via constructor
|
||||
*
|
||||
* @param {?Object<string,string>|string} data request params or post data
|
||||
* @param {function(?Error,*):string} callback continuation upon reply
|
||||
*
|
||||
* @return {DataApi} self
|
||||
*/
|
||||
'virtual public request'( data, callback )
|
||||
{
|
||||
this._reqCallback( data, callback );
|
||||
return this;
|
||||
},
|
||||
} );
|
|
@ -0,0 +1,146 @@
|
|||
/**
|
||||
* Tests QuoteDataApi
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const { expect } = require( 'chai' );
|
||||
const { Class } = require( 'easejs' );
|
||||
const DummyDataApi = require( './DummyDataApi' );
|
||||
|
||||
const {
|
||||
DataApi,
|
||||
QuoteDataApi: Sut
|
||||
} = require( '../../' ).dapi;
|
||||
|
||||
|
||||
describe( 'QuoteDataApi', () =>
|
||||
{
|
||||
[
|
||||
// empty request; use defaults
|
||||
{
|
||||
given: {},
|
||||
|
||||
expected: {
|
||||
"effective_date": "",
|
||||
"rate_date": "",
|
||||
"insured": {
|
||||
"location": {
|
||||
"city": "",
|
||||
"state": "",
|
||||
"zip": "",
|
||||
"county": ""
|
||||
},
|
||||
"business_year_count": 0,
|
||||
},
|
||||
"coverages": [],
|
||||
"losses": [],
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
// empty coverage
|
||||
{
|
||||
given: {
|
||||
classes: [ "11111" ],
|
||||
},
|
||||
|
||||
expected: {
|
||||
"effective_date": "",
|
||||
"rate_date": "",
|
||||
"insured": {
|
||||
"location": {
|
||||
"city": "",
|
||||
"state": "",
|
||||
"zip": "",
|
||||
"county": ""
|
||||
},
|
||||
"business_year_count": 0,
|
||||
},
|
||||
"coverages": [
|
||||
{
|
||||
"class": "11111",
|
||||
"limit": {
|
||||
"occurrence": 0,
|
||||
"aggregate": 0,
|
||||
},
|
||||
"exposure": 0,
|
||||
},
|
||||
],
|
||||
"losses": [],
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
// full request
|
||||
{
|
||||
given: {
|
||||
effective_date: "12345",
|
||||
rate_date: "2345",
|
||||
insured_city: "Buffalo",
|
||||
insured_state: "NY",
|
||||
insured_zip: "14043",
|
||||
insured_county: "Erie",
|
||||
business_year_count: "1",
|
||||
classes: [ "11111", "11112" ],
|
||||
limit_occurrence: "100",
|
||||
limit_aggregate: "200",
|
||||
exposure: [ "200", "300" ],
|
||||
loss_type: [ "gl", "property" ],
|
||||
},
|
||||
|
||||
expected: {
|
||||
"effective_date": "12345T00:00:00",
|
||||
"rate_date": "2345T00:00:00",
|
||||
"insured": {
|
||||
"location": {
|
||||
"city": "Buffalo",
|
||||
"state": "NY",
|
||||
"zip": "14043",
|
||||
"county": "Erie"
|
||||
},
|
||||
"business_year_count": 1,
|
||||
},
|
||||
"coverages": [
|
||||
{
|
||||
"class": "11111",
|
||||
"limit": {
|
||||
"occurrence": 100,
|
||||
"aggregate": 200,
|
||||
},
|
||||
"exposure": 200,
|
||||
},
|
||||
{
|
||||
"class": "11112",
|
||||
"limit": {
|
||||
"occurrence": 100,
|
||||
"aggregate": 200,
|
||||
},
|
||||
"exposure": 300,
|
||||
},
|
||||
],
|
||||
"losses": [
|
||||
{ type: "gl" },
|
||||
{ type: "property" },
|
||||
],
|
||||
},
|
||||
},
|
||||
].forEach( ( { given, expected }, i ) => {
|
||||
it( `maps input data to structured object (#${i})`, done =>
|
||||
{
|
||||
const dummyc = () => {};
|
||||
|
||||
const mock_dapi = DummyDataApi( ( data, callback ) =>
|
||||
{
|
||||
expect( data ).to.deep.equal( expected );
|
||||
expect( callback ).to.equal( dummyc );
|
||||
|
||||
done();
|
||||
} );
|
||||
|
||||
const sut = Sut( mock_dapi );
|
||||
|
||||
sut.request( given, dummyc );
|
||||
} );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* Tests ResponseApply
|
||||
*
|
||||
* Copyright (C) 2017 R-T Specialty, LLC.
|
||||
*
|
||||
* This file is part of the Liza Data Collection Framework
|
||||
*
|
||||
* liza is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const { expect } = require( 'chai' );
|
||||
const { Class } = require( 'easejs' );
|
||||
const DummyDataApi = require( '../DummyDataApi' );
|
||||
|
||||
const {
|
||||
DataApi,
|
||||
format: {
|
||||
ResponseApply: Sut
|
||||
},
|
||||
} = require( '../../../' ).dapi;
|
||||
|
||||
|
||||
describe( 'ResponseApply', () =>
|
||||
{
|
||||
it( 'applies function to response', done =>
|
||||
{
|
||||
const expected = {};
|
||||
const given = { given: 'data' };
|
||||
|
||||
const f = src =>
|
||||
{
|
||||
expect( src ).to.equal( given );
|
||||
return expected;
|
||||
};
|
||||
|
||||
DummyDataApi.use( Sut( f ) )( ( _, c ) => c( null, given ) )
|
||||
.request( given, ( e, data ) =>
|
||||
{
|
||||
expect( data ).to.equal( expected );
|
||||
done();
|
||||
} );
|
||||
} );
|
||||
|
||||
|
||||
it( 'returns self', () =>
|
||||
{
|
||||
const sut = DummyDataApi.use( Sut( _ => {} ) )( _ => {} );
|
||||
|
||||
expect( sut.request( {}, () => {} ) )
|
||||
.to.equal( sut );
|
||||
} );
|
||||
} );
|
|
@ -105,7 +105,7 @@ describe( 'HttpDataApi', function()
|
|||
*/
|
||||
it( 'delegates to provided HTTP implementation', function()
|
||||
{
|
||||
var method = 'POST',
|
||||
var method = 'GET',
|
||||
data = "ribbit",
|
||||
c = function() {};
|
||||
|
||||
|
@ -119,37 +119,58 @@ describe( 'HttpDataApi', function()
|
|||
} );
|
||||
|
||||
|
||||
/**
|
||||
* It's nice to do this for the HttpImpl so that they don't have to
|
||||
* worry about the proper way to handle it, or duplicate the logic.
|
||||
*/
|
||||
describe( 'given key-value data', function()
|
||||
[
|
||||
// default is urlencoded
|
||||
{
|
||||
it( 'converts data into encoded string', function()
|
||||
{
|
||||
var method = 'POST',
|
||||
data = { foo: "bar=baz", '&bar': "moo%cow" },
|
||||
c = function() {};
|
||||
|
||||
Sut( dummy_url, method, impl ).request( data, c );
|
||||
|
||||
expect( impl.provided[ 2 ] ).to.equal(
|
||||
'foo=' + encodeURIComponent( data.foo ) +
|
||||
enctype: '',
|
||||
method: 'POST',
|
||||
data: { foo: "bar=baz", '&bar': "moo%cow" },
|
||||
expected: 'foo=' + encodeURIComponent( 'bar=baz' ) +
|
||||
'&' + encodeURIComponent( '&bar' ) + '=' +
|
||||
encodeURIComponent( data[ '&bar' ] )
|
||||
);
|
||||
} );
|
||||
encodeURIComponent( 'moo%cow' )
|
||||
},
|
||||
|
||||
|
||||
it( 'with no keys, results in empty string', function()
|
||||
// same as above
|
||||
{
|
||||
var method = 'POST',
|
||||
data = {},
|
||||
c = function() {};
|
||||
enctype: 'application/x-www-form-urlencoded',
|
||||
method: 'POST',
|
||||
data: { foo: "bar=baz", '&bar': "moo%cow" },
|
||||
expected: 'foo=' + encodeURIComponent( 'bar=baz' ) +
|
||||
'&' + encodeURIComponent( '&bar' ) + '=' +
|
||||
encodeURIComponent( 'moo%cow' )
|
||||
},
|
||||
|
||||
Sut( dummy_url, method, impl ).request( data, c );
|
||||
// empty string
|
||||
{
|
||||
enctype: 'application/x-www-form-urlencoded',
|
||||
method: 'POST',
|
||||
data: {},
|
||||
expected: "",
|
||||
},
|
||||
|
||||
expect( impl.provided[ 2 ] ).to.equal( "" );
|
||||
// json
|
||||
{
|
||||
enctype: 'application/json',
|
||||
method: 'POST',
|
||||
data: { foo: 'bar' },
|
||||
expected: '{"foo":"bar"}',
|
||||
},
|
||||
|
||||
// ignored if GET
|
||||
{
|
||||
enctype: 'application/json',
|
||||
method: 'GET',
|
||||
data: { foo: "bar" },
|
||||
expected: "foo=bar",
|
||||
},
|
||||
].forEach( ( { enctype, method, data, expected }, i ) =>
|
||||
{
|
||||
it( `${method} encodes (${i})`, () =>
|
||||
{
|
||||
Sut( dummy_url, method, impl, enctype )
|
||||
.request( data, _ => {} );
|
||||
|
||||
expect( impl.provided[ 2 ] ).to.equal( expected );
|
||||
} );
|
||||
} );
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ describe( 'DataProcessor', () =>
|
|||
}
|
||||
};
|
||||
|
||||
Sut( filter, () => {}, meta_source )
|
||||
Sut( filter, () => {}, meta_source, createStubStagingBucket )
|
||||
.processDiff( data, request, program );
|
||||
|
||||
expect( data.filtered ).to.equal( true );
|
||||
|
@ -109,7 +109,7 @@ describe( 'DataProcessor', () =>
|
|||
done();
|
||||
}
|
||||
|
||||
Sut( filter, dapi_factory )
|
||||
Sut( filter, dapi_factory, null, createStubStagingBucket )
|
||||
.processDiff( {}, request, program );
|
||||
} );
|
||||
|
||||
|
@ -136,7 +136,12 @@ describe( 'DataProcessor', () =>
|
|||
meta_source,
|
||||
} = createStubs( false, {}, getFieldData );
|
||||
|
||||
const sut = Sut( filter, () => dapi_manager, meta_source );
|
||||
const sut = Sut(
|
||||
filter,
|
||||
() => dapi_manager,
|
||||
meta_source,
|
||||
createStubStagingBucket
|
||||
);
|
||||
|
||||
program.meta.fields = {
|
||||
foo: {
|
||||
|
@ -249,7 +254,13 @@ function createSutFromStubs( /* see createStubs */ )
|
|||
program: program,
|
||||
filter: filter,
|
||||
meta_source: meta_source,
|
||||
sut: Sut( filter, () => {}, meta_source ),
|
||||
|
||||
sut: Sut(
|
||||
filter,
|
||||
() => {},
|
||||
meta_source,
|
||||
createStubStagingBucket
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -281,6 +292,8 @@ function createStubProgram( internals )
|
|||
internal: internals,
|
||||
meta: { qtypes: {}, fields: {} },
|
||||
apis: {},
|
||||
|
||||
initQuote() {},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -305,3 +318,28 @@ function createStubBucket( data )
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function createStubStagingBucket( bucket )
|
||||
{
|
||||
let data = {};
|
||||
|
||||
return {
|
||||
getDataByName( name )
|
||||
{
|
||||
return bucket.getDataByName( name );
|
||||
},
|
||||
|
||||
setValues( values )
|
||||
{
|
||||
data = values;
|
||||
},
|
||||
|
||||
forbidBypass() {},
|
||||
getDiff()
|
||||
{
|
||||
return data;
|
||||
},
|
||||
commit() {},
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue