Skip to content

Commit

Permalink
Merge pull request #108 from scossu/gh_92_93
Browse files Browse the repository at this point in the history
Issues 92, 93 & 102
  • Loading branch information
scossu committed Apr 17, 2019
2 parents 191c3ee + 12f074f commit 7c57b95
Show file tree
Hide file tree
Showing 11 changed files with 340 additions and 60 deletions.
2 changes: 1 addition & 1 deletion lakesuperior/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


version = '1.0 alpha'
release = '1.0.0a21'
release = '1.0.0a22'

basedir = path.dirname(path.realpath(__file__))
"""
Expand Down
9 changes: 6 additions & 3 deletions lakesuperior/api/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,19 +237,22 @@ def create_or_replace(uid, **kwargs):


@transaction(True)
def update(uid, update_str, is_metadata=False):
def update(uid, update_str, is_metadata=False, handling='strict'):
"""
Update a resource with a SPARQL-Update string.
:param string uid: Resource UID.
:param string update_str: SPARQL-Update statements.
:param bool is_metadata: Whether the resource metadata are being updated.
:param str handling: How to handle servre-managed triples. ``strict``
(the default) rejects the update with an exception if server-managed
triples are being changed. ``lenient`` modifies the update graph so
offending triples are removed and the update can be applied.
:raise InvalidResourceError: If ``is_metadata`` is False and the resource
being updated is a LDP-NR.
"""
# FCREPO is lenient here and Hyrax requires it.
rsrc = LdpFactory.from_stored(uid, handling='lenient')
rsrc = LdpFactory.from_stored(uid, handling=handling)
if LDP_NR_TYPE in rsrc.ldp_types and not is_metadata:
raise InvalidResourceError(
'Cannot use this method to update an LDP-NR content.')
Expand Down
29 changes: 24 additions & 5 deletions lakesuperior/endpoints/ldp.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
from lakesuperior.exceptions import (
ChecksumValidationError, ResourceNotExistsError, TombstoneError,
ServerManagedTermError, InvalidResourceError, SingleSubjectError,
ResourceExistsError, IncompatibleLdpTypeError)
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 @@ -283,18 +284,33 @@ def post_resource(parent_uid):
kwargs['mimetype'] = mimetype
# Check digest if requested.
if 'digest' in request.headers:
kwargs['prov_cksum_algo'], kwargs['prov_cksum'] = \
try:
kwargs['prov_cksum_algo'], kwargs['prov_cksum'] = (
request.headers['digest'].split('=')
)
except ValueError:
return (
f'Cannot parse digest value: {request.headers["digest"]}',
400
)

try:
rsrc = rsrc_api.create(parent_uid, slug, **kwargs)
except IndigestibleError:
return (
f'Unable to parse digest header: {request.headers["digest"]}'
), 400
except ResourceNotExistsError as e:
return str(e), 404
except (InvalidResourceError, ChecksumValidationError) as e:
return str(e), 409
except TombstoneError as e:
return _tombstone_response(e, uid)
except ServerManagedTermError as e:
rsp_headers['Link'] = (
f'<{uri}>; rel="{nsc["ldp"].constrainedBy}"; '
f'{g.webroot}/info/ldp_constraints"'
)
return str(e), 412

uri = g.tbox.uid_to_uri(rsrc.uid)
Expand All @@ -303,8 +319,9 @@ def post_resource(parent_uid):
rsp_headers['Location'] = uri

if mimetype and kwargs.get('rdf_fmt') is None:
rsp_headers['Link'] = (f'<{uri}/fcr:metadata>; rel="describedby"; '
f'anchor="{uri}"')
rsp_headers['Link'] = (
f'<{uri}/fcr:metadata>; rel="describedby"; anchor="{uri}"'
)

return uri, 201, rsp_headers

Expand Down Expand Up @@ -393,6 +410,8 @@ def patch_resource(uid, is_metadata=False):
if cond_ret:
return cond_ret

handling, _ = set_post_put_params()

rsp_headers = {'Content-Type' : 'text/plain; charset=utf-8'}
if request.mimetype != 'application/sparql-update':
return 'Provided content type is not a valid parsable format: {}'\
Expand All @@ -401,7 +420,7 @@ def patch_resource(uid, is_metadata=False):
update_str = request.get_data().decode('utf-8')
local_update_str = g.tbox.localize_ext_str(update_str, nsc['fcres'][uid])
try:
rsrc = rsrc_api.update(uid, local_update_str, is_metadata)
rsrc = rsrc_api.update(uid, local_update_str, is_metadata, handling)
except (ServerManagedTermError, SingleSubjectError) as e:
return str(e), 412
except InvalidResourceError as e:
Expand Down
17 changes: 10 additions & 7 deletions lakesuperior/endpoints/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

from os import path

from flask import Blueprint, render_template
from flask import Blueprint, jsonify, render_template

from lakesuperior import release
from lakesuperior.dictionaries import srv_mgd_terms as smt

logger = logging.getLogger(__name__)

Expand All @@ -21,9 +22,11 @@ def index():
return render_template('index.html', release=release)


@main.route('/debug', methods=['GET'])
def debug():
"""Debug page."""
raise RuntimeError()


@main.route('/info/ldp_constraints', methods=['GET'])
def ldp_constraints():
""" LDP term constraints. """
return jsonify({
'srv_mgd_subjects': [*smt.srv_mgd_subjects],
'srv_mgd_predicates': [*smt.srv_mgd_predicates],
'srv_mgd_types': [*smt.srv_mgd_types],
})
8 changes: 1 addition & 7 deletions lakesuperior/etc.defaults/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ uuid:
#
# ``blake2b`` is a strong, fast cryptographic alternative to SHA2/3:
# https://blake2.net/
algo: sha1
algo: blake2b

###
# Data store configuration.
Expand Down Expand Up @@ -77,12 +77,6 @@ store:
# Changes to this parameter require a full migration.
referential_integrity: lenient

###
# Split newly minted URIs into pairtrees.
#
# This mimics Fedora4 behavior which segments an identifier on POST.
legacy_ptree_split: False

###
# The path used to persist LDP-NR (bitstreams).
#
Expand Down
10 changes: 8 additions & 2 deletions lakesuperior/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,17 @@ def __init__(self, uid, prov_cksum, calc_cksum, msg=None):


def __str__(self):
return self.msg or (f'Validation failed for resource {self.uid}. '
f'Provided checksum: {self.prov_cksum}; '
return self.msg or (f'validation failed for resource {self.uid}. '
f'provided checksum: {self.prov_cksum}; '
f'calculated checksum: {self.calc_cksum}')


class IndigestibleError(ResourceError):
"""
Raised when an unsupported digest algorithm is requested.
"""
pass


class ResourceNotExistsError(ResourceError):
'''
Expand Down
19 changes: 6 additions & 13 deletions lakesuperior/model/ldp/ldp_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,10 @@ def from_provided(
'Binary stream must be provided if mimetype is specified.')

# Determine whether it is a basic, direct or indirect container.
if provided_imr[ : Ldpr.MBR_RSRC_URI : ] and \
provided_imr[ : Ldpr.MBR_REL_URI : ]:
if provided_imr[ : Ldpr.INS_CNT_REL_URI : ]:
cls = LdpIc
else:
cls = LdpDc
if provided_imr[nsc['rdf'].type] == nsc['ldp'].IndirectContainer:
cls = LdpIc
elif provided_imr[nsc['rdf'].type] == nsc['ldp'].DirectContainer:
cls = LdpDc
else:
cls = Ldpc

Expand Down Expand Up @@ -173,16 +171,11 @@ def mint_uid(parent_uid, path=None):
:return: The confirmed resource UID. This may be different from
what has been indicated.
"""
def split_if_legacy(uid):
if config['application']['store']['ldp_rs']['legacy_ptree_split']:
uid = tbox.split_uuid(uid)
return uid

