require 'json-schema'

class SchemaValidator
  def initialize(payload, type:, schema_name: nil, schema: nil)
    @payload = payload
    @type = type
    @schema = schema
    @schema_name = schema_name
  end

  def validate
    return true if schema_name_exception?
    validate_schema
  end

private

  attr_reader :payload, :type

  def validate_schema
    JSON::Validator.validate!(schema_for_validation, payload)
  rescue JSON::Schema::ValidationError => error
    Airbrake.notify_or_ignore(error, parameters: {
      explanation: "#{payload} schema validation error"
    })
    false
  end

  def schema_for_validation
    return schema unless schema.has_key?("oneOf")
    index = payload.has_key?(:format) ? 0 : 1
    schema.merge(schema["oneOf"][index]).except("oneOf")
  end

  def schema
    @schema || JSON.load(File.read("govuk-content-schemas/formats/#{schema_name}/publisher_v2/#{type}.json"))
  rescue Errno::ENOENT => error
    Airbrake.notify_or_ignore(error, parameters: {
      explanation: "#{payload} is missing schema_name #{schema_name} or type #{type}"
    })
    @schema = {}
  end

  def schema_name
    @schema_name || payload[:schema_name] || payload[:format]
  end

  def schema_name_exception?
    schema_name.to_s.match(/placeholder_/)
  end
end

require 'json-schema'

class SchemaValidator
  def initialize(payload, type:, schema_name: nil, schema: nil)
    @payload = payload
    @type = type
    @schema = schema
    @schema_name = schema_name
  end

  def validate
    return true if schema_name_exception?
    validate_schema
  end

private

  attr_reader :payload, :type

  def validate_schema
    errors = JSON::Validator.fully_validate(
      schema_for_validation,
      payload,
      errors_as_objects: true,
    )

    return true if errors.empty?

    Airbrake.notify_or_ignore(
      {
        error_class: "SchemaValidationError",
        error_message: "Error validating payload against schema"
      },
      parameters: {
        errors: errors,
        message_data: payload
      }
    )
    false
  end

  def schema_for_validation
    return schema unless schema.has_key?("oneOf")
    index = payload.has_key?(:format) ? 0 : 1
    schema.merge(schema["oneOf"][index]).except("oneOf")
  end

  def schema
    @schema || JSON.load(File.read("govuk-content-schemas/formats/#{schema_name}/publisher_v2/#{type}.json"))
  rescue Errno::ENOENT => error
    Airbrake.notify_or_ignore(error, parameters: {
      explanation: "#{payload} is missing schema_name #{schema_name} or type #{type}"
    })
    @schema = {}
  end

  def schema_name
    @schema_name || payload[:schema_name] || payload[:format]
  end

  def schema_name_exception?
    schema_name.to_s.match(/placeholder_/)
  end
end

Send a representation of all errors when schema validation fails
Send a representation of all errors when schema validation fails

Instead of just the top level error.
