Merge lp:~jamesh/storage-provider-webdav/create-folder into lp:storage-provider-webdav/devel

Proposed by James Henstridge
Status: Merged
Approved by: James Henstridge
Approved revision: 17
Merged at revision: 15
Proposed branch: lp:~jamesh/storage-provider-webdav/create-folder
Merge into: lp:storage-provider-webdav/devel
Diff against target: 262 lines (+213/-0)
5 files modified
src/CMakeLists.txt (+1/-0)
src/CreateFolderHandler.cpp (+55/-0)
src/CreateFolderHandler.h (+35/-0)
src/DavProvider.cpp (+3/-0)
tests/davprovider/davprovider_test.cpp (+119/-0)
To merge this branch: bzr merge lp:~jamesh/storage-provider-webdav/create-folder
Reviewer Review Type Date Requested Status
unity-api-1-bot continuous-integration Approve
Michi Henning (community) Approve
Review via email: mp+310516@code.launchpad.net

Commit message

Implement the create_folder() D-Bus method.

Description of the change

Implement the create_folder() D-Bus method.

This is implemented in terms of an MKCOL HTTP request, followed by a PROPFIND on the item to retrieve its metadata if we get a "201 Created" response to the first request.

The test for the non-error case is mostly disabled due to missing ETag problems with storage-framework 0.1. It will need to be reenabled when porting to 0.2.

To post a comment you must log in.
Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :

PASSED: Continuous integration, rev:17
https://jenkins.canonical.com/unity-api-1/job/lp-storage-provider-webdav-ci/23/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build/1043
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/1050
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/841
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/841/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/841
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/841/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/841
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/841/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/841
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/841/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/841
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/841/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/841
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/841/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/841
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/841/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/841
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/841/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/841
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/841/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/unity-api-1/job/lp-storage-provider-webdav-ci/23/rebuild

review: Approve (continuous-integration)
Revision history for this message
Michi Henning (michihenning) wrote :

