def create(parent, slug=None, **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.ldp_factory.LdpFactory.from_provided` method. :rtype: tuple(str, lakesuperior.model.ldp.ldpr.Ldpr) :return: A tuple of: 1. Event type (str): whether the resource was created or updated. 2. Resource (lakesuperior.model.ldp.ldpr.Ldpr): The new or updated 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 rsrc
def _containment_rel(self, create, ignore_type=True): """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. "param bool ignore_type: If False (the default), an exception is raised if trying to create a resource under a non-container. This can be overridden in special cases (e.g. when migrating a repository in which a LDP-NR has "children" under ``fcr:versions``) by setting this to True. """ from lakesuperior.model.ldp.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 (not ignore_type and 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 resurrect(uid): """ Reinstate a buried (soft-deleted) resource. :param str uid: Resource UID. """ try: rsrc = LdpFactory.from_stored(uid) except TombstoneError as e: if e.uid != uid: raise else: return LdpFactory.from_stored(uid, strict=False).resurrect() else: raise InvalidResourceError(uid, msg='Resource {} is not dead.'.format(uid))
def revert_to_version(uid, ver_uid): """ Restore a resource to a previous version state. :param str uid: Resource UID. :param str ver_uid: Version UID. """ return LdpFactory.from_stored(uid).revert_to_version(ver_uid)
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 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 bury(self, inbound, tstone_pointer=None): """ Delete a single resource and create a tombstone. :param bool inbound: Whether inbound relationships are removed. If ``False``, resources will keep referring to the deleted resource; their link will point to a tombstone (which will raise a ``TombstoneError`` in the Python API or a ``410 Gone`` in the LDP API). :param rdflib.URIRef tstone_pointer: If set to a URI, this creates a pointer to the tombstone of the resource that used to contain the deleted resource. Otherwise the deleted resource becomes a tombstone. """ logger.info('Burying resource {}'.format(self.uid)) # ldp:Resource is also used in rdfly.ask_rsrc_exists. remove_trp = {(nsc['fcrepo'].uid, nsc['rdf'].type, nsc['ldp'].Resource) } if tstone_pointer: add_trp = {(self.uri, nsc['fcsystem'].tombstone, tstone_pointer)} else: add_trp = { (self.uri, RDF.type, nsc['fcsystem'].Tombstone), (self.uri, nsc['fcsystem'].buried, thread_env.timestamp_term), } # Bury descendants. from lakesuperior.model.ldp.ldp_factory import LdpFactory for desc_uri in rdfly.get_descendants(self.uid): try: desc_rsrc = LdpFactory.from_stored( env.app_globals.rdfly.uri_to_uid(desc_uri), repr_opts={'incl_children': False}) except (TombstoneError, ResourceNotExistsError): continue desc_rsrc.bury(inbound, tstone_pointer=self.uri) # Cut inbound relationships if inbound: for ib_rsrc_uri in self.imr[::self.uri]: remove_trp = {(ib_rsrc_uri, None, self.uri)} ib_rsrc = Ldpr(ib_rsrc_uri) # To preserve inbound links in history, create a snapshot ib_rsrc.create_version() ib_rsrc.modify(RES_UPDATED, remove_trp) self.modify(RES_DELETED, remove_trp, add_trp) return RES_DELETED
def resurrect(self): """ Resurrect a resource from a tombstone. """ remove_trp = { (self.uri, nsc['rdf'].type, nsc['fcsystem'].Tombstone), (self.uri, nsc['fcsystem'].tombstone, None), (self.uri, nsc['fcsystem'].buried, None), } add_trp = { (self.uri, nsc['rdf'].type, nsc['ldp'].Resource), } self.modify(RES_CREATED, remove_trp, add_trp) # Resurrect descendants. from lakesuperior.model.ldp.ldp_factory import LdpFactory descendants = env.app_globals.rdfly.get_descendants(self.uid) for desc_uri in descendants: LdpFactory.from_stored(rdfly.uri_to_uid(desc_uri), strict=False).resurrect() return self.uri
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.ldp_factory.LdpFactory.from_provided` method. :rtype: tuple(str, lakesuperior.model.ldp.ldpr.Ldpr) :return: A tuple of: 1. Event type (str): whether the resource was created or updated. 2. Resource (lakesuperior.model.ldp.ldpr.Ldpr): The new or updated resource. """ rsrc = LdpFactory.from_provided(uid, **kwargs) return rsrc.create_or_replace(), rsrc
def delete(uid, soft=True, inbound=True): """ Delete a resource. :param string uid: Resource UID. :param bool soft: 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 = env.app_globals.rdfly.config['referential_integrity'] inbound = True if refint else inbound if soft: repr_opts = {'incl_inbound': True} if inbound else {} rsrc = LdpFactory.from_stored(uid, repr_opts) return rsrc.bury(inbound) else: Ldpr.forget(uid, inbound)
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 string uid: 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_opts=repr_options) # Load graph before leaving the transaction. rsrc.imr return rsrc
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) # FIXME Wrong place to put this, should be at the LDP level. remove_trp = {(rel_uri_to_urn(s, uid), p, rel_uri_to_urn(o, uid)) for s, p, o in remove_trp} add_trp = {(rel_uri_to_urn(s, uid), p, rel_uri_to_urn(o, uid)) for s, p, o in add_trp} 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 _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. """ logger.info('Checking direct or indirect containment.') add_trp = {(self.uri, nsc['fcrepo'].hasParent, cont_rsrc.uri)} 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 cont_p = cont_rsrc.metadata.terms_by_type('p') logger.debug('Parent predicates: {}'.format(cont_p)) 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() 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) 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)}) return add_trp
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 server-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. """ 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.') delta = rsrc.sparql_delta(update_str) rsrc.modify(RES_UPDATED, *delta) return rsrc
def get_version_info(uid): """ Get version metadata (fcr:versions). """ return LdpFactory.from_stored(uid).version_info
def get_version(uid, ver_uid): """ Get version metadata (fcr:versions). """ return LdpFactory.from_stored(uid).get_version(ver_uid)