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:
Brian Warner 2009-11-17 23:09:00 -08:00
parent d2426ea3bd
commit f85690697a
13 changed files with 296 additions and 65 deletions

View File

@ -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) ===

View File

@ -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)

View File

@ -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())
d = self._nodemaker.create_new_mutable_directory(initial_children)
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)

View File

@ -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.

View File

@ -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)

View File

@ -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):

View File

@ -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,8 +178,32 @@ 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"
self.set_up_grid()
@ -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)

View File

@ -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 = "\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)
body = ""
headers = {}
if fields:
body = "\r\n".join(form) + "\r\n"
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

View File

@ -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 = "\r\n".join(form) + "\r\n"
headers = {"content-type": "multipart/form-data; boundary=%s" % sepbase,
}
body = ""
headers = {}
if fields:
body = "\r\n".join(form) + "\r\n"
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("/")

View File

@ -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):

View File

@ -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:

View File

@ -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)

View File

@ -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