if path and path.startswith('/'):
raise ValueError('Slug cannot start with a slash.')
# Shortcut!
if not path and parent_uid == '/':
return '/' + split_if_legacy(str(uuid4()))
return f'/{uuid4()}'

if not parent_uid.startswith('/'):
raise ValueError('Invalid parent UID: {}'.format(parent_uid))
Expand All @@ -198,6 +191,6 @@ def split_if_legacy(uid):
if not rdfly.ask_rsrc_exists(cnd_uid):
return cnd_uid

return pfx + split_if_legacy(str(uuid4()))
return f'{pfx}{uuid4()}'


35 changes: 21 additions & 14 deletions lakesuperior/model/ldp/ldpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
from lakesuperior.store.ldp_rs.rsrc_centric_layout import VERS_CONT_LABEL
from lakesuperior.util.toolbox import replace_term_domain

DEF_MBR_REL_URI = nsc['ldp'].member
DEF_INS_CNT_REL_URI = nsc['ldp'].memberSubject

rdfly = env.app_globals.rdfly
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -910,30 +912,35 @@ def _add_ldp_dc_ic_rel(self, cont_rsrc):
:param rdflib.resource.Resouce cont_rsrc: The container resource.
"""
cont_p = cont_rsrc.metadata.terms_by_type('p')

logger.info('Checking direct or indirect containment.')
logger.debug('Parent predicates: {}'.format(cont_p))

add_trp = {(self.uri, nsc['fcrepo'].hasParent, cont_rsrc.uri)}

if self.MBR_RSRC_URI in cont_p and self.MBR_REL_URI in cont_p:
if (
nsc['ldp'].DirectContainer in cont_rsrc.ldp_types
or nsc['ldp'].IndirectContainer in cont_rsrc.ldp_types
):
from lakesuperior.model.ldp.ldp_factory import LdpFactory

s = cont_rsrc.metadata.value(self.MBR_RSRC_URI)
p = cont_rsrc.metadata.value(self.MBR_REL_URI)
cont_p = cont_rsrc.metadata.terms_by_type('p')
logger.debug('Parent predicates: {}'.format(cont_p))

if nsc['ldp'].DirectContainer in cont_rsrc.ldp_types:
logger.info('Parent is a direct container.')
logger.debug('Creating DC triples.')
o = self.uri
s = cont_rsrc.metadata.value(self.MBR_RSRC_URI) or cont_rsrc.uri
p = cont_rsrc.metadata.value(self.MBR_REL_URI) or DEF_MBR_REL_URI
#import pdb; pdb.set_trace()

elif nsc['ldp'].IndirectContainer in cont_rsrc.ldp_types:
if nsc['ldp'].IndirectContainer in cont_rsrc.ldp_types:
logger.info('Parent is an indirect container.')
cont_rel_uri = cont_rsrc.metadata.value(self.INS_CNT_REL_URI)
o = self.provided_imr.value(cont_rel_uri)
logger.debug('Target URI: {}'.format(o))
logger.debug('Creating IC triples.')
o = (
self.provided_imr.value(cont_rel_uri)
or DEF_INS_CNT_REL_URI
)
logger.debug(f'Target URI: {o}')

else:
logger.info('Parent is a direct container.')
o = self.uri

target_rsrc = LdpFactory.from_stored(rdfly.uri_to_uid(s))
target_rsrc.modify(RES_UPDATED, add_trp={(s, p, o)})
Expand Down
11 changes: 8 additions & 3 deletions lakesuperior/store/ldp_nr/default_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from lakesuperior import env
from lakesuperior.store.ldp_nr.base_non_rdf_layout import BaseNonRdfLayout
from lakesuperior.exceptions import ChecksumValidationError
from lakesuperior.exceptions import ChecksumValidationError, IndigestibleError


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -98,9 +98,14 @@ def persist(
logger.debug(f'Writing temp file to {tmp_fname}.')

store_hash = hashlib.new(default_hash_algo)
verify_hash = (
try:
verify_hash = (
store_hash if prov_cksum_algo == default_hash_algo
else hashlib.new(prov_cksum_algo))
else hashlib.new(prov_cksum_algo)
)
except ValueError as e:
raise IndigestibleError(uid, str(e))

size = 0
while True:
buf = stream.read(bufsize)
Expand Down

0 comments on commit 7c57b95

Please sign in to comment.