]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/commitdiff
webish: implement replace= for PUT commands
authorBrian Warner <warner@allmydata.com>
Wed, 15 Aug 2007 20:22:23 +0000 (13:22 -0700)
committerBrian Warner <warner@allmydata.com>
Wed, 15 Aug 2007 20:22:23 +0000 (13:22 -0700)
src/allmydata/test/test_web.py
src/allmydata/webish.py

index d489a2adfd55f8018e307b5f418a3539af106bb5..1804e30892c67d4be4c95ad56346431f672ef175 100644 (file)
@@ -400,6 +400,8 @@ class Web(WebMixin, unittest.TestCase):
     def test_PUT_NEWFILEURL(self):
         d = self.PUT("/vdrive/global/foo/new.txt", self.NEWFILE_CONTENTS)
         def _check(res):
+            # TODO: we lose the response code, so we can't check this
+            #self.failUnlessEqual(responsecode, 201)
             self.failUnless("new.txt" in self._foo_node.children)
             new_uri = self._foo_node.children["new.txt"]
             new_contents = self.files[new_uri]
@@ -408,6 +410,28 @@ class Web(WebMixin, unittest.TestCase):
         d.addCallback(_check)
         return d
 
+    def test_PUT_NEWFILEURL_replace(self):
+        d = self.PUT("/vdrive/global/foo/bar.txt", self.NEWFILE_CONTENTS)
+        def _check(res):
+            # TODO: we lose the response code, so we can't check this
+            #self.failUnlessEqual(responsecode, 200)
+            self.failUnless("bar.txt" in self._foo_node.children)
+            new_uri = self._foo_node.children["bar.txt"]
+            new_contents = self.files[new_uri]
+            self.failUnlessEqual(new_contents, self.NEWFILE_CONTENTS)
+            self.failUnlessEqual(res.strip(), new_uri)
+        d.addCallback(_check)
+        return d
+
+    def test_PUT_NEWFILEURL_no_replace(self):
+        d = self.PUT("/vdrive/global/foo/bar.txt?replace=false",
+                     self.NEWFILE_CONTENTS)
+        d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_no_replace",
+                  "409 Conflict",
+                  "There was already a child by that name, and you asked me "
+                  "to not replace it")
+        return d
+
     def test_PUT_NEWFILEURL_mkdirs(self):
         d = self.PUT("/vdrive/global/foo/newdir/new.txt", self.NEWFILE_CONTENTS)
         def _check(res):
@@ -667,6 +691,24 @@ class Web(WebMixin, unittest.TestCase):
         d.addCallback(_check)
         return d
 
+    def test_PUT_NEWDIRURL_replace(self):
+        d = self.PUT("/vdrive/global/foo/sub?t=mkdir", "")
+        def _check(res):
+            self.failUnless("sub" in self._foo_node.children)
+            newdir_uri = self._foo_node.children["sub"]
+            newdir_node = self.nodes[newdir_uri]
+            self.failIf(newdir_node.children)
+        d.addCallback(_check)
+        return d
+
+    def test_PUT_NEWDIRURL_no_replace(self):
+        d = self.PUT("/vdrive/global/foo/sub?t=mkdir&replace=false", "")
+        d.addBoth(self.shouldFail, error.Error, "PUT_NEWDIRURL_no_replace",
+                  "409 Conflict",
+                  "There was already a child by that name, and you asked me "
+                  "to not replace it")
+        return d
+
     def test_PUT_NEWDIRURL_mkdirs(self):
         d = self.PUT("/vdrive/global/foo/subdir/newdir?t=mkdir", "")
         def _check(res):
@@ -1112,6 +1154,27 @@ class Web(WebMixin, unittest.TestCase):
         d.addCallback(_check)
         return d
 
+    def test_PUT_NEWFILEURL_uri_replace(self):
+        new_uri = self.makefile(8)
+        d = self.PUT("/vdrive/global/foo/bar.txt?t=uri", new_uri)
+        def _check(res):
+            self.failUnless("bar.txt" in self._foo_node.children)
+            new_uri = self._foo_node.children["bar.txt"]
+            new_contents = self.files[new_uri]
+            self.failUnlessEqual(new_contents, self.files[new_uri])
+            self.failUnlessEqual(res.strip(), new_uri)
+        d.addCallback(_check)
+        return d
+
+    def test_PUT_NEWFILEURL_uri_no_replace(self):
+        new_uri = self.makefile(8)
+        d = self.PUT("/vdrive/global/foo/bar.txt?t=uri&replace=false", new_uri)
+        d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_uri_no_replace",
+                  "409 Conflict",
+                  "There was already a child by that name, and you asked me "
+                  "to not replace it")
+        return d
+
     def test_XMLRPC(self):
         raise unittest.SkipTest("not yet")
         pass
