Skip to content

Commit

Permalink
Extend schema validator
Browse files Browse the repository at this point in the history
  • Loading branch information
nichtich committed Dec 14, 2023
1 parent aec56e1 commit f296385
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 29 deletions.
88 changes: 62 additions & 26 deletions lib/schema-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,17 @@ export class SchemaValidator {
}
}

validateViaSchema(schema) {
validateWithJSONSchema(schema) {
return this.validator(schema) ? [] : this.validator.errors
}

validate(schema) {
const errors = this.validator ? this.validateViaSchema(schema) : []
const errors = this.validator ? this.validateWithJSONSchema(schema) : []

if (typeof schema.fields === "object") {
errors.push(...this.validateFieldSchedule(schema.fields))
} else if (!this.validator) {
errors.push({ message: "missing or malformed required key 'fields'" })
}
errors.push(...this.validateFieldSchedule(schema.fields))

if (typeof schema.codelists === "object") {
for (let codelist of Object.values(schema.codelists)) {
if (typeof codelist === "object") {
errors.push(...this.validateCodes(codelist.codes || {}))
}
}
for (let codelist of Object.values(schema.codelists || {})) {
errors.push(...this.validateCodes(codelist.codes || {}))
}

return errors
Expand All @@ -49,28 +41,72 @@ export class SchemaValidator {
const errors = []
for (let id in fields) {
const field = fields[id]
if (typeof field === "object") {
if ("tag" in field) {
let hasId = field.tag
+ ("occurrence" in field ? "/" + field.occurrence : "")
+ ("counter" in field ? "/$x" + field.counter : "")
if (hasId !== id) {
errors.push({
message: `field identifier '${id}' does not match field definition '${hasId}'`,
})
if ("tag" in field) {
let hasId = field.tag
+ ("occurrence" in field ? "/" + field.occurrence : "")
+ ("counter" in field ? "/$x" + field.counter : "")
if (hasId !== id) {
errors.push({
message: `field identifier '${id}' does not match field definition '${hasId}'`,
})
}
}
for (let ind of ["indicator1","indicator2"]) {
if (ind in field && typeof field[ind].codes == "object") {
errors.push(...this.validateCodes(field[ind].codes || {}))
}
}
if (field.subfields) {
for (let key of ["positions", "pattern", "codes"]) {
if (key in field) {
errors.push({ message: `variable field must not have ${key}` })
}
}
} else if (!this.validator) {
errors.push({ message: "field definition must be object" })
errors.push(...this.validateSubfieldSchedule(field.subfields))
} else {
errors.push(
...this.validateCodes(field.codes || {}),
...this.validatePositions(field.positions || {}),
...this.validatePattern(field.pattern),
)
}
}
// TODO: additional constraints
return errors
}

validateSubfieldSchedule(subfields) {
// TODO: not covered by test suite
const errors = []
for (let code in subfields) {
const sf = subfields[code]
if ("code" in sf && sf.code !== code) {
errors.push({ message: `subfield code '${sf.code}' must be '${code}'` })
}
errors.push(
...this.validateCodes(sf.codes || {}),
...this.validatePositions(sf.positions || {}),
...this.validatePattern(sf.pattern),
)
}
return errors
}

validateCodes(codes) {
return Object.entries(codes)
.filter(([key, code]) => "code" in code && code.code !== key)
.map(([key]) => ({ message: `code must be '${key}' in codelist` }))
.map(([key, code]) => ({ message: `code '${code.code}' must be '${key}' in codelist` }))
}

validatePositions(/*positions*/) {
return [] // TODO
}

validatePattern(pattern) {
if (pattern != null) {
try { new RegExp(pattern) } catch (e) {
return [{ message: `Invalid pattern '${pattern}'` }]
}
}
return []
}
}
34 changes: 31 additions & 3 deletions test/schema-suite.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{
"valid": [
{
"fields": {},
"fields": {
"number": {
"pattern": "^[0-9]+$"
}
},
"codelists": {
"languages": {
"title": "langauges",
Expand Down Expand Up @@ -49,15 +53,39 @@
{
"schema": {
"fields": {
"abc": { "tag": "xyz" }
"abc": {
"tag": "xyz",
"indicator1": { },
"indicator2": {
"codes": { "a": { "code": "b" } }
}
}
},
"codelists": {
"test": { "codes": { "x": {}, "y": { "code": "z" } } }
}
},
"errors": [
"field identifier 'abc' does not match field definition 'xyz'",
"code must be 'y' in codelist"
"code 'b' must be 'a' in codelist",
"code 'z' must be 'y' in codelist"
]
},
{
"schema": {
"fields": {
"abc": {
"subfields": { "x": {} },
"codes": {}
},
"number": {
"pattern": "["
}
}
},
"errors": [
"variable field must not have codes",
"Invalid pattern '['"
]
}
]
Expand Down

0 comments on commit f296385

Please sign in to comment.