Vehicle-Anti-Theft-Face-Rec.../venv/Lib/site-packages/gcloud/pubsub/topic.py

451 lines
16 KiB
Python

# Copyright 2015 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Define API Topics."""
import base64
from gcloud._helpers import _datetime_to_rfc3339
from gcloud._helpers import _NOW
from gcloud.exceptions import NotFound
from gcloud.pubsub._helpers import subscription_name_from_path
from gcloud.pubsub._helpers import topic_name_from_path
from gcloud.pubsub.iam import Policy
from gcloud.pubsub.subscription import Subscription
class Topic(object):
"""Topics are targets to which messages can be published.
Subscribers then receive those messages.
See:
https://cloud.google.com/pubsub/reference/rest/v1/projects.topics
:type name: string
:param name: the name of the topic
:type client: :class:`gcloud.pubsub.client.Client`
:param client: A client which holds credentials and project configuration
for the topic (which requires a project).
:type timestamp_messages: boolean
:param timestamp_messages: If true, the topic will add a ``timestamp`` key
to the attributes of each published message:
the value will be an RFC 3339 timestamp.
"""
def __init__(self, name, client, timestamp_messages=False):
self.name = name
self._client = client
self.timestamp_messages = timestamp_messages
def subscription(self, name, ack_deadline=None, push_endpoint=None):
"""Creates a subscription bound to the current topic.
Example: pull-mode subcription, default paramter values
.. literalinclude:: pubsub_snippets.py
:start-after: [START topic_subscription_defaults]
:end-before: [END topic_subscription_defaults]
Example: pull-mode subcription, override ``ack_deadline`` default
.. literalinclude:: pubsub_snippets.py
:start-after: [START topic_subscription_ack90]
:end-before: [END topic_subscription_ack90]
Example: push-mode subcription
.. literalinclude:: pubsub_snippets.py
:start-after: [START topic_subscription_push]
:end-before: [END topic_subscription_push]
:type name: string
:param name: the name of the subscription
:type ack_deadline: int
:param ack_deadline: the deadline (in seconds) by which messages pulled
from the back-end must be acknowledged.
:type push_endpoint: string
:param push_endpoint: URL to which messages will be pushed by the
back-end. If not set, the application must pull
messages.
"""
return Subscription(name, self, ack_deadline=ack_deadline,
push_endpoint=push_endpoint)
@classmethod
def from_api_repr(cls, resource, client):
"""Factory: construct a topic given its API representation
:type resource: dict
:param resource: topic resource representation returned from the API
:type client: :class:`gcloud.pubsub.client.Client`
:param client: Client which holds credentials and project
configuration for the topic.
:rtype: :class:`gcloud.pubsub.topic.Topic`
:returns: Topic parsed from ``resource``.
:raises: :class:`ValueError` if ``client`` is not ``None`` and the
project from the resource does not agree with the project
from the client.
"""
topic_name = topic_name_from_path(resource['name'], client.project)
return cls(topic_name, client=client)
@property
def project(self):
"""Project bound to the topic."""
return self._client.project
@property
def full_name(self):
"""Fully-qualified name used in topic / subscription APIs"""
return 'projects/%s/topics/%s' % (self.project, self.name)
def _require_client(self, client):
"""Check client or verify over-ride.
:type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current topic.
:rtype: :class:`gcloud.pubsub.client.Client`
:returns: The client passed in or the currently bound client.
"""
if client is None:
client = self._client
return client
def create(self, client=None):
"""API call: create the topic via a PUT request
See:
https://cloud.google.com/pubsub/reference/rest/v1/projects.topics/create
Example:
.. literalinclude:: pubsub_snippets.py
:start-after: [START topic_create]
:end-before: [END topic_create]
:type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current topic.
"""
client = self._require_client(client)
api = client.publisher_api
api.topic_create(topic_path=self.full_name)
def exists(self, client=None):
"""API call: test for the existence of the topic via a GET request
See
https://cloud.google.com/pubsub/reference/rest/v1/projects.topics/get
Example:
.. literalinclude:: pubsub_snippets.py
:start-after: [START topic_exists]
:end-before: [END topic_exists]
:type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current topic.
"""
client = self._require_client(client)
api = client.publisher_api
try:
api.topic_get(topic_path=self.full_name)
except NotFound:
return False
else:
return True
def delete(self, client=None):
"""API call: delete the topic via a DELETE request
See:
https://cloud.google.com/pubsub/reference/rest/v1/projects.topics/delete
Example:
.. literalinclude:: pubsub_snippets.py
:start-after: [START topic_delete]
:end-before: [END topic_delete]
:type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current topic.
"""
client = self._require_client(client)
api = client.publisher_api
api.topic_delete(topic_path=self.full_name)
def _timestamp_message(self, attrs):
"""Add a timestamp to ``attrs``, if the topic is so configured.
If ``attrs`` already has the key, do nothing.
Helper method for ``publish``/``Batch.publish``.
"""
if self.timestamp_messages and 'timestamp' not in attrs:
attrs['timestamp'] = _datetime_to_rfc3339(_NOW())
def publish(self, message, client=None, **attrs):
"""API call: publish a message to a topic via a POST request
See:
https://cloud.google.com/pubsub/reference/rest/v1/projects.topics/publish
Example without message attributes:
.. literalinclude:: pubsub_snippets.py
:start-after: [START topic_publish_simple_message]
:end-before: [END topic_publish_simple_message]
With message attributes:
.. literalinclude:: pubsub_snippets.py
:start-after: [START topic_publish_message_with_attrs]
:end-before: [END topic_publish_message_with_attrs]
:type message: bytes
:param message: the message payload
:type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current topic.
:type attrs: dict (string -> string)
:param attrs: key-value pairs to send as message attributes
:rtype: str
:returns: message ID assigned by the server to the published message
"""
client = self._require_client(client)
api = client.publisher_api
self._timestamp_message(attrs)
message_b = base64.b64encode(message).decode('ascii')
message_data = {'data': message_b, 'attributes': attrs}
message_ids = api.topic_publish(self.full_name, [message_data])
return message_ids[0]
def batch(self, client=None):
"""Return a batch to use as a context manager.
Example:
.. literalinclude:: pubsub_snippets.py
:start-after: [START topic_batch]
:end-before: [END topic_batch]
.. note::
The only API request happens during the ``__exit__()`` of the topic
used as a context manager, and only if the block exits without
raising an exception.
:type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current topic.
:rtype: :class:`Batch`
:returns: A batch to use as a context manager.
"""
client = self._require_client(client)
return Batch(self, client)
def list_subscriptions(self, page_size=None, page_token=None, client=None):
"""List subscriptions for the project associated with this client.
See:
https://cloud.google.com/pubsub/reference/rest/v1/projects.topics.subscriptions/list
Example:
.. literalinclude:: pubsub_snippets.py
:start-after: [START topic_list_subscriptions]
:end-before: [END topic_list_subscriptions]
:type page_size: int
:param page_size: maximum number of topics to return, If not passed,
defaults to a value set by the API.
:type page_token: string
:param page_token: opaque marker for the next "page" of topics. If not
passed, the API will return the first page of
topics.
:type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current topic.
: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``).
"""
client = self._require_client(client)
api = client.publisher_api
sub_paths, next_token = api.topic_list_subscriptions(
self.full_name, page_size, page_token)
subscriptions = []
for sub_path in sub_paths:
sub_name = subscription_name_from_path(sub_path, self.project)
subscriptions.append(Subscription(sub_name, self))
return subscriptions, next_token
def get_iam_policy(self, client=None):
"""Fetch the IAM policy for the topic.
See:
https://cloud.google.com/pubsub/reference/rest/v1/projects.topics/getIamPolicy
Example:
.. literalinclude:: pubsub_snippets.py
:start-after: [START topic_get_iam_policy]
:end-before: [END topic_get_iam_policy]
:type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current batch.
:rtype: :class:`gcloud.pubsub.iam.Policy`
:returns: policy created from the resource returned by the
``getIamPolicy`` API request.
"""
client = self._require_client(client)
api = client.iam_policy_api
resp = api.get_iam_policy(self.full_name)
return Policy.from_api_repr(resp)
def set_iam_policy(self, policy, client=None):
"""Update the IAM policy for the topic.
See:
https://cloud.google.com/pubsub/reference/rest/v1/projects.topics/setIamPolicy
Example:
.. literalinclude:: pubsub_snippets.py
:start-after: [START topic_set_iam_policy]
:end-before: [END topic_set_iam_policy]
:type policy: :class:`gcloud.pubsub.iam.Policy`
:param policy: the new policy, typically fetched via
:meth:`get_iam_policy` and updated in place.
:type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current batch.
:rtype: :class:`gcloud.pubsub.iam.Policy`
:returns: updated policy created from the resource returned by the
``setIamPolicy`` API request.
"""
client = self._require_client(client)
api = client.iam_policy_api
resource = policy.to_api_repr()
resp = api.set_iam_policy(self.full_name, resource)
return Policy.from_api_repr(resp)
def check_iam_permissions(self, permissions, client=None):
"""Verify permissions allowed for the current user.
See:
https://cloud.google.com/pubsub/reference/rest/v1/projects.topics/testIamPermissions
Example:
.. literalinclude:: pubsub_snippets.py
:start-after: [START topic_check_iam_permissions]
:end-before: [END topic_check_iam_permissions]
:type permissions: list of string
:param permissions: list of permissions to be tested
:type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current batch.
:rtype: sequence of string
:returns: subset of ``permissions`` allowed by current IAM policy.
"""
client = self._require_client(client)
api = client.iam_policy_api
return api.test_iam_permissions(
self.full_name, list(permissions))
class Batch(object):
"""Context manager: collect messages to publish via a single API call.
Helper returned by :meth:Topic.batch
:type topic: :class:`gcloud.pubsub.topic.Topic`
:param topic: the topic being published
:type client: :class:`gcloud.pubsub.client.Client`
:param client: The client to use.
"""
def __init__(self, topic, client):
self.topic = topic
self.messages = []
self.message_ids = []
self.client = client
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is None:
self.commit()
def __iter__(self):
return iter(self.message_ids)
def publish(self, message, **attrs):
"""Emulate publishing a message, but save it.
:type message: bytes
:param message: the message payload
:type attrs: dict (string -> string)
:param attrs: key-value pairs to send as message attributes
"""
self.topic._timestamp_message(attrs)
self.messages.append(
{'data': base64.b64encode(message).decode('ascii'),
'attributes': attrs})
def commit(self, client=None):
"""Send saved messages as a single API call.
:type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current batch.
"""
if client is None:
client = self.client
api = client.publisher_api
message_ids = api.topic_publish(self.topic.full_name, self.messages[:])
self.message_ids.extend(message_ids)
del self.messages[:]