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

New: Add 'accept' header option on validator #88

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
21 changes: 19 additions & 2 deletions iiif-presentation-validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

from iiif_prezi.loader import ManifestReader

IIIF_HEADER = "application/ld+json;profile=http://iiif.io/api/presentation/{iiif_version}/context.json"


class Validator(object):
"""Validator class that runs with Bottle."""
Expand All @@ -34,11 +36,14 @@ def __init__(self):
"""Initialize Validator with default_version."""
self.default_version = "2.1"

def fetch(self, url):
def fetch(self, url, accept_header):
"""Fetch manifest from url."""
req = Request(url)
req.add_header('Accept-Encoding', 'gzip')

if accept_header:
req.add_header('Accept', accept_header)

try:
wh = urlopen(req)
except HTTPError as wh:
Expand Down Expand Up @@ -106,13 +111,24 @@ def do_GET_test(self):
"""Implement GET request to test url at version."""
url = request.query.get('url', '')
version = request.query.get('version', self.default_version)
accept = request.query.get('accept')
accept_header = None
url = url.strip()
parsed_url = urlparse(url)

if accept and accept == 'true':
if version in ("2.0", "2.1"):
accept_header = IIIF_HEADER.format(iiif_version=2)
elif version in ("3.0",):
accept_header = IIIF_HEADER.format(iiif_version=3)
else:
accept_header = "application/json"

if (parsed_url.scheme != 'http' and parsed_url.scheme != 'https'):
return self.return_json({'okay': 0, 'error': 'URLs must use HTTP or HTTPS', 'url': url})

try:
(data, webhandle) = self.fetch(url)
(data, webhandle) = self.fetch(url, accept_header)
except:
return self.return_json({'okay': 0, 'error': 'Cannot fetch url', 'url': url})

Expand Down Expand Up @@ -192,6 +208,7 @@ def main():
args = parser.parse_args()

v = Validator()

run(host=args.hostname, port=args.port, app=v.get_bottle_app())


Expand Down
38 changes: 26 additions & 12 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@

<section class="wrapper">
<div>
This service will validate a IIIF Presentation API resource against the specification. Fill in the URL of your manifest, and it will try to parse it and issue errors for failed requirements, and warnings for recommendations that haven't been followed.
This service will validate a IIIF Presentation API resource against the specification. Fill in the URL
of your manifest, and it will try to parse it and issue errors for failed requirements, and warnings for
recommendations that haven't been followed.
</div>

<div style="border: 1px solid black;margin-left: 10px; margin-top: 20px; padding: 10px">
Expand All @@ -79,6 +81,8 @@
<option value="1.0">1.0</option>
</select>
<br/>
<input type="checkbox" id="accept" name="accept">
<label for="accept">Include <code>Accept</code> header in request for specified version</label><br />

<input type="submit" value="Go!" id="submit-url">
</form>
Expand All @@ -94,13 +98,20 @@ <h3>Validation Results:</h3>
<hr/>

<div style="margin-top:20px">
<b>Technical Note</b>
<b>Technical Notes</b>
<p>
The <code>Accept</code> header option tells the validator to use <a href="https://tools.ietf.org/html/rfc7231#section-5.3.2">content negotiation</a>
to retrieve a manifest at a given URL. This may be used to retrieve manifests from service
providers that support content negotiation for switching between IIIF versions.
</p>
<p>
If you would like to use the validator programatically, there are two options:
</p>
<ul>
<li><a href="https://github.com/IIIF/presentation-validator">Download</a> the code from github and run it locally.</li>
<li>Use it online with JSON based output, by an HTTP GET to this endpoint:<br/>http://iiif.io/api/presentation/validator/service/validate?version=2.1&amp;url=<i>manifest-url-here</i></li>
<li>Use it online with JSON based output, by an HTTP GET to this endpoint: <br/>
http://iiif.io/api/presentation/validator/service/validate?version=2.1&amp;url=<i>manifest-url-here</i>&accept=<i>true|false</i>
</li>
</ul>
</div>
</section>
Expand All @@ -124,23 +135,26 @@ <h3>Validation Results:</h3>
// Call out to the validation service and get a result
function handleSubmission(e) {
e.preventDefault();

var data = {
url: $("input[name='url']").val(),
version: $("select[name='version']").val()
}
version: $("select[name='version']").val(),
accept: $("#accept").is(":checked"),
};

