Browse Source

Only send messages once per resource request.

Stefano Cossu 7 years ago
parent
commit
6305baccb3

+ 3 - 2
lakesuperior/api/resource.py

@@ -185,7 +185,7 @@ def create(parent, slug, **kwargs):
     logger.debug('Minted UID for new resource: {}'.format(uid))
     rsrc = LdpFactory.from_provided(uid, **kwargs)
 
-    rsrc.create_or_replace_rsrc(create_only=True)
+    rsrc.create_or_replace(create_only=True)
 
     return uid
 
@@ -210,12 +210,13 @@ def create_or_replace(uid, stream=None, **kwargs):
     @return string Event type: whether the resource was created or updated.
     '''
     rsrc = LdpFactory.from_provided(uid, stream=stream, **kwargs)
+    create = not rsrc.is_stored
 
     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()
+    return rsrc.create_or_replace(create_only=create)
 
 
 @transaction(True)

+ 2 - 2
lakesuperior/model/ldp_nr.py

@@ -66,7 +66,7 @@ class LdpNr(Ldpr):
         return nonrdfly.local_path(cksum)
 
 
-    def create_or_replace_rsrc(self, create_only=False):
+    def create_or_replace(self, create_only=False):
         '''
         Create a new binary resource with a corresponding RDF representation.
 
@@ -78,7 +78,7 @@ class LdpNr(Ldpr):
         # Try to persist metadata. If it fails, delete the file.
         logger.debug('Persisting LDP-NR triples in {}'.format(self.uri))
         try:
-            ev_type = super().create_or_replace_rsrc(create_only)
+            ev_type = super().create_or_replace(create_only)
         except:
             # self.digest is also the file UID.
             nonrdfly.delete(self.digest)

+ 36 - 19
lakesuperior/model/ldpr.py

@@ -364,35 +364,43 @@ class Ldpr(metaclass=ABCMeta):
         return rdfly.extract_imr(self.uid, ver_uid, **kwargs).graph
 
 
-    def create_or_replace_rsrc(self, create_only=False):
+    def create_or_replace(self, create_only=False):
         '''
         Create or update a resource. PUT and POST methods, which are almost
         identical, are wrappers for this method.
 
         @param create_only (boolean) Whether this is a create-only operation.
         '''
+        pdb.set_trace()
         create = create_only or not self.is_stored
+        ev_type = RES_CREATED if create else RES_UPDATED
 
         self._add_srv_mgd_triples(create)
-        #self._ensure_single_subject_rdf(self.provided_imr.graph)
         ref_int = rdfly.config['referential_integrity']
         if ref_int:
             self._check_ref_int(ref_int)
 
-        rdfly.create_or_replace_rsrc(self.uid, self.provided_imr.graph)
-        self.imr = self.provided_imr
+        # Delete existing triples if replacing.
+        if not create:
+            rdfly.truncate_rsrc(self.uid)
+
+        add_trp = set(self.provided_imr.graph) | self._containment_rel(create)
 
-        self._set_containment_rel()
+        self._modify_rsrc(ev_type, add_trp=add_trp)
+        new_gr = Graph()
+        for trp in add_trp:
+            new_gr.add(trp)
 
-        return RES_CREATED if create else RES_UPDATED
-        #return self._head(self.provided_imr.graph)
+        self.imr = new_gr.resource(self.uri)
+
+        return ev_type
 
 
     def put(self):
         '''
         https://www.w3.org/TR/ldp/#ldpr-HTTP_PUT
         '''
-        return self.create_or_replace_rsrc()
+        return self.create_or_replace()
 
 
     def patch(self, update_str):
@@ -535,8 +543,8 @@ class Ldpr(metaclass=ABCMeta):
         elif nsc['ldp'].Container in laz_gr[: RDF.type :]:
             laz_gr.add((self.uri, RDF.type, nsc['fcrepo'].Container))
 
-        self._modify_rsrc(RES_CREATED, tstone_trp, set(laz_gr))
-        self._set_containment_rel()
+        laz_set = set(laz_gr) | self._containment_rel()
+        self._modify_rsrc(RES_CREATED, tstone_trp, laz_set)
 
         return self.uri
 
@@ -581,7 +589,7 @@ class Ldpr(metaclass=ABCMeta):
             # @TODO Check individual objects: if they are repo-managed URIs
             # and not existing or tombstones, they are not added.
 
-        return self.create_or_replace_rsrc(create_only=False)
+        return self.create_or_replace(create_only=False)
 
 
     ## PROTECTED METHODS ##