Looks good! (Error handling needs work, but we've decided to that in one go later, rather than piecemeal.)

One other test that would be interesting is to create a folder after deleting its parent folder, but I don't think that's essential right now.

review: Approve
Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :

FAILED: Autolanding.
More details in the following jenkins job:
https://jenkins.canonical.com/unity-api-1/job/lp-storage-provider-webdav-autoland/15/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build/1053/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/1060
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/849
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/849/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/849
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/849/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/849
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/849/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/849
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/849/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/849
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/849/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/849
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/849/artifact/output/*zip*/output.zip
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/849/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/849
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/849/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/849
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/849/artifact/output/*zip*/output.zip

review: Needs Fixing (continuous-integration)
Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :

FAILED: Autolanding.
More details in the following jenkins job:
https://jenkins.canonical.com/unity-api-1/job/lp-storage-provider-webdav-autoland/16/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build/1054/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/1061
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/850
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/850/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/850
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/850/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/850
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/850/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/850
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/850/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/850
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/850/artifact/output/*zip*/output.zip
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/850/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/850
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/850/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/850
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/850/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/850
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/850/artifact/output/*zip*/output.zip

review: Needs Fixing (continuous-integration)
Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :

FAILED: Autolanding.
More details in the following jenkins job:
https://jenkins.canonical.com/unity-api-1/job/lp-storage-provider-webdav-autoland/17/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build/1056/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/1063
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/852
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/852/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/852
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/852/artifact/output/*zip*/output.zip
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/852/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/852
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/852/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/852
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/852/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/852
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/852/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/852
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/852/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/852
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/852/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/852
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/852/artifact/output/*zip*/output.zip

review: Needs Fixing (continuous-integration)
Revision history for this message
unity-api-1-bot (unity-api-1-bot) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/CMakeLists.txt'
2--- src/CMakeLists.txt 2016-11-08 08:11:31 +0000
3+++ src/CMakeLists.txt 2016-11-10 10:21:24 +0000
4@@ -5,6 +5,7 @@
5 DavUploadJob.cpp
6 MultiStatusParser.cpp
7 item_id.cpp
8+ CreateFolderHandler.cpp
9 PropFindHandler.cpp
10 ListHandler.cpp
11 LookupHandler.cpp
12
13=== added file 'src/CreateFolderHandler.cpp'
14--- src/CreateFolderHandler.cpp 1970-01-01 00:00:00 +0000
15+++ src/CreateFolderHandler.cpp 2016-11-10 10:21:24 +0000
16@@ -0,0 +1,55 @@
17+#include "CreateFolderHandler.h"
18+#include "RetrieveMetadataHandler.h"
19+#include "item_id.h"
20+
21+using namespace std;
22+using namespace unity::storage::provider;
23+
24+CreateFolderHandler::CreateFolderHandler(DavProvider const& provider,
25+ string const& parent_id,
26+ string const& name,
27+ Context const& ctx)
28+ : provider_(provider), item_id_(make_child_id(parent_id, name) + '/'),
29+ context_(ctx)
30+{
31+ QUrl const base_url = provider.base_url(ctx);
32+ QNetworkRequest request(id_to_url(item_id_, base_url));
33+ mkcol_.reset(provider.send_request(request, QByteArrayLiteral("MKCOL"),
34+ nullptr, ctx));
35+ connect(mkcol_.get(), &QNetworkReply::finished,
36+ this, &CreateFolderHandler::onFinished);
37+}
38+
39+CreateFolderHandler::~CreateFolderHandler() = default;
40+
41+boost::future<Item> CreateFolderHandler::get_future()
42+{
43+ return promise_.get_future();
44+}
45+
46+void CreateFolderHandler::onFinished()
47+{
48+ auto status = mkcol_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
49+
50+ if (status != 201)
51+ {
52+ promise_.set_exception(RemoteCommsException("Error from MKCOL: " + to_string(status)));
53+ deleteLater();
54+ return;
55+ }
56+
57+ metadata_.reset(
58+ new RetrieveMetadataHandler(
59+ provider_, item_id_, context_,
60+ [this](Item const& item, boost::exception_ptr const& error) {
61+ if (error)
62+ {
63+ promise_.set_exception(error);
64+ }
65+ else
66+ {
67+ promise_.set_value(item);
68+ }
69+ deleteLater();
70+ }));
71+}
72
73=== added file 'src/CreateFolderHandler.h'
74--- src/CreateFolderHandler.h 1970-01-01 00:00:00 +0000
75+++ src/CreateFolderHandler.h 2016-11-10 10:21:24 +0000
76@@ -0,0 +1,35 @@
77+#pragma once
78+
79+#include <QBuffer>
80+#include <QObject>
81+#include <QNetworkReply>
82+#include <unity/storage/provider/ProviderBase.h>
83+
84+#include <memory>
85+
86+class DavProvider;
87+class RetrieveMetadataHandler;
88+
89+class CreateFolderHandler : public QObject {
90+ Q_OBJECT
91+public:
92+ CreateFolderHandler(DavProvider const& provider,
93+ std::string const& parent_id, std::string const& name,
94+ unity::storage::provider::Context const& ctx);
95+ ~CreateFolderHandler();
96+
97+ boost::future<unity::storage::provider::Item> get_future();
98+
99+private Q_SLOTS:
100+ void onFinished();
101+
102+private:
103+ boost::promise<unity::storage::provider::Item> promise_;
104+
105+ DavProvider const& provider_;
106+ std::string const item_id_;
107+ unity::storage::provider::Context const context_;
108+
109+ std::unique_ptr<QNetworkReply> mkcol_;
110+ std::unique_ptr<RetrieveMetadataHandler> metadata_;
111+};
112
113=== modified file 'src/DavProvider.cpp'
114--- src/DavProvider.cpp 2016-11-08 08:11:31 +0000
115+++ src/DavProvider.cpp 2016-11-10 10:21:24 +0000
116@@ -6,6 +6,7 @@
117 #include "MetadataHandler.h"
118 #include "DavDownloadJob.h"
119 #include "DavUploadJob.h"
120+#include "CreateFolderHandler.h"
121 #include "item_id.h"
122
123 #include <QDateTime>
124@@ -62,6 +63,8 @@
125 boost::future<Item> DavProvider::create_folder(
126 string const& parent_id, string const& name, Context const& ctx)
127 {
128+ auto handler = new CreateFolderHandler(*this, parent_id, name, ctx);
129+ return handler->get_future();
130 }
131
132 boost::future<unique_ptr<UploadJob>> DavProvider::create_file(
133
134=== modified file 'tests/davprovider/davprovider_test.cpp'
135--- tests/davprovider/davprovider_test.cpp 2016-11-10 07:20:56 +0000
136+++ tests/davprovider/davprovider_test.cpp 2016-11-10 10:21:24 +0000
137@@ -309,6 +309,125 @@
138 EXPECT_EQ(ItemType::file, item->type());
139 }
140
141+TEST_F(DavProviderTests, create_folder)
142+{
143+ auto account = get_client();
144+
145+ shared_ptr<Root> root;
146+ {
147+ QFutureWatcher<QVector<shared_ptr<Root>>> watcher;
148+ QSignalSpy spy(&watcher, &decltype(watcher)::finished);
149+ watcher.setFuture(account->roots());
150+ if (spy.count() == 0)
151+ {
152+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
153+ }
154+ auto roots = watcher.result();
155+ ASSERT_EQ(1, roots.size());
156+ root = roots[0];
157+ }
158+
159+ // FIXME: the test webdav server returns no ETag for folders, so
160+ // the client throws away the create_folder() response. We can
161+ // reenable this when porting to storage-framework 0.2.
162+ return;
163+
164+ shared_ptr<Folder> folder;
165+ {
166+ QFutureWatcher<shared_ptr<Folder>> watcher;
167+ QSignalSpy spy(&watcher, &decltype(watcher)::finished);
168+ watcher.setFuture(root->create_folder("folder"));
169+ if (spy.count() == 0)
170+ {
171+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
172+ }
173+ folder = watcher.result();
174+ }
175+
176+ EXPECT_EQ("folder/", folder->native_identity());
177+ EXPECT_EQ(".", folder->parent_ids().at(0));
178+ EXPECT_EQ("folder", folder->name());
179+ EXPECT_EQ(ItemType::folder, folder->type());
180+}
181+
182+TEST_F(DavProviderTests, create_folder_overwrite_file)
183+{
184+ auto account = get_client();
185+ make_file("folder");
186+
187+ shared_ptr<Root> root;
188+ {
189+ QFutureWatcher<QVector<shared_ptr<Root>>> watcher;
190+ QSignalSpy spy(&watcher, &decltype(watcher)::finished);
191+ watcher.setFuture(account->roots());
192+ if (spy.count() == 0)
193+ {
194+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
195+ }
196+ auto roots = watcher.result();
197+ ASSERT_EQ(1, roots.size());
198+ root = roots[0];
199+ }
200+
201+ {
202+ QFutureWatcher<shared_ptr<Folder>> watcher;
203+ QSignalSpy spy(&watcher, &decltype(watcher)::finished);
204+ watcher.setFuture(root->create_folder("folder"));
205+ if (spy.count() == 0)
206+ {
207+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
208+ }
209+ try
210+ {
211+ watcher.result();
212+ FAIL();
213+ }
214+ catch (RemoteCommsException const& e)
215+ {
216+ EXPECT_EQ("Error from MKCOL: 405", e.error_message());
217+ }
218+ }
219+}
220+
221+TEST_F(DavProviderTests, create_folder_overwrite_folder)
222+{
223+ auto account = get_client();
224+ ASSERT_EQ(0, mkdir(local_file("folder").c_str(), 0755));
225+
226+ shared_ptr<Root> root;
227+ {
228+ QFutureWatcher<QVector<shared_ptr<Root>>> watcher;
229+ QSignalSpy spy(&watcher, &decltype(watcher)::finished);
230+ watcher.setFuture(account->roots());
231+ if (spy.count() == 0)
232+ {
233+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
234+ }
235+ auto roots = watcher.result();
236+ ASSERT_EQ(1, roots.size());
237+ root = roots[0];
238+ }
239+
240+ {
241+ QFutureWatcher<shared_ptr<Folder>> watcher;
242+ QSignalSpy spy(&watcher, &decltype(watcher)::finished);
243+ watcher.setFuture(root->create_folder("folder"));
244+ if (spy.count() == 0)
245+ {
246+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
247+ }
248+ try
249+ {
250+ watcher.result();
251+ FAIL();
252+ }
253+ catch (RemoteCommsException const& e)
254+ {
255+ EXPECT_EQ("Error from MKCOL: 405", e.error_message());
256+ }
257+ }
258+}
259+
260 TEST_F(DavProviderTests, create_file)
261 {
262 int const segments = 50;

Subscribers

People subscribed via source and target branches