Return the conflicting path in the reply, when refusing to change permissions because...
authorAntony Chazapis <chazapis@gmail.com>
Mon, 4 Jul 2011 12:47:01 +0000 (15:47 +0300)
committerAntony Chazapis <chazapis@gmail.com>
Mon, 4 Jul 2011 12:47:01 +0000 (15:47 +0300)
Refs #449

docs/source/devguide.rst
pithos/api/functions.py
pithos/api/util.py
pithos/backends/simple.py

index 8f99ba4..0f89ca5 100644 (file)
@@ -596,6 +596,7 @@ The ``X-Object-Sharing`` header may include either a ``read=...`` comma-separate
 Return Code                  Description
 ===========================  ==============================
 201 (Created)                The object has been created
+409 (Conflict)               The object can not be created from the provided hashmap, or there are conflicting permissions (a list of missing hashes, or a conflicting sharing path will be included in the reply - in JSON format)
 411 (Length Required)        Missing ``Content-Length`` or ``Content-Type`` in the request
 422 (Unprocessable Entity)   The MD5 checksum of the data written to the storage system does not match the (optionally) supplied ETag value
 ===========================  ==============================
@@ -626,6 +627,7 @@ No reply content/headers.
 Return Code                  Description
 ===========================  ==============================
 201 (Created)                The object has been created
+409 (Conflict)               There are conflicting permissions (a conflicting sharing path will be included in the reply - in JSON format)
 ===========================  ==============================
 
 
@@ -692,6 +694,7 @@ Return Code                  Description
 ===========================  ==============================
 202 (Accepted)               The request has been accepted (not a data update)
 204 (No Content)             The request succeeded (data updated)
+409 (Conflict)               There are conflicting permissions (a conflicting sharing path will be included in the reply - in JSON format)
 411 (Length Required)        Missing ``Content-Length`` in the request
 416 (Range Not Satisfiable)  The supplied range is invalid
 ===========================  ==============================
index 461c33e..509982f 100644 (file)
@@ -650,21 +650,18 @@ def object_write(request, v_account, v_container, v_object):
         if etag and parse_etags(etag)[0].lower() != meta['hash']:
             raise UnprocessableEntity('Object ETag does not match')
     
-    payload = ''
-    code = 201
     try:
         backend.update_object_hashmap(request.user, v_account, v_container, v_object, size, hashmap, meta, True, permissions)
     except NotAllowedError:
         raise Unauthorized('Access denied')
     except IndexError, e:
-        payload = json.dumps(e.data)
-        code = 409
+        raise Conflict(json.dumps(e.data))
     except NameError:
         raise ItemNotFound('Container does not exist')
     except ValueError:
         raise BadRequest('Invalid sharing header')
-    except AttributeError:
-        raise Conflict('Sharing already set above or below this path in the hierarchy')
+    except AttributeError, e:
+        raise Conflict(json.dumps(e.data))
     if public is not None:
         try:
             backend.update_object_public(request.user, v_account, v_container, v_object, public)
@@ -673,7 +670,7 @@ def object_write(request, v_account, v_container, v_object):
         except NameError:
             raise ItemNotFound('Object does not exist')
     
-    response = HttpResponse(content=payload, status=code)
+    response = HttpResponse(status=201)
     response['ETag'] = meta['hash']
     return response
 
@@ -754,8 +751,8 @@ def object_update(request, v_account, v_container, v_object):
                 raise ItemNotFound('Object does not exist')
             except ValueError:
                 raise BadRequest('Invalid sharing header')
-            except AttributeError:
-                raise Conflict('Sharing already set above or below this path in the hierarchy')
+            except AttributeError, e:
+                raise Conflict(json.dumps(e.data))
         if public is not None:
             try:
                 backend.update_object_public(request.user, v_account, v_container, v_object, public)
@@ -827,8 +824,8 @@ def object_update(request, v_account, v_container, v_object):
         raise ItemNotFound('Container does not exist')
     except ValueError:
         raise BadRequest('Invalid sharing header')
-    except AttributeError:
-        raise Conflict('Sharing already set above or below this path in the hierarchy')
+    except AttributeError, e:
+        raise Conflict(json.dumps(e.data))
     if public is not None:
         try:
             backend.update_object_public(request.user, v_account, v_container, v_object, public)
index 342242e..430cac5 100644 (file)
@@ -39,6 +39,7 @@ from binascii import hexlify
 
 from django.conf import settings
 from django.http import HttpResponse
+from django.utils import simplejson as json
 from django.utils.http import http_date, parse_etags
 
 from pithos.api.compat import parse_http_date_safe, parse_http_date
@@ -258,8 +259,8 @@ def copy_or_move_object(request, v_account, src_container, src_name, dest_contai
         raise ItemNotFound('Container or object does not exist')
     except ValueError:
         raise BadRequest('Invalid sharing header')
-    except AttributeError:
-        raise Conflict('Sharing already set above or below this path in the hierarchy')
+    except AttributeError, e:
+        raise Conflict(json.dumps(e.data))
     if public is not None:
         try:
             backend.update_object_public(request.user, v_account, v_container, v_object, public)
@@ -666,7 +667,7 @@ def update_response_headers(request, response):
 def render_fault(request, fault):
     if settings.DEBUG or settings.TEST:
         fault.details = format_exc(fault)
-
+    
     request.serialization = 'text'
     data = '\n'.join((fault.message, fault.details)) + '\n'
     response = HttpResponse(data, status=fault.code)
index 137e8ab..45e9c3f 100644 (file)
@@ -458,6 +458,8 @@ class SimpleBackend(BaseBackend):
         self._put_version(path, user, 0, 1)
         sql = 'delete from permissions where name = ?'
         self.con.execute(sql, (path,))
+        sql = 'delete from public where name = ?'
+        self.con.execute(sql, (path,))
         self.con.commit()
     
     def list_versions(self, user, account, container, name):
@@ -694,9 +696,11 @@ class SimpleBackend(BaseBackend):
         sql = '''select name from permissions
                     where name != ? and (name like ? or ? like name || ?)'''
         c = self.con.execute(sql, (path, path + '%', path, '%'))
-        rows = c.fetchall()
-        if rows:
-            raise AttributeError('Permissions already set')
+        row = c.fetchone()
+        if row:
+            ae = AttributeError()
+            ae.data = row[0]
+            raise ae
         
         # Format given permissions.
         if len(permissions) == 0:
@@ -803,6 +807,4 @@ class SimpleBackend(BaseBackend):
         self.con.execute(sql, (path,))
         sql = '''delete from versions where name = ?'''
         self.con.execute(sql, (path,))
-        sql = '''delete from permissions where name like ?'''
-        self.con.execute(sql, (path + '%',)) # Redundant.
         self.con.commit()