@@ -610,7 +618,6 @@ class Ldpr(metaclass=ABCMeta):
         @param add_trp (set) Triples to be added.
         @param notify (boolean) Whether to send a message about the change.
         '''
-        pdb.set_trace()
         rdfly.modify_rsrc(self.uid, remove_trp, add_trp)
 
         if notify and env.config['application'].get('messaging'):
@@ -736,7 +743,7 @@ class Ldpr(metaclass=ABCMeta):
         self.provided_imr.set(nsc['fcrepo'].lastModifiedBy, self.DEFAULT_USER)
 
 
-    def _set_containment_rel(self):
+    def _containment_rel(self, create):
         '''Find the closest parent in the path indicated by the uid and
         establish a containment triple.
 
@@ -752,6 +759,9 @@ class Ldpr(metaclass=ABCMeta):
           fcres:/a/b/c.
         - If fcres:/e is being created, the root node becomes container of
           fcres:/e.
+
+        @param create (bool) Whether the resource is being created. If false,
+        the parent container is not updated.
         '''
         from lakesuperior.model.ldp_factory import LdpFactory
 
@@ -770,25 +780,32 @@ class Ldpr(metaclass=ABCMeta):
                 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_rsrc()
+                parent_rsrc.create_or_replace()
                 parent_uid = parent_rsrc.uid
         else:
             parent_uid = ROOT_UID
 
-        add_gr = Graph()
-        add_gr.add((nsc['fcres'][parent_uid], nsc['ldp'].contains, self.uri))
         parent_rsrc = LdpFactory.from_stored(
             parent_uid, repr_opts={'incl_children' : False}, handling='none')
-        parent_rsrc._modify_rsrc(RES_UPDATED, add_trp=add_gr)
+
+        # 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_rsrc(RES_UPDATED, add_trp=add_gr)
 
         # Direct or indirect container relationship.
-        self._add_ldp_dc_ic_rel(parent_rsrc)
+        return self._add_ldp_dc_ic_rel(parent_rsrc)
 
 
     def _dedup_deltas(self, remove_gr, add_gr):
         '''
         Remove duplicate triples from add and remove delta graphs, which would
         otherwise contain unnecessary statements that annul each other.
+
+        @return tuple 2 "clean" sets of respectively remove statements and
+        add statements.
         '''
         return (
             remove_gr - add_gr,
@@ -834,7 +851,7 @@ class Ldpr(metaclass=ABCMeta):
             target_rsrc = LdpFactory.from_stored(rdfly.uri_to_uid(s))
             target_rsrc._modify_rsrc(RES_UPDATED, add_trp={(s, p, o)})
 
-        self._modify_rsrc(RES_UPDATED, add_trp=add_trp)
+        return add_trp
 
 
     def _sparql_update(self, update_str, notify=True):

+ 22 - 8
lakesuperior/store/ldp_rs/rsrc_centric_layout.py

@@ -282,6 +282,20 @@ class RsrcCentricLayout:
         return rsrc
 
 
+    def get_user_data(self, uid):
+        '''
+        Get all the user-provided data.
+
+        @param uid (string) Resource UID.
+        '''
+        # @TODO This only works as long as there is only one user-provided
+        # graph. If multiple user-provided graphs will be supported, this
+        # should use another query to get all of them.
+        userdata_gr = self.ds.graph(nsc['fcmain'][uid])
+
+        return userdata_gr | Graph()
+
+
     def get_version_info(self, uid, strict=True):
         '''
         Get all metadata about a resource's versions.
@@ -422,20 +436,20 @@ class RsrcCentricLayout:
         # Remove versions.
         for ver_uri in self.ds.graph(nsc['fcadmin'][uid])[
                 uri : nsc['fcrepo'].hasVersion : None]:
-            self._delete_rsrc(uid_fn(ver_uri), True)
+            self.delete_rsrc(uid_fn(ver_uri), True)
 
         # Remove resource itself.
-        self._delete_rsrc(uid)
+        self.delete_rsrc(uid)
 
 
-    def create_or_replace_rsrc(self, uid, trp):
+    def truncate_rsrc(self, uid):
         '''
-        Create a new resource or replace an existing one.
+        Remove all user-provided data from a resource and only leave admin and
+        structure data.
         '''
-        if self.ask_rsrc_exists(uid):
-            self._delete_rsrc(uid)
+        userdata = set(self.get_user_data(uid))
 
-        return self.modify_rsrc(uid, add_trp=trp)
+        return self.modify_rsrc(uid, remove_trp=userdata)
 
 
     def modify_rsrc(self, uid, remove_trp=set(), add_trp=set()):
@@ -490,7 +504,7 @@ class RsrcCentricLayout:
             meta_gr.add((gr_uri, RDF.type, gr_type))
 
 
-    def _delete_rsrc(self, uid, historic=False):
+    def delete_rsrc(self, uid, historic=False):
         '''
         Delete all aspect graphs of an individual resource.