index 878c7fbb204da3ec42ce3cef5bfb54279fe3cfd3..9d0538f1ecf3de06ae89a66c797478f305c4a950 100644 (file)
@@ -330,6 +330,8 @@ class FileDownloader(resource.Resource):
 class BlockingFileError(Exception):
     """We cannot auto-create a parent directory, because there is a file in
     the way"""
+class NoReplacementError(Exception):
+    """There was already a child by that name, and you asked me to not replace it"""
 
 LOCALHOST = "127.0.0.1"
 
@@ -549,8 +551,9 @@ class RenameForm(rend.Page):
         return ctx.tag
 
 class POSTHandler(rend.Page):
-    def __init__(self, node):
+    def __init__(self, node, replace):
         self._node = node
+        self._replace = replace
 
     def renderHTTP(self, ctx):
         req = inevow.IRequest(ctx)
@@ -661,12 +664,13 @@ class DELETEHandler(rend.Page):
         return d
 
 class PUTHandler(rend.Page):
-    def __init__(self, node, path, t, localfile, localdir):
+    def __init__(self, node, path, t, localfile, localdir, replace):
         self._node = node
         self._path = path
         self._t = t
         self._localfile = localfile
         self._localdir = localdir
+        self._replace = replace
 
     def renderHTTP(self, ctx):
         req = inevow.IRequest(ctx)
@@ -677,6 +681,7 @@ class PUTHandler(rend.Page):
         # we must traverse the path, creating new directories as necessary
         d = self._get_or_create_directories(self._node, self._path[:-1])
         name = self._path[-1]
+        d.addCallback(self._check_replacement, name, self._replace)
         if t == "upload":
             if localfile:
                 d.addCallback(self._upload_localfile, localfile, name)
@@ -698,6 +703,12 @@ class PUTHandler(rend.Page):
             req.setHeader("content-type", "text/plain")
             return str(f.value)
         d.addErrback(_check_blocking)
+        def _check_replacement(f):
+            f.trap(NoReplacementError)
+            req.setResponseCode(http.CONFLICT)
+            req.setHeader("content-type", "text/plain")
+            return str(f.value)
+        d.addErrback(_check_replacement)
         return d
 
     def _get_or_create_directories(self, node, path):
@@ -716,6 +727,19 @@ class PUTHandler(rend.Page):
         d.addCallback(self._get_or_create_directories, path[1:])
         return d
 
+    def _check_replacement(self, node, name, replace):
+        if replace:
+            return node
+        d = node.has_child(name)
+        def _got(present):
+            if present:
+                raise NoReplacementError("There was already a child by that "
+                                         "name, and you asked me to not "
+                                         "replace it.")
+            return node
+        d.addCallback(_got)
+        return d
+
     def _mkdir(self, node, name):
         d = node.create_empty_directory(name)
         def _done(newnode):
@@ -852,6 +876,14 @@ class VDrive(rend.Page):
                 return NeedLocalhostError(), ()
         # TODO: think about clobbering/revealing config files and node secrets
 
+        replace = True
+#        if "replace" in req.fields:
+#            if req.fields["replace"].value.lower() in ("false", "0"):
+#                replace = False
+        if "replace" in req.args:
+            if req.args["replace"][0].lower() in ("false", "0"):
+                replace = False
+
         if method == "GET":
             # the node must exist, and our operation will be performed on the
             # node itself.
@@ -909,7 +941,7 @@ class VDrive(rend.Page):
             # node itself.
             d = self.get_child_at_path(path)
             def _got(node):
-                return POSTHandler(node), ()
+                return POSTHandler(node, replace), ()
             d.addCallback(_got)
         elif method == "DELETE":
             # the node must exist, and our operation will be performed on its
@@ -922,7 +954,7 @@ class VDrive(rend.Page):
         elif method in ("PUT",):
             # the node may or may not exist, and our operation may involve
             # all the ancestors of the node.
-            return PUTHandler(self.node, path, t, localfile, localdir), ()
+            return PUTHandler(self.node, path, t, localfile, localdir, replace), ()
         else:
             return rend.NotFound
         def _trap_KeyError(f):