Skip to content

Commit

Permalink
Rename validation options
Browse files Browse the repository at this point in the history
  • Loading branch information
nichtich committed Dec 19, 2023
1 parent fa76c8b commit ccf2a31
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 63 deletions.
7 changes: 0 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,6 @@ Options:
An empty string schema argument uses the empty schema. Combining -n and -v emits
parsed records. Validation options (separable with any of [ ,|+]):
ignore_unknown_fields
ignore_subfields
ignore_unknown_subfields
ignore_values
ignore_codes
ignore_unknown_codelists
~~~

The list of supported input formats depends on installed parsing libraries (see [Install]).
Expand Down
70 changes: 34 additions & 36 deletions lib/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ export class Validator {

validateRecords(records, options={}) {
const { schedule, codelists } = this
options = { ...this.options, ...options, codelists }
options = { ...Validator.options, ...this.options, ...options, codelists }

const errors = []
const recordsPerField = options.count_records && options.count_fields
const recordsPerField = options.countRecords && options.countFields
? new ScheduleVisitor(schedule) : null

// TODO: AR19 to AR24 (counting and external rules)
Expand All @@ -39,11 +39,11 @@ export class Validator {
if (recordsPerField && !fieldVisitor.seen[id]) {
recordsPerField.visit(id)
}
if (!fieldVisitor.visit(id) && !options.ignoreNonrepeatableField) {
if (!fieldVisitor.visit(id) && options.nonrepeatableField) {
const message = `Field '${id}' must not be repeated.`
errors.push({ message, identifier: id, error: "AR3" })
}
} else if (!options.ignore_unknown_fields) {
} else if (options.undefinedField) {
const { tag, occurrence } = field
const error = occurrence
? {message: `Unknown field '${tag}/${occurrence}'.`, tag, occurrence}
Expand All @@ -60,14 +60,14 @@ export class Validator {
errors.push(...missing)
}

if (options.count_records) {
if (options.countRecords) {
if (this.records >= 0 && records.length != this.records) {
errors.push({
error: "AR17",
message: `expected ${this.records} records, got ${records.length}`,
})
}
if (options.count_fields) {
if (options.countFields) {
for (let id in recordsPerField.seen) {
const expect = schedule[id].records
const got = recordsPerField.seen[id]
Expand All @@ -90,24 +90,22 @@ Validator.legacyOptions = {
}

Validator.options = {
ignoreNonrepeatableField: true,
ignoreNonrepeatableSubfield: true,
ignoreMissingSubfield: true,
ignoreIndicator: true,
ignorePattern: true,

ignore_unknown_fields: true,
ignore_subfields: true,
ignore_unknown_subfields: true,
ignore_values: true,
ignore_codes: true,
ignore_unknown_codelists: true,

count_records: false,
count_fields: false,
count_subfields: false,
count_codes: false,
count_all: false,
undefinedField: true,
nonrepeatableField: true,
missingField: true, // TODO
invalidIndicator: true,
invalidSubfield: true,
undefinedSubfield: true,
nonrepeatableSubfield: true,
missingSubfield: true,
invalidValue: true,
patternMismatch: true,
undefinedCode: true,
undefinedCodelist: false,
countRecords: false,
countFields: false,
countSubfields: false,
countCodes: false,
}

const validateFieldContent = (field, definition, options) => {
Expand All @@ -119,7 +117,7 @@ const validateFieldContent = (field, definition, options) => {
}

const validateIndicators = (field, definition, options) => {
if (options.ignoreIndicator) return []
if (!options.invalidIndicator) return []
const errors = []
for (let ind of ["indicator1", "indicator2"]) {
const value = ind in field ? field[ind] : " "
Expand All @@ -131,7 +129,7 @@ const validateIndicators = (field, definition, options) => {

// See <https://format.gbv.de/schema/avram/specification#subfield-validation>
export const validateSubfields = (subfields, schedule, options) => {
if (options.ignore_subfields) return []
if (!options.invalidSubfield) return []

const errors = []
const visitor = new ScheduleVisitor(schedule)
Expand All @@ -141,21 +139,21 @@ export const validateSubfields = (subfields, schedule, options) => {
const subfieldDefinition = schedule[code]

if (code in schedule) {
if (!visitor.visit(code) && !options.ignoreNonrepeatableSubfield) {
if (!visitor.visit(code) && options.nonrepeatableSubfield) {
errors.push({
message: `Subfield '${code}' must not be repeated.`,
error: "AR8",
code,
})
}
if (!options.ignore_subfield_values) {
if (options.invalidValue) {
const value = subfields[i+1]
const valueErrors = validateValue(value, subfieldDefinition, options)
if (valueErrors) {
errors.push(...valueErrors)
}
}
} else if(!options.ignore_unknown_subfields) {
} else if(options.undefinedSubfield) {
errors.push({
message: `Unknown subfield '${code}'.`,
code,
Expand All @@ -164,7 +162,7 @@ export const validateSubfields = (subfields, schedule, options) => {
}
}

if (!options.ignoreMissingSubfield) {
if (options.missingSubfield) {
errors.push(...visitor.missing().map(code => ({
message: `Missing subfield '${code}'.`, code, error: "AR9",
})))
Expand All @@ -176,11 +174,11 @@ export const validateSubfields = (subfields, schedule, options) => {
// See https://format.gbv.de/schema/avram/specification#value-validation
export const validateValue = (value, definition, options = {}) => {
const { pattern, positions } = definition
return options.ignore_values ? [] : [
...(options.ignorePattern ? [] : validatePattern(value, pattern)),
return options.invalidValue ? [
...(options.patternMismatch ? validatePattern(value, pattern) : []),
...validatePositions(value, positions, options),
...validateCode(value, definition, options),
]
] : []
}

export const validatePattern = (value, pattern) => {
Expand All @@ -199,17 +197,17 @@ export const validatePattern = (value, pattern) => {

// See https://format.gbv.de/schema/avram/specification#validation-with-codelist
export const validateCode = (value, definition, options = {}, isIndicator = false) => {
const { ignore_codes, ignore_unknown_codelists, codelists } = options
const { undefinedCode, undefinedCodelist, codelists } = options

var { codes } = definition
if (!codes || (ignore_codes && !isIndicator)) return []
if (!codes || (!undefinedCode && !isIndicator)) return []

// expand referenced codelist
if (typeof codes === "string") {
if (codelists && codes in codelists) {
codes = codelists[codes]?.codes || {}
} else {
return ignore_unknown_codelists ? [] :
return undefinedCodelist ? [] :
[{
message: `Unknown codelist '${codes}'.`,
value: codes,
Expand Down
12 changes: 3 additions & 9 deletions test/suite.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,21 @@
import { expect, localFiles, jsonFile } from "./test.js"
import { Validator } from "../index.js"

const asObject = obj => {
if (!obj) return {}
if (Array.isArray(obj)) return Object.fromEntries(obj.map(key => [key, true]))
return obj
}

localFiles("suite",/\.json$/).forEach(file => {
describe(file, () => {
jsonFile(file).forEach((testCase, caseIndex) => {
const caseDescription = testCase.description || caseIndex + 1
const { schema, options, tests } = testCase
const validator = new Validator(schema, asObject(options))
const validator = new Validator(schema, options)

tests.forEach((test, index) => {
const { options, errors } = test
const description = test.description ||
(caseDescription + (tests.length > 1 ? `-${index + 1}` : ""))
it(description, () => {
const result = test.records
? validator.validateRecords(test.records, asObject(options))
: validator.validate(test.record, asObject(options))
? validator.validateRecords(test.records, options)
: validator.validate(test.record, options)
if (errors && errors.length) {
expect(result).deep.equal(errors)
} else {
Expand Down
6 changes: 3 additions & 3 deletions test/suite/codes.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,20 @@
}
]
},{
"description": "ignore_codes",
"description": "-undefinedCode",
"record": [
{ "tag": "lang", "value": "deu" },
{ "tag": "bool", "value": "y" },
{ "tag": "wtf", "value": "xy" }
],
"options": ["ignore_codes"]
"options": { "undefinedCode": false }
},{
"description": "ignore_unknown_codelists",
"record": [
{ "tag": "bool", "value": "y" },
{ "tag": "wtf", "value": "xy" }
],
"options": ["ignore_unknown_codelists"],
"options": { "undefinedCodelist": true },
"errors": [
{
"message": "Value 'y' is not defined in codelist.",
Expand Down
4 changes: 2 additions & 2 deletions test/suite/counting.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@
},
"tests": [
{
"options": ["count_records"],
"options": { "countRecords": true },
"records": [],
"errors": [
{ "message": "expected 2 records, got 0", "error": "AR17" }
]
},
{
"options": ["count_records", "count_fields"],
"options": { "countRecords": true, "countFields": true },
"records": [
[
{ "tag": "a", "value": "" },
Expand Down
8 changes: 4 additions & 4 deletions test/suite/ignore_unknown.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@
]
},
{
"description": "ignore_unknown_fields",
"description": "-undefinedField",
"record": [ { "tag": "unknown", "value": "" } ],
"options": ["ignore_unknown_fields"]
"options": { "undefinedField": false }
},
{
"description": "ignore_unknown_subfields",
"description": "-undefinedSubfield",
"record": [ { "tag": "known", "subfields": [ "a", "" ] } ],
"options": ["ignore_unknown_subfields"]
"options": { "undefinedSubfield": false }
}
]
}
Expand Down
2 changes: 1 addition & 1 deletion test/suite/indicators.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
{ "tag": "010", "indicator2": "x", "value": "123" },
{ "tag": "210", "value": "123" }
],
"options": [ "ignore_codes" ],
"options": { "ignore_codes": true },
"errors": [
{
"error": "AR11",
Expand Down
2 changes: 1 addition & 1 deletion test/suite/validate-values.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
},
{
"record": [{"tag":"_", "value":"x"}],
"options": ["ignore_codes"]
"options": { "undefinedCode": false }
}
]
},{
Expand Down

0 comments on commit ccf2a31

Please sign in to comment.