Skip to content

Commit 01961c4

Browse files
authored
Merge pull request #68 from scossu/accept_header
Accept header
2 parents 1d914c1 + 2e38892 commit 01961c4

File tree

6 files changed

+243
-182
lines changed

6 files changed

+243
-182
lines changed

VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.0.0a15
1+
1.0.0a16

lakesuperior/api/resource.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ def delete(uid, soft=True, inbound=True):
298298
299299
:param string uid: Resource UID.
300300
:param bool soft: Whether to perform a soft-delete and leave a
301-
tombstone resource, or wipe any memory of the resource.
301+
tombstone resource, or wipe any memory of the resource.
302302
"""
303303
# If referential integrity is enforced, grab all inbound relationships
304304
# to break them.

lakesuperior/endpoints/ldp.py

+65-42
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
import arrow
1010

1111
from flask import (
12-
Blueprint, g, make_response, render_template,
12+
Blueprint, Response, g, make_response, render_template,
1313
request, send_file)
14-
from rdflib import Graph
14+
from rdflib import Graph, plugin, parser#, serializer
1515

1616
from lakesuperior.api import resource as rsrc_api
1717
from lakesuperior.dictionaries.namespaces import ns_collection as nsc
@@ -28,44 +28,58 @@
2828
from lakesuperior.toolbox import Toolbox
2929

3030

31-
logger = logging.getLogger(__name__)
32-
33-
# Blueprint for LDP REST API. This is what is usually found under `/rest/` in
34-
# standard fcrepo4. Here, it is under `/ldp` but initially `/rest` can be kept
35-
# for backward compatibility.
31+
DEFAULT_RDF_MIMETYPE = 'text/turtle'
32+
"""
33+
Fallback serialization format used when no acceptable formats are specified.
34+
"""
3635

37-
ldp = Blueprint(
38-
'ldp', __name__, template_folder='templates',
39-
static_url_path='/static', static_folder='templates/static')
36+
logger = logging.getLogger(__name__)
37+
rdf_parsable_mimetypes = {
38+
mt.name for mt in plugin.plugins()
39+
if mt.kind is parser.Parser and '/' in mt.name
40+
}
41+
"""MIMEtypes that can be parsed into RDF."""
4042

41-
accept_patch = (
42-
'application/sparql-update',
43-
)
44-
accept_rdf = (
43+
rdf_serializable_mimetypes = {
44+
#mt.name for mt in plugin.plugins()
45+
#if mt.kind is serializer.Serializer and '/' in mt.name
4546
'application/ld+json',
4647
'application/n-triples',
4748
'application/rdf+xml',
48-
#'application/x-turtle',
49-
#'application/xhtml+xml',
50-
#'application/xml',
51-
#'text/html',
52-
'text/n3',
53-
#'text/plain',
54-
'text/rdf+n3',
5549
'text/turtle',
50+
'text/n3',
51+
}
52+
"""
53+
MIMEtypes that RDF can be serialized into.
54+
55+
These are not automatically derived from RDFLib because only triple
56+
(not quad) serializations are applicable.
57+
"""
58+
59+
accept_patch = (
60+
'application/sparql-update',
5661
)
5762

5863
std_headers = {
5964
'Accept-Patch' : ','.join(accept_patch),
60-
'Accept-Post' : ','.join(accept_rdf),
61-
#'Allow' : ','.join(allow),
65+
'Accept-Post' : ','.join(rdf_parsable_mimetypes),
6266
}
6367

6468
"""Predicates excluded by view."""
6569
vw_blacklist = {
6670
}
6771

6872

73+
ldp = Blueprint(
74+
'ldp', __name__, template_folder='templates',
75+
static_url_path='/static', static_folder='templates/static')
76+
"""
77+
Blueprint for LDP REST API. This is what is usually found under ``/rest/`` in
78+
standard fcrepo4. Here, it is under ``/ldp`` but initially ``/rest`` will be
79+
kept for backward compatibility.
80+
"""
81+
82+
## ROUTE PRE- & POST-PROCESSING ##
6983

7084
@ldp.url_defaults
7185
def bp_url_defaults(endpoint, values):
@@ -140,16 +154,20 @@ def get_resource(uid, out_fmt=None):
140154
return _tombstone_response(e, uid)
141155
else:
142156
if out_fmt is None:
157+
rdf_mimetype = _best_rdf_mimetype()
143158
out_fmt = (
144159
'rdf'
145-
if isinstance(rsrc, LdpRs) or is_accept_hdr_rdf_parsable()
160+
if isinstance(rsrc, LdpRs) or rdf_mimetype is not None
146161
else 'non_rdf')
147162
out_headers.update(_headers_from_metadata(rsrc))
148163
uri = g.tbox.uid_to_uri(uid)
149164
if out_fmt == 'rdf':
165+
if locals().get('rdf_mimetype', None) is None:
166+
rdf_mimetype = DEFAULT_RDF_MIMETYPE
150167
ggr = g.tbox.globalize_graph(rsrc.out_graph)
151168
ggr.namespace_manager = nsm
152-
return _negotiate_content(ggr, out_headers, uid=uid, uri=uri)
169+
return _negotiate_content(
170+
ggr, rdf_mimetype, out_headers, uid=uid, uri=uri)
153171
else:
154172
if not getattr(rsrc, 'local_path', False):
155173
return ('{} has no binary content.'.format(rsrc.uid), 404)
@@ -174,6 +192,7 @@ def get_version_info(uid):
174192
175193
:param str uid: UID of resource to retrieve versions for.
176194
"""
195+
rdf_mimetype = _best_rdf_mimetype() or DEFAULT_RDF_MIMETYPE
177196
try:
178197
gr = rsrc_api.get_version_info(uid)
179198
except ResourceNotExistsError as e:
@@ -183,7 +202,7 @@ def get_version_info(uid):
183202
except TombstoneError as e:
184203
return _tombstone_response(e, uid)
185204
else:
186-
return _negotiate_content(g.tbox.globalize_graph(gr))
205+
return _negotiate_content(g.tbox.globalize_graph(gr), rdf_mimetype)
187206

