Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#744: return Subscription instances from pubsub.list_subscriptions() #794

Merged
merged 2 commits into from
Apr 6, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gcloud/pubsub/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from gcloud.connection import get_scoped_connection
from gcloud.pubsub import _implicit_environ
from gcloud.pubsub._implicit_environ import get_default_connection
from gcloud.pubsub.api import list_subscriptions
from gcloud.pubsub.api import list_topics
from gcloud.pubsub.connection import Connection

Expand Down
60 changes: 52 additions & 8 deletions gcloud/pubsub/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from gcloud._helpers import get_default_project
from gcloud.pubsub._implicit_environ import get_default_connection
from gcloud.pubsub.subscription import Subscription
from gcloud.pubsub.topic import Topic


Expand Down Expand Up @@ -65,10 +66,8 @@ def list_topics(page_size=None, page_token=None,

path = '/projects/%s/topics' % project
resp = connection.api_request(method='GET', path=path, query_params=params)
topics = []
for full_name in [topic['name'] for topic in resp['topics']]:
_, t_project, _, name = full_name.split('/')
topics.append(Topic(name, t_project, connection))
topics = [_topic_from_resource(resource, connection)
for resource in resp['topics']]
return topics, resp.get('nextPageToken')


Expand Down Expand Up @@ -102,9 +101,9 @@ def list_subscriptions(page_size=None, page_token=None, topic_name=None,
defaults to the connection inferred from the
environment.

:rtype: dict
:returns: keys include ``subscriptions`` (a list of subscription mappings)
and ``nextPageToken`` (a string: if non-empty, indicates that
:rtype: tuple, (list, str)
:returns: list of :class:`gcloud.pubsub.subscription.Subscription`, plus a
"next page token" string: if not None, indicates that
more topics can be retrieved with another call (pass that
value as ``page_token``).
"""
Expand All @@ -127,4 +126,49 @@ def list_subscriptions(page_size=None, page_token=None, topic_name=None,
else:
path = '/projects/%s/topics/%s/subscriptions' % (project, topic_name)

return connection.api_request(method='GET', path=path, query_params=params)
resp = connection.api_request(method='GET', path=path, query_params=params)
topics = {}
subscriptions = [_subscription_from_resource(resource, topics, connection)
for resource in resp['subscriptions']]
return subscriptions, resp.get('nextPageToken')


def _topic_from_resource(resource, connection):
"""Construct a topic given its full path-like name.

:type resource: dict
:param resource: topic resource representation returned from the API

:type connection: :class:`gcloud.pubsub.connection.Connection`
:param connection: connection to use for the topic.

:rtype: :class:`gcloud.pubsub.topic.Topic`
"""
_, project, _, name = resource['name'].split('/')
return Topic(name, project, connection)


def _subscription_from_resource(resource, topics, connection):
"""Construct a topic given its full path-like name.

:type resource: string
:param resource: subscription resource representation returned from the API

:type topics: dict, full_name -> :class:`gcloud.pubsub.topic.Topic`
:param topics: the topics to which subscriptions have been bound

:type connection: :class:`gcloud.pubsub.connection.Connection`
:param connection: connection to use for the topic.

:rtype: :class:`gcloud.pubsub.subscription.Subscription`
"""
t_name = resource['topic']
topic = topics.get(t_name)
if topic is None:
topic = topics[t_name] = _topic_from_resource({'name': t_name},
connection)
_, _, _, name = resource['name'].split('/')
ack_deadline = resource.get('ackDeadlineSeconds')
push_config = resource.get('pushConfig', {})
push_endpoint = push_config.get('pushEndpoint')
return Subscription(name, topic, ack_deadline, push_endpoint)
2 changes: 1 addition & 1 deletion gcloud/pubsub/subscription.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def create(self):
See:
https://cloud.google.com/pubsub/reference/rest/v1beta2/projects/subscriptions/create
"""
data = {'topic': self.topic.path}
data = {'topic': self.topic.full_name}

if self.ack_deadline is not None:
data['ackDeadline'] = self.ack_deadline
Expand Down
76 changes: 47 additions & 29 deletions gcloud/pubsub/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,48 +97,57 @@ def _callFUT(self, *args, **kw):
def test_w_implicit_connection_wo_paging(self):
from gcloud._testing import _monkey_defaults as _monkey_base_defaults
from gcloud.pubsub._testing import _monkey_defaults
from gcloud.pubsub.subscription import Subscription
PROJECT = 'PROJECT'
SUB_NAME = 'topic_name'
SUB_NAME = 'subscription_name'
SUB_PATH = 'projects/%s/subscriptions/%s' % (PROJECT, SUB_NAME)
TOPIC_NAME = 'topic_name'
TOPIC_PATH = 'projects/%s/topics/%s' % (PROJECT, TOPIC_NAME)
TOKEN = 'TOKEN'
returned = {'subscriptions': [{'name': SUB_PATH, 'topic': TOPIC_PATH}],
'nextPageToken': TOKEN}
SUB_INFO = [{'name': SUB_PATH, 'topic': TOPIC_PATH}]
returned = {'subscriptions': SUB_INFO}
conn = _Connection(returned)
with _monkey_base_defaults(project=PROJECT):
with _monkey_defaults(connection=conn):
response = self._callFUT()
subscriptions = response['subscriptions']
subscriptions, next_page_token = self._callFUT()
self.assertEqual(len(subscriptions), 1)
self.assertEqual(subscriptions[0],
{'name': SUB_PATH, 'topic': TOPIC_PATH})
self.assertEqual(response['nextPageToken'], TOKEN)
self.assertTrue(isinstance(subscriptions[0], Subscription))
self.assertEqual(subscriptions[0].name, SUB_NAME)
self.assertEqual(subscriptions[0].topic.name, TOPIC_NAME)
self.assertEqual(next_page_token, None)
self.assertEqual(len(conn._requested), 1)
req = conn._requested[0]
self.assertEqual(req['method'], 'GET')
self.assertEqual(req['path'], '/projects/%s/subscriptions' % PROJECT)
self.assertEqual(req['query_params'], {})

def test_w_explicit_connection_and_project_w_paging(self):
from gcloud.pubsub.subscription import Subscription
PROJECT = 'PROJECT'
SUB_NAME = 'topic_name'
SUB_NAME = 'subscription_name'
SUB_PATH = 'projects/%s/subscriptions/%s' % (PROJECT, SUB_NAME)
TOPIC_NAME = 'topic_name'
TOPIC_PATH = 'projects/%s/topics/%s' % (PROJECT, TOPIC_NAME)
ACK_DEADLINE = 42
PUSH_ENDPOINT = 'https://push.example.com/endpoint'
TOKEN1 = 'TOKEN1'
TOKEN2 = 'TOKEN2'
SIZE = 1
returned = {'subscriptions': [{'name': SUB_PATH, 'topic': TOPIC_PATH}],
'nextPageToken': TOKEN2}
SUB_INFO = [{'name': SUB_PATH,
'topic': TOPIC_PATH,
'ackDeadlineSeconds': ACK_DEADLINE,
'pushConfig': {'pushEndpoint': PUSH_ENDPOINT}}]
returned = {'subscriptions': SUB_INFO, 'nextPageToken': TOKEN2}
conn = _Connection(returned)
response = self._callFUT(SIZE, TOKEN1,
project=PROJECT, connection=conn)
subscriptions = response['subscriptions']
subscriptions, next_page_token = self._callFUT(SIZE, TOKEN1,
project=PROJECT,
connection=conn)
self.assertEqual(len(subscriptions), 1)
self.assertEqual(subscriptions[0],
{'name': SUB_PATH, 'topic': TOPIC_PATH})
self.assertEqual(response['nextPageToken'], TOKEN2)
self.assertTrue(isinstance(subscriptions[0], Subscription))
self.assertEqual(subscriptions[0].name, SUB_NAME)
self.assertEqual(subscriptions[0].topic.name, TOPIC_NAME)
self.assertEqual(subscriptions[0].ack_deadline, ACK_DEADLINE)
self.assertEqual(subscriptions[0].push_endpoint, PUSH_ENDPOINT)
self.assertEqual(next_page_token, TOKEN2)
self.assertEqual(len(conn._requested), 1)
req = conn._requested[0]
self.assertEqual(req['method'], 'GET')
Expand All @@ -147,22 +156,31 @@ def test_w_explicit_connection_and_project_w_paging(self):
{'pageSize': SIZE, 'pageToken': TOKEN1})

def test_w_topic_name(self):
from gcloud.pubsub.subscription import Subscription
PROJECT = 'PROJECT'
SUB_NAME = 'topic_name'
SUB_PATH = 'projects/%s/subscriptions/%s' % (PROJECT, SUB_NAME)
SUB_NAME_1 = 'subscription_1'
SUB_PATH_1 = 'projects/%s/subscriptions/%s' % (PROJECT, SUB_NAME_1)
SUB_NAME_2 = 'subscription_2'
SUB_PATH_2 = 'projects/%s/subscriptions/%s' % (PROJECT, SUB_NAME_2)
TOPIC_NAME = 'topic_name'
TOPIC_PATH = 'projects/%s/topics/%s' % (PROJECT, TOPIC_NAME)
SUB_INFO = [{'name': SUB_PATH_1, 'topic': TOPIC_PATH},
{'name': SUB_PATH_2, 'topic': TOPIC_PATH}]
TOKEN = 'TOKEN'
returned = {'subscriptions': [{'name': SUB_PATH, 'topic': TOPIC_PATH}],
'nextPageToken': TOKEN}
returned = {'subscriptions': SUB_INFO, 'nextPageToken': TOKEN}
conn = _Connection(returned)
response = self._callFUT(topic_name=TOPIC_NAME,
project=PROJECT, connection=conn)
subscriptions = response['subscriptions']
self.assertEqual(len(subscriptions), 1)
self.assertEqual(subscriptions[0],
{'name': SUB_PATH, 'topic': TOPIC_PATH})
self.assertEqual(response['nextPageToken'], TOKEN)
subscriptions, next_page_token = self._callFUT(topic_name=TOPIC_NAME,
project=PROJECT,
connection=conn)
self.assertEqual(len(subscriptions), 2)
self.assertTrue(isinstance(subscriptions[0], Subscription))
self.assertEqual(subscriptions[0].name, SUB_NAME_1)
self.assertEqual(subscriptions[0].topic.name, TOPIC_NAME)
self.assertTrue(isinstance(subscriptions[1], Subscription))
self.assertEqual(subscriptions[1].name, SUB_NAME_2)
self.assertEqual(subscriptions[1].topic.name, TOPIC_NAME)
self.assertTrue(subscriptions[1].topic is subscriptions[0].topic)
self.assertEqual(next_page_token, TOKEN)
self.assertEqual(len(conn._requested), 1)
req = conn._requested[0]
self.assertEqual(req['method'], 'GET')
Expand Down
3 changes: 2 additions & 1 deletion gcloud/pubsub/test_subscription.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,4 +296,5 @@ def __init__(self, name, project, connection):
self.name = name
self.project = project
self.connection = connection
self.path = 'projects/%s/topics/%s' % (project, name)
self.full_name = 'projects/%s/topics/%s' % (project, name)
self.path = '/projects/%s/topics/%s' % (project, name)
4 changes: 4 additions & 0 deletions gcloud/pubsub/test_topic.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ def test_ctor_wo_inferred_project_or_connection(self):
topic = self._makeOne(TOPIC_NAME)
self.assertEqual(topic.name, TOPIC_NAME)
self.assertEqual(topic.project, PROJECT)
self.assertEqual(topic.full_name,
'projects/%s/topics/%s' % (PROJECT, TOPIC_NAME))
self.assertTrue(topic.connection is conn)

def test_ctor_w_explicit_project_and_connection(self):
Expand All @@ -44,6 +46,8 @@ def test_ctor_w_explicit_project_and_connection(self):
topic = self._makeOne(TOPIC_NAME, project=PROJECT, connection=conn)
self.assertEqual(topic.name, TOPIC_NAME)
self.assertEqual(topic.project, PROJECT)
self.assertEqual(topic.full_name,
'projects/%s/topics/%s' % (PROJECT, TOPIC_NAME))
self.assertTrue(topic.connection is conn)

def test_create(self):
Expand Down
7 changes: 6 additions & 1 deletion gcloud/pubsub/topic.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,15 @@ def __init__(self, name, project=None, connection=None):
self.project = project
self.connection = connection

@property
def full_name(self):
"""Fully-qualified name used in topic / subscription APIs"""
return 'projects/%s/topics/%s' % (self.project, self.name)

@property
def path(self):
"""URL path for the topic's APIs"""
return '/projects/%s/topics/%s' % (self.project, self.name)
return '/%s' % (self.full_name)

def create(self):
"""API call: create the topic via a PUT request
Expand Down
56 changes: 47 additions & 9 deletions regression/pubsub.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@

from gcloud import _helpers
from gcloud import pubsub
from gcloud.pubsub.subscription import Subscription
from gcloud.pubsub.topic import Topic


_helpers._PROJECT_ENV_VAR_NAME = 'GCLOUD_TESTS_PROJECT_ID'
pubsub.set_defaults()


class TestPubsubTopics(unittest2.TestCase):
class TestPubsub(unittest2.TestCase):

def setUp(self):
self.to_delete = []
Expand All @@ -35,21 +36,20 @@ def tearDown(self):
doomed.delete()

def test_create_topic(self):
new_topic_name = 'a-new-topic'
topic = Topic(new_topic_name)
TOPIC_NAME = 'a-new-topic'
topic = Topic(TOPIC_NAME)
self.assertFalse(topic.exists())
topic.create()
self.to_delete.append(topic)
self.assertTrue(topic.exists())
self.assertEqual(topic.name, new_topic_name)
self.assertEqual(topic.name, TOPIC_NAME)

def test_list_topics(self):
topics_to_create = [
'new%d' % (1000 * time.time(),),
'newer%d' % (1000 * time.time(),),
'newest%d' % (1000 * time.time(),),
]
created_topics = []
for topic_name in topics_to_create:
topic = Topic(topic_name)
topic.create()
Expand All @@ -58,7 +58,45 @@ def test_list_topics(self):
# Retrieve the topics.
all_topics, _ = pubsub.list_topics()
project_id = pubsub.get_default_project()
created_topics = [topic for topic in all_topics
if topic.name in topics_to_create and
topic.project == project_id]
self.assertEqual(len(created_topics), len(topics_to_create))
created = [topic for topic in all_topics
if topic.name in topics_to_create and
topic.project == project_id]
self.assertEqual(len(created), len(topics_to_create))

def test_create_subscription(self):
TOPIC_NAME = 'subscribe-me'
topic = Topic(TOPIC_NAME)
self.assertFalse(topic.exists())
topic.create()
self.to_delete.append(topic)
SUBSCRIPTION_NAME = 'subscribing-now'
subscription = Subscription(SUBSCRIPTION_NAME, topic)
self.assertFalse(subscription.exists())
subscription.create()
self.to_delete.append(subscription)
self.assertTrue(subscription.exists())
self.assertEqual(subscription.name, SUBSCRIPTION_NAME)
self.assertTrue(subscription.topic is topic)

def test_list_subscriptions(self):
TOPIC_NAME = 'subscribe-me'
topic = Topic(TOPIC_NAME)
self.assertFalse(topic.exists())
topic.create()
self.to_delete.append(topic)
subscriptions_to_create = [
'new%d' % (1000 * time.time(),),
'newer%d' % (1000 * time.time(),),
'newest%d' % (1000 * time.time(),),
]
for subscription_name in subscriptions_to_create:
subscription = Subscription(subscription_name, topic)
subscription.create()
self.to_delete.append(subscription)

# Retrieve the subscriptions.
all_subscriptions, _ = pubsub.list_subscriptions()
created = [subscription for subscription in all_subscriptions
if subscription.name in subscriptions_to_create and
subscription.topic.name == TOPIC_NAME]
self.assertEqual(len(created), len(subscriptions_to_create))