Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(508)

Unified Diff: reviewbot/third_party/google-api-python-client/apiclient/push.py

Issue 20515002: Add google-api-python-client in third_party/ (Closed) Base URL: https://src.chromium.org/chrome/trunk/tools/
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: reviewbot/third_party/google-api-python-client/apiclient/push.py
===================================================================
--- reviewbot/third_party/google-api-python-client/apiclient/push.py (revision 0)
+++ reviewbot/third_party/google-api-python-client/apiclient/push.py (revision 0)
@@ -0,0 +1,274 @@
+# 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.
+
+"""Push notifications support.
+
+This code is based on experimental APIs and is subject to change.
+"""
+
+__author__ = 'afshar@google.com (Ali Afshar)'
+
+import binascii
+import collections
+import os
+import urllib
+
+SUBSCRIBE = 'X-GOOG-SUBSCRIBE'
+SUBSCRIPTION_ID = 'X-GOOG-SUBSCRIPTION-ID'
+TOPIC_ID = 'X-GOOG-TOPIC-ID'
+TOPIC_URI = 'X-GOOG-TOPIC-URI'
+CLIENT_TOKEN = 'X-GOOG-CLIENT-TOKEN'
+EVENT_TYPE = 'X-GOOG-EVENT-TYPE'
+UNSUBSCRIBE = 'X-GOOG-UNSUBSCRIBE'
+
+
+class InvalidSubscriptionRequestError(ValueError):
+ """The request cannot be subscribed."""
+
+
+def new_token():
+ """Gets a random token for use as a client_token in push notifications.
+
+ Returns:
+ str, a new random token.
+ """
+ return binascii.hexlify(os.urandom(32))
+
+
+class Channel(object):
+ """Base class for channel types."""
+
+ def __init__(self, channel_type, channel_args):
+ """Create a new Channel.
+
+ You probably won't need to create this channel manually, since there are
+ subclassed Channel for each specific type with a more customized set of
+ arguments to pass. However, you may wish to just create it manually here.
+
+ Args:
+ channel_type: str, the type of channel.
+ channel_args: dict, arguments to pass to the channel.
+ """
+ self.channel_type = channel_type
+ self.channel_args = channel_args
+
+ def as_header_value(self):
+ """Create the appropriate header for this channel.
+
+ Returns:
+ str encoded channel description suitable for use as a header.
+ """
+ return '%s?%s' % (self.channel_type, urllib.urlencode(self.channel_args))
+
+ def write_header(self, headers):
+ """Write the appropriate subscribe header to a headers dict.
+
+ Args:
+ headers: dict, headers to add subscribe header to.
+ """
+ headers[SUBSCRIBE] = self.as_header_value()
+
+
+class WebhookChannel(Channel):
+ """Channel for registering web hook notifications."""
+
+ def __init__(self, url, app_engine=False):
+ """Create a new WebhookChannel
+
+ Args:
+ url: str, URL to post notifications to.
+ app_engine: bool, default=False, whether the destination for the
+ notifications is an App Engine application.
+ """
+ super(WebhookChannel, self).__init__(
+ channel_type='web_hook',
+ channel_args={
+ 'url': url,
+ 'app_engine': app_engine and 'true' or 'false',
+ }
+ )
+
+
+class Headers(collections.defaultdict):
+ """Headers for managing subscriptions."""
+
+
+ ALL_HEADERS = set([SUBSCRIBE, SUBSCRIPTION_ID, TOPIC_ID, TOPIC_URI,
+ CLIENT_TOKEN, EVENT_TYPE, UNSUBSCRIBE])
+
+ def __init__(self):
+ """Create a new subscription configuration instance."""
+ collections.defaultdict.__init__(self, str)
+
+ def __setitem__(self, key, value):
+ """Set a header value, ensuring the key is an allowed value.
+
+ Args:
+ key: str, the header key.
+ value: str, the header value.
+ Raises:
+ ValueError if key is not one of the accepted headers.
+ """
+ normal_key = self._normalize_key(key)
+ if normal_key not in self.ALL_HEADERS:
+ raise ValueError('Header name must be one of %s.' % self.ALL_HEADERS)
+ else:
+ return collections.defaultdict.__setitem__(self, normal_key, value)
+
+ def __getitem__(self, key):
+ """Get a header value, normalizing the key case.
+
+ Args:
+ key: str, the header key.
+ Returns:
+ String header value.
+ Raises:
+ KeyError if the key is not one of the accepted headers.
+ """
+ normal_key = self._normalize_key(key)
+ if normal_key not in self.ALL_HEADERS:
+ raise ValueError('Header name must be one of %s.' % self.ALL_HEADERS)
+ else:
+ return collections.defaultdict.__getitem__(self, normal_key)
+
+ def _normalize_key(self, key):
+ """Normalize a header name for use as a key."""
+ return key.upper()
+
+ def items(self):
+ """Generator for each header."""
+ for header in self.ALL_HEADERS:
+ value = self[header]
+ if value:
+ yield header, value
+
+ def write(self, headers):
+ """Applies the subscription headers.
+
+ Args:
+ headers: dict of headers to insert values into.
+ """
+ for header, value in self.items():
+ headers[header.lower()] = value
+
+ def read(self, headers):
+ """Read from headers.
+
+ Args:
+ headers: dict of headers to read from.
+ """
+ for header in self.ALL_HEADERS:
+ if header.lower() in headers:
+ self[header] = headers[header.lower()]
+
+
+class Subscription(object):
+ """Information about a subscription."""
+
+ def __init__(self):
+ """Create a new Subscription."""
+ self.headers = Headers()
+
+ @classmethod
+ def for_request(cls, request, channel, client_token=None):
+ """Creates a subscription and attaches it to a request.
+
+ Args:
+ request: An http.HttpRequest to modify for making a subscription.
+ channel: A apiclient.push.Channel describing the subscription to
+ create.
+ client_token: (optional) client token to verify the notification.
+
+ Returns:
+ New subscription object.
+ """
+ subscription = cls.for_channel(channel=channel, client_token=client_token)
+ subscription.headers.write(request.headers)
+ if request.method != 'GET':
+ raise InvalidSubscriptionRequestError(
+ 'Can only subscribe to requests which are GET.')
+ request.method = 'POST'
+
+ def _on_response(response, subscription=subscription):
+ """Called with the response headers. Reads the subscription headers."""
+ subscription.headers.read(response)
+
+ request.add_response_callback(_on_response)
+ return subscription
+
+ @classmethod
+ def for_channel(cls, channel, client_token=None):
+ """Alternate constructor to create a subscription from a channel.
+
+ Args:
+ channel: A apiclient.push.Channel describing the subscription to
+ create.
+ client_token: (optional) client token to verify the notification.
+
+ Returns:
+ New subscription object.
+ """
+ subscription = cls()
+ channel.write_header(subscription.headers)
+ if client_token is None:
+ client_token = new_token()
+ subscription.headers[SUBSCRIPTION_ID] = new_token()
+ subscription.headers[CLIENT_TOKEN] = client_token
+ return subscription
+
+ def verify(self, headers):
+ """Verifies that a webhook notification has the correct client_token.
+
+ Args:
+ headers: dict of request headers for a push notification.
+
+ Returns:
+ Boolean value indicating whether the notification is verified.
+ """
+ new_subscription = Subscription()
+ new_subscription.headers.read(headers)
+ return new_subscription.client_token == self.client_token
+
+ @property
+ def subscribe(self):
+ """Subscribe header value."""
+ return self.headers[SUBSCRIBE]
+
+ @property
+ def subscription_id(self):
+ """Subscription ID header value."""
+ return self.headers[SUBSCRIPTION_ID]
+
+ @property
+ def topic_id(self):
+ """Topic ID header value."""
+ return self.headers[TOPIC_ID]
+
+ @property
+ def topic_uri(self):
+ """Topic URI header value."""
+ return self.headers[TOPIC_URI]
+
+ @property
+ def client_token(self):
+ """Client Token header value."""
+ return self.headers[CLIENT_TOKEN]
+
+ @property
+ def event_type(self):
+ """Event Type header value."""
+ return self.headers[EVENT_TYPE]
+
+ @property
+ def unsubscribe(self):
+ """Unsuscribe header value."""
+ return self.headers[UNSUBSCRIBE]
Property changes on: reviewbot/third_party/google-api-python-client/apiclient/push.py
___________________________________________________________________
Added: svn:eol-style
+ LF

Powered by Google App Engine
This is Rietveld 408576698