Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

switch LanguageTagValidator to operate on DRO::TYPES so that it's invoked automatically when building item models #643

Merged
merged 1 commit into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 35 additions & 9 deletions lib/cocina/models/validators/language_tag_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,56 @@ def initialize(clazz, attributes)
def validate
return unless meets_preconditions?

return if valid_language_tag?
return if invalid_files.empty?

raise ValidationError, 'The provided language tag is not valid according to RFC 4646: ' \
"#{attributes[:languageTag]}"
raise ValidationError, 'Some files have invalid language tags according to RFC 4646: ' \
"#{invalid_filenames_with_language_tags.join(', ')}"
end

private

attr_reader :clazz, :attributes

def meets_preconditions?
file? && attributes[:languageTag].present?
dro?
end

def file?
(clazz::TYPES & File::TYPES).any?
def dro?
(clazz::TYPES & DRO::TYPES).any?
rescue NameError
false
end

def valid_language_tag?
parsed_tag = I18n::Locale::Tag::Rfc4646.tag(attributes[:languageTag])
def valid_language_tag?(file)
# I18n::Locale::Tag::Rfc4646.tag will return an instance of I18n::Locale::Tag::Rfc4646 (with fields like language, script,
# region) for strings that can be parsed according to RFC 4646, and nil for strings that do not conform to the spec.
I18n::Locale::Tag::Rfc4646.tag(file[:languageTag]).present?
end

def invalid_files
@invalid_files ||= language_tag_files.reject { |file| valid_language_tag?(file) }
end

def invalid_filenames_with_language_tags
invalid_files.map { |invalid_file| "#{invalid_file[:filename] || invalid_file[:label]} (#{invalid_file[:languageTag]})" }
end

def language_tag_files
files.select { |file| file[:languageTag].present? }
end

def files
[].tap do |files|
next if attributes.dig(:structural, :contains).nil?

attributes[:structural][:contains].each do |fileset|
next if fileset.dig(:structural, :contains).nil?

parsed_tag.present? && parsed_tag.is_a?(I18n::Locale::Tag::Rfc4646)
fileset[:structural][:contains].each do |file|
files << file
end
end
end
end
end
end
Expand Down
58 changes: 39 additions & 19 deletions spec/cocina/models/validators/language_tag_validator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,27 @@
require 'spec_helper'

RSpec.describe Cocina::Models::Validators::LanguageTagValidator do
let(:validate) { described_class.validate(clazz, props) }
let(:validate) { described_class.validate(clazz, dro_props) }

let(:dro_props) do
{
type: Cocina::Models::ObjectType.book,
access: { view: 'dark', download: 'none' },
structural: {
contains: [
{
externalIdentifier: 'bc123df4567_1',
label: 'Fileset 1',
type: Cocina::Models::FileSetType.file,
version: 1,
structural: {
contains: [props]
}
}
]
}
}
end

let(:props) { file_props }

Expand All @@ -26,16 +46,16 @@
end

context 'with no value for languageTag specified' do
context 'with a File' do
let(:clazz) { Cocina::Models::File }
context 'with a DRO' do
let(:clazz) { Cocina::Models::DRO }

it 'does not raise' do
expect { validate }.not_to raise_error
end
end

context 'with a RequestFile' do
let(:clazz) { Cocina::Models::RequestFile }
context 'with a RequestDRO' do
let(:clazz) { Cocina::Models::RequestDRO }

it 'does not raise' do
expect { validate }.not_to raise_error
Expand All @@ -53,16 +73,16 @@
context 'with a recognized language, script, and region' do
let(:language_tag) { 'ru-Cyrl-RU' }

context 'with a File' do
let(:clazz) { Cocina::Models::File }
context 'with a DRO' do
let(:clazz) { Cocina::Models::DRO }

it 'does not raise' do
expect { validate }.not_to raise_error
end
end

context 'with a RequestFile' do
let(:clazz) { Cocina::Models::RequestFile }
context 'with a RequestDRO' do
let(:clazz) { Cocina::Models::RequestDRO }

it 'does not raise' do
expect { validate }.not_to raise_error
Expand All @@ -73,16 +93,16 @@
context 'with an unrecognized language, script, and region' do
let(:language_tag) { 'foo-Barr-BZ' } # still conforms to the expected format of BCP 47/RFC 4646, should parse

context 'with a File' do
let(:clazz) { Cocina::Models::File }
context 'with a DRO' do
let(:clazz) { Cocina::Models::DRO }

it 'does not raise' do
expect { validate }.not_to raise_error
end
end

context 'with a RequestFile' do
let(:clazz) { Cocina::Models::RequestFile }
context 'with a RequestDRO' do
let(:clazz) { Cocina::Models::RequestDRO }

it 'does not raise' do
expect { validate }.not_to raise_error
Expand All @@ -98,19 +118,19 @@
end
end

context 'with a File' do
let(:clazz) { Cocina::Models::File }
context 'with a DRO' do
let(:clazz) { Cocina::Models::DRO }

it 'raises a validation error' do
expect { validate }.to raise_error(Cocina::Models::ValidationError, 'The provided language tag is not valid according to RFC 4646: fooooooooooooo')
expect { validate }.to raise_error(Cocina::Models::ValidationError, 'Some files have invalid language tags according to RFC 4646: page1.txt (fooooooooooooo)')
end
end

context 'with a RequestFile' do
let(:clazz) { Cocina::Models::RequestFile }
context 'with a RequestDRO' do
let(:clazz) { Cocina::Models::RequestDRO }

it 'raises a validation error' do
expect { validate }.to raise_error(Cocina::Models::ValidationError, 'The provided language tag is not valid according to RFC 4646: fooooooooooooo')
expect { validate }.to raise_error(Cocina::Models::ValidationError, 'Some files have invalid language tags according to RFC 4646: page1.txt (fooooooooooooo)')
end
end
end
Expand Down