Change the arbitrary URI support from implied to explicit
authorMarcus Wanner <marcus@wanners.net>
Sat, 19 Nov 2011 01:42:10 +0000 (20:42 -0500)
committerBrian Warner <warner@lothar.com>
Wed, 9 May 2012 20:07:14 +0000 (13:07 -0700)
The move webapi function now takes a target_type argument which lets it
know whether the target is a subdirectory name or URI. This is an
improvement over the old system in which the move handler tried to guess
whether the target was a name or a URI. Also fixed a little docs
copypaste problem and tweaked some line wrapping.

docs/frontends/webapi.rst
src/allmydata/test/test_web.py
src/allmydata/uri.py
src/allmydata/web/directory.py
src/allmydata/web/move-form.xhtml

index 5f1060b1a9485d4433e589a25db0723a2ebd4ef7..11cee7229924c155fd85b0bf7e4442e7c86a4636 100644 (file)
@@ -1281,16 +1281,21 @@ Renaming A Child
 Moving A Child
 ----------------
 
-``POST /uri/$DIRCAP/[SUBDIRS../]?t=rename&from_name=OLD&to_dir=TARGET[&to_name=NEW]``
+``POST /uri/$DIRCAP/[SUBDIRS../]?t=move&from_name=OLD&to_dir=TARGET``
 
  This instructs the node to move a child of the given directory to a
- different directory, both of which must be mutable. The child can also be
- renamed in the process. The to_dir parameter can be either the name of a
- subdirectory of the dircap from which the child is being moved (multiple
- levels of descent are supported) or the writecap of an unrelated directory.
-
- This operation will replace any existing child of the new name, making it
- behave like the UNIX "``mv -f``" command. The original child is not
+ different directory, both of which must be mutable. The child can also
+ be renamed in the process. The to_dir parameter should contain the name
+ of a subdirectory of the dircap from which the child is being moved
+ (multiple levels of descent are supported), unless the file is to be
+ moved to an unrelated directory. In the latter case, this can be
+ specified by passing a target_type=uri argument and the target URI in
+ to_dir=. If the target is the name of a subdirectory, this can be
+ signified by passing target_type=name. A new name for the child can
+ also be specified using the to_name= parameter.
+
+ This operation will replace any existing child of the new name, making
+ it behave like the UNIX "``mv -f``" command. The original child is not
  unlinked until it is linked into the target directory.
 
 Other Utilities
index a03d56c0b66ff71fb77f3943b7f0bda64c3949b0..baf0017847d90907c7e69db710333b12ca1e80b6 100644 (file)
@@ -3247,7 +3247,6 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
         return d
 
     def test_POST_move_file(self):
