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

V3 manifest: improve specificity, validation and tests #22

Merged
merged 4 commits into from
Jul 28, 2017
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion lib/iiif/v3/presentation/annotation_collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module V3
module Presentation
class AnnotationCollection < IIIF::V3::AbstractResource

TYPE = 'AnnotationCollection'
TYPE = 'AnnotationCollection'.freeze

def required_keys
super + %w{ id }
Expand All @@ -17,6 +17,16 @@ def array_only_keys
super + %w{ content }
end

# TODO: paging properties
# Collection, AnnotationCollection, (formerly layer --> AnnotationPage???) allow; forbidden o.w.
# ---
# first, last, next, prev
# id is URI, but may have other info
# total, startIndex
# The value must be a non-negative integer.
#
# don't forget to validate

def initialize(hsh={})
hsh['type'] = TYPE unless hsh.has_key? 'type'
super(hsh)
Expand Down
21 changes: 20 additions & 1 deletion lib/iiif/v3/presentation/collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module V3
module Presentation
class Collection < IIIF::V3::AbstractResource

TYPE = 'Collection'
TYPE = 'Collection'.freeze

def required_keys
super + %w{ id label }
Expand All @@ -13,6 +13,16 @@ def array_only_keys
super + %w{ collections manifests }
end

# TODO: navDate (collection or manifest only) - The value must be an xsd:dateTime literal in UTC, expressed in the form “YYYY-MM-DDThh:mm:ssZ”; There must be at most one navDate associated with any given resource.

# TODO: paging properties
# Collection, AnnotationCollection, (formerly layer --> AnnotationPage???) allow; forbidden o.w.
# ---
# first, last, next, prev
# id is URI, but may have other info
# total, startIndex
# The value must be a non-negative integer.

def legal_viewing_hint_values
%w{ auto-advance together }
end
Expand All @@ -26,6 +36,15 @@ def validate
super
# TODO: each member of collections and manifests must be a Hash
# TODO: each member of collections and manifests MUST have id, type, and label
# TODO: navDate (collection or manifest only) - The value must be an xsd:dateTime literal in UTC, expressed in the form “YYYY-MM-DDThh:mm:ssZ”; There must be at most one navDate associated with any given resource.

# TODO: paging properties
# Collection, AnnotationCollection, (formerly layer --> AnnotationPage???) allow; forbidden o.w.
# ---
# first, last, next, prev
# id is URI, but may have other info
# total, startIndex
# The value must be a non-negative integer.
end
end
end
Expand Down
81 changes: 76 additions & 5 deletions lib/iiif/v3/presentation/manifest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,32 @@ module V3
module Presentation
class Manifest < IIIF::V3::AbstractResource

TYPE = 'Manifest'
TYPE = 'Manifest'.freeze

def required_keys
# NOTE: relaxing requirement for items as Universal Viewer currently only accepts sequences
# see https://github.com/sul-dlss/osullivan/issues/27, sul-dlss/purl/issues/167
# super + %w{ id label items }
super + %w{ id label }
end

def prohibited_keys
super + CONTENT_RESOURCE_PROPERTIES + PAGING_PROPERTIES + %w{ start_canvas content_annotation }
end

def uri_only_keys
super + %w{ id }
end

def array_only_keys
super + %w{ sequences structures }
# NOTE: allowing 'items' or 'sequences' as Universal Viewer currently only accepts sequences
# see https://github.com/sul-dlss/osullivan/issues/27, sul-dlss/purl/issues/167
# super + %w{ items structures }
super + %w{ items structures sequences }
end

def legal_viewing_hint_values
%w{ individuals paged continuous auto-advance none }
%w{ individuals paged continuous auto-advance }
end

def initialize(hsh={})
Expand All @@ -23,8 +37,65 @@ def initialize(hsh={})
end

def validate
super
# TODO: check types of sequences and structure members
super # also checks navDate format

unless self['id'] =~ /^https?:/
err_msg = "id must be an http(s) URI for #{self.class}"
raise IIIF::V3::Presentation::IllegalValueError, err_msg
end

# Sequence object list
# NOTE: allowing 'items' or 'sequences' as Universal Viewer currently only accepts sequences
# see https://github.com/sul-dlss/osullivan/issues/27, sul-dlss/purl/issues/167
unless (self['items'] && self['items'].any?) ||
(self['sequences'] && self['sequences'].any?)
m = 'The (items or sequences) list must have at least one entry (and it must be a IIIF::V3::Presentation::Sequence)'
raise IIIF::V3::Presentation::MissingRequiredKeyError, m
end
validate_sequence_list(self['items']) if self['items']
validate_sequence_list(self['sequences']) if self['sequences']