var url = $('#manifest-validation-form').attr("action");
$.getJSON(url,data,handleValidationResponse);
$.getJSON(url, data, handleValidationResponse);
}

// Handle validation service response, render response block
function handleValidationResponse(data) {
str = '<div style="margin-left: 20px">'
str += '<div>URL Tested: '+ data.url + '</div><br/>'
var str = '<div style="margin-left: 20px">';
str += '<div>URL Tested: '+ data.url + '</div><br/>';
if (data.okay) {
str += '<div><h2 style="color:green">Validated successfully</h2></div>'
str += '<div><h2 style="color:green">Validated successfully</h2></div>';
} else {
if (data.errorList) {
for (var i = 0; i < data.errorList.length; i++) {
for (var i = 0, len = data.errorList.length; i < len; i++) {
var error = data.errorList[i];
str+='<div>';
str+='<h2 style="color:red">' + error.title + '</h2>';
Expand All @@ -155,12 +169,12 @@ <h3>Validation Results:</h3>
str+='</div>';
}
} else {
str += '<div><h2 style="color:red">Validation Error: '+data.error+'</h2></div>'
str += '<div><h2 style="color:red">Validation Error: '+data.error+'</h2></div>';
}
}
if (data.warnings && data.warnings.length){
str += '<div style="margin-top: 20px">Warnings:<ul>';
for(var i =0; i < data.warnings.length; i++) {
for(var i =0, len = data.warnings.length; i < len; i++) {
str+= '<li>'+data.warnings[i]+'</li>';
}
str += '</ul></div>';
Expand Down
27 changes: 22 additions & 5 deletions tests/test_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ def test01_get_bottle_app(self):

def test02_fetch(self):
v = val_mod.Validator()
(data, wh) = v.fetch('file:fixtures/1/manifest.json')
(data, wh) = v.fetch('file:fixtures/1/manifest.json', 'false')
self.assertTrue(data.startswith('{'))
self.assertRaises(URLError, v.fetch, 'file:DOES_NOT_EXIST')
self.assertRaises(URLError, v.fetch, 'file:DOES_NOT_EXIST', 'false')

def test03_check_manifest(self):
v = val_mod.Validator()
Expand Down Expand Up @@ -120,6 +120,21 @@ def test05_do_GET_test(self):
v.fetch = Mock(return_value=(read_fixture('fixtures/1/manifest.json'), MockWebHandle()))
j = json.loads(v.do_GET_test())
self.assertEqual(j['okay'], 0)
# Check v3 requests pass
request = LocalRequest({'QUERY_STRING': 'version=3.0&url=https://a.b/&accept=true'})
v.fetch = Mock(return_value=(read_fixture('fixtures/3/full_example.json'), MockWebHandle()))
j = json.loads(v.do_GET_test())
self.assertEqual(j['okay'], 1)
# Check v3 requests allow accept = false
request = LocalRequest({'QUERY_STRING': 'version=3.0&url=https://a.b/&accept=false'})
v.fetch = Mock(return_value=(read_fixture('fixtures/3/full_example.json'), MockWebHandle()))
j = json.loads(v.do_GET_test())
self.assertEqual(j['okay'], 1)
# Check v2 requests do not validate v3 manifests
request = LocalRequest({'QUERY_STRING': 'version=2.1&url=https://a.b/&accept=false'})
v.fetch = Mock(return_value=(read_fixture('fixtures/3/full_example.json'), MockWebHandle()))
j = json.loads(v.do_GET_test())
self.assertEqual(j['okay'], 0)

def test06_index_route(self):
"""Test index page."""
Expand Down Expand Up @@ -157,20 +172,22 @@ def test07_check_manifest3(self):
j = json.loads(v.check_manifest(data, '3.0'))

if j['okay'] == 1:
print ("Expected {} to fail validation but it passed....".format(bad_data))
print("Expected {} to fail validation but it passed....".format(bad_data))

self.assertEqual(j['okay'], 0)

def printValidationerror(self, filename, errors):
print('Failed to validate: {}'.format(filename))

def printValidationerror(self, filename, errors):
print ('Failed to validate: {}'.format(filename))
errorCount = 1

for err in errors:
print(err['title'])
print(err['detail'])
print('\n Path for error: {}'.format(err['path']))
print('\n Context: {}'.format(err['context']))
errorCount += 1


if __name__ == '__main__':
unittest.main()