diff --git a/doc/server.texi b/doc/server.texi
index 10a3229..8463adf 100644
--- a/doc/server.texi
+++ b/doc/server.texi
@@ -13,7 +13,9 @@
@maintenance{The @srcrefjs{server/daemon,Daemon} monolith and
@srcrefjs{server,Server},
among other things,
- need refactoring.}
+ need refactoring.
+ Quote initialization code should be moved into
+ @srcrefjs{server,ProgramInit}.}
@helpwanted{}
diff --git a/src/program/ProgramInit.js b/src/program/ProgramInit.js
new file mode 100644
index 0000000..e363c9f
--- /dev/null
+++ b/src/program/ProgramInit.js
@@ -0,0 +1,69 @@
+/**
+ * Initialize document data for a given Program
+ *
+ * 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 .
+ */
+
+'use strict';
+
+const { Class } = require( 'easejs' );
+
+
+/**
+ * Initialize document bucket data for given Programs
+ *
+ * A default bucket is initialized considering certain aspects of a given
+ * Program (see `#init`).
+ *
+ * TODO: This should really contain _all_ of the init code, extracted from
+ * Server, but time did not permit. Refactoring can continue at a later date.
+ */
+module.exports = Class( 'ProgramInit',
+{
+ /**
+ * Initialize document bucket data for `program`
+ *
+ * The original object `doc_data` will be modified by reference and
+ * returned. If `doc_data` evaluates to `false`, an empty object will
+ * be returned. Any other input results in undefined behavior.
+ *
+ * Note: This implementation used to cache default bucket objects, but
+ * doing so risks causing subtle and nasty bugs if the system modifies
+ * the default bucket object somewhere down the line, thereby affecting
+ * all documents going forward.
+ *
+ * @param {Program} program source program
+ * @param {Object} doc_data existing document data, if any
+ *
+ * @return {Object} `doc_data` modified
+ */
+ 'public init'( program, doc_data )
+ {
+ const defaults = program.defaults || {};
+
+ // initialize to an array with a single element of the default value
+ return Promise.resolve(
+ Object.keys( defaults ).reduce(
+ ( data, key ) => ( data[ key ] === undefined )
+ ? ( data[ key ] = [ defaults[ key ] ], data )
+ : data,
+ doc_data || {}
+ )
+ );
+ },
+} );
diff --git a/src/server/DocumentServer.js b/src/server/DocumentServer.js
index 7d450c9..7ed6279 100644
--- a/src/server/DocumentServer.js
+++ b/src/server/DocumentServer.js
@@ -32,6 +32,10 @@ const {
DataApiManager,
},
+ program: {
+ ProgramInit,
+ },
+
server: {
Server,
@@ -74,6 +78,7 @@ module.exports = Class( 'DocumentServer',
),
DapiMetaSource( QuoteDataBucket ),
StagingBucket
- )
+ ),
+ ProgramInit()
) )
} );
diff --git a/src/server/Server.js b/src/server/Server.js
index 5b70bde..ae10d76 100644
--- a/src/server/Server.js
+++ b/src/server/Server.js
@@ -118,9 +118,15 @@ module.exports = Class( 'Server' )
*/
'private _dataProcessor': null,
+ /**
+ * Initializes program
+ * @type {ProgramInit}
+ */
+ 'private _progInit': null,
+
'public __construct': function(
- response, dao, logger, encsvc, data_processor
+ response, dao, logger, encsvc, data_processor, init
)
{
if ( !Class.isA( DataProcessor, data_processor ) )
@@ -133,6 +139,7 @@ module.exports = Class( 'Server' )
this.logger = logger;
this._encService = encsvc;
this._dataProcessor = data_processor;
+ this._progInit = init;
},
@@ -287,7 +294,10 @@ module.exports = Class( 'Server' )
}
// we're good
- init_finish( program );
+ server._getDefaultBucket( program, quote_data )
+ .then( default_bucket =>
+ init_finish( program, default_bucket )
+ );
});
});
}
@@ -297,20 +307,21 @@ module.exports = Class( 'Server' )
server.getProgram( quote_data.programId )
.then( function( quote_program )
{
- init_finish( quote_program );
+ server._getDefaultBucket( quote_program, quote_data )
+ .then( default_bucket =>
+ init_finish( quote_program, default_bucket )
+ );
} );
}
- function init_finish( quote_program )
+ function init_finish( quote_program, default_bucket )
{
// fill in the quote data (with reasonable defaults if the quote
// does not yet exist); IMPORTANT: do not set pver to the
// current version here; the quote will be repaired if it is not
// set
quote
- .setData(
- server._getDefaultBucket( quote_program, quote_data )
- )
+ .setData( default_bucket )
.setMetadata( quote_data.meta || {} )
.setQuickSaveData( quote_data.quicksave || {} )
.setAgentId( quote_data.agentId || agent_id )
@@ -523,41 +534,7 @@ module.exports = Class( 'Server' )
*/
'private _getDefaultBucket': function( program, quote_data )
{
- var defaults = program.defaults,
- bucket = quote_data.data || {},
- pre = this._defaultBuckets[ program.getId() ];
-
- // we only want to merge in the defaults if this is the first visit to
- // the quote
- if ( quote_data.currentStepId > 0 )
- {
- // todo: uncomment later; for now we want older quotes to still work
- //return bucket;
- }
-
- // if we already generated the default bucket data and have no
- // quote-specific data, return it
- if ( pre && ( quote_data.data === undefined ) )
- {
- return pre;
- }
-
- // generate
- for ( item in program.defaults )
- {
- if ( bucket[ item ] === undefined )
- {
- bucket[ item ] = [ defaults[ item ] ];
- }
- }
-
- // set as default bucket only if we didn't merge
- if ( quote_data.data === undefined )
- {
- this._defaultBuckets[ program.getId() ] = bucket;
- }
-
- return bucket;
+ return this._progInit.init( program, quote_data.data );
},
diff --git a/test/program/ProgramInitTest.js b/test/program/ProgramInitTest.js
new file mode 100644
index 0000000..581a5c4
--- /dev/null
+++ b/test/program/ProgramInitTest.js
@@ -0,0 +1,95 @@
+/**
+ * Tests ProgramInit
+ *
+ * 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 .
+ */
+
+'use strict';
+
+const chai = require( 'chai' );
+const expect = chai.expect;
+const { ProgramInit: Sut } = require( '../../' ).program;
+
+chai.use( require( 'chai-as-promised' ) );
+
+
+describe( 'ProgramInit', () =>
+{
+ [
+ {
+ label: "initializes defaults",
+ defaults: { a: "one", b: "two" },
+ doc_data: {},
+ expected: {
+ a: [ "one" ],
+ b: [ "two" ],
+ },
+ },
+ {
+ label: "does nothing with no data or defaults",
+ defaults: {},
+ doc_data: {},
+ expected: {},
+ },
+ {
+ label: "produces empty object given undefined data",
+ defaults: {},
+ doc_data: undefined,
+ expected: {},
+ },
+ {
+ label: "keeps existing data with defaults",
+ defaults: { foo: "init" },
+ doc_data: { bar: [ "baz" ] },
+ expected: {
+ foo: [ "init" ],
+ bar: [ "baz" ],
+ },
+ },
+ {
+ label: "keeps existing doc data with no defaults",
+ defaults: {},
+ doc_data: { foo: [ "bar" ] },
+ expected: {
+ foo: [ "bar" ],
+ },
+ },
+ {
+ label: "does not overwrite existing data with defaults",
+ defaults: { foo: "init" },
+ doc_data: { foo: [ "bar" ] },
+ expected: {
+ foo: [ "bar" ],
+ },
+ },
+ ].forEach( ({ label, doc_data, id, defaults, expected }) =>
+ {
+ it( label, () =>
+ {
+ const sut = Sut( null );
+
+ const program = {
+ id: "foo",
+ defaults: defaults,
+ };
+
+ return expect( sut.init( program, doc_data ) )
+ .to.eventually.deep.equal( expected );
+ } );
+ } );
+} );