188207

189208
@ldp.route('/<path:uid>/fcr:versions/<ver_uid>', methods=['GET'])
@@ -194,6 +213,7 @@ def get_version(uid, ver_uid):
194213
:param str uid: Resource UID.
195214
:param str ver_uid: Version UID.
196215
"""
216+
rdf_mimetype = _best_rdf_mimetype() or DEFAULT_RDF_MIMETYPE
197217
try:
198218
gr = rsrc_api.get_version(uid, ver_uid)
199219
except ResourceNotExistsError as e:
@@ -203,7 +223,7 @@ def get_version(uid, ver_uid):
203223
except TombstoneError as e:
204224
return _tombstone_response(e, uid)
205225
else:
206-
return _negotiate_content(g.tbox.globalize_graph(gr))
226+
return _negotiate_content(g.tbox.globalize_graph(gr), rdf_mimetype)
207227

208228

209229
@ldp.route('/<path:parent_uid>', methods=['POST'], strict_slashes=False)
@@ -225,7 +245,7 @@ def post_resource(parent_uid):
225245
handling, disposition = set_post_put_params()
226246
stream, mimetype = _bistream_from_req()
227247

228-
if LdpFactory.is_rdf_parsable(mimetype):
248+
if mimetype in rdf_parsable_mimetypes:
229249
# If the content is RDF, localize in-repo URIs.
230250
global_rdf = stream.read()
231251
rdf_data = g.tbox.localize_payload(global_rdf)
@@ -277,7 +297,7 @@ def put_resource(uid):
277297
handling, disposition = set_post_put_params()
278298
stream, mimetype = _bistream_from_req()
279299

280-
if LdpFactory.is_rdf_parsable(mimetype):
300+
if mimetype in rdf_parsable_mimetypes:
281301
# If the content is RDF, localize in-repo URIs.
282302
global_rdf = stream.read()
283303
rdf_data = g.tbox.localize_payload(global_rdf)
@@ -459,7 +479,19 @@ def patch_version(uid, ver_uid):
459479

460480
## PRIVATE METHODS ##
461481

462-
def _negotiate_content(gr, headers=None, **vw_kwargs):
482+
def _best_rdf_mimetype():
483+
"""
484+
Check if any of the 'Accept' header values provided is a RDF parsable
485+
format.
486+
"""
487+
for accept in request.accept_mimetypes:
488+
mimetype = accept[0]
489+
if mimetype in rdf_parsable_mimetypes:
490+
return mimetype
491+
return None
492+
493+
494+
def _negotiate_content(gr, rdf_mimetype, headers=None, **vw_kwargs):
463495
"""
464496
Return HTML or serialized RDF depending on accept headers.
465497
"""
@@ -470,7 +502,9 @@ def _negotiate_content(gr, headers=None, **vw_kwargs):
470502
else:
471503
for p in vw_blacklist:
472504
gr.remove((None, p, None))
473-
return (gr.serialize(format='turtle'), headers)
505+
return Response(
506+
gr.serialize(format=rdf_mimetype), 200, headers,
507+
mimetype=rdf_mimetype)
474508

475509

476510
def _bistream_from_req():
@@ -534,17 +568,6 @@ def set_post_put_params():
534568
return handling, disposition
535569

536570

537-
def is_accept_hdr_rdf_parsable():
538-
"""
539-
Check if any of the 'Accept' header values provided is a RDF parsable
540-
format.
541-
"""
542-
for mimetype in request.accept_mimetypes.values():
543-
if LdpFactory.is_rdf_parsable(mimetype):
544-
return True
545-
return False
546-
547-
548571
def parse_repr_options(retr_opts):
549572
"""
550573
Set options to retrieve IMR.

lakesuperior/model/ldp_factory.py

+1-31
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from pprint import pformat
44
from uuid import uuid4
55

6-
from rdflib import Graph, parser, plugin, serializer
6+
from rdflib import Graph, parser
77
from rdflib.resource import Resource
88
from rdflib.namespace import RDF
99

@@ -151,36 +151,6 @@ def from_provided(
151151
return inst
152152

153153

154-
@staticmethod
155-
def is_rdf_parsable(mimetype):
156-
"""
157-
Checks whether a MIME type support RDF parsing by a RDFLib plugin.
158-
159-
:param str mimetype: MIME type to check.
160-
"""
161-
try:
162-
plugin.get(mimetype, parser.Parser)
163-
except plugin.PluginException:
164-
return False
165-
else:
166-
return True
167-
168-
169-
@staticmethod
170-
def is_rdf_serializable(mimetype):
171-
"""
172-
Checks whether a MIME type support RDF serialization by a RDFLib plugin
173-
174-
:param str mimetype: MIME type to check.
175-
"""
176-
try:
177-
plugin.get(mimetype, serializer.Serializer)
178-
except plugin.PluginException:
179-
return False
180-
else:
181-
return True
182-
183-
184154
@staticmethod
185155
def mint_uid(parent_uid, path=None):
186156
"""

setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
'gunicorn',
9494
'lmdb',
9595
'rdflib',
96+
'rdflib-jsonld',
9697
'requests',
9798
'requests-toolbelt',
9899
'sphinx-rtd-theme',

0 commit comments

Comments
 (0)