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 | 5 | DavUploadJob.cpp | 5 | DavUploadJob.cpp |
6 | 6 | MultiStatusParser.cpp | 6 | MultiStatusParser.cpp |
7 | 7 | item_id.cpp | 7 | item_id.cpp |
8 | 8 | CreateFolderHandler.cpp | ||
9 | 8 | PropFindHandler.cpp | 9 | PropFindHandler.cpp |
10 | 9 | ListHandler.cpp | 10 | ListHandler.cpp |
11 | 10 | LookupHandler.cpp | 11 | LookupHandler.cpp |
12 | 11 | 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 | 1 | #include "CreateFolderHandler.h" | ||
18 | 2 | #include "RetrieveMetadataHandler.h" | ||
19 | 3 | #include "item_id.h" | ||
20 | 4 | |||
21 | 5 | using namespace std; | ||
22 | 6 | using namespace unity::storage::provider; | ||
23 | 7 | |||
24 | 8 | CreateFolderHandler::CreateFolderHandler(DavProvider const& provider, | ||
25 | 9 | string const& parent_id, | ||
26 | 10 | string const& name, | ||
27 | 11 | Context const& ctx) | ||
28 | 12 | : provider_(provider), item_id_(make_child_id(parent_id, name) + '/'), | ||
29 | 13 | context_(ctx) | ||
30 | 14 | { | ||
31 | 15 | QUrl const base_url = provider.base_url(ctx); | ||
32 | 16 | QNetworkRequest request(id_to_url(item_id_, base_url)); | ||
33 | 17 | mkcol_.reset(provider.send_request(request, QByteArrayLiteral("MKCOL"), | ||
34 | 18 | nullptr, ctx)); | ||
35 | 19 | connect(mkcol_.get(), &QNetworkReply::finished, | ||
36 | 20 | this, &CreateFolderHandler::onFinished); | ||
37 | 21 | } | ||
38 | 22 | |||
39 | 23 | CreateFolderHandler::~CreateFolderHandler() = default; | ||
40 | 24 | |||
41 | 25 | boost::future<Item> CreateFolderHandler::get_future() | ||
42 | 26 | { | ||
43 | 27 | return promise_.get_future(); | ||
44 | 28 | } | ||
45 | 29 | |||
46 | 30 | void CreateFolderHandler::onFinished() | ||
47 | 31 | { | ||
48 | 32 | auto status = mkcol_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); | ||
49 | 33 | |||
50 | 34 | if (status != 201) | ||
51 | 35 | { | ||
52 | 36 | promise_.set_exception(RemoteCommsException("Error from MKCOL: " + to_string(status))); | ||
53 | 37 | deleteLater(); | ||
54 | 38 | return; | ||
55 | 39 | } | ||
56 | 40 | |||
57 | 41 | metadata_.reset( | ||
58 | 42 | new RetrieveMetadataHandler( | ||
59 | 43 | provider_, item_id_, context_, | ||
60 | 44 | [this](Item const& item, boost::exception_ptr const& error) { | ||
61 | 45 | if (error) | ||
62 | 46 | { | ||
63 | 47 | promise_.set_exception(error); | ||
64 | 48 | } | ||
65 | 49 | else | ||
66 | 50 | { | ||
67 | 51 | promise_.set_value(item); | ||
68 | 52 | } | ||
69 | 53 | deleteLater(); | ||
70 | 54 | })); | ||
71 | 55 | } | ||
72 | 0 | 56 | ||
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 | 1 | #pragma once | ||
78 | 2 | |||
79 | 3 | #include <QBuffer> | ||
80 | 4 | #include <QObject> | ||
81 | 5 | #include <QNetworkReply> | ||
82 | 6 | #include <unity/storage/provider/ProviderBase.h> | ||
83 | 7 | |||
84 | 8 | #include <memory> | ||
85 | 9 | |||
86 | 10 | class DavProvider; | ||
87 | 11 | class RetrieveMetadataHandler; | ||
88 | 12 | |||
89 | 13 | class CreateFolderHandler : public QObject { | ||
90 | 14 | Q_OBJECT | ||
91 | 15 | public: | ||
92 | 16 | CreateFolderHandler(DavProvider const& provider, | ||
93 | 17 | std::string const& parent_id, std::string const& name, | ||
94 | 18 | unity::storage::provider::Context const& ctx); | ||
95 | 19 | ~CreateFolderHandler(); | ||
96 | 20 | |||
97 | 21 | boost::future<unity::storage::provider::Item> get_future(); | ||
98 | 22 | |||
99 | 23 | private Q_SLOTS: | ||
100 | 24 | void onFinished(); | ||
101 | 25 | |||
102 | 26 | private: | ||
103 | 27 | boost::promise<unity::storage::provider::Item> promise_; | ||
104 | 28 | |||
105 | 29 | DavProvider const& provider_; | ||
106 | 30 | std::string const item_id_; | ||
107 | 31 | unity::storage::provider::Context const context_; | ||
108 | 32 | |||
109 | 33 | std::unique_ptr<QNetworkReply> mkcol_; | ||
110 | 34 | std::unique_ptr<RetrieveMetadataHandler> metadata_; | ||
111 | 35 | }; | ||
112 | 0 | 36 | ||
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 | 6 | #include "MetadataHandler.h" | 6 | #include "MetadataHandler.h" |
118 | 7 | #include "DavDownloadJob.h" | 7 | #include "DavDownloadJob.h" |
119 | 8 | #include "DavUploadJob.h" | 8 | #include "DavUploadJob.h" |
120 | 9 | #include "CreateFolderHandler.h" | ||
121 | 9 | #include "item_id.h" | 10 | #include "item_id.h" |
122 | 10 | 11 | ||
123 | 11 | #include <QDateTime> | 12 | #include <QDateTime> |
124 | @@ -62,6 +63,8 @@ | |||
125 | 62 | boost::future<Item> DavProvider::create_folder( | 63 | boost::future<Item> DavProvider::create_folder( |
126 | 63 | string const& parent_id, string const& name, Context const& ctx) | 64 | string const& parent_id, string const& name, Context const& ctx) |
127 | 64 | { | 65 | { |
128 | 66 | auto handler = new CreateFolderHandler(*this, parent_id, name, ctx); | ||
129 | 67 | return handler->get_future(); | ||
130 | 65 | } | 68 | } |
131 | 66 | 69 | ||
132 | 67 | boost::future<unique_ptr<UploadJob>> DavProvider::create_file( | 70 | boost::future<unique_ptr<UploadJob>> DavProvider::create_file( |
133 | 68 | 71 | ||
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 | 309 | EXPECT_EQ(ItemType::file, item->type()); | 309 | EXPECT_EQ(ItemType::file, item->type()); |
139 | 310 | } | 310 | } |
140 | 311 | 311 | ||
141 | 312 | TEST_F(DavProviderTests, create_folder) | ||
142 | 313 | { | ||
143 | 314 | auto account = get_client(); | ||
144 | 315 | |||
145 | 316 | shared_ptr<Root> root; | ||
146 | 317 | { | ||
147 | 318 | QFutureWatcher<QVector<shared_ptr<Root>>> watcher; | ||
148 | 319 | QSignalSpy spy(&watcher, &decltype(watcher)::finished); | ||
149 | 320 | watcher.setFuture(account->roots()); | ||
150 | 321 | if (spy.count() == 0) | ||
151 | 322 | { | ||
152 | 323 | ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); | ||
153 | 324 | } | ||
154 | 325 | auto roots = watcher.result(); | ||
155 | 326 | ASSERT_EQ(1, roots.size()); | ||
156 | 327 | root = roots[0]; | ||
157 | 328 | } | ||
158 | 329 | |||
159 | 330 | // FIXME: the test webdav server returns no ETag for folders, so | ||
160 | 331 | // the client throws away the create_folder() response. We can | ||
161 | 332 | // reenable this when porting to storage-framework 0.2. | ||
162 | 333 | return; | ||
163 | 334 | |||
164 | 335 | shared_ptr<Folder> folder; | ||
165 | 336 | { | ||
166 | 337 | QFutureWatcher<shared_ptr<Folder>> watcher; | ||
167 | 338 | QSignalSpy spy(&watcher, &decltype(watcher)::finished); | ||
168 | 339 | watcher.setFuture(root->create_folder("folder")); | ||
169 | 340 | if (spy.count() == 0) | ||
170 | 341 | { | ||
171 | 342 | ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); | ||
172 | 343 | } | ||
173 | 344 | folder = watcher.result(); | ||
174 | 345 | } | ||
175 | 346 | |||
176 | 347 | EXPECT_EQ("folder/", folder->native_identity()); | ||
177 | 348 | EXPECT_EQ(".", folder->parent_ids().at(0)); | ||
178 | 349 | EXPECT_EQ("folder", folder->name()); | ||
179 | 350 | EXPECT_EQ(ItemType::folder, folder->type()); | ||
180 | 351 | } | ||
181 | 352 | |||
182 | 353 | TEST_F(DavProviderTests, create_folder_overwrite_file) | ||
183 | 354 | { | ||
184 | 355 | auto account = get_client(); | ||
185 | 356 | make_file("folder"); | ||
186 | 357 | |||
187 | 358 | shared_ptr<Root> root; | ||
188 | 359 | { | ||
189 | 360 | QFutureWatcher<QVector<shared_ptr<Root>>> watcher; | ||
190 | 361 | QSignalSpy spy(&watcher, &decltype(watcher)::finished); | ||
191 | 362 | watcher.setFuture(account->roots()); | ||
192 | 363 | if (spy.count() == 0) | ||
193 | 364 | { | ||
194 | 365 | ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); | ||
195 | 366 | } | ||
196 | 367 | auto roots = watcher.result(); | ||
197 | 368 | ASSERT_EQ(1, roots.size()); | ||
198 | 369 | root = roots[0]; | ||
199 | 370 | } | ||
200 | 371 | |||
201 | 372 | { | ||
202 | 373 | QFutureWatcher<shared_ptr<Folder>> watcher; | ||
203 | 374 | QSignalSpy spy(&watcher, &decltype(watcher)::finished); | ||
204 | 375 | watcher.setFuture(root->create_folder("folder")); | ||
205 | 376 | if (spy.count() == 0) | ||
206 | 377 | { | ||
207 | 378 | ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); | ||
208 | 379 | } | ||
209 | 380 | try | ||
210 | 381 | { | ||
211 | 382 | watcher.result(); | ||
212 | 383 | FAIL(); | ||
213 | 384 | } | ||
214 | 385 | catch (RemoteCommsException const& e) | ||
215 | 386 | { | ||
216 | 387 | EXPECT_EQ("Error from MKCOL: 405", e.error_message()); | ||
217 | 388 | } | ||
218 | 389 | } | ||
219 | 390 | } | ||
220 | 391 | |||
221 | 392 | TEST_F(DavProviderTests, create_folder_overwrite_folder) | ||
222 | 393 | { | ||
223 | 394 | auto account = get_client(); | ||
224 | 395 | ASSERT_EQ(0, mkdir(local_file("folder").c_str(), 0755)); | ||
225 | 396 | |||
226 | 397 | shared_ptr<Root> root; | ||
227 | 398 | { | ||
228 | 399 | QFutureWatcher<QVector<shared_ptr<Root>>> watcher; | ||
229 | 400 | QSignalSpy spy(&watcher, &decltype(watcher)::finished); | ||
230 | 401 | watcher.setFuture(account->roots()); | ||
231 | 402 | if (spy.count() == 0) | ||
232 | 403 | { | ||
233 | 404 | ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); | ||
234 | 405 | } | ||
235 | 406 | auto roots = watcher.result(); | ||
236 | 407 | ASSERT_EQ(1, roots.size()); | ||
237 | 408 | root = roots[0]; | ||
238 | 409 | } | ||
239 | 410 | |||
240 | 411 | { | ||
241 | 412 | QFutureWatcher<shared_ptr<Folder>> watcher; | ||
242 | 413 | QSignalSpy spy(&watcher, &decltype(watcher)::finished); | ||
243 | 414 | watcher.setFuture(root->create_folder("folder")); | ||
244 | 415 | if (spy.count() == 0) | ||
245 | 416 | { | ||
246 | 417 | ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); | ||
247 | 418 | } | ||
248 | 419 | try | ||
249 | 420 | { | ||
250 | 421 | watcher.result(); | ||
251 | 422 | FAIL(); | ||
252 | 423 | } | ||
253 | 424 | catch (RemoteCommsException const& e) | ||
254 | 425 | { | ||
255 | 426 | EXPECT_EQ("Error from MKCOL: 405", e.error_message()); | ||
256 | 427 | } | ||
257 | 428 | } | ||
258 | 429 | } | ||
259 | 430 | |||
260 | 312 | TEST_F(DavProviderTests, create_file) | 431 | TEST_F(DavProviderTests, create_file) |
261 | 313 | { | 432 | { |
262 | 314 | int const segments = 50; | 433 | 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:/