Merge lp:~facundo/magicicada-server/publicfiles into lp:magicicada-server

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
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 :

Now the client and protocol new stuff is properly indicated in the sourcedeps, so this is all set for review.

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:

Subscribers

People subscribed via source and target branches

to all changes: