Merge lp:~jamesh/storage-provider-webdav/create-folder into lp:storage-provider-webdav/devel
- create-folder
- Merge into devel
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 |
Related bugs: |
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.
unity-api-1-bot (unity-api-1-bot) wrote : | # |
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.
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
unity-api-1-bot (unity-api-1-bot) : | # |
Preview Diff
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; |
PASSED: Continuous integration, rev:17 /jenkins. canonical. com/unity- api-1/job/ lp-storage- provider- webdav- ci/23/ /jenkins. canonical. com/unity- api-1/job/ build/1043 /jenkins. canonical. com/unity- api-1/job/ build-0- fetch/1050 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= vivid+overlay/ 841 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= vivid+overlay/ 841/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 841 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 841/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= yakkety/ 841 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= yakkety/ 841/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= vivid+overlay/ 841 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= vivid+overlay/ 841/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 841 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 841/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= yakkety/ 841 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= yakkety/ 841/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= vivid+overlay/ 841 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= vivid+overlay/ 841/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 841 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 841/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= yakkety/ 841 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= yakkety/ 841/artifact/ output/ *zip*/output. zip
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild: /jenkins. canonical. com/unity- api-1/job/ lp-storage- provider- webdav- ci/23/rebuild
https:/