Skip to content

Commit

Permalink
Merge pull request #118 from /issues/115
Browse files Browse the repository at this point in the history
Issues/115
  • Loading branch information
scossu committed Sep 5, 2019
2 parents f0c7e53 + 1f118c7 commit 6562722
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 55 deletions.
82 changes: 41 additions & 41 deletions lakesuperior/endpoints/ldp.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,10 @@
from werkzeug.http import parse_date

from lakesuperior import env
from lakesuperior import exceptions as exc
from lakesuperior.api import resource as rsrc_api
from lakesuperior.dictionaries.namespaces import ns_collection as nsc
from lakesuperior.dictionaries.namespaces import ns_mgr as nsm
from lakesuperior.exceptions import (
ChecksumValidationError, ResourceNotExistsError, TombstoneError,
ServerManagedTermError, InvalidResourceError, SingleSubjectError,
ResourceExistsError, IncompatibleLdpTypeError,
IndigestibleError)
from lakesuperior.globals import RES_CREATED
from lakesuperior.model.ldp.ldp_factory import LdpFactory
from lakesuperior.model.ldp.ldp_nr import LdpNr
Expand Down Expand Up @@ -152,7 +148,7 @@ def get_resource(uid, out_fmt=None):
try:
if not rsrc_api.exists(uid):
return '', 404
except TombstoneError as e:
except exc.TombstoneError as e:
return _tombstone_response(e, uid)

# Then process the condition headers.
Expand Down Expand Up @@ -229,11 +225,11 @@ def get_version_info(uid):
rdf_mimetype = _best_rdf_mimetype() or DEFAULT_RDF_MIMETYPE
try:
imr = rsrc_api.get_version_info(uid)
except ResourceNotExistsError as e:
except exc.ResourceNotExistsError as e:
return str(e), 404
except InvalidResourceError as e:
except exc.InvalidResourceError as e:
return str(e), 409
except TombstoneError as e:
except exc.TombstoneError as e:
return _tombstone_response(e, uid)
else:
with store.txn_ctx():
Expand All @@ -251,11 +247,11 @@ def get_version(uid, ver_uid):
rdf_mimetype = _best_rdf_mimetype() or DEFAULT_RDF_MIMETYPE
try:
imr = rsrc_api.get_version(uid, ver_uid)
except ResourceNotExistsError as e:
except exc.ResourceNotExistsError as e:
return str(e), 404
except InvalidResourceError as e:
except exc.InvalidResourceError as e:
return str(e), 409
except TombstoneError as e:
except exc.TombstoneError as e:
return _tombstone_response(e, uid)
else:
with store.txn_ctx():
Expand All @@ -277,17 +273,19 @@ def post_resource(parent_uid):
try:
kwargs = _create_args_from_req(slug)
rsrc = rsrc_api.create(parent_uid, slug, **kwargs)
except IndigestibleError:
except exc.RdfParsingError as e:
return str(e), 400
except exc.IndigestibleError:
return (
f'Unable to parse digest header: {request.headers["digest"]}'
), 400
except ResourceNotExistsError as e:
except exc.ResourceNotExistsError as e:
return str(e), 404
except (InvalidResourceError, ChecksumValidationError) as e:
except (exc.InvalidResourceError, exc.ChecksumValidationError) as e:
return str(e), 409
except TombstoneError as e:
except exc.TombstoneError as e:
return _tombstone_response(e, uid)
except ServerManagedTermError as e:
except exc.ServerManagedTermError as e:
rsp_headers['Link'] = (
f'<{uri}>; rel="{nsc["ldp"].constrainedBy}"; '
f'{g.webroot}/info/ldp_constraints"'
Expand Down Expand Up @@ -326,19 +324,21 @@ def put_resource(uid):
try:
kwargs = _create_args_from_req(uid)
evt, rsrc = rsrc_api.create_or_replace(uid, **kwargs)
except IndigestibleError:
except exc.RdfParsingError as e:
return str(e), 400
except exc.IndigestibleError:
return (
f'Unable to parse digest header: {request.headers["digest"]}'
), 400
f'Unable to parse digest header: {request.headers["digest"]}',
400)
except (
InvalidResourceError, ChecksumValidationError,
ResourceExistsError) as e:
exc.InvalidResourceError, exc.ChecksumValidationError,
exc.ResourceExistsError) as e:
return str(e), 409
except (ServerManagedTermError, SingleSubjectError) as e:
except (exc.ServerManagedTermError, exc.SingleSubjectError) as e:
return str(e), 412
except IncompatibleLdpTypeError as e:
except exc.IncompatibleLdpTypeError as e:
return str(e), 415
except TombstoneError as e:
except exc.TombstoneError as e:
return _tombstone_response(e, uid)

with store.txn_ctx():
Expand Down Expand Up @@ -371,7 +371,7 @@ def patch_resource(uid, is_metadata=False):
try:
if not rsrc_api.exists(uid):
return '', 404
except TombstoneError as e:
except exc.TombstoneError as e:
return _tombstone_response(e, uid)

