1
0
Fork 0

[DEV-6353] Add bucket definitions

master
Austin Schaffer 2019-11-07 13:33:14 -05:00 committed by Schaffer, Austin
parent 6fcf72f1b6
commit 4f39c754a2
11 changed files with 595 additions and 41 deletions

123
src/bucket/QuoteDataBucket.d.ts vendored 100644
View File

@ -0,0 +1,123 @@
/**
* Key/value store
*
* Copyright (C) 2010-2019 R-T Specialty, LLC.
*
* This file is part of liza.
*
* 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/>.
*/
import { PositiveInteger } from "../numeric";
/**
* General key/value store for document
*
* The term "Quote" here is an artifact from the initial design of the
* system used for insurance quoting. It will be renamed.
*
* @todo Rename to DocumentDataBucket
*/
export declare class QuoteDataBucket
{
/**
* Triggered when data in the bucket is updated, before it's committed
*/
static readonly EVENT_UPDATE: string;
/**
* Explicitly sets the contents of the bucket
*
* @param data - associative array of the data
*/
setValues( data: Record<string, any> ): this;
/**
* Alias of setValues
*/
setCommittedValues(): this;
/**
* Clears all data from the bucket
*/
clear(): this;
/**
* Overwrites values in the original bucket
*
* For this buckeet, overwriteValues() is an alias for setValues() without
* index merging. However, other Bucket implementations may handle it
* differently.
*
* @param data - associative array of the data
*/
overwriteValues( data: Record<string, any> ): this;
/**
* Calls a function for each each of the values in the bucket
*
* Note: This format is intended to be consistent with Array.forEach()
*
* @param callback - function to call for each value in the bucket
*/
each( callback: ( val: any, key: string) => {} ): this;
/**
* Calls a function for each each of the values in the bucket matching the
* given predicate
*
* @param pred - predicate
* @param c - function to call for each value in the bucket
*/
filter(
pred: ( key: string ) => {},
_c: ( val: any, key: string) => {}
): this;
/**
* Returns the data for the requested field
*
* @param name - name of the field (with or without trailing brackets)
*
* @return data for the field, or empty array if none
*/
getDataByName( name: string ): any;
/**
* Returns the data as a JSON string
*
* @return data represented as JSON
*/
getDataJson(): string;
/**
* Return raw bucket data
*
* TODO: remove; breaks encapsulation
*
* @return raw bucket data
*/
getData(): Record<string, any>;
}

206
src/bucket/StagingBucket.d.ts vendored 100644
View File

