Browse Source

Allow empty PUTs on existing resources.

Stefano Cossu 7 years ago
parent
commit
a642c8668a
3 changed files with 24 additions and 14 deletions
  1. 13 0
      docs/fcrepo4_deltas.rst
  2. 2 11
      lakesuperior/api/resource.py
  3. 9 3
      lakesuperior/model/ldp_factory.py

+ 13 - 0
docs/fcrepo4_deltas.rst

@@ -76,6 +76,19 @@ identifiers will be different).
 This seems to break Hyrax at some point, but might have been fixed. This
 This seems to break Hyrax at some point, but might have been fixed. This
 needs to be verified further.
 needs to be verified further.
 
 
+Allow PUT requests with empty body on existing resources
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+FCREPO4 returns a ``409 Conflict`` if a PUT request with no payload is sent
+to an existing resource.
+
+LAKEsuperior allows to perform this operation, which would result in deleting
+all the user-provided properties in that resource.
+
+If the original resource is an LDP-NR, however, the operation will raise a
+``415 Unsupported Media Type`` because the resource will be treated as an empty
+LDP-RS, which cannot replace an existing LDP-NR.
+
 Non-standard client breaking changes
 Non-standard client breaking changes
 ------------------------------------
 ------------------------------------
 
 

+ 2 - 11
lakesuperior/api/resource.py

@@ -215,15 +215,10 @@ def create(parent, slug, **kwargs):
 
 
 
 
 @transaction(True)
 @transaction(True)
-def create_or_replace(uid, stream=None, **kwargs):
+def create_or_replace(uid, **kwargs):
     r"""
     r"""
     Create or replace a resource with a specified UID.
     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 string uid: UID of the resource to be created or updated.
     :param string uid: UID of the resource to be created or updated.
     :param BytesIO stream: Content stream. If empty, an empty container is
     :param BytesIO stream: Content stream. If empty, an empty container is
         created.
         created.
@@ -234,11 +229,7 @@ def create_or_replace(uid, stream=None, **kwargs):
     :rtype: str
     :rtype: str
     :return: Event type: whether the resource was created or updated.
     :return: 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.')
+    rsrc = LdpFactory.from_provided(uid, **kwargs)
 
 
     return rsrc.create_or_replace()
     return rsrc.create_or_replace()
 
 

+ 9 - 3
lakesuperior/model/ldp_factory.py

@@ -105,6 +105,11 @@ class LdpFactory:
                     'Creating empty container.')
                     'Creating empty container.')
             inst = Ldpc(uid, provided_imr=Graph(identifier=uri), **kwargs)
             inst = Ldpc(uid, provided_imr=Graph(identifier=uri), **kwargs)
 
 
+            # 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)
+
+        # Otherwise, determine LDP type from provided content.
         else:
         else:
             # If the stream is RDF, or an IMR is provided, create a container
             # If the stream is RDF, or an IMR is provided, create a container
             # and populate it with provided RDF data.
             # and populate it with provided RDF data.
@@ -119,7 +124,8 @@ class LdpFactory:
             #        pformat(set(provided_imr))))
             #        pformat(set(provided_imr))))
 
 
             if not mimetype or __class__.is_rdf_parsable(mimetype):
             if not mimetype or __class__.is_rdf_parsable(mimetype):
-                # Determine whether it is a basic, direct or indirect container.
+                # Determine whether it is a basic, direct or indirect
+                # container.
                 if Ldpr.MBR_RSRC_URI in provided_imr.predicates() and \
                 if Ldpr.MBR_RSRC_URI in provided_imr.predicates() and \
                         Ldpr.MBR_REL_URI in provided_imr.predicates():
                         Ldpr.MBR_REL_URI in provided_imr.predicates():
                     if Ldpr.INS_CNT_REL_URI in provided_imr.predicates():
                     if Ldpr.INS_CNT_REL_URI in provided_imr.predicates():
@@ -131,7 +137,7 @@ class LdpFactory:
 
 
                 inst = cls(uid, provided_imr=provided_imr, **kwargs)
                 inst = cls(uid, provided_imr=provided_imr, **kwargs)
 
 
-                # Make sure we are not updating an LDP-RS with an LDP-NR.
+                # 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:
                 if inst.is_stored and LDP_NR_TYPE in inst.ldp_types:
                     raise IncompatibleLdpTypeError(uid, mimetype)
                     raise IncompatibleLdpTypeError(uid, mimetype)
 
 
@@ -143,7 +149,7 @@ class LdpFactory:
                 inst = LdpNr(uid, stream=stream, mimetype=mimetype,
                 inst = LdpNr(uid, stream=stream, mimetype=mimetype,
                         provided_imr=provided_imr, **kwargs)
                         provided_imr=provided_imr, **kwargs)
 
 
-                # Make sure we are not updating an LDP-NR with an LDP-RS.
+                # 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:
                 if inst.is_stored and LDP_RS_TYPE in inst.ldp_types:
                     raise IncompatibleLdpTypeError(uid, mimetype)
                     raise IncompatibleLdpTypeError(uid, mimetype)