# Then process the condition headers.
Expand All @@ -390,9 +390,9 @@ def patch_resource(uid, is_metadata=False):
local_update_str = g.tbox.localize_ext_str(update_str, nsc['fcres'][uid])
try:
rsrc = rsrc_api.update(uid, local_update_str, is_metadata, handling)
except (ServerManagedTermError, SingleSubjectError) as e:
except (exc.ServerManagedTermError, exc.SingleSubjectError) as e:
return str(e), 412
except InvalidResourceError as e:
except exc.InvalidResourceError as e:
return str(e), 415
else:
with store.txn_ctx():
Expand Down Expand Up @@ -423,7 +423,7 @@ def delete_resource(uid):
try:
if not rsrc_api.exists(uid):
return '', 404
except TombstoneError as e:
except exc.TombstoneError as e:
return _tombstone_response(e, uid)

# Then process the condition headers.
Expand Down Expand Up @@ -455,7 +455,7 @@ def tombstone(uid):
"""
try:
rsrc_api.get(uid)
except TombstoneError as e:
except exc.TombstoneError as e:
if request.method == 'DELETE':
if e.uid == uid:
rsrc_api.delete(uid, False)
Expand All @@ -471,7 +471,7 @@ def tombstone(uid):
return _tombstone_response(e, uid)
else:
return 'Method Not Allowed.', 405
except ResourceNotExistsError as e:
except exc.ResourceNotExistsError as e:
return str(e), 404
else:
return '', 404
Expand All @@ -488,11 +488,11 @@ def post_version(uid):

try:
ver_uid = rsrc_api.create_version(uid, ver_uid)
except ResourceNotExistsError as e:
except exc.ResourceNotExistsError as e:
return str(e), 404
except InvalidResourceError as e:
except exc.InvalidResourceError as e:
return str(e), 409
except TombstoneError as e:
except exc.TombstoneError as e:
return _tombstone_response(e, uid)
else:
return '', 201, {'Location': g.tbox.uid_to_uri(ver_uid)}
Expand All @@ -510,11 +510,11 @@ def patch_version(uid, ver_uid):
"""
try:
rsrc_api.revert_to_version(uid, ver_uid)
except ResourceNotExistsError as e:
except exc.ResourceNotExistsError as e:
return str(e), 404
except InvalidResourceError as e:
except exc.InvalidResourceError as e:
return str(e), 409
except TombstoneError as e:
except exc.TombstoneError as e:
return _tombstone_response(e, uid)
else:
return '', 204
Expand Down Expand Up @@ -611,7 +611,7 @@ def _create_args_from_req(uid):
request.headers['digest'].split('=')
)
except ValueError:
raise IndigestibleError(uid)
raise exc.IndigestibleError(uid)

return kwargs

Expand Down Expand Up @@ -798,7 +798,7 @@ def _condition_hdr_match(uid, headers, safe=True):
with store.txn_ctx():
try:
rsrc_meta = rsrc_api.get_metadata(uid)
except ResourceNotExistsError:
except exc.ResourceNotExistsError:
rsrc_meta = Graph(uri=nsc['fcres'][uid])

digest_prop = rsrc_meta.value(nsc['premis'].hasMessageDigest)
Expand All @@ -820,7 +820,7 @@ def _condition_hdr_match(uid, headers, safe=True):