@ -0,0 +1,206 @@
/**
* StagingBucket class
*
* Copyright (C) 2010-2019 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 Affero 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 Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { PositiveInteger } from "../numeric";
export type StagingBucketConstructor = (
bucket: StagingBucket
) => StagingBucket;
/**
* Stages and merges values into underlying key/value store
*/
export declare class StagingBucket
{
/**
* Analgous to setValues(), but immediately commits the changes
*
* This still calls setValues() to ensure all events are properly kicked
* off.
*
* @param data - data to set and commit
*/
setCommittedValues( data: Record<string, any> ): this;
/**
* Prevent #setCommittedValues from bypassing staging
*
* When set, #setCommittedValues will act as an alias of #setValues.
*/
forbidBypass(): this;
/**
* Explicitly sets the contents of the bucket
*
* Because JSON serializes all undefined values to `null`, only the
* final null in a diff is considered terminating; the rest are
* converted into `undefined`. Therefore, it is important that all
* truncations include no elements in the vector after the truncating null.
*
* @param given_data - associative array of the data
*/
setValues( given_data: Record<string, any> ): this;
/**
* Overwrites values in the original bucket
*
* @param data - associative array of the data
*/
overwriteValues( data: Record<string, any> ): this;
/**
* Returns staged data
*
* @return staged data
*/
getDiff(): Record<string, any>;
/**
* Returns a field-oriented diff filled with all values rather than a
* value-oriented diff
*
* Only the fields that have changed are returned. Each field contains its
* actual value---not the diff representation of what portions of the field
* have changed.
*
* @return filled diff
*/
getFilledDiff(): Record<string, any>;
/**
* Reverts staged changes, preventing them from being committed
*
* This will also generate a diff and raise the same events that would be
* raised by setting values in the conventional manner, allowing reverts to
* transparently integrate with the remainder of the system.
*
* @param evented - whether to emit events as part of the revert
*/
revert( evented?: boolean ): this;
/**
* Commits staged changes, merging them with the bucket
*
* @param store - object to save old staged values to
*/
commit( store?: { old: Record<string, any> } ): this
/**
* Clears all data from the bucket
*/
clear(): this;
/**
* Calls a function for each each of the values in the bucket
*
* @param callback - function to call for each value in the bucket
*/
each( callback: ( value: any, name: string ) => void ): this;
/**
* Returns the data for the requested field
*
* WARNING: This can be a potentially expensive operation if there is a
* great deal of staged data. The staged data is merged with the bucket data
* on each call. Do not make frequent calls to retrieve the same data. Cache
* it instead.
*
* @param name - field name (with or without trailing brackets)
*
* @return data for the field, or empty array if none
*/
getDataByName( name: string ): Record<string, any>;
/**
* Returns original bucket data by name, even if there is data staged atop
* of it
*
* There is no additional overhead of this operation versus getDataByName()
*
* @param name - field name (with or without trailing brackets)
*
* @return data for the field, or empty array if none
*/
getOriginalDataByName( name: string ): Record<string, any>;
/**
* Returns the data as a JSON string
*
* @return data represented as JSON
*/
getDataJson(): string;
/**
* Return raw bucket data
*
* todo: remove; breaks encapsulation
*
* @return raw bucket data
*/
getData(): Record<string, any>;
/**
* Calls a function for each each of the values in the bucket matching the
* given predicate
*
* @param pred - predicate
* @param c - function to call for each value in the bucket
*/
filter(
pred: ( name: string ) => boolean,
c: ( value: any, name: string ) => void
): this;
/**
* Returns true if the index for the given key exists
*
* @param name - the data key
* @param i - the index
*
* @return whether the key exists
*/
hasIndex( name: string, i: PositiveInteger ): boolean;
/**
* Returns true if the bucket has been changed and not saved
*
* @return true if the bucket has been changed and not saved
*/
isDirty(): boolean;
}

65
src/bucket/bucket_filter.d.ts vendored 100644
View File

@ -0,0 +1,65 @@
/**
* Filters bucket data
*
* Copyright (C) 2010-2019 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/>.
*/
export declare module bucket_filter {
export type filter = {
/**
* Filters bucket data based on the provided types
*
* If a type is not provided, the data is considered to be unwanted and is
* removed entirely. Otherwise, the filter is applied to every element in the
* array.
*
* The data is modified in place.
*
* @param data - data to filter
* @param key_types - filter types
* @param ignore_types - types to ignore
* @param permit_null - Allow nulls in results
*
* @return Object modified data
*/
filter(
data: Record<string, any>,
key_types: Record<string, any>,
ignore_types: Record<string, boolean>,
permit_null: boolean,
): Record<string, any>
/**
* Filter bucket data based on values
*
* @param values - The values to filter
* @param filter - The filter to apply
* @param permit_null - Allow nulls in results
*
* @return the filtered values
*/
filterValues(
values: string[],
filter: string,
permit_null: boolean,
): string[];
};
}

View File

@ -19,9 +19,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { StagingBucket } from "../bucket/StagingBucket";
export declare abstract class Program
{
readonly ineligibleLockCount: number;
getId(): string;
initQuote( bucket: StagingBucket, store_only: boolean ): void
}

View File

@ -23,6 +23,7 @@
import { Program } from "../program/Program";
import { Quote, QuoteId } from "./Quote";
import { QuoteDataBucket } from "../bucket/QuoteDataBucket";
export declare class BaseQuote implements Quote
@ -90,4 +91,12 @@ export declare class BaseQuote implements Quote
* @return last calculated time or 0
*/
getLastPremiumDate(): UnixTimestamp;
/**
* Returns the bucket used to store the quote form data
*
* @return the data bucket
*/
getBucket(): QuoteDataBucket
}