# TODO: when embedding a sequence without any extensions within a manifest, the sequence must not have the @context field.

# TODO: AnnotationLists must not be embedded within the manifest

if self['structures']
unless self['structures'].all? { |entry| entry.instance_of?(IIIF::V3::Presentation::Range)}
m = 'All entries in the structures list must be a IIIF::V3::Presentation::Range'
raise IIIF::V3::Presentation::IllegalValueError, m
end
end
end

# NOTE: allowing 'items' or 'sequences' as Universal Viewer currently only accepts sequences
# see https://github.com/sul-dlss/osullivan/issues/27, sul-dlss/purl/issues/167
def validate_sequence_list(sequence_array)
unless sequence_array.size >= 1
m = 'The (items or sequences) list must have at least one entry (and it must be a IIIF::V3::Presentation::Sequence)'
raise IIIF::V3::Presentation::MissingRequiredKeyError, m
end

unless sequence_array.all? { |entry| entry.instance_of?(IIIF::V3::Presentation::Sequence) }
m = 'All entries in the (items or sequences) list must be a IIIF::V3::Presentation::Sequence'
raise IIIF::V3::Presentation::IllegalValueError, m
end

default_sequence = sequence_array.first
# NOTE: allowing 'items' or 'canvases' as Universal Viewer currently only accepts canvases
# see https://github.com/sul-dlss/osullivan/issues/27, sul-dlss/purl/issues/167
canvas_array = default_sequence['items'] || default_sequence['canvases']
unless canvas_array && canvas_array.size >= 1 &&
canvas_array.all? { |entry| entry.instance_of?(IIIF::V3::Presentation::Canvas) }
m = 'The default Sequence (the first entry of (items or sequences)) must be written out in full within the Manifest file'
raise IIIF::V3::Presentation::IllegalValueError, m
end

if sequence_array.size > 1
unless sequence_array.all? { |entry| entry['label'] }
m = 'If there are multiple Sequences in a manifest then they must each have at least one label'
raise IIIF::V3::Presentation::IllegalValueError, m
end
end
end
end
end
Expand Down
9 changes: 8 additions & 1 deletion lib/iiif/v3/presentation/range.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
module IIIF
module V3
module Presentation
# Ranges are linked or embedded within the manifest in a structures field
class Range < Sequence

TYPE = 'Range'
TYPE = 'Range'.freeze

def required_keys
super + %w{ id label }
end

# TODO: contentAnnotations: links to AnnotationCollection
# TODO: startCanvas: A link from a Sequence or Range to a Canvas that is contained within it

def array_only_keys
super + %w{ members }
end
Expand All @@ -24,7 +28,10 @@ def initialize(hsh={})

def validate
super
# TODO: Ranges must have URIs and they should be http(s) URIs.
# TODO: Values of the members array must be canvas or range
# TODO: contentAnnotations: links to AnnotationCollection
# TODO: startCanvas: A link from a Sequence or Range to a Canvas that is contained within it
end
end
end
Expand Down
14 changes: 6 additions & 8 deletions spec/integration/iiif/v3/abstract_resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"id": "http://www.example.org/library/catalog/book1.marc",
"format": "application/marc"
},
"sequences": [
"items": [
{
"id":"http://www.example.org/iiif/book1/sequence/normal",
"type": "Sequence",
Expand Down Expand Up @@ -122,16 +122,15 @@
expect(parsed.to_ordered_hash.to_a - from_file.to_ordered_hash.to_a).to eq []
expect(from_file.to_ordered_hash.to_a - parsed.to_ordered_hash.to_a).to eq []
end
it 'turns each member of "sequences" into an instance of Sequence' do
expected_klass = IIIF::V3::Presentation::Sequence
it 'turns each member of "items" into an instance of Sequence' do
parsed = described_class.from_ordered_hash(fixture)
parsed['sequences'].each do |s|
expect(s.class).to be expected_klass
parsed['items'].each do |s|
expect(s.class).to be IIIF::V3::Presentation::Sequence
end
end
it 'turns each member of items into an instance of Canvas' do
it 'turns each member of sequences/items into an instance of Canvas' do
parsed = described_class.from_ordered_hash(fixture)
parsed['sequences'].each do |s|
parsed['items'].each do |s|
s.items.each do |c|
expect(c.class).to be IIIF::V3::Presentation::Canvas
end
Expand All @@ -145,7 +144,6 @@
parsed = described_class.from_ordered_hash(fixture)
expect(parsed['label']).to eq 'My Manifest'
end

end

describe '#to_ordered_hash' do
Expand Down
Loading