try:
rsrc_meta = rsrc_api.get_metadata(uid)
except ResourceNotExistsError:
except exc.ResourceNotExistsError:
return {
'if-modified-since': False,
'if-unmodified-since': False
Expand Down Expand Up @@ -861,7 +861,7 @@ def _process_cond_headers(uid, headers, safe=True):
"""
try:
cond_match = _condition_hdr_match(uid, headers, safe)
except TombstoneError as e:
except exc.TombstoneError as e:
return _tombstone_response(e, uid)

if cond_match:
Expand Down
15 changes: 14 additions & 1 deletion lakesuperior/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,20 @@ def __str__(self):



class InvalidTripleError(RuntimeError):
class RdfParsingError(ValueError):
"""
Raised when a string cannot be parsed as RDF in the given format.
"""
def __init__(self, fmt, parser_msg=''):
self.fmt = fmt
self.parser_msg = parser_msg

def __str__(self):
return (f'Error parsing RDF in {self.fmt} format: {self.parser_msg}')



class InvalidTripleError(ValueError):
'''
Raised when a triple in a delta is not valid.
Expand Down
27 changes: 14 additions & 13 deletions lakesuperior/model/ldp/ldp_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,12 @@
from rdflib.namespace import RDF

from lakesuperior import env
from lakesuperior import exceptions as exc
from lakesuperior.model.ldp.ldpr import Ldpr
from lakesuperior.model.ldp.ldp_nr import LdpNr
from lakesuperior.model.ldp.ldp_rs import LdpRs, Ldpc, LdpDc, LdpIc
from lakesuperior.config_parser import config
from lakesuperior.dictionaries.namespaces import ns_collection as nsc
from lakesuperior.exceptions import (
IncompatibleLdpTypeError, InvalidResourceError, ResourceExistsError,
ResourceNotExistsError, TombstoneError)
from lakesuperior.model.rdf.graph import Graph, from_rdf
from lakesuperior.util.toolbox import rel_uri_to_urn

Expand All @@ -34,9 +32,9 @@ class LdpFactory:
@staticmethod
def new_container(uid):
if not uid.startswith('/') or uid == '/':
raise InvalidResourceError(uid)
raise exc.InvalidResourceError(uid)
if rdfly.ask_rsrc_exists(uid):
raise ResourceExistsError(uid)
raise exc.ResourceExistsError(uid)
rsrc = Ldpc(uid, provided_imr=Graph(uri=nsc['fcres'][uid]))

return rsrc
Expand Down Expand Up @@ -66,7 +64,7 @@ def from_stored(uid, ver_label=None, repr_opts={}, strict=True, **kwargs):
logger.info('Resource is a LDP-RS.')
cls = LdpRs
else:
raise ResourceNotExistsError(uid)
raise exc.ResourceNotExistsError(uid)

rsrc = cls(uid, repr_opts, **kwargs)
# Sneak in the already extracted metadata to save a query.
Expand Down Expand Up @@ -101,10 +99,13 @@ def from_provided(
"""
uri = nsc['fcres'][uid]
if rdf_data:
provided_imr = from_rdf(
uri=uri, data=rdf_data,
format=rdf_fmt, publicID=nsc['fcres'][uid]
)
try:
provided_imr = from_rdf(
uri=uri, data=rdf_data,
format=rdf_fmt, publicID=nsc['fcres'][uid])
except Exception as e:
raise exc.RdfParsingError(rdf_fmt, str(e))

elif graph:
provided_imr = Graph(
uri=uri, data={
Expand Down Expand Up @@ -136,7 +137,7 @@ def from_provided(

# Make sure we are not updating an LDP-NR with an LDP-RS.
if inst.is_stored and LDP_NR_TYPE in inst.ldp_types:
raise IncompatibleLdpTypeError(uid, mimetype)
raise exc.IncompatibleLdpTypeError(uid, mimetype)

if kwargs.get('handling', 'strict') != 'none':
inst.check_mgd_terms(inst.provided_imr)
Expand All @@ -151,7 +152,7 @@ def from_provided(

# Make sure we are not updating an LDP-RS with an LDP-NR.
if inst.is_stored and LDP_RS_TYPE in inst.ldp_types:
raise IncompatibleLdpTypeError(uid, mimetype)
raise exc.IncompatibleLdpTypeError(uid, mimetype)

logger.debug('Creating resource of type: {}'.format(
inst.__class__.__name__))
Expand Down Expand Up @@ -189,7 +190,7 @@ def mint_uid(parent_uid, path=None):

parent = LdpFactory.from_stored(parent_uid)
if nsc['ldp'].Container not in parent.types:
raise InvalidResourceError(parent_uid,
raise exc.InvalidResourceError(parent_uid,
'Parent {} is not a container.')

pfx = parent_uid.rstrip('/') + '/'
Expand Down
46 changes: 46 additions & 0 deletions tests/3_endpoints/test_3_0_ldp.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,52 @@ def test_get_ldp_nr(self, rnd_img):
assert isomorphic(gr1, gr2)


def test_put_ldprs_invalid_rdf(self):
"""
Verify that PUTting invalid RDF body returns HTTP 400.
However, when forcing LDP-RS, invalid RDF is always accepted.
"""
from lakesuperior.endpoints.ldp import rdf_serializable_mimetypes

rdfstr = b'This is valid RDF because it ends with a period.'
for mt in rdf_serializable_mimetypes:
rsp_notok = self.client.put(
f'/ldp/{uuid4()}', data=rdfstr, content_type=mt)
assert rsp_notok.status_code == 400

rsp_ok = self.client.put(
f'/ldp/{uuid4()}', data=rdfstr, content_type=mt,
headers={
'link': '<http://www.w3.org/ns/ldp#NonRDFSource>;rel="type"'
}
)
assert rsp_ok.status_code == 201


def test_post_ldprs_invalid_rdf(self):
"""
Verify that POSTing invalid RDF body returns HTTP 400.
However, when forcing LDP-RS, invalid RDF is always accepted.
"""
from lakesuperior.endpoints.ldp import rdf_serializable_mimetypes

rdfstr = b'This is valid RDF because it ends with a period.'
for mt in rdf_serializable_mimetypes:
rsp_notok = self.client.post(
f'/ldp', data=rdfstr, content_type=mt)
assert rsp_notok.status_code == 400

rsp_ok = self.client.post(
f'/ldp', data=rdfstr, content_type=mt,
headers={
'link': '<http://www.w3.org/ns/ldp#NonRDFSource>;rel="type"'
}
)
assert rsp_ok.status_code == 201


def test_metadata_describe_header(self):
"""
Verify that a "describe" Link header is presented for LDP-NR metadata.
Expand Down

0 comments on commit 6562722

Please sign in to comment.