View File

@ -23,6 +23,7 @@
import { Program } from "../../program/Program";
import { BaseQuote } from "../../quote/BaseQuote";
import { QuoteDataBucket } from "../../bucket/QuoteDataBucket";
export declare class ServerSideQuote extends BaseQuote
@ -43,4 +44,28 @@ export declare class ServerSideQuote extends BaseQuote
* @return self
*/
setRatedDate( timestamp: UnixTimestamp ): this;
/**
* Set rating bucket
*
* @param bucket - the data bucket
*/
setRateBucket( bucket: QuoteDataBucket ): this;
/**
* Set rating data
*
* @param data - rating data
*/
setRatingData( data: Record<string, any> ): this;
/**
* Get rating data
*
* @return rating data
*/
getRatingData(): Record<string, any>;
}

View File

@ -40,7 +40,6 @@ module.exports = Class( 'ServerSideQuote' )
*/
'private _creditScoreRef': 0,
/**
* Unix timestamp containing date of first premium calculation
* @type {number}
@ -53,6 +52,12 @@ module.exports = Class( 'ServerSideQuote' )
*/
'private _metabucket': null,
/**
* Rating Data Bucket
* @type {Bucket}
*/
'private _rate_bucket': null,
'public setProgramVersion': function( version )
{
@ -148,6 +153,64 @@ module.exports = Class( 'ServerSideQuote' )
this._metabucket.setValues( data );
return this;
}
},
/**
* Set rating bucket
*
* @param {Bucket} bucket the rate bucket to set
*/
'public setRateBucket': function( bucket )
{
this._rate_bucket = bucket;
return this;
},
/**
* Get rating bucket
*
* @return {Bucket}
*/
'public getRateBucket': function()
{
return this._rate_bucket;
},
/**
* Set rating data
*
* @param {Object.<string,Array>} data rating data
*/
'public setRatingData': function( data )
{
if ( !this._rate_bucket )
{
throw Error( "No rating bucket available for #setRatingData" );
}
this._rate_bucket.setValues( data );
return this;
},
/**
* Get rating data
*
* @return {Object.<string,Array>} rating data
*/
'public getRatingData': function()
{
if ( !this._rate_bucket )
{
throw Error( "No rating bucket available for #setRatingData" );
}
return this._rate_bucket.getData();
},
} );

View File

