t=upload
name=childname (optional)
file=newfile
+
This instructs the node to upload a file into the given directory. We need
this because forms are the only way for a web browser to upload a file
(browsers do not know how to do PUT or DELETE). The file's contents and the
used to upload a single file at a time. To avoid confusion, name= is not
allowed to contain a slash (a 400 Bad Request error will result).
+
POST $URL
t=upload
name=childname (optional)
- mutable="on"
+ mutable="true"
file=newfile
+
This instructs the node to upload a file into the given directory, using a
- mutable file (SSK) rather than the usual immutable file (CHK).
+ mutable file (SSK) rather than the usual immutable file (CHK). As a result,
+ further operations to the same $URL will not cause the identity of the file
+ to change.
+
+
+ POST $URL
+ t=overwrite
+ file=newfile
+
+ This is used to replace the existing (mutable) file's contents with new
+ ones. It may only be used when $URL refers to a mutable file, as created by
+ POST $URL?t=upload&mutable=true, or PUT /uri?t=mutable . The name
+ associated with the uploaded file is ignored. TODO: rethink this, it's kind
+ of weird.
+
POST $URL
t=mkdir
This instructs the node to create a new empty directory. The name of the
new child directory will be included in the form's arguments.
+
POST $URL
t=uri
name=childname
like the PUT $URL?t=uri method). The name and URI of the new child
will be included in the form's arguments.
+
POST $URL
t=delete
name=childname
This instructs the node to delete a file from the given directory. The name
of the child to be deleted will be included in the form's arguments.
+
POST $URL
t=rename
from_name=oldchildname
import re, os.path, urllib
+import simplejson
from twisted.application import service
from twisted.trial import unittest
from twisted.internet import defer
from allmydata import webish, interfaces, provisioning
from allmydata.util import fileutil
from allmydata.test.common import NonGridDirectoryNode, FakeCHKFileNode, FakeMutableFileNode, create_chk_filenode
-from allmydata.interfaces import IURI, INewDirectoryURI, IReadonlyNewDirectoryURI, IFileURI, IMutableFileURI
+from allmydata.interfaces import IURI, INewDirectoryURI, IReadonlyNewDirectoryURI, IFileURI, IMutableFileURI, IMutableFileNode
# create a fake uploader/downloader, and a couple of fake dirnodes, then
# create a webserver that works against them
def failUnlessIsBarDotTxt(self, res):
self.failUnlessEqual(res, self.BAR_CONTENTS)
- def worlds_cheapest_json_decoder(self, json):
- # don't write tests that use 'true' or 'false' as filenames
- json = re.sub('false', 'False', json)
- json = re.sub('true', 'True', json)
- json = re.sub(r'\\/', '/', json)
- return eval(json)
-
def failUnlessIsBarJSON(self, res):
- data = self.worlds_cheapest_json_decoder(res)
+ data = simplejson.loads(res)
self.failUnless(isinstance(data, list))
self.failUnlessEqual(data[0], "filenode")
self.failUnless(isinstance(data[1], dict))
self.failUnlessEqual(data[1]["size"], len(self.BAR_CONTENTS))
def failUnlessIsFooJSON(self, res):
- data = self.worlds_cheapest_json_decoder(res)
+ data = simplejson.loads(res)
self.failUnless(isinstance(data, list))
self.failUnlessEqual(data[0], "dirnode", res)
self.failUnless(isinstance(data[1], dict))
self.NEWFILE_CONTENTS))
return d
+ def test_POST_upload_mutable(self):
+ # this creates a mutable file
+ d = self.POST(self.public_url + "/foo", t="upload", mutable="true",
+ file=("new.txt", self.NEWFILE_CONTENTS))
+ fn = self._foo_node
+ d.addCallback(self.failUnlessURIMatchesChild, fn, "new.txt")
+ d.addCallback(lambda res:
+ self.failUnlessChildContentsAre(fn, "new.txt",
+ self.NEWFILE_CONTENTS))
+ d.addCallback(lambda res: self._foo_node.get("new.txt"))
+ def _got(newnode):
+ self.failUnless(IMutableFileNode.providedBy(newnode))
+ self.failUnless(newnode.is_mutable())
+ self.failIf(newnode.is_readonly())
+ self._mutable_uri = newnode.get_uri()
+ d.addCallback(_got)
+
+ # now upload it again and make sure that the URI doesn't change
+ NEWER_CONTENTS = self.NEWFILE_CONTENTS + "newer\n"
+ d.addCallback(lambda res:
+ self.POST(self.public_url + "/foo", t="upload",
+ mutable="true",
+ file=("new.txt", NEWER_CONTENTS)))
+ d.addCallback(self.failUnlessURIMatchesChild, fn, "new.txt")
+ d.addCallback(lambda res:
+ self.failUnlessChildContentsAre(fn, "new.txt",
+ NEWER_CONTENTS))
+ d.addCallback(lambda res: self._foo_node.get("new.txt"))
+ def _got2(newnode):
+ self.failUnless(IMutableFileNode.providedBy(newnode))
+ self.failUnless(newnode.is_mutable())
+ self.failIf(newnode.is_readonly())
+ self.failUnlessEqual(self._mutable_uri, newnode.get_uri())
+ d.addCallback(_got2)
+
+ # also test t=overwrite while we're here
+ EVEN_NEWER_CONTENTS = NEWER_CONTENTS + "even newer\n"
+ d.addCallback(lambda res:
+ self.POST(self.public_url + "/foo/new.txt",
+ t="overwrite",
+ file=("new.txt", EVEN_NEWER_CONTENTS)))
+ d.addCallback(self.failUnlessURIMatchesChild, fn, "new.txt")
+ d.addCallback(lambda res:
+ self.failUnlessChildContentsAre(fn, "new.txt",
+ EVEN_NEWER_CONTENTS))
+ d.addCallback(lambda res: self._foo_node.get("new.txt"))
+ def _got3(newnode):
+ self.failUnless(IMutableFileNode.providedBy(newnode))
+ self.failUnless(newnode.is_mutable())
+ self.failIf(newnode.is_readonly())
+ self.failUnlessEqual(self._mutable_uri, newnode.get_uri())
+ d.addCallback(_got3)
+
+ return d
+
def test_POST_upload_replace(self):
d = self.POST(self.public_url + "/foo", t="upload",
file=("bar.txt", self.NEWFILE_CONTENTS))
return d
def failUnlessIsEmptyJSON(self, res):
- data = self.worlds_cheapest_json_decoder(res)
+ data = simplejson.loads(res)
self.failUnlessEqual(data[0], "dirnode", data)
self.failUnlessEqual(len(data[1]["children"]), 0)
data = contents.file.read()
uploadable = upload.FileHandle(contents.file)
d = self._check_replacement(name)
- d.addCallback(lambda res:
- IClient(ctx).create_mutable_file(data))
- def _uploaded(newnode):
- d1 = self._node.set_node(name, newnode)
- d1.addCallback(lambda res: newnode.get_uri())
- return d1
- d.addCallback(_uploaded)
+ d.addCallback(lambda res: self._node.has_child(name))
+ def _checked(present):
+ if present:
+ # modify the existing one instead of creating a new
+ # one
+ d2 = self._node.get(name)
+ def _got_newnode(newnode):
+ d3 = newnode.replace(data)
+ d3.addCallback(lambda res: newnode.get_uri())
+ return d3
+ d2.addCallback(_got_newnode)
+ else:
+ d2 = IClient(ctx).create_mutable_file(data)
+ def _uploaded(newnode):
+ d1 = self._node.set_node(name, newnode)
+ d1.addCallback(lambda res: newnode.get_uri())
+ return d1
+ d2.addCallback(_uploaded)
+ return d2
+ d.addCallback(_checked)
else:
contents = req.fields["file"]
name = name or contents.filename
# SDMF: files are small, and we can only upload data.
contents.file.seek(0)
data = contents.file.read()
- d = self._node.get(name)
+ # TODO: 'name' handling needs review
+ d = defer.succeed(self._node)
def _got_child(child_node):
child_node.replace(data)
return child_node.get_uri()