@@ -57,9 +57,13 @@ class Validator {
57
57
if ( ! todos || ! todos . length ) {
58
58
return ;
59
59
}
60
+ // todos is an array of strings.
61
+ const slotType = this . getSlotType ( slotDefinition ) ; // LinkML schema.type object
60
62
61
- const slotType = this . getSlotType ( slotDefinition ) ;
62
-
63
+ // Slot type could be:
64
+ // a number, string, date ...
65
+ // null - if it is only a menu
66
+ // both date and NullValueList
63
67
if ( slotType ?. uri === 'xsd:date' ) {
64
68
for ( const todo of todos ) {
65
69
if ( todo . substring ( 0 , 2 ) === '>=' ) {
@@ -70,16 +74,11 @@ class Validator {
70
74
}
71
75
}
72
76
73
- for ( const def of slotDefinition . any_of || [ ] ) {
74
- processTodos ( def , todos ) ;
75
- }
76
- for ( const def of slotDefinition . all_of || [ ] ) {
77
- processTodos ( def , todos ) ;
78
- }
79
- for ( const def of slotDefinition . exactly_one_of || [ ] ) {
80
- processTodos ( def , todos ) ;
81
- }
82
- for ( const def of slotDefinition . none_of || [ ] ) {
77
+ // Cycle through each slotDefinition any_of etc. object entries and get
78
+ // the datatype of its .range (or recurse) and in LinkML fashion attach
79
+ // minimum_value and maximum_value to the slotDefinition OR its any_of
80
+ // etc array BASED ON top level todos. E.g. inheriting min/max criteria.
81
+ for ( const def of slotDefinition . any_of ?? slotDefinition . all_of ?? slotDefinition . exactly_one_of ?? slotDefinition . none_of ?? [ ] ) {
83
82
processTodos ( def , todos ) ;
84
83
}
85
84
} ;
@@ -94,13 +93,15 @@ class Validator {
94
93
*/
95
94
this . #dependantMinimumValuesMap = new Map ( ) ;
96
95
this . #dependantMaximumValuesMap = new Map ( ) ;
96
+ const RE_TODOS = new RegExp ( / ^ ( [ > < ] ) = { ( .* ?) } $ / ) ;
97
+
97
98
for ( const slotDefinition of Object . values ( this . #targetClassInducedSlots) ) {
98
99
const { todos } = slotDefinition ;
99
100
if ( ! todos || ! todos . length ) {
100
101
continue ;
101
102
}
102
103
for ( const todo of todos ) {
103
- const match = todo . match ( / ^ ( [ > < ] ) = { ( . * ? ) } $ / ) ;
104
+ const match = todo . match ( RE_TODOS ) ;
104
105
if ( match == null ) {
105
106
continue ;
106
107
}
@@ -140,7 +141,7 @@ class Validator {
140
141
this . #valueValidatorMap = new Map ( ) ;
141
142
}
142
143
143
- /* This returns a single primitve data type for a slot - a decimal, date,
144
+ /* This returns a single primitive data type for a slot - a decimal, date,
144
145
string etc. or possibly an enumeration. Enumerations are handled
145
146
separately however (by const slotEnum = ...). Slots either use "range"
146
147
attribute, OR they use any_of or exactly_one_of etc. attribute expression
@@ -164,6 +165,12 @@ class Validator {
164
165
return slotType ;
165
166
}
166
167
168
+ /* A validation function is prepared and cached for every slot presented, so
169
+ that validation throug rows of data can make use of already established
170
+ validator for each column. Particular columns may have sensitivity to other
171
+ column values (via min/max references) or special {today} e.g. so
172
+
173
+ */
167
174
getValidatorForSlot ( slot , options = { } ) {
168
175
const { cacheKey, inheritedRange } = options ;
169
176
if ( typeof cacheKey === 'string' && this . #valueValidatorMap. has ( cacheKey ) ) {
@@ -177,11 +184,13 @@ class Validator {
177
184
slotDefinition = slot ;
178
185
}
179
186
187
+ // This digs down into any_of, all_of etc to find first date, number etc.
188
+ // slotType is LinkML schema.type object
180
189
if ( ! slotDefinition . range && inheritedRange ) {
181
190
slotDefinition . range = inheritedRange ;
182
191
}
183
192
184
- const slotType = this . getSlotType ( slotDefinition ) ;
193
+ const slotType = this . getSlotType ( slotDefinition ) ; // LinkML schema.type object
185
194
186
195
const slotEnum = this . #schema. enums ?. [ slotDefinition . range ] ;
187
196
const slotPermissibleValues = Object . values (
@@ -194,11 +203,13 @@ class Validator {
194
203
// TEST CASE:
195
204
// if (slotDefinition.name == "sample_collection_date")
196
205
// console.log("any_of", DEBUG INFO)
206
+ // inheritedRange comes from original slot, so it might be a date or number + menu
197
207
const anyOfValidators = ( slotDefinition . any_of ?? [ ] ) . map ( ( subSlot ) =>
198
208
this . getValidatorForSlot ( subSlot , {
199
209
inheritedRange : slotDefinition . range ,
200
210
} )
201
211
) ;
212
+
202
213
const allOfValidators = ( slotDefinition . all_of ?? [ ] ) . map ( ( subSlot ) =>
203
214
this . getValidatorForSlot ( subSlot , {
204
215
inheritedRange : slotDefinition . range ,
@@ -239,6 +250,7 @@ class Validator {
239
250
if ( ! value ) {
240
251
if ( slotDefinition . required )
241
252
return 'This field is required' ;
253
+ // value_presence is subject to dynamic rules?
242
254
if ( slotDefinition . value_presence === 'PRESENT' )
243
255
return 'Value is not present' ;
244
256
return ;
@@ -266,39 +278,62 @@ class Validator {
266
278
splitValues = [ value ] ;
267
279
}
268
280
281
+ // For any of the value(s), whether they are valid depends on either
282
+ // an ok primitive data type parsing, OR a categorical menu match.
283
+ // Message needs
269
284
for ( const value of splitValues ) {
270
- if ( slotType ) {
285
+ let parse_error = false ;
286
+ if ( slotType ) { // Doesn't pertain to slots which are ONLY enumerations.
271
287
const parsed = this . #parser. parse ( value , slotType . uri ) ;
272
288
289
+ // Issue: parse can fail on decimal but menu has "Missing"
273
290
if ( parsed === undefined ) {
274
- return `Value does not match format for ${ slotType . uri } ` ;
275
- }
291
+ parse_error = `Value does not match format for ${ slotType . uri } ` ;
276
292
277
- if ( slotMinimumValue !== undefined && parsed < slotMinimumValue ) {
278
- return 'Value is less than minimum value' ;
293
+ //if (!(anyOfValidators.length || allOfValidators.length || exactlyOneOfValidators.length || noneOfValidators.length)) {
294
+ // return parse_error;
295
+ //}
279
296
}
297
+ // All these cases have encountered an item which matches basic data
298
+ // datatype and so sudden death is ok.
299
+ else {
280
300
281
- if ( slotMaximumValue !== undefined && parsed > slotMaximumValue ) {
282
- return 'Value is greater than maximum value' ;
283
- }
301
+ if ( slotMinimumValue !== undefined && parsed < slotMinimumValue ) {
302
+ return 'Value is less than minimum value' ;
303
+ }
304
+
305
+ if ( slotMaximumValue !== undefined && parsed > slotMaximumValue ) {
306
+ return 'Value is greater than maximum value' ;
307
+ }
308
+
309
+ if (
310
+ ( slotDefinition . equals_string !== undefined &&
311
+ parsed !== slotDefinition . equals_string ) ||
312
+ ( slotDefinition . equals_number !== undefined &&
313
+ parsed !== slotDefinition . equals_number )
314
+ ) {
315
+ return 'Value does not match constant' ;
316
+ }
284
317
285
- if (
286
- ( slotDefinition . equals_string !== undefined &&
287
- parsed !== slotDefinition . equals_string ) ||
288
- ( slotDefinition . equals_number !== undefined &&
289
- parsed !== slotDefinition . equals_number )
290
- ) {
291
- return 'Value does not match constant' ;
292
- }
318
+ if (
319
+ slotDefinition . pattern !== undefined &&
320
+ ! value . match ( slotDefinition . pattern )
321
+ ) {
322
+ return 'Value does not match pattern' ;
323
+ }
293
324
294
- if (
295
- slotDefinition . pattern !== undefined &&
296
- ! value . match ( slotDefinition . pattern )
297
- ) {
298
- return 'Value does not match pattern' ;
325
+ // Here slotType value tested and is ok!
326
+ continue ;
299
327
}
328
+
329
+ // Here value didn't parse to slotType
330
+
331
+ }
332
+ else {
333
+ // No basic slot type here so only enumeration handling.
300
334
}
301
335
336
+ // Single range for slot given so no need to evaluate all_of, any_of etc.
302
337
if ( slotEnum && ! slotPermissibleValues . includes ( value ) ) {
303
338
return 'Value is not allowed' ;
304
339
}
@@ -342,12 +377,17 @@ class Validator {
342
377
return 'One or more expressions of none_of held' ;
343
378
}
344
379
}
380
+
381
+ if ( anyOfValidators . length || allOfValidators . length || exactlyOneOfValidators . length || noneOfValidators . length ) {
382
+ // We passed validation here which means a parse error can be overriden
383
+ }
384
+ else if ( parse_error . length ) {
385
+ //There were no other ranges besides basic slotType so
386
+ return parse_error ;
387
+ }
345
388
}
346
389
} ;
347
390
348
- if ( typeof cacheKey === 'string' ) {
349
- this . #valueValidatorMap. set ( cacheKey , validate ) ;
350
- }
351
391
352
392
return validate ;
353
393
}
0 commit comments