From: Zooko O'Whielacronx <zooko@zooko.com>
Date: Sat, 1 Mar 2008 01:40:27 +0000 (-0700)
Subject: wapi: add POST /uri/$DIRECTORY?t=set_children
X-Git-Tag: allmydata-tahoe-0.9.0~93
X-Git-Url: https://git.rkrishnan.org/components/com_hotproperty/frontends/%3C?a=commitdiff_plain;h=99f006c5841aa326b8349bc0a727ba672083a6a7;p=tahoe-lafs%2Ftahoe-lafs.git

wapi: add POST /uri/$DIRECTORY?t=set_children
Unfinished bits: doc in webapi.txt, test handling of badly formed JSON, return reasonable HTTP response, examination of the effect of this patch on code coverage -- but I'm committing it anyway because MikeB can use it and I'm being called to dinner...
---

diff --git a/src/allmydata/dirnode.py b/src/allmydata/dirnode.py
index fc6debc9..f80e71b7 100644
--- a/src/allmydata/dirnode.py
+++ b/src/allmydata/dirnode.py
@@ -250,7 +250,7 @@ class NewDirectoryNode:
         assert isinstance(name, unicode)
         return self.set_node(name, self._create_node(child_uri), metadata)
 
-    def set_uris(self, entries):
+    def set_children(self, entries):
         node_entries = []
         for e in entries:
             if len(e) == 2:
diff --git a/src/allmydata/interfaces.py b/src/allmydata/interfaces.py
index 07d1cc79..d4e6cc7c 100644
--- a/src/allmydata/interfaces.py
+++ b/src/allmydata/interfaces.py
@@ -669,7 +669,7 @@ class IDirectoryNode(IMutableFilesystemNode):
         If this directory node is read-only, the Deferred will errback with a
         NotMutableError."""
 
-    def set_uris(entries):
+    def set_children(entries):
         """Add multiple (name, child_uri) pairs (or (name, child_uri,
         metadata) triples) to a directory node. Returns a Deferred that fires
         (with None) when the operation finishes. This is equivalent to
diff --git a/src/allmydata/test/common.py b/src/allmydata/test/common.py
index c88e7f1a..d0f32ceb 100644
--- a/src/allmydata/test/common.py
+++ b/src/allmydata/test/common.py
@@ -110,7 +110,7 @@ def make_verifier_uri():
     return uri.SSKVerifierURI(storage_index=os.urandom(16),
                               fingerprint=os.urandom(32))
 
