Merge lp:~facundo/magicicada-server/publicfiles into lp:magicicada-server
- publicfiles
- Merge into trunk
Proposed by
Facundo Batista
Status: | Merged | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Approved by: | Natalia Bidart | ||||||||||||
Approved revision: | 81 | ||||||||||||
Merged at revision: | 80 | ||||||||||||
Proposed branch: | lp:~facundo/magicicada-server/publicfiles | ||||||||||||
Merge into: | lp:magicicada-server | ||||||||||||
Diff against target: |
781 lines (+418/-18) 16 files modified
config-manager.txt (+2/-2) magicicada/filesync/services.py (+1/-0) magicicada/filesync/utilities/make_abundant_files.py (+1/-0) magicicada/rpcdb/backend.py (+19/-2) magicicada/rpcdb/tests/test_backend.py (+124/-4) magicicada/server/content.py (+22/-0) magicicada/server/integration/integtests_udf.py (+2/-0) magicicada/server/integtests/test_sync.py (+0/-1) magicicada/server/server.py (+78/-0) magicicada/server/testing/aq_helpers.py (+1/-0) magicicada/server/testing/testcase.py (+1/-0) magicicada/server/tests/test_content.py (+39/-7) magicicada/server/tests/test_fileops.py (+88/-1) magicicada/server/tests/test_server.py (+33/-0) magicicada/settings/__init__.py (+1/-0) test (+6/-1) |
||||||||||||
To merge this branch: | bzr merge lp:~facundo/magicicada-server/publicfiles | ||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Natalia Bidart | Approve | ||
Review via email: mp+310158@code.launchpad.net |
Commit message
Support public files changing and listing through the normal Server.
Description of the change
Support public files changing and listing through the normal Server.
You need Protocol and Client changes for this to work:
lp:~facundo/magicicada-protocol/publicfiles
lp:~facundo/magicicada-client/publicfiles
To post a comment you must log in.
- 81. By Facundo Batista
-
New sourcedeps and aesthetics issues
Revision history for this message
Facundo Batista (facundo) wrote : | # |
Revision history for this message
Natalia Bidart (nataliabidart) wrote : | # |
Looks good. I don't like the tests that use mocker but I understand is following the existing test infrastructure.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'config-manager.txt' |
2 | --- config-manager.txt 2016-09-01 17:12:32 +0000 |
3 | +++ config-manager.txt 2016-12-27 22:00:10 +0000 |
4 | @@ -29,5 +29,5 @@ |
5 | ./.sourcecode/requests ~ubuntuone-pqm-team/requests/stable;revno=816 |
6 | ./.sourcecode/requests_oauthlib ~ubuntuone-pqm-team/requests-oauthlib/stable;revno=16 |
7 | ./.sourcecode/u1sync ~facundo/u1sync/opensourcing;revno=10 |
8 | -./.sourcecode/magicicada-client ~chicharreros/magicicada-client/trunk;revno=1429 |
9 | -./.sourcecode/magicicada-protocol ~chicharreros/magicicada-protocol/trunk;revno=166 |
10 | +./.sourcecode/magicicada-client ~chicharreros/magicicada-client/trunk;revno=1433 |
11 | +./.sourcecode/magicicada-protocol ~chicharreros/magicicada-protocol/trunk;revno=169 |
12 | |
13 | === modified file 'magicicada/filesync/services.py' |
14 | --- magicicada/filesync/services.py 2016-06-03 00:35:53 +0000 |
15 | +++ magicicada/filesync/services.py 2016-12-27 22:00:10 +0000 |
16 | @@ -1090,6 +1090,7 @@ |
17 | return result |
18 | return wrapper |
19 | |
20 | + |
21 | timing_metric = TimingMetrics() |
22 | |
23 | |
24 | |
25 | === modified file 'magicicada/filesync/utilities/make_abundant_files.py' |
26 | --- magicicada/filesync/utilities/make_abundant_files.py 2016-06-03 20:57:04 +0000 |
27 | +++ magicicada/filesync/utilities/make_abundant_files.py 2016-12-27 22:00:10 +0000 |
28 | @@ -133,6 +133,7 @@ |
29 | if sys.stdout.isatty(): |
30 | sys.stdout.write(home + curses.tigetstr('el')) |
31 | |
32 | + |
33 | if __name__ == '__main__': |
34 | from optparse import OptionParser |
35 | parser = OptionParser("%prog [options] username") |
36 | |
37 | === modified file 'magicicada/rpcdb/backend.py' |
38 | --- magicicada/rpcdb/backend.py 2016-05-29 20:37:49 +0000 |
39 | +++ magicicada/rpcdb/backend.py 2016-12-27 22:00:10 +0000 |
40 | @@ -1,5 +1,5 @@ |
41 | # Copyright 2008-2015 Canonical |
42 | -# Copyright 2015 Chicharreros (https://launchpad.net/~chicharreros) |
43 | +# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros) |
44 | # |
45 | # This program is free software: you can redistribute it and/or modify |
46 | # it under the terms of the GNU Affero General Public License as |
47 | @@ -100,6 +100,22 @@ |
48 | shares=shares, udfs=udfs) |
49 | return result |
50 | |
51 | + def list_public_files(self, user_id): |
52 | + """List all the public files for the user.""" |
53 | + user = self._get_user(user_id) |
54 | + |
55 | + public_files = [self._process_node(n) for n in user.get_public_files()] |
56 | + result = dict(public_files=public_files) |
57 | + return result |
58 | + |
59 | + def change_public_access(self, user_id, volume_id, node_id, |
60 | + is_public, session_id=None): |
61 | + """Change the public access for a node.""" |
62 | + user = self._get_user(user_id, session_id) |
63 | + node = user.volume(volume_id).node(node_id) |
64 | + node = node.change_public_access(is_public) |
65 | + return dict(public_url=node.public_url) |
66 | + |
67 | def move(self, user_id, volume_id, node_id, |
68 | new_parent_id, new_name, session_id=None): |
69 | """Move a node and/or rename it.""" |
70 | @@ -308,7 +324,8 @@ |
71 | storage_key=storage_key, is_live=is_live, |
72 | size=size, is_file=is_file, volume_id=node.vol_id, |
73 | parent_id=node.parent_id, content_hash=node.content_hash, |
74 | - path=node.path, has_content=has_content) |
75 | + path=node.path, has_content=has_content, |
76 | + public_url=node.public_url) |
77 | return d |
78 | |
79 | def get_delta(self, user_id, volume_id, from_generation, limit): |
80 | |
81 | === modified file 'magicicada/rpcdb/tests/test_backend.py' |
82 | --- magicicada/rpcdb/tests/test_backend.py 2016-06-03 20:57:04 +0000 |
83 | +++ magicicada/rpcdb/tests/test_backend.py 2016-12-27 22:00:10 +0000 |
84 | @@ -1,5 +1,5 @@ |
85 | # Copyright 2008-2015 Canonical |
86 | -# Copyright 2015 Chicharreros (https://launchpad.net/~chicharreros) |
87 | +# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros) |
88 | # |
89 | # This program is free software: you can redistribute it and/or modify |
90 | # it under the terms of the GNU Affero General Public License as |
91 | @@ -243,6 +243,118 @@ |
92 | self.assertEqual(udf2['path'], 'path2') |
93 | self.assertEqual(udf2['generation'], 8) |
94 | |
95 | + def test_change_public_access(self): |
96 | + """Change the public acces of a node.""" |
97 | + mocker = Mocker() |
98 | + |
99 | + # node, with a generation attribute |
100 | + node = mocker.mock() |
101 | + expect(node.public_url).result('test public url') |
102 | + |
103 | + # user, with the chained calls to the action |
104 | + user = mocker.mock() |
105 | + expect( |
106 | + user.volume('vol_id').node('node_id').change_public_access(True) |
107 | + ).result(node) |
108 | + self.backend._get_user = lambda *a: user |
109 | + |
110 | + with mocker: |
111 | + kwargs = dict(user_id='user_id', volume_id='vol_id', |
112 | + node_id='node_id', is_public=True, |
113 | + session_id='session_id') |
114 | + result = self.backend.change_public_access(**kwargs) |
115 | + |
116 | + self.assertEqual(result, dict(public_url='test public url')) |
117 | + |
118 | + def test_list_public_files(self): |
119 | + """List public files.""" |
120 | + mocker = Mocker() |
121 | + |
122 | + # node 1 |
123 | + node1 = mocker.mock() |
124 | + expect(node1.id).result('node_id1') |
125 | + expect(node1.path).result('path1') |
126 | + expect(node1.name).result('name1') |
127 | + expect(node1.vol_id).result('volume_id1') |
128 | + expect(node1.generation).result('generation1') |
129 | + expect(node1.is_public).result(True) |
130 | + expect(node1.parent_id).result('parent_id1') |
131 | + expect(node1.status).result(STATUS_LIVE) |
132 | + expect(node1.content_hash).result('content_hash1') |
133 | + expect(node1.kind).result(StorageObject.FILE) |
134 | + expect(node1.when_last_modified).result('last_modified1') |
135 | + content1 = mocker.mock() |
136 | + expect(content1.size).result('size1') |
137 | + expect(content1.crc32).result('crc321') |
138 | + expect(content1.deflated_size).result('deflated_size1') |
139 | + expect(content1.storage_key).result('storage_key1') |
140 | + expect(node1.content).result(content1) |
141 | + expect(node1.public_url).result('public url 1') |
142 | + |
143 | + # node 2 |
144 | + node2 = mocker.mock() |
145 | + expect(node2.id).result('node_id2') |
146 | + expect(node2.path).result('path2') |
147 | + expect(node2.name).result('name2') |
148 | + expect(node2.vol_id).result('volume_id2') |
149 | + expect(node2.generation).result('generation2') |
150 | + expect(node2.is_public).result(True) |
151 | + expect(node2.parent_id).result('parent_id2') |
152 | + expect(node2.status).result(STATUS_DEAD) |
153 | + expect(node2.content_hash).result('content_hash2') |
154 | + expect(node2.kind).result(StorageObject.DIRECTORY) |
155 | + expect(node2.when_last_modified).result('last_modified2') |
156 | + content2 = mocker.mock() |
157 | + expect(content2.size).result('size2') |
158 | + expect(content2.crc32).result('crc322') |
159 | + expect(content2.deflated_size).result('deflated_size2') |
160 | + expect(content2.storage_key).result('storage_key2') |
161 | + expect(node2.content).result(content2) |
162 | + expect(node2.public_url).result('public url 2') |
163 | + |
164 | + # user |
165 | + user = mocker.mock() |
166 | + self.backend._get_user = lambda *a: user |
167 | + expect(user.get_public_files()).result([node1, node2]) |
168 | + |
169 | + with mocker: |
170 | + result = self.backend.list_public_files(user_id='user_id',) |
171 | + node1, node2 = result['public_files'] |
172 | + |
173 | + self.assertEqual(node1['id'], 'node_id1') |
174 | + self.assertEqual(node1['path'], 'path1') |
175 | + self.assertEqual(node1['name'], 'name1') |
176 | + self.assertEqual(node1['volume_id'], 'volume_id1') |
177 | + self.assertEqual(node1['parent_id'], 'parent_id1') |
178 | + self.assertEqual(node1['is_live'], True) |
179 | + self.assertEqual(node1['generation'], 'generation1') |
180 | + self.assertEqual(node1['is_public'], True) |
181 | + self.assertEqual(node1['content_hash'], 'content_hash1') |
182 | + self.assertEqual(node1['is_file'], True) |
183 | + self.assertEqual(node1['size'], 'size1') |
184 | + self.assertEqual(node1['crc32'], 'crc321') |
185 | + self.assertEqual(node1['deflated_size'], 'deflated_size1') |
186 | + self.assertEqual(node1['storage_key'], 'storage_key1') |
187 | + self.assertEqual(node1['last_modified'], 'last_modified1') |
188 | + self.assertEqual(node1['public_url'], 'public url 1') |
189 | + |
190 | + self.assertEqual(node2['id'], 'node_id2') |
191 | + self.assertEqual(node2['path'], 'path2') |
192 | + self.assertEqual(node2['name'], 'name2') |
193 | + self.assertEqual(node2['volume_id'], 'volume_id2') |
194 | + self.assertEqual(node2['parent_id'], 'parent_id2') |
195 | + self.assertEqual(node2['is_live'], False) |
196 | + self.assertEqual(node2['generation'], 'generation2') |
197 | + self.assertEqual(node2['is_public'], True) |
198 | + self.assertEqual(node2['content_hash'], 'content_hash2') |
199 | + self.assertEqual(node2['is_file'], False) |
200 | + self.assertEqual(node2['size'], 'size2') |
201 | + self.assertEqual(node2['crc32'], 'crc322') |
202 | + self.assertEqual(node2['deflated_size'], 'deflated_size2') |
203 | + self.assertEqual(node2['storage_key'], 'storage_key2') |
204 | + self.assertEqual(node2['last_modified'], 'last_modified2') |
205 | + self.assertEqual(node2['public_url'], 'public url 2') |
206 | + |
207 | def test_move(self): |
208 | """Move.""" |
209 | mocker = Mocker() |
210 | @@ -706,6 +818,7 @@ |
211 | expect(content.deflated_size).result('deflated_size') |
212 | expect(content.storage_key).result('storage_key') |
213 | expect(node.content).count(1).result(content) |
214 | + expect(node.public_url).result(None) |
215 | |
216 | # user |
217 | user = mocker.mock() |
218 | @@ -722,7 +835,8 @@ |
219 | last_modified='last_modified', crc32='crc32', |
220 | generation='generation', content_hash='content_hash', |
221 | deflated_size='deflated_size', storage_key='storage_key', |
222 | - volume_id='volume_id', path='path', has_content=True) |
223 | + volume_id='volume_id', path='path', has_content=True, |
224 | + public_url=None) |
225 | self.assertEqual(result, should) |
226 | |
227 | def test_get_node_no_content(self): |
228 | @@ -743,6 +857,7 @@ |
229 | expect(node.kind).result(StorageObject.FILE) |
230 | expect(node.when_last_modified).result('last_modified') |
231 | expect(node.content).result(None) |
232 | + expect(node.public_url).result(None) |
233 | |
234 | # user |
235 | user = mocker.mock() |
236 | @@ -758,7 +873,7 @@ |
237 | is_public=False, is_live=True, is_file=True, size=None, |
238 | last_modified='last_modified', crc32=None, |
239 | generation='generation', content_hash='content_hash', |
240 | - deflated_size=None, storage_key=None, |
241 | + deflated_size=None, storage_key=None, public_url=None, |
242 | volume_id='volume_id', path="path", has_content=False) |
243 | self.assertEqual(result, should) |
244 | |
245 | @@ -780,6 +895,7 @@ |
246 | expect(node.kind).result(StorageObject.FILE) |
247 | expect(node.when_last_modified).result('last_modified') |
248 | expect(node.content).count(1).result(None) |
249 | + expect(node.public_url).result(None) |
250 | |
251 | # user |
252 | user = mocker.mock() |
253 | @@ -798,7 +914,7 @@ |
254 | is_public=False, is_live=True, is_file=True, size=None, |
255 | last_modified='last_modified', crc32=None, |
256 | generation='generation', content_hash='content_hash', |
257 | - deflated_size=None, storage_key=None, |
258 | + deflated_size=None, storage_key=None, public_url=None, |
259 | volume_id='volume_id', path='path', has_content=False) |
260 | self.assertEqual(result, should) |
261 | |
262 | @@ -825,6 +941,7 @@ |
263 | expect(content1.deflated_size).count(2).result('deflated_size1') |
264 | expect(content1.storage_key).count(2).result('storage_key1') |
265 | expect(node1.content).count(2).result(content1) |
266 | + expect(node1.public_url).count(2).result('public url') |
267 | |
268 | # node 2 |
269 | node2 = mocker.mock() |
270 | @@ -845,6 +962,7 @@ |
271 | expect(content2.deflated_size).count(2).result('deflated_size2') |
272 | expect(content2.storage_key).count(2).result('storage_key2') |
273 | expect(node2.content).count(2).result(content2) |
274 | + expect(node2.public_url).count(2).result(None) |
275 | |
276 | # user |
277 | user = mocker.mock() |
278 | @@ -880,6 +998,7 @@ |
279 | self.assertEqual(node1['deflated_size'], 'deflated_size1') |
280 | self.assertEqual(node1['storage_key'], 'storage_key1') |
281 | self.assertEqual(node1['last_modified'], 'last_modified1') |
282 | + self.assertEqual(node1['public_url'], 'public url') |
283 | |
284 | self.assertEqual(node2['id'], 'node_id2') |
285 | self.assertEqual(node2['path'], 'path2') |
286 | @@ -896,6 +1015,7 @@ |
287 | self.assertEqual(node2['deflated_size'], 'deflated_size2') |
288 | self.assertEqual(node2['storage_key'], 'storage_key2') |
289 | self.assertEqual(node2['last_modified'], 'last_modified2') |
290 | + self.assertEqual(node2['public_url'], None) |
291 | |
292 | def test_get_user(self): |
293 | """Get accessable nodes and their hashes.""" |
294 | |
295 | === modified file 'magicicada/server/content.py' |
296 | --- magicicada/server/content.py 2016-08-07 22:40:29 +0000 |
297 | +++ magicicada/server/content.py 2016-12-27 22:00:10 +0000 |
298 | @@ -95,6 +95,7 @@ |
299 | self.is_live = node['is_live'] |
300 | self.generation = node['generation'] |
301 | self.is_public = node['is_public'] |
302 | + self.public_url = node['public_url'] |
303 | last_modif = node['last_modified'] |
304 | |
305 | # special cases for no content |
306 | @@ -774,6 +775,27 @@ |
307 | r['name'], r['mimetype'])) |
308 | |
309 | @defer.inlineCallbacks |
310 | + def list_public_files(self): |
311 | + """List the public files for an user.""" |
312 | + r = yield self.rpc_dal.call('list_public_files', user_id=self.id) |
313 | + nodes = [Node(self.manager, n) for n in r['public_files']] |
314 | + defer.returnValue(nodes) |
315 | + |
316 | + @defer.inlineCallbacks |
317 | + def change_public_access(self, volume_id, node_id, |
318 | + is_public, session_id=None): |
319 | + """Change public access of a node. |
320 | + |
321 | + @param volume_id: the id of the volume of the node. |
322 | + @param node_id: the id of the node. |
323 | + @param is_public: if the node should be public or not. |
324 | + """ |
325 | + r = yield self.rpc_dal.call('change_public_access', user_id=self.id, |
326 | + volume_id=volume_id, node_id=node_id, |
327 | + is_public=is_public, session_id=session_id) |
328 | + defer.returnValue(r['public_url']) |
329 | + |
330 | + @defer.inlineCallbacks |
331 | def get_upload_job(self, vol_id, node_id, previous_hash, hash_value, crc32, |
332 | inflated_size, deflated_size, session_id=None, |
333 | magic_hash=None, upload_id=None): |
334 | |
335 | === modified file 'magicicada/server/integration/integtests_udf.py' |
336 | --- magicicada/server/integration/integtests_udf.py 2016-05-29 20:58:45 +0000 |
337 | +++ magicicada/server/integration/integtests_udf.py 2016-12-27 22:00:10 +0000 |
338 | @@ -300,6 +300,7 @@ |
339 | assert actual2 == expected_with_conflict, \ |
340 | 'directory merge must be correct for SD2' |
341 | |
342 | + |
343 | test_merge_directories_with_overlap.skip = """ |
344 | The noconflict.txt file gets into conflict! Bug: #711389 |
345 | """ |
346 | @@ -419,6 +420,7 @@ |
347 | debug(prefix, 'contents for SD2', udf_content) |
348 | assert udf_content == [], msg |
349 | |
350 | + |
351 | test_remove_udf.skip = """ |
352 | This test exposes a problem we have now in SD: deleting everything |
353 | under the UDF depends on timing of operations between one client |
354 | |
355 | === modified file 'magicicada/server/integtests/test_sync.py' |
356 | --- magicicada/server/integtests/test_sync.py 2016-06-03 21:44:35 +0000 |
357 | +++ magicicada/server/integtests/test_sync.py 2016-12-27 22:00:10 +0000 |
358 | @@ -112,7 +112,6 @@ |
359 | self.source_dir = self.mktemp("source/root") |
360 | self.share_source_dir = self.mktemp("source/share") |
361 | |
362 | - self.patch(Main, "start_status_listener", self.no_op) |
363 | self.patch(hash_queue, "HASHQUEUE_DELAY", 0.1) |
364 | self.main = Main(root_dir, shares_dir, data_dir, partials_dir, |
365 | "localhost", self.ssl_port, dns_srv=None, ssl=True, |
366 | |
367 | === modified file 'magicicada/server/server.py' |
368 | --- magicicada/server/server.py 2016-08-14 21:51:15 +0000 |
369 | +++ magicicada/server/server.py 2016-12-27 22:00:10 +0000 |
370 | @@ -535,6 +535,16 @@ |
371 | request = Unlink(self, message) |
372 | request.start() |
373 | |
374 | + def handle_LIST_PUBLIC_FILES(self, message): |
375 | + """Handle LIST_PUBLIC_FILES message.""" |
376 | + request = ListPublicFiles(self, message) |
377 | + request.start() |
378 | + |
379 | + def handle_CHANGE_PUBLIC_ACCESS(self, message): |
380 | + """Handle UNLINK message.""" |
381 | + request = ChangePublicAccess(self, message) |
382 | + request.start() |
383 | + |
384 | def handle_CREATE_UDF(self, message): |
385 | """Handle CREATE_UDF message.""" |
386 | request = CreateUDF(self, message) |
387 | @@ -1299,6 +1309,74 @@ |
388 | share_id, node_id, kind, mime, extension) |
389 | |
390 | |
391 | +class ListPublicFiles(SimpleRequestResponse): |
392 | + """LIST_PUBLIC_FILES Request Response.""" |
393 | + |
394 | + __slots__ = () |
395 | + |
396 | + @inlineCallbacks |
397 | + def _process(self): |
398 | + """List public files for the client.""" |
399 | + user = self.protocol.user |
400 | + public_files = yield user.list_public_files() |
401 | + |
402 | + counter = 0 |
403 | + for node in public_files: |
404 | + message = protocol_pb2.Message() |
405 | + message.type = protocol_pb2.Message.PUBLIC_FILE_INFO |
406 | + message.public_file_info.share = node.volume_id or '' |
407 | + message.public_file_info.node = str(node.id) |
408 | + message.public_file_info.is_public = node.is_public |
409 | + message.public_file_info.public_url = str(node.public_url) |
410 | + self.sendMessage(message) |
411 | + counter += 1 |
412 | + |
413 | + # we're done! |
414 | + self.length = counter |
415 | + response = protocol_pb2.Message() |
416 | + response.type = protocol_pb2.Message.PUBLIC_FILE_INFO_END |
417 | + self.sendMessage(response) |
418 | + |
419 | + # save data to be logged on operation end |
420 | + self.operation_data = 'public_files=%d' % len(public_files) |
421 | + |
422 | + |
423 | +class ChangePublicAccess(SimpleRequestResponse): |
424 | + """CHANGE_PUBLIC_ACCESS Request Response.""" |
425 | + |
426 | + __slots__ = () |
427 | + |
428 | + expected_foreign_errors = [dataerror.NoPermission] |
429 | + |
430 | + user_activity = 'sync_activity' |
431 | + |
432 | + def _get_node_info(self): |
433 | + """Return node info from the message.""" |
434 | + node_id = self.source_message.change_public_access.node |
435 | + return 'node: %r' % (node_id,) |
436 | + |
437 | + @inlineCallbacks |
438 | + def _process(self): |
439 | + """Change the public access to a node.""" |
440 | + share_id = self.convert_share_id( |
441 | + self.source_message.change_public_access.share) |
442 | + node_id = self.source_message.change_public_access.node |
443 | + is_public = self.source_message.change_public_access.is_public |
444 | + public_url = yield self.protocol.user.change_public_access( |
445 | + share_id, node_id, is_public, session_id=self.protocol.session_id) |
446 | + |
447 | + # answer the ok to original user |
448 | + response = protocol_pb2.Message() |
449 | + response.public_url = bytes(public_url) |
450 | + response.type = protocol_pb2.Message.OK |
451 | + self.sendMessage(response) |
452 | + |
453 | + # save data to be logged on operation end |
454 | + self.operation_data = ( |
455 | + 'vol_id=%s node_id=%s is_public=%s public_url=%r' % ( |
456 | + share_id, node_id, is_public, public_url)) |
457 | + |
458 | + |
459 | class BytesMessageProducer(object): |
460 | """Adapt a bytes producer to produce BYTES messages.""" |
461 | |
462 | |
463 | === modified file 'magicicada/server/testing/aq_helpers.py' |
464 | --- magicicada/server/testing/aq_helpers.py 2016-08-30 00:50:31 +0000 |
465 | +++ magicicada/server/testing/aq_helpers.py 2016-12-27 22:00:10 +0000 |
466 | @@ -573,6 +573,7 @@ |
467 | def __cmp__(self, other): |
468 | return cmp(self.shares, other.shares) |
469 | |
470 | + |
471 | aHash = _HashPlaceholder('a hash') |
472 | anUUID = _UUIDPlaceholder('an UUID') |
473 | aShareUUID = _UUIDPlaceholder('a share UUID', ('',)) |
474 | |
475 | === modified file 'magicicada/server/testing/testcase.py' |
476 | --- magicicada/server/testing/testcase.py 2016-08-14 21:51:15 +0000 |
477 | +++ magicicada/server/testing/testcase.py 2016-12-27 22:00:10 +0000 |
478 | @@ -55,6 +55,7 @@ |
479 | """In the present we trust.""" |
480 | return int(time.time()) |
481 | |
482 | + |
483 | # need to patch this timestamp checker so it doesn't go to the real server |
484 | client.tx_timestamp_checker = FakeTimestampChecker() |
485 | |
486 | |
487 | === modified file 'magicicada/server/tests/test_content.py' |
488 | --- magicicada/server/tests/test_content.py 2016-09-15 11:46:39 +0000 |
489 | +++ magicicada/server/tests/test_content.py 2016-12-27 22:00:10 +0000 |
490 | @@ -1700,13 +1700,13 @@ |
491 | root = yield client.get_root() |
492 | filename = 'hola_12' |
493 | mkfile_req = yield client.make_file(request.ROOT, root, filename) |
494 | - upload_id = [] |
495 | + upload_info = [] |
496 | try: |
497 | yield client.put_content( |
498 | request.ROOT, mkfile_req.new_id, NO_CONTENT_HASH, |
499 | hash_value, crc32_value, size, deflated_size, |
500 | StringIO(deflated_data), |
501 | - upload_id_cb=upload_id.append) |
502 | + upload_id_cb=lambda *a: upload_info.append(a)) |
503 | except EOFError: |
504 | # check upload stat and log, with the offset sent, |
505 | # first time tarts from beginning. |
506 | @@ -1745,7 +1745,7 @@ |
507 | req = sp_client.PutContent( |
508 | client, request.ROOT, mkfile_req.new_id, NO_CONTENT_HASH, |
509 | hash_value, crc32_value, size, deflated_size, |
510 | - StringIO(deflated_data), upload_id=str(upload_id[0])) |
511 | + StringIO(deflated_data), upload_id=str(upload_info[0][0])) |
512 | req.start() |
513 | yield req.deferred |
514 | |
515 | @@ -1800,19 +1800,21 @@ |
516 | yield client.dummy_authenticate("open sesame") |
517 | root = yield client.get_root() |
518 | mkfile_req = yield client.make_file(request.ROOT, root, 'hola') |
519 | - upload_id = [] |
520 | + upload_info = [] |
521 | req = client.put_content_request( |
522 | request.ROOT, mkfile_req.new_id, NO_CONTENT_HASH, |
523 | hash_value, crc32_value, size, deflated_size, |
524 | StringIO(deflated_data), upload_id="invalid id", |
525 | - upload_id_cb=upload_id.append) |
526 | + upload_id_cb=lambda *a: upload_info.append(a)) |
527 | yield req.deferred |
528 | self.assertTrue(('UploadJob.upload', 0) in gauge) |
529 | self.assertTrue(('UploadJob.upload.begin', 1) in meter) |
530 | self.handler.assert_debug( |
531 | "UploadJob begin content from offset 0") |
532 | - self.assertEqual(len(upload_id), 1) |
533 | - self.assertIsInstance(uuid.UUID(upload_id[0]), uuid.UUID) |
534 | + self.assertEqual(len(upload_info), 1) |
535 | + upload_id, start_from = upload_info[0] |
536 | + self.assertIsInstance(uuid.UUID(upload_id), uuid.UUID) |
537 | + self.assertEqual(start_from, 0) |
538 | |
539 | yield self.callback_test(auth, add_default_callbacks=True) |
540 | |
541 | @@ -2302,6 +2304,36 @@ |
542 | d = self.user.get_free_bytes(share.id) |
543 | yield self.assertFailure(d, errors.DoesNotExist) |
544 | |
545 | + @defer.inlineCallbacks |
546 | + def test_change_public_access(self): |
547 | + """Test change public access action.""" |
548 | + root_id, root_gen = yield self.user.get_root() |
549 | + volume_id = yield self.user.get_volume_id(root_id) |
550 | + node_id, generation, _ = yield self.user.make_file( |
551 | + volume_id, root_id, u"name") |
552 | + public_url = yield self.user.change_public_access( |
553 | + volume_id, node_id, True) |
554 | + self.assertTrue(public_url.startswith(settings.PUBLIC_URL_PREFIX)) |
555 | + |
556 | + @defer.inlineCallbacks |
557 | + def test_list_public_files(self): |
558 | + """Test the public files listing.""" |
559 | + root_id, _ = yield self.user.get_root() |
560 | + volume_id = yield self.user.get_volume_id(root_id) |
561 | + |
562 | + # create three files, make two public |
563 | + node_id_1, _, _ = yield self.user.make_file( |
564 | + volume_id, root_id, u"name1") |
565 | + yield self.user.make_file(volume_id, root_id, u"name2") |
566 | + node_id_3, _, _ = yield self.user.make_file( |
567 | + volume_id, root_id, u"name3") |
568 | + yield self.user.change_public_access(volume_id, node_id_1, True) |
569 | + yield self.user.change_public_access(volume_id, node_id_3, True) |
570 | + |
571 | + public_files = yield self.user.list_public_files() |
572 | + self.assertEqual(set(node.id for node in public_files), |
573 | + {node_id_1, node_id_3}) |
574 | + |
575 | |
576 | class TestUploadJob(TestWithDatabase): |
577 | """Tests for UploadJob class.""" |
578 | |
579 | === modified file 'magicicada/server/tests/test_fileops.py' |
580 | --- magicicada/server/tests/test_fileops.py 2016-05-29 20:58:45 +0000 |
581 | +++ magicicada/server/tests/test_fileops.py 2016-12-27 22:00:10 +0000 |
582 | @@ -1,5 +1,5 @@ |
583 | # Copyright 2008-2015 Canonical |
584 | -# Copyright 2015 Chicharreros (https://launchpad.net/~chicharreros) |
585 | +# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros) |
586 | # |
587 | # This program is free software: you can redistribute it and/or modify |
588 | # it under the terms of the GNU Affero General Public License as |
589 | @@ -421,3 +421,90 @@ |
590 | self.assertEqual(unlink_req.new_generation, |
591 | make_req.new_generation + 1) |
592 | return self.callback_test(test, add_default_callbacks=True) |
593 | + |
594 | + |
595 | +class TestPublicFiles(TestWithDatabase): |
596 | + """Test the public files management.""" |
597 | + |
598 | + def test_set_public_file_true(self): |
599 | + @defer.inlineCallbacks |
600 | + def auth(client): |
601 | + yield client.dummy_authenticate("open sesame") |
602 | + root = yield client.get_root() |
603 | + req = yield client.make_file(request.ROOT, root, "hola") |
604 | + resp = yield client.change_public_access( |
605 | + request.ROOT, req.new_id, True) |
606 | + fileobj = self.usr0.get_node(req.new_id) |
607 | + self.assertTrue(fileobj.is_public) |
608 | + self.assertEqual(resp.public_url, fileobj.public_url) |
609 | + |
610 | + return self.callback_test(auth, add_default_callbacks=True) |
611 | + |
612 | + def test_set_public_file_false(self): |
613 | + @defer.inlineCallbacks |
614 | + def auth(client): |
615 | + yield client.dummy_authenticate("open sesame") |
616 | + root = yield client.get_root() |
617 | + req = yield client.make_file(request.ROOT, root, "hola") |
618 | + |
619 | + # set it to public first |
620 | + yield client.change_public_access(request.ROOT, req.new_id, True) |
621 | + fileobj = self.usr0.get_node(req.new_id) |
622 | + assert fileobj.is_public |
623 | + |
624 | + # set it to false, check |
625 | + yield client.change_public_access(request.ROOT, req.new_id, False) |
626 | + fileobj = self.usr0.get_node(req.new_id) |
627 | + self.assertFalse(fileobj.is_public) |
628 | + |
629 | + return self.callback_test(auth, add_default_callbacks=True) |
630 | + |
631 | + def test_list_public_files_none(self): |
632 | + @defer.inlineCallbacks |
633 | + def auth(client): |
634 | + yield client.dummy_authenticate("open sesame") |
635 | + |
636 | + # list and check |
637 | + resp = yield client.list_public_files() |
638 | + self.assertEqual(resp.public_files, []) |
639 | + |
640 | + return self.callback_test(auth, add_default_callbacks=True) |
641 | + |
642 | + def test_list_public_files_several(self): |
643 | + @defer.inlineCallbacks |
644 | + def auth(client): |
645 | + yield client.dummy_authenticate("open sesame") |
646 | + root = yield client.get_root() |
647 | + |
648 | + # create three files, and set two of them to be public |
649 | + req1 = yield client.make_file(request.ROOT, root, "file1") |
650 | + yield client.make_file(request.ROOT, root, "file2") |
651 | + req3 = yield client.make_file(request.ROOT, root, "file3") |
652 | + yield client.change_public_access(request.ROOT, req1.new_id, True) |
653 | + yield client.change_public_access(request.ROOT, req3.new_id, True) |
654 | + |
655 | + # list and check |
656 | + resp = yield client.list_public_files() |
657 | + self.assertEqual({node.node_id for node in resp.public_files}, |
658 | + set([req1.new_id, req3.new_id])) |
659 | + |
660 | + return self.callback_test(auth, add_default_callbacks=True) |
661 | + |
662 | + def test_list_public_files_unpublished(self): |
663 | + @defer.inlineCallbacks |
664 | + def auth(client): |
665 | + yield client.dummy_authenticate("open sesame") |
666 | + root = yield client.get_root() |
667 | + |
668 | + # create a file, publish, and assert |
669 | + req = yield client.make_file(request.ROOT, root, "file") |
670 | + yield client.change_public_access(request.ROOT, req.new_id, True) |
671 | + resp = yield client.list_public_files() |
672 | + assert [node.node_id for node in resp.public_files] == [req.new_id] |
673 | + |
674 | + # unpublish it and check |
675 | + yield client.change_public_access(request.ROOT, req.new_id, False) |
676 | + resp = yield client.list_public_files() |
677 | + self.assertEqual(resp.public_files, []) |
678 | + |
679 | + return self.callback_test(auth, add_default_callbacks=True) |
680 | |
681 | === modified file 'magicicada/server/tests/test_server.py' |
682 | --- magicicada/server/tests/test_server.py 2016-08-26 21:53:42 +0000 |
683 | +++ magicicada/server/tests/test_server.py 2016-12-27 22:00:10 +0000 |
684 | @@ -48,6 +48,7 @@ |
685 | Action, |
686 | AuthenticateResponse, |
687 | BytesMessageProducer, |
688 | + ChangePublicAccess, |
689 | CreateShare, |
690 | CreateUDF, |
691 | DeleteShare, |
692 | @@ -56,6 +57,7 @@ |
693 | GetContentResponse, |
694 | GetDeltaResponse, |
695 | ListShares, |
696 | + ListPublicFiles, |
697 | ListVolumes, |
698 | LoopingPing, |
699 | MakeResponse, |
700 | @@ -99,6 +101,8 @@ |
701 | last_modified = 2334524 |
702 | is_public = False |
703 | path = u"path" |
704 | + volume_id = 'volumeid' |
705 | + public_url = 'public_url' |
706 | |
707 | |
708 | class FakeUser(object): |
709 | @@ -1177,6 +1181,35 @@ |
710 | self.assertEqual(self.response.length, 6) |
711 | |
712 | |
713 | +class ChangePublicAccessTestCase(SimpleRequestResponseTestCase): |
714 | + """Test the ChangePublicAccess class.""" |
715 | + |
716 | + response_class = ChangePublicAccess |
717 | + |
718 | + |
719 | +class ListPublicFilesTestCase(SimpleRequestResponseTestCase): |
720 | + """Test the ListPublicFiles class.""" |
721 | + |
722 | + response_class = ListPublicFiles |
723 | + |
724 | + @defer.inlineCallbacks |
725 | + def test_process_set_values(self): |
726 | + """Set length attribute and operation data while processing.""" |
727 | + mocker = Mocker() |
728 | + |
729 | + user = mocker.mock() |
730 | + self.response.protocol.user = user |
731 | + |
732 | + nodes = [FakeNode(), FakeNode()] |
733 | + expect(user.list_public_files()).result(nodes) |
734 | + |
735 | + with mocker: |
736 | + yield self.response._process() |
737 | + |
738 | + self.assertEqual(self.response.length, 2) |
739 | + self.assertEqual(self.response.operation_data, "public_files=2") |
740 | + |
741 | + |
742 | class UnlinkTestCase(SimpleRequestResponseTestCase): |
743 | """Test the Unlink class.""" |
744 | |
745 | |
746 | === modified file 'magicicada/settings/__init__.py' |
747 | --- magicicada/settings/__init__.py 2016-09-15 11:46:39 +0000 |
748 | +++ magicicada/settings/__init__.py 2016-12-27 22:00:10 +0000 |
749 | @@ -147,6 +147,7 @@ |
750 | if self.isEnabledFor(TRACE): |
751 | self._log(TRACE, msg, args, **kwargs) |
752 | |
753 | + |
754 | logging.setLoggerClass(MagicicadaLogger) |
755 | |
756 | |
757 | |
758 | === modified file 'test' |
759 | --- test 2016-09-15 11:46:39 +0000 |
760 | +++ test 2016-12-27 22:00:10 +0000 |
761 | @@ -1,7 +1,7 @@ |
762 | #!/usr/bin/env python |
763 | |
764 | # Copyright 2008-2015 Canonical |
765 | -# Copyright 2015 Chicharreros (https://launchpad.net/~chicharreros) |
766 | +# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros) |
767 | # |
768 | # This program is free software: you can redistribute it and/or modify |
769 | # it under the terms of the GNU Affero General Public License as |
770 | @@ -44,6 +44,11 @@ |
771 | os.environ.setdefault( |
772 | 'XDG_CACHE_HOME', os.path.join(ROOTDIR, 'tmp', 'xdg_cache')) |
773 | |
774 | + # repeated setting from makefile, as some tests check this, to work |
775 | + # ok when running them directly from ./test |
776 | + os.environ.setdefault('PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION', 'cpp') |
777 | + os.environ.setdefault('PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION', '2') |
778 | + |
779 | dbus_address_file = os.path.join(ROOTDIR, 'tmp', 'dbus.address') |
780 | if os.path.exists(dbus_address_file): |
781 | with open(dbus_address_file) as fh: |
Now the client and protocol new stuff is properly indicated in the sourcedeps, so this is all set for review.