Merge lp:~alecu/ubuntuone-client/delivery-call-api into lp:ubuntuone-client

Proposed by Alejandro J. Cura
Status: Work in progress
Proposed branch: lp:~alecu/ubuntuone-client/delivery-call-api
Merge into: lp:ubuntuone-client
Diff against target: 238 lines (+154/-6)
3 files modified
tests/syncdaemon/test_action_queue.py (+107/-1)
ubuntuone/syncdaemon/action_queue.py (+45/-1)
ubuntuone/syncdaemon/event_queue.py (+2/-4)
To merge this branch: bzr merge lp:~alecu/ubuntuone-client/delivery-call-api
Reviewer Review Type Date Requested Status
dobey Pending
Review via email: mp+144950@code.launchpad.net

Commit message

- Add call to newest-purchases web api (LP: #1103688).

To post a comment you must log in.

Unmerged revisions

1379. By Alejandro J. Cura

Add call to newest-purchases web api

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'tests/syncdaemon/test_action_queue.py'
--- tests/syncdaemon/test_action_queue.py 2013-01-17 19:16:53 +0000
+++ tests/syncdaemon/test_action_queue.py 2013-01-25 14:59:21 +0000
@@ -1,6 +1,6 @@
1#-*- coding: utf-8 -*-1#-*- coding: utf-8 -*-
2#2#
3# Copyright 2009-2012 Canonical Ltd.3# Copyright 2009-2013 Canonical Ltd.
4#4#
5# This program is free software: you can redistribute it and/or modify it5# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published6# under the terms of the GNU General Public License version 3, as published
@@ -487,6 +487,112 @@
487 yield self.action_queue.get_webclient(self.fake_iri)487 yield self.action_queue.get_webclient(self.fake_iri)
488 self.assertEqual(calls[1]["context_factory"], fake_context)488 self.assertEqual(calls[1]["context_factory"], fake_context)
489489
490 def test_queue_music_delivery_results(self):
491 """The music delivery results are queued."""
492 albums = ["fake", "list", "of", "albums"]
493 self.action_queue.music_delivery_results(albums)
494 expected = ('AQ_MUSIC_DELIVERY_RESULTS', {'purchased_albums': albums})
495 self.assertEqual([expected], self.action_queue.event_queue.events)
496
497
498class MusicDeliveryLifeCycleTestCase(BasicTestCase):
499 """Tests to check the lifecycle of the music delivery checks."""
500
501 def test_delivery_checker_initialization(self):
502 """Check the initialization of the checker."""
503 self.assertEqual(self.action_queue.delivery_checker.action_queue,
504 self.action_queue)
505
506 def test_delivery_checker_started(self):
507 """Check that the delivery checker is started on connection."""
508 called = []
509 self.patch(self.action_queue, "_lookup_srv", lambda: defer.Deferred())
510 self.patch(self.action_queue.delivery_checker, "start",
511 lambda: called.append(True))
512 self.action_queue.connect()
513 self.assertEqual(len(called), 1)
514
515 def test_delivery_checker_stopped(self):
516 """Check that the delivery checker is stopped on disconnection."""
517 called = []
518 self.patch(self.action_queue.delivery_checker, "stop",
519 lambda: called.append(True))
520 self.action_queue._cleanup_connection_state()
521 self.assertEqual(len(called), 1)
522
523
524FAKE_DELIVERY_RESPONSE = """{
525 "server_timestamp": 1234,
526 "purchased_albums": [{
527 "album_name": "7digital Essential: 50s Rock",
528 "cloud_delivery_status": "COMPLETED",
529 "timestamp_purchased": 1230,
530 "open_url": "http://one.ubuntu.com/......",
531 "tracks_udf_paths": [
532 "Bill Haley/7digital Essential: 50s Rock/Rock Around the Clock.mp3",
533 "Bo Diddley/7digital Essential: 50s Rock/I'm a Man.mp3",
534 "Buddy Holly/7digital Essential: 50s Rock/Little Baby.mp3",
535 "Chuck Berry/7digital Essential: 50s Rock/Johnny Be Good.mp3",
536 "Chuck Berry/7digital Essential: 50s Rock/Roll Over Beethoven.mp3",
537 "Chuck Berry/7digital Essential: 50s Rock/Sweet Little Rock&Roll.mp3",
538 "Eddie Cochran/7digital Essential: 50s Rock/Let's Get Together.mp3",
539 "Eddie Cochran/7digital Essential: 50s Rock/My Way.mp3",
540 "Elvis Presley/7digital Essential: 50s Rock/Blue Suede Shoes.mp3",
541 "Elvis Presley/7digital Essential: 50s Rock/Don't Be Cruel.mp3",
542 "Elvis Presley/7digital Essential: 50s Rock/Shake, Rattle and Roll.mp3"
543 ]}
544 ]
545}"""
546
547
548class FakeAQ(object):
549 """A fake action queue."""
550
551 def __init__(self, result=FAKE_DELIVERY_RESPONSE):
552 self.result = result
553 self.called = []
554 self.delivery_results = None
555
556 def webcall(self, *args, **kwargs):
557 """A fake web call."""
558 self.called.append((args, kwargs))
559 response = action_queue.txweb.Response(self.result)
560 return defer.succeed(response)
561
562 def music_delivery_results(self, albums):
563 """The music results are sent to the event queue."""
564 self.delivery_results = albums
565
566
567class MusicDeliveryCheckerTestCase(TwistedTestCase):
568 """Tests for the MusicDeliveryChecker."""
569
570 @defer.inlineCallbacks
571 def test_webservice_called_with_timestamp(self):
572 """The webservice is called with the last server timestamp."""
573 fakeaq = FakeAQ()
574 checker = action_queue.MusicDeliveryChecker(fakeaq)
575 expected = ((action_queue.MusicDeliveryChecker.ALBUM_DELIVERY_IRI %
576 checker.last_server_timestamp,), {'method': 'GET'})
577 yield checker.check_api()
578 self.assertEqual(fakeaq.called, [expected])
579
580 @defer.inlineCallbacks
581 def test_new_server_timestamp_is_stored(self):
582 """The webservice response is json parsed and the timestamp stored."""
583 fakeaq = FakeAQ()
584 checker = action_queue.MusicDeliveryChecker(fakeaq)
585 yield checker.check_api()
586 self.assertEqual(checker.last_server_timestamp, 1234)
587
588 @defer.inlineCallbacks
589 def test_event_queue_is_pushed(self):
590 """The webservice response is pushed into the event queue."""
591 fakeaq = FakeAQ()
592 checker = action_queue.MusicDeliveryChecker(fakeaq)
593 yield checker.check_api()
594 self.assertEqual(len(fakeaq.delivery_results), 1)
595
490596
491class TestLoggingStorageClient(TwistedTestCase):597class TestLoggingStorageClient(TwistedTestCase):
492 """Tests for ensuring magic hash dont show in logs."""598 """Tests for ensuring magic hash dont show in logs."""
493599
=== modified file 'ubuntuone/syncdaemon/action_queue.py'
--- ubuntuone/syncdaemon/action_queue.py 2013-01-11 20:05:24 +0000
+++ ubuntuone/syncdaemon/action_queue.py 2013-01-25 14:59:21 +0000
@@ -1,6 +1,6 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2#2#
3# Copyright 2009-2012 Canonical Ltd.3# Copyright 2009-2013 Canonical Ltd.
4#4#
5# This program is free software: you can redistribute it and/or modify it5# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published6# under the terms of the GNU General Public License version 3, as published
@@ -761,6 +761,43 @@
761 return getattr(self.fd, attr)761 return getattr(self.fd, attr)
762762
763763
764class MusicDeliveryChecker(object):
765 """Checks the api for new albums delivered to the cloud."""
766
767 ALBUM_DELIVERY_IRI = (u"https://edge.one.ubuntu.com/" +
768 u"music-store/api/1/user/newest-purchases?last_server_timestamp=%d")
769
770 def __init__(self, action_queue):
771 self.action_queue = action_queue
772 self.last_server_timestamp = 0
773
774 def start(self):
775 """Start calling the api periodically."""
776 # TODO: coming in following branches. See pad.lv/1103690
777 # reactor.callLater(0, self.check_and_schedule_next_check)
778
779 def stop(self):
780 """Stop calling the api."""
781
782 @defer.inlineCallbacks
783 def check_and_schedule_next_check(self):
784 """Check the api, and schedule the next check."""
785 # TODO: coming in following branches. See pad.lv/1103690
786 # on any error, wait 10 minutes
787 # on success, wait 60 minutes
788
789 @defer.inlineCallbacks
790 def check_api(self):
791 """Do the actual checking."""
792 iri = self.ALBUM_DELIVERY_IRI % self.last_server_timestamp
793 logger.debug("Checking music delivery: %d", self.last_server_timestamp)
794 response = yield self.action_queue.webcall(iri, method="GET")
795 logger.debug("Music delivery returned %d bytes", len(response.content))
796 parsed = json.loads(response.content)
797 self.last_server_timestamp = int(parsed["server_timestamp"])
798 self.action_queue.music_delivery_results(parsed["purchased_albums"])
799
800
764class ActionQueue(ThrottlingStorageClientFactory, object):801class ActionQueue(ThrottlingStorageClientFactory, object):
765 """The ActionQueue itself."""802 """The ActionQueue itself."""
766803
@@ -813,6 +850,7 @@
813 self.commands = dict((x, y) for x, y in globals().iteritems()850 self.commands = dict((x, y) for x, y in globals().iteritems()
814 if inspect.isclass(y) and851 if inspect.isclass(y) and
815 issubclass(y, ActionQueueCommand))852 issubclass(y, ActionQueueCommand))
853 self.delivery_checker = MusicDeliveryChecker(self)
816854
817 def check_conditions(self):855 def check_conditions(self):
818 """Check conditions in the locker, to release all the waiting ops."""856 """Check conditions in the locker, to release all the waiting ops."""
@@ -843,6 +881,7 @@
843 self.client = None881 self.client = None
844 self.connector = None882 self.connector = None
845 self.connect_in_progress = False883 self.connect_in_progress = False
884 self.delivery_checker.stop()
846885
847 def _share_change_callback(self, info):886 def _share_change_callback(self, info):
848 """Called by the client when notified that a share changed."""887 """Called by the client when notified that a share changed."""
@@ -921,6 +960,10 @@
921 else:960 else:
922 return defer.succeed((self.host, self.port))961 return defer.succeed((self.host, self.port))
923962
963 def music_delivery_results(self, results):
964 """Push the music delivery results into the event queue."""
965 self.event_queue.push('AQ_MUSIC_DELIVERY_RESULTS',
966 purchased_albums=results)
924967
925 @defer.inlineCallbacks968 @defer.inlineCallbacks
926 def webcall(self, iri, **kwargs):969 def webcall(self, iri, **kwargs):
@@ -968,6 +1011,7 @@
968 d = self._lookup_srv()1011 d = self._lookup_srv()
969 # DNS lookup always succeeds, proceed to actually connect1012 # DNS lookup always succeeds, proceed to actually connect
970 d.addCallback(self._make_connection)1013 d.addCallback(self._make_connection)
1014 self.delivery_checker.start()
9711015
972 def buildProtocol(self, addr):1016 def buildProtocol(self, addr):
973 """Build the client and store it. Connect callbacks."""1017 """Build the client and store it. Connect callbacks."""
9741018
=== modified file 'ubuntuone/syncdaemon/event_queue.py'
--- ubuntuone/syncdaemon/event_queue.py 2012-08-08 13:21:13 +0000
+++ ubuntuone/syncdaemon/event_queue.py 2013-01-25 14:59:21 +0000
@@ -1,9 +1,6 @@
1# ubuntuone.syncdaemon.event_queue - Event queuing1# ubuntuone.syncdaemon.event_queue - Event queuing
2#2#
3# Authors: Facundo Batista <facundo@canonical.com>3# Copyright 2009-2013 Canonical Ltd.
4# Manuel de la Pena <manuel@canonical.com>
5#
6# Copyright 2009-2012 Canonical Ltd.
7#4#
8# This program is free software: you can redistribute it and/or modify it5# This program is free software: you can redistribute it and/or modify it
9# under the terms of the GNU General Public License version 3, as published6# under the terms of the GNU General Public License version 3, as published
@@ -100,6 +97,7 @@
100 'AQ_CHANGE_PUBLIC_ACCESS_ERROR': ('share_id', 'node_id', 'error'),97 'AQ_CHANGE_PUBLIC_ACCESS_ERROR': ('share_id', 'node_id', 'error'),
101 'AQ_PUBLIC_FILES_LIST_OK': ('public_files',),98 'AQ_PUBLIC_FILES_LIST_OK': ('public_files',),
102 'AQ_PUBLIC_FILES_LIST_ERROR': ('error',),99 'AQ_PUBLIC_FILES_LIST_ERROR': ('error',),
100 'AQ_MUSIC_DELIVERY_RESULTS': ('purchased_albums',),
103 'AQ_DELTA_OK': ('volume_id', 'delta_content', 'end_generation',101 'AQ_DELTA_OK': ('volume_id', 'delta_content', 'end_generation',
104 'full', 'free_bytes'),102 'full', 'free_bytes'),
105 'AQ_DELTA_ERROR': ('volume_id', 'error'),103 'AQ_DELTA_ERROR': ('volume_id', 'error'),

Subscribers

People subscribed via source and target branches