-class NonGridDirectoryNode(dirnode.NewDirectoryNode):
+class FakeDirectoryNode(dirnode.NewDirectoryNode):
     """This offers IDirectoryNode, but uses a FakeMutableFileNode for the
     backing store, so it doesn't go to the grid. The child data is still
     encrypted and serialized, so this isn't useful for tests that want to
diff --git a/src/allmydata/test/test_dirnode.py b/src/allmydata/test/test_dirnode.py
index c97d8dd2..9d053dc8 100644
--- a/src/allmydata/test/test_dirnode.py
+++ b/src/allmydata/test/test_dirnode.py
@@ -7,15 +7,13 @@ from allmydata.interfaces import IURI, IClient, IMutableFileNode, \
      INewDirectoryURI, IReadonlyNewDirectoryURI, IFileNode
 from allmydata.util import hashutil, testutil
 from allmydata.test.common import make_chk_file_uri, make_mutable_file_uri, \
-     NonGridDirectoryNode, create_chk_filenode
+     FakeDirectoryNode, create_chk_filenode
 from twisted.internet import defer, reactor
 
 # to test dirnode.py, we want to construct a tree of real DirectoryNodes that
 # contain pointers to fake files. We start with a fake MutableFileNode that
 # stores all of its data in a static table.
 
-FakeDirectoryNode = NonGridDirectoryNode
-
 class Marker:
     implements(IFileNode, IMutableFileNode) # sure, why not
     def __init__(self, nodeuri):
@@ -281,8 +279,8 @@ class Dirnode(unittest.TestCase, testutil.ShouldFailMixin):
             d.addCallback(lambda res: n.delete(u"d3"))
             d.addCallback(lambda res: n.delete(u"d4"))
 
-            # metadata through set_uris()
-            d.addCallback(lambda res: n.set_uris([ (u"e1", fake_file_uri),
+            # metadata through set_children()
+            d.addCallback(lambda res: n.set_children([ (u"e1", fake_file_uri),
                                                    (u"e2", fake_file_uri, {}),
                                                    (u"e3", fake_file_uri,
                                                     {"key": "value"}),
diff --git a/src/allmydata/test/test_web.py b/src/allmydata/test/test_web.py
index c78b0401..122dbacc 100644
--- a/src/allmydata/test/test_web.py
+++ b/src/allmydata/test/test_web.py
@@ -7,7 +7,7 @@ from twisted.web import client, error, http
 from twisted.python import failure, log
 from allmydata import interfaces, provisioning, uri, webish, upload, download
 from allmydata.util import fileutil
-from allmydata.test.common import NonGridDirectoryNode, FakeCHKFileNode, FakeMutableFileNode, create_chk_filenode
+from allmydata.test.common import FakeDirectoryNode, FakeCHKFileNode, FakeMutableFileNode, create_chk_filenode
 from allmydata.interfaces import IURI, INewDirectoryURI, IReadonlyNewDirectoryURI, IFileURI, IMutableFileURI, IMutableFileNode
 
 # create a fake uploader/downloader, and a couple of fake dirnodes, then
@@ -35,18 +35,18 @@ class FakeClient(service.MultiService):
     def connected_to_introducer(self):
         return False
 
-    def create_node_from_uri(self, uri):
-        u = IURI(uri)
+    def create_node_from_uri(self, auri):
+        u = uri.from_string(auri)
         if (INewDirectoryURI.providedBy(u)
             or IReadonlyNewDirectoryURI.providedBy(u)):
-            return NonGridDirectoryNode(self).init_from_uri(u)
+            return FakeDirectoryNode(self).init_from_uri(u)
         if IFileURI.providedBy(u):
             return FakeCHKFileNode(u, self)
         assert IMutableFileURI.providedBy(u), u
         return FakeMutableFileNode(self).init_from_uri(u)
 
     def create_empty_dirnode(self):
-        n = NonGridDirectoryNode(self)
+        n = FakeDirectoryNode(self)
         d = n.create()
         d.addCallback(lambda res: n)
         return d
@@ -1299,6 +1299,46 @@ class Web(WebMixin, unittest.TestCase):
         d.addCallback(self.failUnlessNodeKeysAre, [])
         return d
 
+    def test_POST_set_children(self):
+        contents9, n9, newuri9 = self.makefile(9)
+        contents10, n10, newuri10 = self.makefile(10)
+        contents11, n11, newuri11 = self.makefile(11)
+
+        reqbody = """{
+                     "atomic_added_1": [ "filenode", { "rw_uri": "%s",
+                                                "size": 0,
+                                                "metadata": {
+                                                  "ctime": 1002777696.7564139,
+                                                  "mtime": 1002777696.7564139
+                                                 }
+                                               } ], 
+                     "atomic_added_2": [ "filenode", { "rw_uri": "%s",
+                                                "size": 1,
+                                                "metadata": {
+                                                  "ctime": 1002777696.7564139,
+                                                  "mtime": 1002777696.7564139
+                                                 }
+                                               } ],
+                     "atomic_added_3": [ "filenode", { "rw_uri": "%s",
+                                                "size": 2,
+                                                "metadata": {
+                                                  "ctime": 1002777696.7564139,
+                                                  "mtime": 1002777696.7564139
+                                                 }
+                                               } ]
+                    }""" % (newuri9, newuri10, newuri11)
+
+        url = self.webish_url + self.public_url + "/foo" + "?t=set_children"
+
+        d = client.getPage(url, method="POST", postdata=reqbody)
+        def _then(res):
+            self.failUnlessURIMatchesChild(newuri9, self._foo_node, u"atomic_added_1")
+            self.failUnlessURIMatchesChild(newuri10, self._foo_node, u"atomic_added_2")
+            self.failUnlessURIMatchesChild(newuri11, self._foo_node, u"atomic_added_3")
+
+        d.addCallback(_then)
+        return d
+
     def test_POST_put_uri(self):
         contents, n, newuri = self.makefile(8)
         d = self.POST(self.public_url + "/foo", t="uri", name="new.txt", uri=newuri)
diff --git a/src/allmydata/webish.py b/src/allmydata/webish.py
index 7a8e954f..f8da6271 100644
--- a/src/allmydata/webish.py
+++ b/src/allmydata/webish.py
@@ -899,6 +899,16 @@ class POSTHandler(rend.Page):
         d.addCallback(_got_child_check)
         return d
 
+    def _POST_set_children(self, children):
+        cs = []
+        for name, (file_or_dir, mddict) in children.iteritems():
+            cap = str(mddict.get('rw_uri') or mddict.get('ro_uri'))
+            cs.append((name, cap, mddict.get('metadata')))
+
+        d = self._node.set_children(cs)
+        d.addCallback(lambda res: "Okay so I did it.")
+        return d
+
     def renderHTTP(self, ctx):
         req = inevow.IRequest(ctx)
 
@@ -973,16 +983,16 @@ class POSTHandler(rend.Page):
             d = self._POST_overwrite(contents)
         elif t == "check":
             d = self._POST_check(name)
-        # elif t == "set_children":
-        #     d = self._POST_set_(name)
-        #     if not name:
-        #         raise RuntimeError("set-uri requires a name")
-        #     newuri = get_arg(req, "uri")
-        #     assert newuri is not None
-        #     d = self._check_replacement(name)
-        #     d.addCallback(lambda res: self._node.set_uri(name, newuri))
-        #     d.addCallback(lambda res: newuri)
-
+        elif t == "set_children":
+            req.content.seek(0)
+            body = req.content.read()
+            try:
+                children = simplejson.loads(body)
+            except ValueError, le:
+                le.args = tuple(le.args + (body,))
+                # TODO test handling of bad JSON
+                raise
+            d = self._POST_set_children(children)
         else:
             print "BAD t=%s" % t
             return "BAD t=%s" % t
@@ -1346,9 +1356,8 @@ class UnlinkedPUTSSKUploader(rend.Page):
         req = inevow.IRequest(ctx)
         assert req.method == "PUT"
         # SDMF: files are small, and we can only upload data
-        contents = req.content
-        contents.seek(0)
-        data = contents.read()
+        req.content.seek(0)
+        data = req.content.read()
         d = IClient(ctx).create_mutable_file(data)
         d.addCallback(lambda n: n.get_uri())
         return d