def delete(uid, soft=True): ''' Delete a resource. @param uid (string) Resource UID. @param soft (bool) Whether to perform a soft-delete and leave a tombstone resource, or wipe any memory of the resource. ''' # If referential integrity is enforced, grab all inbound relationships # to break them. refint = app_globals.rdfly.config['referential_integrity'] inbound = True if refint else inbound repr_opts = {'incl_inbound': True} if refint else {} children = app_globals.rdfly.get_descendants(uid) if soft: rsrc = LdpFactory.from_stored(uid, repr_opts) ret = rsrc.bury_rsrc(inbound) for child_uri in children: try: child_rsrc = LdpFactory.from_stored( app_globals.rdfly.uri_to_uid(child_uri), repr_opts={'incl_children': False}) except (TombstoneError, ResourceNotExistsError): continue child_rsrc.bury_rsrc(inbound, tstone_pointer=rsrc.uri) else: ret = app_globals.rdfly.forget_rsrc(uid, inbound) for child_uri in children: child_uid = app_globals.rdfly.uri_to_uid(child_uri) ret = app_globals.rdfly.forget_rsrc(child_uid, inbound) return ret
def create(parent, slug, **kwargs): r""" Mint a new UID and create a resource. The UID is computed from a given parent UID and a "slug", a proposed path relative to the parent. The application will attempt to use the suggested path but it may use a different one if a conflict with an existing resource arises. :param str parent: UID of the parent resource. :param str slug: Tentative path relative to the parent UID. :param \*\*kwargs: Other parameters are passed to the :py:meth:`~lakesuperior.model.ldp_factory.LdpFactory.from_provided` method. :rtype: str :return: UID of the new resource. """ uid = LdpFactory.mint_uid(parent, slug) logger.debug('Minted UID for new resource: {}'.format(uid)) rsrc = LdpFactory.from_provided(uid, **kwargs) rsrc.create_or_replace(create_only=True) return uid
def _containment_rel(self, create): """Find the closest parent in the path indicated by the uid and establish a containment triple. Check the path-wise parent of the new resource. If it exists, add the containment relationship with this UID. Otherwise, create a container resource as the parent. This function may recurse up the path tree until an existing container is found. E.g. if only fcres:/a exists: - If ``fcres:/a/b/c/d`` is being created, a becomes container of ``fcres:/a/b/c/d``. Also, containers are created for fcres:a/b and ``fcres:/a/b/c``. - If ``fcres:/e`` is being created, the root node becomes container of ``fcres:/e``. :param bool create: Whether the resource is being created. If false, the parent container is not updated. """ from lakesuperior.model.ldp_factory import LdpFactory if '/' in self.uid.lstrip('/'): # Traverse up the hierarchy to find the parent. path_components = self.uid.lstrip('/').split('/') cnd_parent_uid = '/' + '/'.join(path_components[:-1]) if rdfly.ask_rsrc_exists(cnd_parent_uid): parent_rsrc = LdpFactory.from_stored(cnd_parent_uid) if nsc['ldp'].Container not in parent_rsrc.types: raise InvalidResourceError( cnd_parent_uid, 'Parent {} is not a container.') parent_uid = cnd_parent_uid else: parent_rsrc = LdpFactory.new_container(cnd_parent_uid) # This will trigger this method again and recurse until an # existing container or the root node is reached. parent_rsrc.create_or_replace() parent_uid = parent_rsrc.uid else: parent_uid = ROOT_UID parent_rsrc = LdpFactory.from_stored( parent_uid, repr_opts={'incl_children': False}, handling='none') # Only update parent if the resource is new. if create: add_gr = Graph() add_gr.add( (nsc['fcres'][parent_uid], nsc['ldp'].contains, self.uri)) parent_rsrc.modify(RES_UPDATED, add_trp=add_gr) # Direct or indirect container relationship. return self._add_ldp_dc_ic_rel(parent_rsrc)
def get_metadata(uid): """ Get metadata (admin triples) of an LDPR resource. :param string uid: Resource UID. """ return LdpFactory.from_stored(uid).metadata
def create_or_replace(uid, stream=None, **kwargs): ''' Create or replace a resource with a specified UID. If the resource already exists, all user-provided properties of the existing resource are deleted. If the resource exists and the provided content is empty, an exception is raised (not sure why, but that's how FCREPO4 handles it). @param uid (string) UID of the resource to be created or updated. @param stream (BytesIO) Content stream. If empty, an empty container is created. @param **kwargs Other parameters are passed to the LdpFactory.from_provided method. Please see the documentation for that method for explanation of individual parameters. @return string Event type: whether the resource was created or updated. ''' rsrc = LdpFactory.from_provided(uid, stream=stream, **kwargs) if not stream and rsrc.is_stored: raise InvalidResourceError( rsrc.uid, 'Resource {} already exists and no data set was provided.') return rsrc.create_or_replace_rsrc()
def resurrect(uid): """ Reinstate a buried (soft-deleted) resource. :param str uid: Resource UID. """ return LdpFactory.from_stored(uid).resurrect_rsrc()
def resurrect(uid): ''' Reinstate a buried (soft-deleted) resource. @param uid (string) Resource UID. ''' return LdpFactory.from_stored(uid).resurrect_rsrc()
def is_accept_hdr_rdf_parsable(): ''' Check if any of the 'Accept' header values provided is a RDF parsable format. ''' for mimetype in request.accept_mimetypes.values(): if LdpFactory.is_rdf_parsable(mimetype): return True return False
def patch_version(uid, ver_uid): ''' Revert to a previous version. NOTE: This creates a new version snapshot. @param uid (string) Resource UID. @param ver_uid (string) Version UID. ''' try: LdpFactory.from_stored(uid).revert_to_version(ver_uid) except ResourceNotExistsError as e: return str(e), 404 except InvalidResourceError as e: return str(e), 409 except TombstoneError as e: return _tombstone_response(e, uid) else: return '', 204
def exists(uid): """ Return whether a resource exists (is stored) in the repository. :param string uid: Resource UID. """ try: exists = LdpFactory.from_stored(uid).is_stored except ResourceNotExistsError: exists = False return exists
def create_version(uid, ver_uid): ''' Create a resource version. @param uid (string) Resource UID. @param ver_uid (string) Version UID to be appended to the resource URI. NOTE: this is a "slug", i.e. the version URI is not guaranteed to be the one indicated. @return string Version UID. ''' return LdpFactory.from_stored(uid).create_version(ver_uid)
def put_resource(uid): """ https://www.w3.org/TR/ldp/#ldpr-HTTP_PUT Add or replace a new resource at a specified URI. """ # Parse headers. logger.debug('Request headers: {}'.format(request.headers)) rsp_headers = {'Content-Type': 'text/plain; charset=utf-8'} handling, disposition = set_post_put_params() stream, mimetype = _bistream_from_req() if LdpFactory.is_rdf_parsable(mimetype): # If the content is RDF, localize in-repo URIs. global_rdf = stream.read() local_rdf = g.tbox.localize_payload(global_rdf) graph = Graph().parse(data=local_rdf, format=mimetype, publicID=nsc['fcres'][uid]) stream = mimetype = None else: graph = None try: evt = rsrc_api.create_or_replace(uid, stream=stream, mimetype=mimetype, graph=graph, handling=handling, disposition=disposition) except (InvalidResourceError, ResourceExistsError) as e: return str(e), 409 except (ServerManagedTermError, SingleSubjectError) as e: return str(e), 412 except IncompatibleLdpTypeError as e: return str(e), 415 except TombstoneError as e: return _tombstone_response(e, uid) uri = g.tbox.uid_to_uri(uid) if evt == RES_CREATED: rsp_code = 201 rsp_headers['Location'] = rsp_body = uri if mimetype and not graph: rsp_headers['Link'] = ( '<{0}/fcr:metadata>; rel="describedby"'.format(uri)) else: rsp_code = 204 rsp_body = '' return rsp_body, rsp_code, rsp_headers
def create_or_replace(uid, **kwargs): r""" Create or replace a resource with a specified UID. :param string uid: UID of the resource to be created or updated. :param \*\*kwargs: Other parameters are passed to the :py:meth:`~lakesuperior.model.ldp_factory.LdpFactory.from_provided` method. :rtype: str :return: Event type: whether the resource was created or updated. """ return LdpFactory.from_provided(uid, **kwargs).create_or_replace()
def create_version(uid, ver_uid): """ Create a resource version. :param string uid: Resource UID. :param string ver_uid: Version UID to be appended to the resource URI. NOTE: this is a "slug", i.e. the version URI is not guaranteed to be the one indicated. :rtype: str :return: Version UID. """ return LdpFactory.from_stored(uid).create_version(ver_uid)
def post_resource(parent_uid): """ https://www.w3.org/TR/ldp/#ldpr-HTTP_POST Add a new resource in a new URI. """ out_headers = std_headers try: slug = request.headers['Slug'] logger.debug('Slug: {}'.format(slug)) except KeyError: slug = None handling, disposition = set_post_put_params() stream, mimetype = _bistream_from_req() if LdpFactory.is_rdf_parsable(mimetype): # If the content is RDF, localize in-repo URIs. global_rdf = stream.read() local_rdf = g.tbox.localize_payload(global_rdf) stream = BytesIO(local_rdf) is_rdf = True else: is_rdf = False try: uid = rsrc_api.create(parent_uid, slug, stream=stream, mimetype=mimetype, handling=handling, disposition=disposition) except ResourceNotExistsError as e: return str(e), 404 except InvalidResourceError as e: return str(e), 409 except TombstoneError as e: return _tombstone_response(e, uid) except ServerManagedTermError as e: return str(e), 412 uri = g.tbox.uid_to_uri(uid) hdr = {'Location': uri} if mimetype and not is_rdf: hdr['Link'] = '<{0}/fcr:metadata>; rel="describedby"; anchor="<{0}>"'\ .format(uri) out_headers.update(hdr) return uri, 201, out_headers
def create(parent, slug, **kwargs): ''' Mint a new UID and create a resource. The UID is computed from a given parent UID and a "slug", a proposed path relative to the parent. The application will attempt to use the suggested path but it may use a different one if a conflict with an existing resource arises. @param parent (string) UID of the parent resource. @param slug (string) Tentative path relative to the parent UID. @param **kwargs Other parameters are passed to the LdpFactory.from_provided method. Please see the documentation for that method for explanation of individual parameters. @return string UID of the new resource. ''' uid = LdpFactory.mint_uid(parent, slug) logger.debug('Minted UID for new resource: {}'.format(uid)) rsrc = LdpFactory.from_provided(uid, **kwargs) rsrc.create_or_replace_rsrc(create_only=True) return uid
def update_delta(uid, remove_trp, add_trp): """ Update a resource graph (LDP-RS or LDP-NR) with sets of add/remove triples. A set of triples to add and/or a set of triples to remove may be provided. :param string uid: Resource UID. :param set(tuple(rdflib.term.Identifier)) remove_trp: Triples to remove, as 3-tuples of RDFLib terms. :param set(tuple(rdflib.term.Identifier)) add_trp: Triples to add, as 3-tuples of RDFLib terms. """ rsrc = LdpFactory.from_stored(uid) remove_trp = rsrc.check_mgd_terms(remove_trp) add_trp = rsrc.check_mgd_terms(add_trp) return rsrc.modify(RES_UPDATED, remove_trp, add_trp)
def update(uid, update_str, is_metadata=False): ''' Update a resource with a SPARQL-Update string. @param uid (string) Resource UID. @param update_str (string) SPARQL-Update statements. @param is_metadata (bool) Whether the resource metadata is being updated. If False, and the resource being updated is a LDP-NR, an error is raised. ''' rsrc = LdpFactory.from_stored(uid) if LDP_NR_TYPE in rsrc.ldp_types: if is_metadata: rsrc.patch_metadata(update_str) else: raise InvalidResourceError(uid) else: rsrc.patch(update_str) return rsrc
def update(uid, update_str, is_metadata=False): """ 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. :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') if LDP_NR_TYPE in rsrc.ldp_types and not is_metadata: raise InvalidResourceError( 'Cannot use this method to update an LDP-NR content.') delta = rsrc.sparql_delta(update_str) rsrc.modify(RES_UPDATED, *delta) return rsrc
def get(uid, repr_options={}): ''' Get an LDPR resource. The resource comes preloaded with user data and metadata as indicated by the `repr_options` argument. Any further handling of this resource is done outside of a transaction. @param uid (string) Resource UID. @param repr_options (dict(bool)) Representation options. This is a dict that is unpacked downstream in the process. The default empty dict results in default values. The accepted dict keys are: - incl_inbound: include inbound references. Default: False. - incl_children: include children URIs. Default: True. - embed_children: Embed full graph of all child resources. Default: False ''' rsrc = LdpFactory.from_stored(uid, repr_options) # Load graph before leaving the transaction. rsrc.imr return rsrc
def _add_ldp_dc_ic_rel(self, cont_rsrc): """ Add relationship triples from a parent direct or indirect container. :param rdflib.resource.Resouce cont_rsrc: The container resource. """ cont_p = set(cont_rsrc.metadata.predicates()) 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: from lakesuperior.model.ldp_factory import LdpFactory s = cont_rsrc.metadata.value(cont_rsrc.uri, self.MBR_RSRC_URI) p = cont_rsrc.metadata.value(cont_rsrc_uri, self.MBR_REL_URI) if cont_rsrc.metadata[RDF.type:nsc['ldp'].DirectContainer]: logger.info('Parent is a direct container.') logger.debug('Creating DC triples.') o = self.uri elif (cont_rsrc.metadata[RDF.type:nsc['ldp'].IndirectContainer] and self.INS_CNT_REL_URI in cont_p): logger.info('Parent is an indirect container.') cont_rel_uri = cont_rsrc.metadata.value( cont_rsrc.uri, self.INS_CNT_REL_URI) o = self.provided_imr.value(self.uri, cont_rel_uri) logger.debug('Target URI: {}'.format(o)) logger.debug('Creating IC triples.') target_rsrc = LdpFactory.from_stored(rdfly.uri_to_uid(s)) target_rsrc.modify(RES_UPDATED, add_trp={(s, p, o)}) return add_trp
def get_version(uid, ver_uid): ''' Get version metadata (fcr:versions). ''' return LdpFactory.from_stored(uid).get_version(ver_uid)
def get_version_info(uid): ''' Get version metadata (fcr:versions). ''' return LdpFactory.from_stored(uid).version_info