Class: Cocina::Models::Validators::JsonSchemaValidator

Inherits:
Object
  • Object
show all
Defined in:
lib/cocina/models/validators/json_schema_validator.rb

Overview

Validates Cocina model instances against the JSON schema.

The schema uses OpenAPI 3.1.0 conventions with ‘unevaluatedProperties: false` to enforce strict validation. However, allOf/$ref composition causes two categories of cascade noise in the structured evaluation output that must be filtered before reporting:

1. falseSchema entries — every `unevaluatedProperties: false` sub-schema emits a
   "False schema does not allow …" entry for each value at that path. These are always
   redundant with the parent unevaluatedProperties entry.

2. Root-level unevaluatedProperties listing only known model attributes as unexpected —
   the validator can't prove top-level fields were "evaluated" through allOf/$ref, so it
   reports every legitimate root property as unexpected at root "".

For example, validating a DRO with an unexpected ‘releaseTags` in `administrative` produces one actionable error plus ~12 cascade entries. The de-noiser keeps the actionable error and discards the rest.

Constant Summary collapse

SCHEMA_PATH =
::File.expand_path('../../../../schema.json', __dir__)

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(clazz, attributes) ⇒ JsonSchemaValidator

Returns a new instance of JsonSchemaValidator.

Parameters:

  • clazz (Class)

    the Cocina model class being validated (e.g., Cocina::Models::DRO)

  • attributes (Hash)

    the attributes of the model instance being validated



47
48
49
50
# File 'lib/cocina/models/validators/json_schema_validator.rb', line 47

def initialize(clazz, attributes)
  @clazz = clazz
  @attributes = attributes
end

Class Method Details

.documentHash

Returns a hash representation of the schema.json document.

Returns:

  • (Hash)

    a hash representation of the schema.json document



38
39
40
41
42
43
# File 'lib/cocina/models/validators/json_schema_validator.rb', line 38

def self.document
  @document ||= begin
    file_content = ::File.read(SCHEMA_PATH)
    JSON.parse(file_content)
  end
end

.validateObject

See Also:



27
28
29
# File 'lib/cocina/models/validators/json_schema_validator.rb', line 27

def self.validate(...)
  new(...).validate
end

.validator_for(def_name) ⇒ JSONSchema validator

Returns a cached per-definition validator.

Returns:

  • (JSONSchema validator)

    a cached per-definition validator



32
33
34
35
# File 'lib/cocina/models/validators/json_schema_validator.rb', line 32

def self.validator_for(def_name)
  @validators ||= {}
  @validators[def_name] ||= JSONSchema.validator_for({ '$ref' => "#/$defs/#{def_name}", '$defs' => document['$defs'] })
end

Instance Method Details

#validateNilClass

Validates attributes against the Cocina model schema.

Injects the cocinaVersion if the model includes it as an attribute, then validates the attributes against the schema definition for this model. De-noises unevaluatedProperties cascade errors before raising a ValidationError.

Uses the structured evaluation API to capture sub-errors inside anyOf branches, producing actionable messages (e.g. path-qualified minItems/pattern failures) rather than the generic “not valid under anyOf” message.

Returns:

  • (NilClass)

    returns nil if validation passes

Raises:

  • (ValidationError)

    if validation fails, with a de-noised error message



64
65
66
67
68
69
70
71
# File 'lib/cocina/models/validators/json_schema_validator.rb', line 64

def validate
  attributes['cocinaVersion'] = Cocina::Models::VERSION if clazz.attribute_names.include?(:cocinaVersion)

  evaluation = self.class.validator_for(method_name).evaluate(attributes.as_json)
  return if evaluation.valid?

  raise ValidationError, "When validating #{method_name}: " + filtered_error_messages(evaluation).join(', ')
end