@ -18,7 +18,9 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { StagingBucket, StagingBucketConstructor } from "../../bucket/StagingBucket";
import { PositiveInteger } from "../../numeric";
import { bucket_filter } from "../../bucket/bucket_filter";
import { UserRequest } from "./UserRequest";
/**
@ -41,10 +43,10 @@ export class DataProcessor
* @param staging_ctor - staging bucket constructor
*/
constructor(
private readonly _filter: any,
private readonly _filter: bucket_filter.filter,
private readonly _dapif: any,
private readonly _meta_source: any,
private readonly _stagingCtor: any,
private readonly _stagingCtor: StagingBucketConstructor,
) {}
@ -169,7 +171,7 @@ export class DataProcessor
dapi_manager: any,
program: any,
data: Record<string, any>,
bucket: any,
bucket: StagingBucket,
): [ any, Record<string, any> ]
{
const {
@ -292,7 +294,7 @@ export class DataProcessor
*/
private _mapDapiData(
dapi: any,
bucket: any,
bucket: StagingBucket,
index: PositiveInteger,
diff_data: Record<string, any>,
): Record<string, any>

View File

@ -21,8 +21,14 @@
'use strict';
const { expect } = require( 'chai' );
const Sut = require( '../../..' ).server.quote.ServerSideQuote;
const root = require( '../../..' );
const Sut = require( '../../..' ).server.quote.ServerSideQuote;
const expect = require( 'chai' ).expect;
const sinon = require( 'sinon' );
const {
QuoteDataBucket,
} = root.bucket;
describe( 'ServerSideQuote', () =>
{
@ -31,80 +37,85 @@ describe( 'ServerSideQuote', () =>
[
{
property: 'startDate',
default: 0,
value: 946684800
default: 0,
value: 946684800
},
{
property: 'initialRatedDate',
default: 0,
value: 946684800
default: 0,
value: 946684800
},
{
property: 'agentId',
default: 0,
value: 12345678
default: 0,
value: 12345678
},
{
property: 'agentEntityId',
default: 0,
value: 12345678
default: 0,
value: 12345678
},
{
property: 'agentName',
default: '',
value: 'name'
default: '',
value: 'name'
},
{
property: 'imported',
default: false,
value: true,
default: false,
value: true,
accessor: 'is'
},
{
property: 'bound',
default: false,
value: true,
default: false,
value: true,
accessor: 'is'
},
{
property: 'currentStepId',
default: 1,
value: 2
default: 1,
value: 2
},
{
property: 'topVisitedStepId',
default: 1,
value: 2
default: 1,
value: 2
},
{
property: 'topSavedStepId',
value: 1
value: 1
},
{
property: 'error',
default: '',
value: 'ERROR'
default: '',
value: 'ERROR'
},
{
property: 'programVersion',
default: '',
value: '1.0.0'
default: '',
value: '1.0.0'
},
{
property: 'creditScoreRef',
default: 0,
value: 800
default: 0,
value: 800
},
{
property: 'lastPremiumDate',
default: 0,
value: 946684800
default: 0,
value: 946684800
},
{
property: 'ratedDate',
default: 0,
value: 946684800
}
default: 0,
value: 946684800
},
{
property: 'rateBucket',
default: null,
value: QuoteDataBucket()
},
].forEach( testCase =>
{
@ -122,4 +133,44 @@ describe( 'ServerSideQuote', () =>
} );
} );
} );
} );
describe( 'rating data', () =>
{
it( `#setRatingData throws an error if no bucket is set`, () =>
{
const data = { foo: 'bar' };
const sut = Sut();
expect( function() { sut.setRatingData( data ); } )
.to.throw( Error );
} );
it( `Bucket values setters/getters work correctly`, () =>
{
const data = { foo: 'bar' };
let bucket_data = null;
const sut = Sut();
var called = false;
const bucket = {
setValues( gdata )
{
expect( gdata ).to.deep.equal( data );
bucket_data = gdata;
called = true;
},
getData()
{
return bucket_data;
},
};
sut.setRateBucket( bucket );
sut.setRatingData( data );
expect( called ).to.equal( true );
} );
} );
} );

View File

@ -25,6 +25,7 @@ import { DocumentId } from "../../../src/document/Document";
import { PositiveInteger } from "../../../src/numeric";
import { UserRequest } from "../../../src/server/request/UserRequest";
import { ServerSideQuote } from "../../../src/server/quote/ServerSideQuote";
import { QuoteDataBucket } from "../../../src/bucket/QuoteDataBucket";
chai_use( require( 'chai-as-promised' ) );
@ -711,7 +712,7 @@ function createStubQuote()
getBucket()
{
return;
return new QuoteDataBucket();
}
};
}

View File

@ -35,6 +35,7 @@ import { ServerSideQuote } from "../../../src/server/quote/ServerSideQuote";
import { UserRequest } from "../../../src/server/request/UserRequest";
import { UserResponse } from "../../../src/server/request/UserResponse";
import { UserSession } from "../../../src/server/request/UserSession";
import { QuoteDataBucket } from "../../../src/bucket/QuoteDataBucket";
import {
ServerDao,
@ -140,7 +141,7 @@ describe( 'RatingService', () =>
.then( () => expect( sent ).to.be.true );
} );
it( "saves rate data to own field", () =>
it( "saves rate data to it's own field", () =>
{
const {
logger,
@ -510,6 +511,10 @@ function getStubs()
getLastPremiumDate: () => <UnixTimestamp>0,
getCurrentStepId: () => 0,
setExplicitLock: () => quote,
setRateBucket: () => quote,
setRatingData: () => quote,
getRatingData: () => stub_rate_data,
getBucket: () => new QuoteDataBucket(),
};
return {