Refactor Validation code in preparation for Conditional Restrictions#215
Merged
Refactor Validation code in preparation for Conditional Restrictions#215
Conversation
… more restrictive conditions
It should be mentioned here that the previously committed version of this had a mix up with the exclusive conditions, where the edge cases (value equal to the edge of the range) was incorrectly valdiated in the version imported from js-lectern-client. This is fixed now with tests to check these cases.
…stead of data records
Original implementation had record processing logic built into the restriction test logic. This refactor sepearates restriction tests to apply to a specific value only, based on the defined restriction rule. This will enable future development to resolve the restrictions on each field based on conditional restrictions and then test based on the resolved conditions. The next step is to add validation testing for full data records, full entity data sets, and then full dictionary data sets. These each loop over the previous element but also introduce new tests. For example, entity data sets enforce unique field and uniqueKey restrictions. Dictionary datasets enforce foreignKey restrictions. Unit tests have been added to fieldValidation function, and to the individual restriction tests. The restriction tests check the detailed set of values vs restriction rules, while the fieldValidation testing is checking that these restrictions are enforced based on schemaField definitions. Note that script validations are not being run in this code, and that the unique constraint cannot be applied to a field. There is additional changes required to move the unique constraint to a field level property (like isArray) so that it can't be affected by conditional restrictions. It will be validated in the Schema level validation (entity dataset).
…ingle schema Applies the `validateRecord` validation to every record, plus runs validations that require knowledge of the entire set of records for this schema: - unique - uniqueKey
- applies foreign key restrictions - detects unrecognized schemas - collects foreign key errors with other record errors
- functions for converting values for field, record, schema, and dictionary - update to validation error type names to be shared with convert value types - includes test specs for each convert function exported
Contributor
Author
|
I still owe some doc files that describe the validation and data conversion functionality before this is ready to be merged. At this point, the code is ready for review. |
* Change `FieldDetails` property `value` to `fieldValue` * Change TestResult invalid property to be `details`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR refactors the validation package from the version brought in from the original
js-lectern-clientto a new form that will enable both the development of conditional restrictions, and for enabling the use of validation code in the Lectern server and in the browser.To enable conditional restrictions we needed to be able to:
To enable running validation in the browser we need to:
Description of Changes
Separation of data parsing and data validation
Previously, data parsing/type conversion and data validation occurred together through use of the Lectern client's
process,processRecords, andprocessSchemasfunctions. These functions would be provided the raw string values as submitted to a server (ie. from a TSV, as is expected for Lectern dictionary data), and the process functions would then validate the structure of this data, parse this data to convert it to the data types defined in a dictionary, and then validate those values using that dictionary.With this refactor, validation is performed entirely on data that has already been transformed. This allowed us to write separate code for data conversion, which is also provided in this PR. The structure of data is clearly tested both during the data parsing and conversion step, and again during the data validation step. It is important that the data structure and data type checks is repeated at validation time so that we can validate data that is provided programatically through web forms or custom data parsers.
Functions for validating different groups of data (
field,record,schema,dictionary)There are now functions with declarative names for validating data of each of these levels:
validateField(),validateRecord(),validateSchema(), andvalidateDictionary(). Notably, directly testing individual fields was not possible in the previous client implementation - record level processing was the lowest level available. Each of the new validation function requires as inputs only the relevant Lectern Dictionary data (Schema definition forvalidateSchemafor instance). Importantly, each of these validation functions relies on the other validation functions to test its component parts... sovalidateDictionaryusesvalidateSchemato ensure every schema is valid, andvalidateSchemausesvalidateRecordwhich usesvalidateField. This ensures the testing is done consistently across the validation suite. Each function introduces new validation tests that can only be done at that scale of data. For example, ForeignKey restrictions can only be tested when a full dictionary worth of data is provided.Fully typed validation errors with logical structure
The validation functions now return a Result object that either indicates that the data is completely valid, or provides error as new structured data. Previously, errors were reported as a flat list of validation errors. Now, errors are grouped by schema, then grouped by record, and finally grouped by field.
Code Content
Validation package
The validation package now contains separate code for converting raw input values, and for validating data with restrictions from the lectern dictionaries.
Parse Value Functions
These are functions to parse raw string values from submitted TSVs and convert them into the proper data types in JS. These apply validations to the data value to ensure it matches the required type (string, number, integer, boolean). This also includes all logic for parsing array values based on the default delimiter value (
,).This functionality was only partially implemented in the previous client code. Array parsing was not previously done, and data conversion was bundles with validation. Now, data conversion can be done separately from validation, allowing for type conversion errors to be communicated and addressed before submitted for validation.
4 validation functions are provided:
parseFieldValueparseRecordValuesparseSchemaValuesparseDictionaryValuesValidation functions
The validation library now exports the following 4 validation functions:
validateFieldValidate a single field based on a
SchemaFielddefinition. This function requires as an argument the value of the entireDataRecordthat this field belongs to. This will be used to determine which conditional restrictions need to be applied. This function tests that the field has the correct value type, and then tests all field restrictions:Note that
uniquerestriction cannot be tested at field level since we require all data for the schema.validateRecordValidates all fields in a record by running the
validateFieldfunction for them. In addition, reports unrecognized fields in the record.validateSchemaValidates all records belonging to a schema. This will apply tests for the following restrictions that require all schema data:
validateDictionaryGiven data for all schemas, validates all data in a data set for a dictionary. This includes running
validateSchemafor each schema, as well as applyingforeignKeytests where on each record of the schemas with this restriction.Removed Features
Some small pieces of functionality have been intentionally removed:
metaproperty, which is not a defined part of the lectern specification. This has been removed. If this functionality is needed we can create a standard mechanism in the Lectern spec, or recommend users add logic to process their data, applying defaults, before passing data to the Lectern clientClient package
The client package has had all data conversion and validation logic removed since this is now handled in the validation package. The processing code has been refactored to rely on the conversion and validation code instead.
Processing functions
The processing functions were providing a mechanism to perform both data conversion and data validation. To keep this functionality available, the old processing functions have been replaced with new ones performing similar roles.
The old processing functions required a pre-processing step that had already parsed all array fields into array data, but had not converted the values from raw text fields. Now, the inputs should strictly contain string values and the lectern standard conversion functionality will be applied. This will separate the values in arrays based on a default delimiter value of
,. The ability to customize the delimiter value will be added to the Lectern schema.The names of the processing functions have been updated to correspond to the now common Lectern terminology.
processSchemas()->processDictionary(): This function will process an entire dictionary worth of data, taking as input all records for all schemas in the dictionary.processRecords()->processSchema(): This function will process a schema worth of data, taking all records for a single schema.process->processRecord(): This function a single record, taking only one object as input for a single schema.