Default data was converted to an empty array if the data evaluated to
false. We only want to convert it if it is undefined so values that are
false remain false.
The `*_label' fields were not being properly set because they were being
considered just as every other expanded field, which is subject to other
considerations to ensure that we do not overwrite user data. Label fields,
on the other hand, _must_ always be set whenever the value changes, and are
_not_ modifiable by the user.
This codifies that `_label' assumption that I would like removed in the
future, but time does not permit that sort of refactoring right
now. Ideally, labels would be treated generically like any other expanded
field and have an attribute set of them that would change their expansion
behavior.
* src/client/dapi/DataApiMediator.js (_updateFieldData): Pass label field id
to `_populateWithMap'.
(_populateWithMap): Use it to always populate the label field when the key
matches, ignoring the other expansion criteria that would inhibit it.
* test/client/dapi/DataApiMediatorTest.js: Update test accordingly.
* src/dapi/DataApiManager.js (triggerFieldUpdate): Provide id to
`_genUiFieldData'.
(_genUiFieldData)[label_id]: New field. Return it as part of the return
object's `label_id' field.
DEV-4077
* src/program/ProgramInit.js (_isKnownType): Account for ancient qtype
representation (as a string).
* src/server/quote/ProgramQuoteCleaner.js (_isKnownType): Likewise.
* test/program/ProgramInitTest.js: New test case for this situation.
* test/server/quote/ProgramQuoteCleanerTest.js: Modify existing test case
for this situation.
These denote fields that are generated but do not actually have any data
associated with them. For example, select options with predicates have a
field generated so that they contribute to the group field count (so that
the group will automatically show/hide appropriately), but those should
never have values associated with them in the bucket.
This was manifesting as a nasty bug: The bucket contained a key for
generated options. When the quote is loaded, the client "empties" the
bucket. In doing so, it set the option value to the empty string, which had
the effect of rendering the dropdown useless---every value was the empty
string!
* src/program/ProgramInit.js (_isKnownType): New method.
(init): Use it and ignore fields with unknown types.
* src/server/Server.js: Add note that we shouldn't have this logic
duplicated between ProgramInit and ProgramQuoteCleaner.
* src/server/quote/ProgramQuoteCleaner.js (_fixGroup): Ignore fields with
unknown types.
(_isKnownType): New method.
* test/program/ProgramInitTest.js: Update existing tests. Add new.
* test/server/quote/ProgramQuoteCleanerTest.js: Test this case.
* src/server/quote/ProgramQuoteCleaner.js (clean): Add docblock.
Replace previous linked group cleaning with call to `_fixGroup'.
(_fixGroup): New method. Similar logic to previous linked group cleaning,
except that fields are never truncated.
(_fixLinkedGroups, _getLinkedIndexLength): Remove methods.
(_getGroupLength): New method determining group size from leader length,
which also accounts for linked groups.
* test/server/quote/ProgramQuoteCleanerTest.js: New test case.
Allowing the stack to clear ensures that (in practice) DelayedStagingBucket
is given a chance to do necessary processing before data are requested from
it by bucket hooks as a result of _this_ invocation, which in turn
results (in some cases) in infinite recursion.
* src/client/dapi/DataApiMediator.js (_updateFieldData): Allow stack to
clear before invoking `quote.setData'.
* test/client/dapi/DataApiMediatorTest.js: Test respectively.
DEV-3257
* src/client/dapi/DataApiMediator.js (_populateWithMap): Update
docblock. Ignore field during expansion if it would overwrite an
existing value.
* test/client/dapi/DataApiMediatorTest.js: Update tests data to include
values for all bucket fields, not just `name'. Add test for new
condition.
DEV-3257
[*] You should not use this commit directly since this may wipe out data in
fields the user has changed. See future commit where this situation is
properly handled.
* src/client/Client (_init): Provide dapimap to DataApiMediator instance.
* src/client/dapi/DataApiMediator.js
(_dapi_map): New field.
(constructor): Accept dapimap. BC break (which is okay, since this is
still part of a topic branch). Assing to _dapi_map. Update docblock.
(monitor): Bind `dapi_manager' to first argument of handlers.
(_updateFieldData): Accept `dapi_manager' as first argument. Use
`_populateWithMap' to generate additional update data.
(_populateWithMap): New method.
(_clearFieldFailures): Accept `dapi_manager' as first argument.
* src/dapi/DataApiManager.js: Update copyright year.
(getDataExpansion): Return empty object (consistent with interface) rather
than `undefined' when field value is undefined. Use
{Error=>MissingDataError} when field data are missing. Throw instead of
emit. Fix missing comma in var declarations.
* src/dapi/MissingDataError.js: New class.
* test/client/dapi/DataApiMediatorTest.js: Update test data to test field
expansion. New test against ignoring field expansion when data are not
available. Update Sut constructors of other tests for new dapimap
parameter.
DEV-3257
This extracts existing code from Client and adds tests. The glue code is
far from ideal and highlights the amount of work needed to decouple Client
from so many parts of the system.
* src/client/Client.js (_dapiManager): New field.
(_init): Use DataApiMediator.
(_createProgram): Assign `_dapiManager' (this is not at all
ideal). Remove hooks from it: fieldLoading, updateFieldData,
clearFieldData.
* src/client/ClientDependencyFactory.js (createDataApiMediator): New alias
to DataApiMediator constructor.
* src/client/dapi/DataApiMediator.js: New class.
* test/client/dapi/DataApiMediatorTest.js: New test case.
DEV-3257
This does not go all the way, but helps improve the readability of the
algorithm a little bit and modernizes the code.
* src/field/FieldClassMatcher.js (constructor): Renamed from
`__constructor'.
(__constructor): Remove method.
(match): Extract most code into `#_reduceFieldMatches'.
(_reduceFieldMatches): New method, simplifying the algorithm slightly.
(_reduceMatch): Simplify.
* test/field/FieldClassMatcherTest.js: Update accordingly.
This is just to make sure that the current system is both well-understood
and does not break with changes. This is a very important class, as it
drives the display of the entire UI.
* test/field/FieldClassMatcherTest.js: New file.
If the submission failed, we probably want to try again next time around.
* src/server/service/RatingServiceSubmitNotify.js
(_maybeNotify): Extract logic from `#postProcessRaterData'. Only set
notification flag in absence of dapi error.
(postProcessRaterData): Use it.
* test/server/service/RatingServiceSubmitNotifyTest.js: Update tests
accordingly.
* src/server/db/MongoServerDao.js
(getDocumentField,setDocumentField): New methods.
* src/server/service/RatingServiceSubmitNotify.js
(postProcessRaterData): Only notify when notification flag is not set.
(_getNotifyState, _setNotified): New methods.
* test/server/service/RatingServiceSubmitNotifyTest.js: Modify accordingly.
An error was being thrown outside the stack of the actual test, which
apparently was never noticed until more recent versions of node. We're
still on a pretty ancient version for local development. :x
* test/ui/step/GeneralStepUiTest.js (createElementStyler)[getAnswerElementByName]:
Properly return array for stub jQuery element.
Before this change, since `undefined' is encoded as `null' when serialized,
there was no way for the server to disambiguate between unmodified values
and a truncation point. For example:
[ undefined, undefined, null, null, null ]
The above array represents two unmodified and three removed indexes. But
this is serialzed into JSON as:
[ null, null, null, null, null ]
It isn't possible for the server to determine what the truncation point is
from that diff. The solution is to therefore truncate the array _before_
sending it to the server, providing a trailing null to indicate that a
truncation has occurred:
[ null, null, null ]
The above means that the first two indexes are unmodified, and that index 2
and later should all be truncated.
* doc/client.texi (Saving to Server): New section.
* src/client/transport/XhttpQuoteTransport.js (_truncateDiff): New method to
perform truncation.
(getBucketDataJson): Use it.
* test/client/transport/XhttpQuoteTransportTest.js: New file with respective
test case.
This is something that managed to slip by (but not unnoticed) for almost
exactly one year to this day (028606242a). It
can only be reproduced by changing classes that result in visibility changes
differing on the same field by index. The issue hides itself on first
load (because all fields are shown by default) and on refresh.
The problem is that, when one index shows a field but another hides it, the
hide overrode the show indexes, so only the hide took place.
* src/client/Cmatch.js (markShowHide): Make virtual. New implementation to
support concurrent show/hide.
(_handleClassMatch): Use it.
* test/client/CmatchTest.js: New test.
* npm-shrinkwrap.json: ease.js v0.2.{8=>9}.
The `set' event already existed---this merely extracts it into its own
handler.
* src/client/Client.js (handleEvent): Extract `set' handler.
* src/client/ClientDependencyFactory.js (createClientEventHandler): Add
`set'.
* src/client/event/ValueSetEventHandler.js: New class.
* test/event/ValueSetEventHandlerTest.js: Associated test case.
This mixes in support for non-terminating nulls. It would have been
nice to handle that in a separate commit for clarity, but the
refactoring came as a consequence of trying to provide a working
implementation.
Various inconsistencies and subtle bugs in unlikely situations have
been fixed by this, including modifying objects passed as arguments to
various methods, and inconsistent handling of diff data.
Changes are more consistently recognized. Perhaps the most noticeable
consequence is that moving between steps no longer prompts to discard
changes---previously calculated values would trigger the dirty flag on
steps even if the user didn't actually change anything. I (and
others) have wanted this fixed for many years.
This is a very dense commit that touches a core part of the
system. Hopefully the Changelog below helps.
* src/bucket/Bucket.js
(setValues): [BC-BREAK] Remove parameters `merge_index' and
`merge_null' parameters.
* src/bucket/DelayedStagingBucket.js
(setValues): [BC-BREAK] Remove `merge_index' and `merge_null
parameters. Remove distinction between `merge_index' and non-.
* src/bucket/QuoteDataBucket.js
(setValues): [BC-BREAK] Remove `merge_index' and `merge_null
parameters. Remove respective arguments from `_mergeData' call.
(_mergeData): Remove same parameters. Remove handling of
`merge_index' and `merge_null'.
(overwriteValues): Append `null' to each vector.
* src/bucket/StagingBucket.js
(_initState): Use `Object.create' instead of explicit prototype
instantiation (functionally equivalent).
(merge): Minor comment correction.
(_hasChanged): Rename to `_parseChanges'.
(_parseChanges): Rename from `_hasChanged'. Remove `merge_index'
parameter. Generate new object rather than mutation original
data (prevent dangerous and subtle bugs from side-effects). Clone
each vector rather than modifying/referencing directly (this was
previously done during merge). Remove `merge_index'
distinction. Handle non-terminating `null' values.
(setValues): [BC-BREAK] Remove `merge_index' and `merge_null'
parameters. Use new object generated by `_parseChanges'. Remove
cloning of each vector (`_parseChanges' now does that). Remove
`merge_index' distinction.
(overwriteValues): Remove argument to `setValues' call.
(getFilledDiff): [BC-BREAK] Use `_staged' rather than `_curdata'.
(commit): Remove second and third arguments of call to `setValues'
of underlying bucket.
* src/client/Client.js
(_initStepUi): Remove second argument of calls to quote `setData'.
* src/client/quote/ClientQuote.js
(setData): [BC-BREAK] Remove `merge_nulls' parameter. Remove second
and third arguments of call to staging bucket `setValues'. Add
comment indicating a long-standing problem with committing the
staging bucket contents before save has succeeded.
* src/server/request/DataProcessor.js
(processDiff): Remove `permit_null' argument of `sanitizeDiff'
call.
(sanitizeDiff): Remove `permit_null' parameter. Hard-code filter
call's `permit_null' argument to `true'.
(_determineDapiFields): Properly handle `null's (ignore) rather than
inadvertently converting them into the string "null".
* test/bucket/StagingBucketTest.js: Modify test cases
accordingly. Add tests to verify that updates and diffs operate
as expected, especially support for non-terminating `null's.
(createStubBucket): Use `QuoteDataBucket'. Ideally remove this
coupling in the future, but this is a more realistic test case for
the time being.
* test/server/request/DataProcessorTest.js: Update test to account for
hard-coded `given_null' argument.
This represents a portion of the refactoring that I had intended to do
until I realized that there was a simpler solution to the problem that
we were having (having proguic add stored calculated values to the
defaults object).
So ideally we'll continue extracting all quote init code out of
`Server' and into `ProgramInit' in the future.
* doc/server.texi (Liza Server): Mention `ProgramInit'.
* src/program/ProgramInit.js: Add class.
* src/server/DocumentServer.js: Use it.
* src/server/Server.js (_progInit): Add private field.
(__construct): Accept ProgramInit instance and assign to field.
(initQuote): Use promise returned by `_getDefaultBucket'.
(_getDefaultBucket): Proxy to `ProgramInit#init', which returns a
promise.
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.
This will make life much easier and less verbose, especially
considering the verbosity of promises.
* src/store/DelimitedKey.js: Add trait.
* test/store/DelimitedKeyTest.js: Add test case.
This is a terrible kluge, but time doesn't permit modifying the
system. All of this also touches old code that is untested, which is
difficult to modify with confidence.
* src/server/DocumentServer.js (DocumentServer#create): Use
StagingBucket.
* src/server/Server.js: Remove logic now handled by DataProcessor.
* src/server/request/DataProcessor.js (processDiff): Wrap in
StagingBucket to filter out values that do not result in changes.
* test/server/request/DataProcessorTest.js: Update failing cases.
This is a kluge until time can be spent better factoring this
system (using Traits).
* src/bucket/StagingBucket.js (_noStagingBypass): Add field.
(forbidBypass): Add method to set field.
(setCommittedValues): Use field.
* src/dapi/DataApiFactory.js (_createDataApi): Add support for
`quote'.
* src/dapi/http/HttpDataApi.js (__construct): New `enctype' argument.
(_encodeData, _encodeKeys): Remove former, rename latter to former.
(encodeData, _urlEncode): Encode based on enctype and method.
(*): Strict mode, es6 style.