diff --git a/fixtures/3/multi_bodies.json b/fixtures/3/multi_bodies.json new file mode 100644 index 0000000..8de4b1b --- /dev/null +++ b/fixtures/3/multi_bodies.json @@ -0,0 +1,49 @@ +{ + "@context": "http://iiif.io/api/presentation/3/context.json", + "id": "http://localhost:4000/recipe/0219-using-caption-file/manifest.json", + "type": "Manifest", + "label": { + "en": [ + "Lunchroom Manners" + ] + }, + "items": [ + { + "id": "http://localhost:4000/recipe/0219-using-caption-file/canvas", + "type": "Canvas", + "height": 360, + "width": 480, + "duration": 572.034, + "items": [ + { + "id": "http://localhost:4000/recipe/0219-using-caption-file/canvas/page", + "type": "AnnotationPage", + "items": [ + { + "id": "http://localhost:4000/recipe/0219-using-caption-file/canvas/page/annotation", + "type": "Annotation", + "motivation": "painting", + "body": [ + { + "id": "https://fixtures.iiif.io/video/indiana/lunchroom_manners/high/lunchroom_manners_1024kb.mp4", + "type": "Video", + "height": 360, + "width": 480, + "duration": 572.034, + "format": "video/mp4" + }, + { + "id": "https://fixtures.iiif.io/video/indiana/lunchroom_manners/lunchroom_manners.vtt", + "type": "Text", + "format": "text/vtt", + "language": "en" + } + ], + "target": "http://localhost:4000/recipe/0219-using-caption-file/canvas" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/iiif-presentation-validator.py b/iiif-presentation-validator.py index 3da51a5..2c2c550 100755 --- a/iiif-presentation-validator.py +++ b/iiif-presentation-validator.py @@ -74,13 +74,22 @@ def check_manifest(self, data, version, url=None, warnings=[]): if url and 'id' in mf and mf['id'] != url: raise ValidationError("The manifest id ({}) should be the same as the URL it is published at ({}).".format(mf["id"], url)) except ValidationError as e: - infojson = { - 'received': data, - 'okay': 0, - 'error': str(e), - 'url': url, - 'warnings': [] - } + if infojson: + infojson['errorList'].append({ + 'title': 'Resolve Error', + 'detail': str(e), + 'description': '', + 'path': '/id', + 'context': '{ \'id\': \'...\'}' + }) + else: + infojson = { + 'received': data, + 'okay': 0, + 'error': str(e), + 'url': url, + 'warnings': [] + } except Exception as e: traceback.print_exc() infojson = { diff --git a/schema/error_processor.py b/schema/error_processor.py index 4b75207..2c6ce7d 100755 --- a/schema/error_processor.py +++ b/schema/error_processor.py @@ -247,6 +247,15 @@ def parse(self, error_path, schemaEl, iiif_asset, IIIFJsonPath, parent=None, jso elif 'const' in option: value.append(option['const']) #print ('Using values: {}'.format(value)) + elif 'anyOf' in schemaEl['type']: + value = [] + for option in schemaEl['type']['anyOf']: + if 'pattern' in option: + value.append(option['pattern']) + elif 'const' in option: + value.append(option['const']) + #print ('Using values: {}'.format(value)) + if not self.isTypeMatch(jsonPath + '.type', iiif_asset, value, IIIFJsonPath): return False # Check child type to see if its a match @@ -273,7 +282,6 @@ def parse(self, error_path, schemaEl, iiif_asset, IIIFJsonPath, parent=None, jso if isinstance(schemaEl[pathEl], dict) and "$ref" in schemaEl[pathEl]: - #print ('Found ref, trying to resolve: {}'.format(schemaEl[pathEl]['$ref'])) return self.parse(error_path, self.resolver.resolve(schemaEl[pathEl]['$ref'])[1], iiif_asset, IIIFJsonPath, pathEl, jsonPath) else: diff --git a/schema/iiif_3_0.json b/schema/iiif_3_0.json index 3efe231..80690de 100644 --- a/schema/iiif_3_0.json +++ b/schema/iiif_3_0.json @@ -598,11 +598,13 @@ ] }, "body": { - "oneOf": [ + "anyOf": [ { + "type": "object", "$ref": "#/classes/resource" }, { + "type": "object", "allOf":[ { "$ref": "#/classes/choice" }, { @@ -615,11 +617,18 @@ "required": ["items"] } ] + }, + { + "type": "array", + "items": { + "type": "object" + } } + ] }, "target": { - "oneOf": [ + "anyOf": [ { "$ref": "#/classes/annoTarget" }, { "type": "array", diff --git a/tests/test_validator.py b/tests/test_validator.py index 5da9e0e..b573d42 100644 --- a/tests/test_validator.py +++ b/tests/test_validator.py @@ -141,7 +141,8 @@ def test07_check_manifest3(self): 'fixtures/3/annoPageMultipleMotivations.json', 'fixtures/3/old_cc_license.json', 'fixtures/3/rightsstatement_license.json', - 'fixtures/3/extension_anno.json' + 'fixtures/3/extension_anno.json', + 'fixtures/3/multi_bodies.json' ]: with open(good, 'r') as fh: data = fh.read() @@ -216,7 +217,7 @@ def test08_errortrees(self): self.assertTrue(errorParser.isValid(path, iiifPath), 'Should have caught the service in thumbnail needs to be an array.') # annotationPage - path = [u'allOf', 1, u'oneOf', 0, u'allOf', 1, u'properties', u'items', u'items', u'allOf', 1, u'properties', u'items', u'items', u'allOf', 1, u'properties', u'items', u'items', u'allOf', 1, u'properties', u'body', u'oneOf'] + path = [u'allOf', 1, u'oneOf', 0, u'allOf', 1, u'properties', u'items', u'items', u'allOf', 1, u'properties', u'items', u'items', u'allOf', 1, u'properties', u'items', u'items', u'allOf', 1, u'properties', u'body', u'anyOf'] iiifPath = [u'items', 0, u'items', 0, u'items', 0, u'body'] self.assertTrue(errorParser.isValid(path, iiifPath), 'Should have caught the service in the canvas needs to be an array') @@ -243,7 +244,7 @@ def test_version3errors(self): filename = 'fixtures/3/broken_service.json' errorPaths = [ '/thumbnail[0]/service', - '/body[0]/items[0]/items[0]/items/items[0]/items[0]/items[0]/body/service' + '/items[0]/items[0]/items[0]/body/' ] response = self.helperRunValidation(v, filename) self.helperTestValidationErrors(filename, response, errorPaths) @@ -264,6 +265,7 @@ def test_lang_rights(self): '/label', '/items[0]/label[0]', '/items[0]/', + '/items[0]/items[0]/items[0]/body/', '/rights', '/metadata[0]/label/', '/metadata[0]/value/',