Add t=mkdir-immutable to the webapi. Closes #607.
* change t=mkdir-with-children to not use multipart/form encoding. Instead, the request body is all JSON. t=mkdir-immutable uses this format too. * make nodemaker.create_immutable_dirnode() get convergence from SecretHolder, but let callers override it * raise NotDeepImmutableError instead of using assert() * add mutable= argument to DirectoryNode.create_subdirectory(), default True
This commit is contained in:
parent
d2426ea3bd
commit
f85690697a
|
@ -356,17 +356,15 @@ POST /uri?t=mkdir-with-children
|
|||
write-cap as the HTTP response body. The new directory is not attached to
|
||||
any other directory: the returned write-cap is the only reference to it.
|
||||
|
||||
Initial children are provided in the "children" field of the POST form. This
|
||||
is more efficient than doing separate mkdir and add-children operations. If
|
||||
this value is empty, the new directory will be empty.
|
||||
|
||||
If not empty, it will be interpreted as a JSON-encoded dictionary of
|
||||
children with which the new directory should be populated, using the same
|
||||
format as would be returned in the 'children' value of the t=json GET
|
||||
request, described below. Each dictionary key should be a child name, and
|
||||
each value should be a list of [TYPE, PROPDICT], where PROPDICT contains
|
||||
"rw_uri", "ro_uri", and "metadata" keys (all others are ignored). For
|
||||
example, the PUT request body could be:
|
||||
Initial children are provided as the body of the POST form (this is more
|
||||
efficient than doing separate mkdir and set_children operations). If the
|
||||
body is empty, the new directory will be empty. If not empty, the body will
|
||||
be interpreted as a JSON-encoded dictionary of children with which the new
|
||||
directory should be populated, using the same format as would be returned in
|
||||
the 'children' value of the t=json GET request, described below. Each
|
||||
dictionary key should be a child name, and each value should be a list of
|
||||
[TYPE, PROPDICT], where PROPDICT contains "rw_uri", "ro_uri", and "metadata"
|
||||
keys (all others are ignored). For example, the PUT request body could be:
|
||||
|
||||
{
|
||||
"Fran\u00e7ais": [ "filenode", {
|
||||
|
@ -391,6 +389,20 @@ POST /uri?t=mkdir-with-children
|
|||
} } } ]
|
||||
}
|
||||
|
||||
Note that the webapi-using client application must not provide the
|
||||
"Content-Type: multipart/form-data" header that usually accompanies HTML
|
||||
form submissions, since the body is not formatted this way. Doing so will
|
||||
cause a server error as the lower-level code misparses the request body.
|
||||
|
||||
POST /uri?t=mkdir-immutable
|
||||
|
||||
Like t=mkdir-with-children above, but the new directory will be
|
||||
deep-immutable. This means that the directory itself is immutable, and that
|
||||
it can only contain deep-immutable objects, like immutable files, literal
|
||||
files, and deep-immutable directories. A non-empty request body is
|
||||
mandatory, since after the directory is created, it will not be possible to
|
||||
add more children to it.
|
||||
|
||||
POST /uri/$DIRCAP/[SUBDIRS../]SUBDIR?t=mkdir
|
||||
PUT /uri/$DIRCAP/[SUBDIRS../]SUBDIR?t=mkdir
|
||||
|
||||
|
@ -410,8 +422,13 @@ PUT /uri/$DIRCAP/[SUBDIRS../]SUBDIR?t=mkdir
|
|||
POST /uri/$DIRCAP/[SUBDIRS../]SUBDIR?t=mkdir-with-children
|
||||
|
||||
Like above, but if the final directory is created, it will be populated with
|
||||
initial children via the POST 'children' form field, as described above in
|
||||
the /uri?t=mkdir-with-children operation.
|
||||
initial children from the POST request body, as described above in the
|
||||
/uri?t=mkdir-with-children operation.
|
||||
|
||||
POST /uri/$DIRCAP/[SUBDIRS../]SUBDIR?t=mkdir-immutable
|
||||
|
||||
Like above, but the final directory will be deep-immutable, with the
|
||||
children specified as a JSON dictionary in the POST request body.
|
||||
|
||||
POST /uri/$DIRCAP/[SUBDIRS../]?t=mkdir&name=NAME
|
||||
|
||||
|
@ -425,8 +442,15 @@ POST /uri/$DIRCAP/[SUBDIRS../]?t=mkdir&name=NAME
|
|||
POST /uri/$DIRCAP/[SUBDIRS../]?t=mkdir-with-children&name=NAME
|
||||
|
||||
As above, but the new directory will be populated with initial children via
|
||||
the POST 'children' form field, as described in /uri?t=mkdir-with-children
|
||||
above.
|
||||
the POST request body, as described in /uri?t=mkdir-with-children above.
|
||||
Note that the name= argument must be passed as a queryarg, because the POST
|
||||
request body is used for the initial children JSON.
|
||||
|
||||
POST /uri/$DIRCAP/[SUBDIRS../]?t=mkdir-immutable&name=NAME
|
||||
|
||||
As above, but the new directory will be deep-immutable, with the children
|
||||
specified as a JSON dictionary in the POST request body. Again, the name=
|
||||
argument must be passed as a queryarg.
|
||||
|
||||
=== Get Information About A File Or Directory (as JSON) ===
|
||||
|
||||
|
|
|
@ -465,9 +465,8 @@ class Client(node.Node, pollmixin.PollMixin):
|
|||
def create_dirnode(self, initial_children={}):
|
||||
d = self.nodemaker.create_new_mutable_directory(initial_children)
|
||||
return d
|
||||
def create_immutable_dirnode(self, children):
|
||||
return self.nodemaker.create_immutable_directory(children,
|
||||
self.convergence)
|
||||
def create_immutable_dirnode(self, children, convergence=None):
|
||||
return self.nodemaker.create_immutable_directory(children, convergence)
|
||||
|
||||
def create_mutable_file(self, contents=None, keysize=None):
|
||||
return self.nodemaker.create_mutable_file(contents, keysize)
|
||||
|
|
|
@ -188,6 +188,8 @@ class DirectoryNode:
|
|||
filenode_class = MutableFileNode
|
||||
|
||||
def __init__(self, filenode, nodemaker, uploader):
|
||||
assert IFileNode.providedBy(filenode), filenode
|
||||
assert not IDirectoryNode.providedBy(filenode), filenode
|
||||
self._node = filenode
|
||||
filenode_cap = filenode.get_cap()
|
||||
self._uri = wrap_dirnode_cap(filenode_cap)
|
||||
|
@ -491,11 +493,15 @@ class DirectoryNode:
|
|||
d.addCallback(lambda res: deleter.old_child)
|
||||
return d
|
||||
|
||||
def create_subdirectory(self, name, initial_children={}, overwrite=True):
|
||||
def create_subdirectory(self, name, initial_children={}, overwrite=True,
|
||||
mutable=True):
|
||||
assert isinstance(name, unicode)
|
||||
if self.is_readonly():
|
||||
return defer.fail(NotMutableError())
|
||||
if mutable:
|
||||
d = self._nodemaker.create_new_mutable_directory(initial_children)
|
||||
else:
|
||||
d = self._nodemaker.create_immutable_directory(initial_children)
|
||||
def _created(child):
|
||||
entries = {name: (child, None)}
|
||||
a = Adder(self, entries, overwrite=overwrite)
|
||||
|
|
|
@ -480,6 +480,9 @@ class UnhandledCapTypeError(Exception):
|
|||
"""I recognize the cap/URI, but I cannot create an IFilesystemNode for
|
||||
it."""
|
||||
|
||||
class NotDeepImmutableError(Exception):
|
||||
"""Deep-immutable directories can only contain deep-immutable children"""
|
||||
|
||||
class IFilesystemNode(Interface):
|
||||
def get_cap():
|
||||
"""Return the strongest 'cap instance' associated with this node.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import weakref
|
||||
from zope.interface import implements
|
||||
from allmydata.util.assertutil import precondition
|
||||
from allmydata.interfaces import INodeMaker
|
||||
from allmydata.interfaces import INodeMaker, NotDeepImmutableError
|
||||
from allmydata.immutable.filenode import FileNode, LiteralFileNode
|
||||
from allmydata.immutable.upload import Data
|
||||
from allmydata.mutable.filenode import MutableFileNode
|
||||
|
@ -101,14 +101,16 @@ class NodeMaker:
|
|||
d.addCallback(self._create_dirnode)
|
||||
return d
|
||||
|
||||
def create_immutable_directory(self, children, convergence):
|
||||
def create_immutable_directory(self, children, convergence=None):
|
||||
if convergence is None:
|
||||
convergence = self.secret_holder.get_convergence_secret()
|
||||
for (name, (node, metadata)) in children.iteritems():
|
||||
precondition(not isinstance(node, UnknownNode),
|
||||
"create_immutable_directory does not accept UnknownNode", node)
|
||||
precondition(isinstance(metadata, dict),
|
||||
"create_immutable_directory requires metadata to be a dict, not None", metadata)
|
||||
precondition(not node.is_mutable(),
|
||||
"create_immutable_directory requires immutable children", node)
|
||||
if node.is_mutable():
|
||||
raise NotDeepImmutableError("%s is not immutable" % (node,))
|
||||
n = DummyImmutableFileNode() # writekey=None
|
||||
packed = pack_children(n, children)
|
||||
uploadable = Data(packed, convergence)
|
||||
|
|
|
@ -47,6 +47,8 @@ class FakeCHKFileNode:
|
|||
return self.my_uri.to_string()
|
||||
def get_readonly_uri(self):
|
||||
return self.my_uri.to_string()
|
||||
def get_cap(self):
|
||||
return self.my_uri
|
||||
def get_verify_cap(self):
|
||||
return self.my_uri.get_verify_cap()
|
||||
def get_repair_cap(self):
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
|
||||
import time
|
||||
from zope.interface import implements
|
||||
from twisted.trial import unittest
|
||||
from twisted.internet import defer
|
||||
from allmydata import uri, dirnode
|
||||
from allmydata.client import Client
|
||||
from allmydata.immutable import upload
|
||||
from allmydata.interfaces import IFileNode, \
|
||||
ExistingChildError, NoSuchChildError, \
|
||||
from allmydata.interfaces import IFileNode, IMutableFileNode, \
|
||||
ExistingChildError, NoSuchChildError, NotDeepImmutableError, \
|
||||
IDeepCheckResults, IDeepCheckAndRepairResults, CannotPackUnknownNodeError
|
||||
from allmydata.mutable.filenode import MutableFileNode
|
||||
from allmydata.mutable.common import UncoordinatedWriteError
|
||||
|
@ -137,14 +138,14 @@ class Dirnode(GridTestMixin, unittest.TestCase,
|
|||
bad_kids2))
|
||||
bad_kids3 = {u"one": (nm.create_from_cap(mut_writecap), {})}
|
||||
d.addCallback(lambda ign:
|
||||
self.shouldFail(AssertionError, "bad_kids3",
|
||||
"create_immutable_directory requires immutable children",
|
||||
self.shouldFail(NotDeepImmutableError, "bad_kids3",
|
||||
"is not immutable",
|
||||
c.create_immutable_dirnode,
|
||||
bad_kids3))
|
||||
bad_kids4 = {u"one": (nm.create_from_cap(mut_readcap), {})}
|
||||
d.addCallback(lambda ign:
|
||||
self.shouldFail(AssertionError, "bad_kids4",
|
||||
"create_immutable_directory requires immutable children",
|
||||
self.shouldFail(NotDeepImmutableError, "bad_kids4",
|
||||
"is not immutable",
|
||||
c.create_immutable_dirnode,
|
||||
bad_kids4))
|
||||
d.addCallback(lambda ign: c.create_immutable_dirnode({}))
|
||||
|
@ -177,7 +178,31 @@ class Dirnode(GridTestMixin, unittest.TestCase,
|
|||
return dn.list()
|
||||
d.addCallback(_created_small)
|
||||
d.addCallback(lambda kids: self.failUnlessEqual(kids.keys(), [u"o"]))
|
||||
|
||||
# now test n.create_subdirectory(mutable=False)
|
||||
d.addCallback(lambda ign: c.create_dirnode())
|
||||
def _made_parent(n):
|
||||
d = n.create_subdirectory(u"subdir", kids, mutable=False)
|
||||
d.addCallback(lambda sd: sd.list())
|
||||
d.addCallback(_check_kids)
|
||||
d.addCallback(lambda ign: n.list())
|
||||
d.addCallback(lambda children:
|
||||
self.failUnlessEqual(children.keys(), [u"subdir"]))
|
||||
d.addCallback(lambda ign: n.get(u"subdir"))
|
||||
d.addCallback(lambda sd: sd.list())
|
||||
d.addCallback(_check_kids)
|
||||
d.addCallback(lambda ign: n.get(u"subdir"))
|
||||
d.addCallback(lambda sd: self.failIf(sd.is_mutable()))
|
||||
bad_kids = {u"one": (nm.create_from_cap(mut_writecap), {})}
|
||||
d.addCallback(lambda ign:
|
||||
self.shouldFail(NotDeepImmutableError, "YZ",
|
||||
"is not immutable",
|
||||
n.create_subdirectory,
|
||||
u"sub2", bad_kids, mutable=False))
|
||||
return d
|
||||
d.addCallback(_made_parent)
|
||||
return d
|
||||
|
||||
|
||||
def test_check(self):
|
||||
self.basedir = "dirnode/Dirnode/test_check"
|
||||
|
@ -972,6 +997,7 @@ class Packing(unittest.TestCase):
|
|||
fn, kids, deep_immutable=True)
|
||||
|
||||
class FakeMutableFile:
|
||||
implements(IMutableFileNode)
|
||||
counter = 0
|
||||
def __init__(self, initial_contents=""):
|
||||
self.data = self._get_initial_contents(initial_contents)
|
||||
|
|
|
@ -714,7 +714,8 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
|
|||
d.addCallback(lambda junk: self.clients[3].create_dirnode())
|
||||
d.addCallback(check_kg_poolsize, -2)
|
||||
# use_helper induces use of clients[3], which is the using-key_gen client
|
||||
d.addCallback(lambda junk: self.POST("uri", use_helper=True, t="mkdir", name='george'))
|
||||
d.addCallback(lambda junk:
|
||||
self.POST("uri?t=mkdir&name=george", use_helper=True))
|
||||
d.addCallback(check_kg_poolsize, -3)
|
||||
|
||||
return d
|
||||
|
@ -1053,10 +1054,6 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
|
|||
return getPage(url, method="GET", followRedirect=followRedirect)
|
||||
|
||||
def POST(self, urlpath, followRedirect=False, use_helper=False, **fields):
|
||||
if use_helper:
|
||||
url = self.helper_webish_url + urlpath
|
||||
else:
|
||||
url = self.webish_url + urlpath
|
||||
sepbase = "boogabooga"
|
||||
sep = "--" + sepbase
|
||||
form = []
|
||||
|
@ -1076,11 +1073,21 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
|
|||
form.append(str(value))
|
||||
form.append(sep)
|
||||
form[-1] += "--"
|
||||
body = ""
|
||||
headers = {}
|
||||
if fields:
|
||||
body = "\r\n".join(form) + "\r\n"
|
||||
headers = {"content-type": "multipart/form-data; boundary=%s" % sepbase,
|
||||
}
|
||||
return getPage(url, method="POST", postdata=body,
|
||||
headers=headers, followRedirect=followRedirect)
|
||||
headers["content-type"] = "multipart/form-data; boundary=%s" % sepbase
|
||||
return self.POST2(urlpath, body, headers, followRedirect, use_helper)
|
||||
|
||||
def POST2(self, urlpath, body="", headers={}, followRedirect=False,
|
||||
use_helper=False):
|
||||
if use_helper:
|
||||
url = self.helper_webish_url + urlpath
|
||||
else:
|
||||
url = self.webish_url + urlpath
|
||||
return getPage(url, method="POST", postdata=body, headers=headers,
|
||||
followRedirect=followRedirect)
|
||||
|
||||
def _test_web(self, res):
|
||||
base = self.webish_url
|
||||
|
|
|
@ -281,7 +281,6 @@ class WebMixin(object):
|
|||
return client.getPage(url, method="DELETE")
|
||||
|
||||
def POST(self, urlpath, followRedirect=False, **fields):
|
||||
url = self.webish_url + urlpath
|
||||
sepbase = "boogabooga"
|
||||
sep = "--" + sepbase
|
||||
form = []
|
||||
|
@ -306,9 +305,15 @@ class WebMixin(object):
|
|||
form.append(value)
|
||||
form.append(sep)
|
||||
form[-1] += "--"
|
||||
body = ""
|
||||
headers = {}
|
||||
if fields:
|
||||
body = "\r\n".join(form) + "\r\n"
|
||||
headers = {"content-type": "multipart/form-data; boundary=%s" % sepbase,
|
||||
}
|
||||
headers["content-type"] = "multipart/form-data; boundary=%s" % sepbase
|
||||
return self.POST2(urlpath, body, headers, followRedirect)
|
||||
|
||||
def POST2(self, urlpath, body="", headers={}, followRedirect=False):
|
||||
url = self.webish_url + urlpath
|
||||
return client.getPage(url, method="POST", postdata=body,
|
||||
headers=headers, followRedirect=followRedirect)
|
||||
|
||||
|
@ -1125,8 +1130,8 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, unittest.TestCase):
|
|||
def test_POST_NEWDIRURL_initial_children(self):
|
||||
(newkids, filecap1, filecap2, filecap3,
|
||||
dircap) = self._create_initial_children()
|
||||
d = self.POST(self.public_url + "/foo/newdir?t=mkdir-with-children",
|
||||
children=simplejson.dumps(newkids))
|
||||
d = self.POST2(self.public_url + "/foo/newdir?t=mkdir-with-children",
|
||||
simplejson.dumps(newkids))
|
||||
def _check(uri):
|
||||
n = self.s.create_node_from_uri(uri.strip())
|
||||
d2 = self.failUnlessNodeKeysAre(n, newkids.keys())
|
||||
|
@ -1150,6 +1155,42 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, unittest.TestCase):
|
|||
d.addCallback(self.failUnlessChildURIIs, u"child-imm", filecap1)
|
||||
return d
|
||||
|
||||
def test_POST_NEWDIRURL_immutable(self):
|
||||
(newkids, filecap1, immdircap) = self._create_immutable_children()
|
||||
d = self.POST2(self.public_url + "/foo/newdir?t=mkdir-immutable",
|
||||
simplejson.dumps(newkids))
|
||||
def _check(uri):
|
||||
n = self.s.create_node_from_uri(uri.strip())
|
||||
d2 = self.failUnlessNodeKeysAre(n, newkids.keys())
|
||||
d2.addCallback(lambda ign:
|
||||
self.failUnlessChildURIIs(n, u"child-imm", filecap1))
|
||||
d2.addCallback(lambda ign:
|
||||
self.failUnlessChildURIIs(n, u"dirchild-imm",
|
||||
immdircap))
|
||||
return d2
|
||||
d.addCallback(_check)
|
||||
d.addCallback(lambda res:
|
||||
self.failUnlessNodeHasChild(self._foo_node, u"newdir"))
|
||||
d.addCallback(lambda res: self._foo_node.get(u"newdir"))
|
||||
d.addCallback(self.failUnlessNodeKeysAre, newkids.keys())
|
||||
d.addCallback(lambda res: self._foo_node.get(u"newdir"))
|
||||
d.addCallback(self.failUnlessChildURIIs, u"child-imm", filecap1)
|
||||
d.addCallback(lambda res: self._foo_node.get(u"newdir"))
|
||||
d.addCallback(self.failUnlessChildURIIs, u"dirchild-imm", immdircap)
|
||||
d.addErrback(self.explain_web_error)
|
||||
return d
|
||||
|
||||
def test_POST_NEWDIRURL_immutable_bad(self):
|
||||
(newkids, filecap1, filecap2, filecap3,
|
||||
dircap) = self._create_initial_children()
|
||||
d = self.shouldFail2(error.Error, "test_POST_NEWDIRURL_immutable_bad",
|
||||
"400 Bad Request",
|
||||
"a mkdir-immutable operation was given a child that was not itself immutable",
|
||||
self.POST2,
|
||||
self.public_url + "/foo/newdir?t=mkdir-immutable",
|
||||
simplejson.dumps(newkids))
|
||||
return d
|
||||
|
||||
def test_PUT_NEWDIRURL_exists(self):
|
||||
d = self.PUT(self.public_url + "/foo/sub?t=mkdir", "")
|
||||
d.addCallback(lambda res:
|
||||
|
@ -1898,8 +1939,9 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, unittest.TestCase):
|
|||
|
||||
def test_POST_mkdir_initial_children(self):
|
||||
newkids, filecap1, ign, ign, ign = self._create_initial_children()
|
||||
d = self.POST(self.public_url + "/foo", t="mkdir-with-children",
|
||||
name="newdir", children=simplejson.dumps(newkids))
|
||||
d = self.POST2(self.public_url +
|
||||
"/foo?t=mkdir-with-children&name=newdir",
|
||||
simplejson.dumps(newkids))
|
||||
d.addCallback(lambda res:
|
||||
self.failUnlessNodeHasChild(self._foo_node, u"newdir"))
|
||||
d.addCallback(lambda res: self._foo_node.get(u"newdir"))
|
||||
|
@ -1908,6 +1950,33 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, unittest.TestCase):
|
|||
d.addCallback(self.failUnlessChildURIIs, u"child-imm", filecap1)
|
||||
return d
|
||||
|
||||
def test_POST_mkdir_immutable(self):
|
||||
(newkids, filecap1, immdircap) = self._create_immutable_children()
|
||||
d = self.POST2(self.public_url +
|
||||
"/foo?t=mkdir-immutable&name=newdir",
|
||||
simplejson.dumps(newkids))
|
||||
d.addCallback(lambda res:
|
||||
self.failUnlessNodeHasChild(self._foo_node, u"newdir"))
|
||||
d.addCallback(lambda res: self._foo_node.get(u"newdir"))
|
||||
d.addCallback(self.failUnlessNodeKeysAre, newkids.keys())
|
||||
d.addCallback(lambda res: self._foo_node.get(u"newdir"))
|
||||
d.addCallback(self.failUnlessChildURIIs, u"child-imm", filecap1)
|
||||
d.addCallback(lambda res: self._foo_node.get(u"newdir"))
|
||||
d.addCallback(self.failUnlessChildURIIs, u"dirchild-imm", immdircap)
|
||||
return d
|
||||
|
||||
def test_POST_mkdir_immutable_bad(self):
|
||||
(newkids, filecap1, filecap2, filecap3,
|
||||
dircap) = self._create_initial_children()
|
||||
d = self.shouldFail2(error.Error, "test_POST_mkdir_immutable_bad",
|
||||
"400 Bad Request",
|
||||
"a mkdir-immutable operation was given a child that was not itself immutable",
|
||||
self.POST2,
|
||||
self.public_url +
|
||||
"/foo?t=mkdir-immutable&name=newdir",
|
||||
simplejson.dumps(newkids))
|
||||
return d
|
||||
|
||||
def test_POST_mkdir_2(self):
|
||||
d = self.POST(self.public_url + "/foo/newdir?t=mkdir", "")
|
||||
d.addCallback(lambda res:
|
||||
|
@ -1957,11 +2026,23 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, unittest.TestCase):
|
|||
}
|
||||
return newkids, filecap1, filecap2, filecap3, dircap
|
||||
|
||||
def _create_immutable_children(self):
|
||||
contents, n, filecap1 = self.makefile(12)
|
||||
md1 = {"metakey1": "metavalue1"}
|
||||
tnode = create_chk_filenode("immutable directory contents\n"*10)
|
||||
dnode = DirectoryNode(tnode, None, None)
|
||||
assert not dnode.is_mutable()
|
||||
immdircap = dnode.get_uri()
|
||||
newkids = {u"child-imm": ["filenode", {"ro_uri": filecap1,
|
||||
"metadata": md1, }],
|
||||
u"dirchild-imm": ["dirnode", {"ro_uri": immdircap}],
|
||||
}
|
||||
return newkids, filecap1, immdircap
|
||||
|
||||
def test_POST_mkdir_no_parentdir_initial_children(self):
|
||||
(newkids, filecap1, filecap2, filecap3,
|
||||
dircap) = self._create_initial_children()
|
||||
d = self.POST("/uri?t=mkdir-with-children",
|
||||
children=simplejson.dumps(newkids))
|
||||
d = self.POST2("/uri?t=mkdir-with-children", simplejson.dumps(newkids))
|
||||
def _after_mkdir(res):
|
||||
self.failUnless(res.startswith("URI:DIR"), res)
|
||||
n = self.s.create_node_from_uri(res)
|
||||
|
@ -1989,8 +2070,8 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, unittest.TestCase):
|
|||
400, "Bad Request",
|
||||
"t=mkdir does not accept children=, "
|
||||
"try t=mkdir-with-children instead",
|
||||
self.POST, "/uri?t=mkdir", # without children
|
||||
children=simplejson.dumps(newkids))
|
||||
self.POST2, "/uri?t=mkdir", # without children
|
||||
simplejson.dumps(newkids))
|
||||
return d
|
||||
|
||||
def test_POST_noparent_bad(self):
|
||||
|
@ -2000,6 +2081,34 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, unittest.TestCase):
|
|||
self.POST, "/uri?t=bogus")
|
||||
return d
|
||||
|
||||
def test_POST_mkdir_no_parentdir_immutable(self):
|
||||
(newkids, filecap1, immdircap) = self._create_immutable_children()
|
||||
d = self.POST2("/uri?t=mkdir-immutable", simplejson.dumps(newkids))
|
||||
def _after_mkdir(res):
|
||||
self.failUnless(res.startswith("URI:DIR"), res)
|
||||
n = self.s.create_node_from_uri(res)
|
||||
d2 = self.failUnlessNodeKeysAre(n, newkids.keys())
|
||||
d2.addCallback(lambda ign:
|
||||
self.failUnlessChildURIIs(n, u"child-imm", filecap1))
|
||||
d2.addCallback(lambda ign:
|
||||
self.failUnlessChildURIIs(n, u"dirchild-imm",
|
||||
immdircap))
|
||||
return d2
|
||||
d.addCallback(_after_mkdir)
|
||||
return d
|
||||
|
||||
def test_POST_mkdir_no_parentdir_immutable_bad(self):
|
||||
(newkids, filecap1, filecap2, filecap3,
|
||||
dircap) = self._create_initial_children()
|
||||
d = self.shouldFail2(error.Error,
|
||||
"test_POST_mkdir_no_parentdir_immutable_bad",
|
||||
"400 Bad Request",
|
||||
"a mkdir-immutable operation was given a child that was not itself immutable",
|
||||
self.POST2,
|
||||
"/uri?t=mkdir-immutable",
|
||||
simplejson.dumps(newkids))
|
||||
return d
|
||||
|
||||
def test_welcome_page_mkdir_button(self):
|
||||
# Fetch the welcome page.
|
||||
d = self.GET("/")
|
||||
|
|
|
@ -7,7 +7,8 @@ from nevow import loaders, appserver
|
|||
from nevow.inevow import IRequest
|
||||
from nevow.util import resource_filename
|
||||
from allmydata.interfaces import ExistingChildError, NoSuchChildError, \
|
||||
FileTooLargeError, NotEnoughSharesError, NoSharesError
|
||||
FileTooLargeError, NotEnoughSharesError, NoSharesError, \
|
||||
NotDeepImmutableError
|
||||
from allmydata.mutable.common import UnrecoverableFileError
|
||||
from allmydata.util import abbreviate # TODO: consolidate
|
||||
|
||||
|
@ -57,8 +58,9 @@ def get_arg(ctx_or_req, argname, default=None, multiple=False):
|
|||
def convert_children_json(nodemaker, children_json):
|
||||
"""I convert the JSON output of GET?t=json into the dict-of-nodes input
|
||||
to both dirnode.create_subdirectory() and
|
||||
client.create_directory(initial_children=)."""
|
||||
initial_children = {}
|
||||
client.create_directory(initial_children=). This is used by
|
||||
t=mkdir-with-children and t=mkdir-immutable"""
|
||||
children = {}
|
||||
if children_json:
|
||||
data = simplejson.loads(children_json)
|
||||
for (name, (ctype, propdict)) in data.iteritems():
|
||||
|
@ -71,8 +73,8 @@ def convert_children_json(nodemaker, children_json):
|
|||
readcap = str(readcap)
|
||||
metadata = propdict.get("metadata", {})
|
||||
childnode = nodemaker.create_from_cap(writecap, readcap)
|
||||
initial_children[name] = (childnode, metadata)
|
||||
return initial_children
|
||||
children[name] = (childnode, metadata)
|
||||
return children
|
||||
|
||||
def abbreviate_time(data):
|
||||
# 1.23s, 790ms, 132us
|
||||
|
@ -176,6 +178,10 @@ def humanize_failure(f):
|
|||
"failure, or disk corruption. You should perform a filecheck on "
|
||||
"this object to learn more.")
|
||||
return (t, http.GONE)
|
||||
if f.check(NotDeepImmutableError):
|
||||
t = ("NotDeepImmutableError: a mkdir-immutable operation was given "
|
||||
"a child that was not itself immutable: %s" % (f.value,))
|
||||
return (t, http.BAD_REQUEST)
|
||||
if f.check(WebError):
|
||||
return (f.value.text, f.value.code)
|
||||
if f.check(FileTooLargeError):
|
||||
|
|
|
@ -94,15 +94,21 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
|
|||
if DEBUG: print " terminal"
|
||||
# terminal node
|
||||
if (method,t) in [ ("POST","mkdir"), ("PUT","mkdir"),
|
||||
("POST", "mkdir-with-children") ]:
|
||||
("POST", "mkdir-with-children"),
|
||||
("POST", "mkdir-immutable") ]:
|
||||
if DEBUG: print " making final directory"
|
||||
# final directory
|
||||
kids = {}
|
||||
if (method,t) == ("POST", "mkdir-with-children"):
|
||||
kids_json = get_arg(req, "children", "")
|
||||
if t in ("mkdir-with-children", "mkdir-immutable"):
|
||||
req.content.seek(0)
|
||||
kids_json = req.content.read()
|
||||
kids = convert_children_json(self.client.nodemaker,
|
||||
kids_json)
|
||||
d = self.node.create_subdirectory(name, kids)
|
||||
mutable = True
|
||||
if t == "mkdir-immutable":
|
||||
mutable = False
|
||||
d = self.node.create_subdirectory(name, kids,
|
||||
mutable=mutable)
|
||||
d.addCallback(make_handler_for,
|
||||
self.client, self.node, name)
|
||||
return d
|
||||
|
@ -186,6 +192,8 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
|
|||
d = self._POST_mkdir(req)
|
||||
elif t == "mkdir-with-children":
|
||||
d = self._POST_mkdir_with_children(req)
|
||||
elif t == "mkdir-immutable":
|
||||
d = self._POST_mkdir_immutable(req)
|
||||
elif t == "mkdir-p":
|
||||
# TODO: docs, tests
|
||||
d = self._POST_mkdir_p(req)
|
||||
|
@ -242,12 +250,28 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
|
|||
return defer.succeed(self.node.get_uri()) # TODO: urlencode
|
||||
name = name.decode("utf-8")
|
||||
replace = boolean_of_arg(get_arg(req, "replace", "true"))
|
||||
kids_json = get_arg(req, "children", "")
|
||||
req.content.seek(0)
|
||||
kids_json = req.content.read()
|
||||
kids = convert_children_json(self.client.nodemaker, kids_json)
|
||||
d = self.node.create_subdirectory(name, kids, overwrite=replace)
|
||||
d.addCallback(lambda child: child.get_uri()) # TODO: urlencode
|
||||
return d
|
||||
|
||||
def _POST_mkdir_immutable(self, req):
|
||||
name = get_arg(req, "name", "")
|
||||
if not name:
|
||||
# our job is done, it was handled by the code in got_child
|
||||
# which created the final directory (i.e. us)
|
||||
return defer.succeed(self.node.get_uri()) # TODO: urlencode
|
||||
name = name.decode("utf-8")
|
||||
replace = boolean_of_arg(get_arg(req, "replace", "true"))
|
||||
req.content.seek(0)
|
||||
kids_json = req.content.read()
|
||||
kids = convert_children_json(self.client.nodemaker, kids_json)
|
||||
d = self.node.create_subdirectory(name, kids, mutable=False)
|
||||
d.addCallback(lambda child: child.get_uri()) # TODO: urlencode
|
||||
return d
|
||||
|
||||
def _POST_mkdir_p(self, req):
|
||||
path = get_arg(req, "path")
|
||||
if not path:
|
||||
|
|
|
@ -73,6 +73,9 @@ class URIHandler(RenderMixin, rend.Page):
|
|||
elif t == "mkdir-with-children":
|
||||
return unlinked.POSTUnlinkedCreateDirectoryWithChildren(req,
|
||||
self.client)
|
||||
elif t == "mkdir-immutable":
|
||||
return unlinked.POSTUnlinkedCreateImmutableDirectory(req,
|
||||
self.client)
|
||||
errmsg = ("/uri accepts only PUT, PUT?t=mkdir, POST?t=upload, "
|
||||
"and POST?t=mkdir")
|
||||
raise WebError(errmsg, http.BAD_REQUEST)
|
||||
|
|
|
@ -91,8 +91,9 @@ def POSTUnlinkedSSK(req, client):
|
|||
|
||||
def POSTUnlinkedCreateDirectory(req, client):
|
||||
# "POST /uri?t=mkdir", to create an unlinked directory.
|
||||
kids_json = get_arg(req, "children", None)
|
||||
if kids_json is not None:
|
||||
req.content.seek(0)
|
||||
kids_json = req.content.read()
|
||||
if kids_json:
|
||||
raise WebError("t=mkdir does not accept children=, "
|
||||
"try t=mkdir-with-children instead",
|
||||
http.BAD_REQUEST)
|
||||
|
@ -112,7 +113,8 @@ def POSTUnlinkedCreateDirectory(req, client):
|
|||
|
||||
def POSTUnlinkedCreateDirectoryWithChildren(req, client):
|
||||
# "POST /uri?t=mkdir", to create an unlinked directory.
|
||||
kids_json = get_arg(req, "children", "")
|
||||
req.content.seek(0)
|
||||
kids_json = req.content.read()
|
||||
kids = convert_children_json(client.nodemaker, kids_json)
|
||||
d = client.create_dirnode(initial_children=kids)
|
||||
redirect = get_arg(req, "redirect_to_result", "false")
|
||||
|
@ -128,3 +130,21 @@ def POSTUnlinkedCreateDirectoryWithChildren(req, client):
|
|||
d.addCallback(lambda dirnode: dirnode.get_uri())
|
||||
return d
|
||||
|
||||
def POSTUnlinkedCreateImmutableDirectory(req, client):
|
||||
# "POST /uri?t=mkdir", to create an unlinked directory.
|
||||
req.content.seek(0)
|
||||
kids_json = req.content.read()
|
||||
kids = convert_children_json(client.nodemaker, kids_json)
|
||||
d = client.create_immutable_dirnode(kids)
|
||||
redirect = get_arg(req, "redirect_to_result", "false")
|
||||
if boolean_of_arg(redirect):
|
||||
def _then_redir(res):
|
||||
new_url = "uri/" + urllib.quote(res.get_uri())
|
||||
req.setResponseCode(http.SEE_OTHER) # 303
|
||||
req.setHeader('location', new_url)
|
||||
req.finish()
|
||||
return ''
|
||||
d.addCallback(_then_redir)
|
||||
else:
|
||||
d.addCallback(lambda dirnode: dirnode.get_uri())
|
||||
return d
|
||||
|
|
Loading…
Reference in New Issue