-        """"""
         d = self.POST(self.public_url + "/foo", t="move",
                       from_name="bar.txt", to_dir="sub")
         d.addCallback(lambda res:
@@ -3331,6 +3330,16 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
         d.addCallback(self.failUnlessIsBazDotTxt)
         return d
 
+    def test_POST_move_file_bad_target_type(self):
+        d = self.POST(self.public_url + "/foo", t="move", target_type="*D",
+                      from_name="bar.txt", to_dir="sub")
+        d.addBoth(self.shouldFail, error.Error,
+                  "test_POST_rename_file_slash_fail",
+                  "400 Bad Request",
+                  "invalid target_type parameter",
+                  )
+        return d
+
     def test_POST_move_file_multi_level(self):
         d = self.POST(self.public_url + "/foo/sub/level2?t=mkdir", "")
         d.addCallback(lambda res: self.POST(self.public_url + "/foo", t="move",
@@ -3344,7 +3353,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
         return d
 
     def test_POST_move_file_to_uri(self):
-        d = self.POST(self.public_url + "/foo", t="move",
+        d = self.POST(self.public_url + "/foo", t="move", target_type="uri",
                       from_name="bar.txt", to_dir=self._sub_uri)
         d.addCallback(lambda res:
                       self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
@@ -3379,7 +3388,8 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
         return d
 
     def test_POST_move_file_to_bad_uri(self):
-        d = self.POST(self.public_url + "/foo", t="move", from_name="bar.txt",
+        d = self.POST(self.public_url + "/foo", t="move",
+                      from_name="bar.txt", target_type="uri",
                       to_dir="URI:DIR2:mn5jlyjnrjeuydyswlzyui72i:rmneifcj6k6sycjljjhj3f6majsq2zqffydnnul5hfa4j577arma")
         d.addBoth(self.shouldFail, error.Error,
                   "POST_move_file_to_bad_uri",
index dfa6cfa9425fab7ba3f69ef9b14a046810f10dd3..ee9c86aaa52ff0f4b2d636fbb252d7e7600f6d66 100644 (file)
@@ -934,13 +934,6 @@ def is_literal_file_uri(s):
             s.startswith(ALLEGED_READONLY_PREFIX + 'URI:LIT:') or
             s.startswith(ALLEGED_IMMUTABLE_PREFIX + 'URI:LIT:'))
 
-def is_writeable_directory_uri(s):
-    if not isinstance(s, str):
-        return False
-    return (s.startswith('URI:DIR2:') or
-            s.startswith(ALLEGED_READONLY_PREFIX + 'URI:DIR2:') or
-            s.startswith(ALLEGED_IMMUTABLE_PREFIX + 'URI:DIR2:'))
-
 def has_uri_prefix(s):
     if not isinstance(s, str):
         return False
index d4f88de29125d403ae536a2c56911b5892ae3841..c7f9af2d3d253fae1dd6be769e04409b31fb595b 100644 (file)
@@ -13,7 +13,7 @@ from nevow.inevow import IRequest
 from foolscap.api import fireEventually
 
 from allmydata.util import base32, time_format
-from allmydata.uri import from_string_dirnode, is_writeable_directory_uri
+from allmydata.uri import from_string_dirnode
 from allmydata.interfaces import IDirectoryNode, IFileNode, IFilesystemNode, \
      IImmutableFileNode, IMutableFileNode, ExistingChildError, \
      NoSuchChildError, EmptyPathnameComponentError, SDMF_VERSION, MDMF_VERSION
@@ -444,23 +444,30 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
         if not from_name or not to_dir:
             raise WebError("move requires from_name and to_dir")
         replace = boolean_of_arg(get_arg(req, "replace", "true"))
+        target_type = get_arg(req, "target_type", "name")
+        if not target_type in ["name", "uri"]:
+            raise WebError("invalid target_type parameter",
+                           http.BAD_REQUEST)
 
         # allow from_name to contain slashes, so they can fix names that
         # were accidentally created with them. But disallow them in to_name
         # (if it's specified), to discourage the practice.
         if to_name and "/" in to_name:
-            raise WebError("to_name= may not contain a slash", http.BAD_REQUEST)
+            raise WebError("to_name= may not contain a slash",
+                           http.BAD_REQUEST)
 
-        d = self.node.has_child(to_dir.split('/')[0])
-        def get_target_node(isname):
-            if isname or not is_writeable_directory_uri(str(to_dir)):
+        d = defer.Deferred()
+        def get_target_node(target_type):
+            if target_type == "name":
                 return self.node.get_child_at_path(to_dir)
-            else:
+            elif target_type == "uri":
                 return self.client.create_node_from_uri(str(to_dir))
         d.addCallback(get_target_node)
+        d.callback(target_type)
         def is_target_node_usable(target_node):
             if not IDirectoryNode.providedBy(target_node):
-                raise WebError("to_dir is not a usable directory", http.GONE)
+                raise WebError("to_dir is not a usable directory",
+                               http.GONE)
             return target_node
         d.addCallback(is_target_node_usable)
         d.addCallback(lambda new_parent: self.node.move_child_to(
index 51baae7dd723298ef3ae33795e3852b1bdba127d..1df427cc18cf1ce59e64377b573fa2ffc6bcf278 100644 (file)
             <input n:render="when_done" />
 
             Move child:
-            <input type="text" name="from_name" readonly="true" n:render="get_name" />
+            <input type="text" name="from_name" readonly="true"
+             n:render="get_name" /><br />
             to
             <input type="text" name="to_dir" /><br />
+            <input checked="checked" type="radio" id="tt-name"
+             value="name" name="target_type" />
+            <label for="tt-name"> Subdirectory</label>
+            <input type="radio" id="tt-uri" value="uri" name="target_type"/>
+            <label for="tt-uri"> URI</label> <br /><br />
             New name?
             <input type="text" name="to_name" />
-            <input type="submit" value="move" />
+            <input type="submit" value="move" /><br />
         </fieldset>
     </form>
 </div>