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

Side by Side Diff: third_party/google_api_python_client/googleapiclient/channel.py

Issue 963953003: OAuth2 support in depot_tools (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: restore git_cl Created 5 years, 8 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 """Channel notifications support.
2
3 Classes and functions to support channel subscriptions and notifications
4 on those channels.
5
6 Notes:
7 - This code is based on experimental APIs and is subject to change.
8 - Notification does not do deduplication of notification ids, that's up to
9 the receiver.
10 - Storing the Channel between calls is up to the caller.
11
12
13 Example setting up a channel:
14
15 # Create a new channel that gets notifications via webhook.
16 channel = new_webhook_channel("https://example.com/my_web_hook")
17
18 # Store the channel, keyed by 'channel.id'. Store it before calling the
19 # watch method because notifications may start arriving before the watch
20 # method returns.
21 ...
22
23 resp = service.objects().watchAll(
24 bucket="some_bucket_id", body=channel.body()).execute()
25 channel.update(resp)
26
27 # Store the channel, keyed by 'channel.id'. Store it after being updated
28 # since the resource_id value will now be correct, and that's needed to
29 # stop a subscription.
30 ...
31
32
33 An example Webhook implementation using webapp2. Note that webapp2 puts
34 headers in a case insensitive dictionary, as headers aren't guaranteed to
35 always be upper case.
36
37 id = self.request.headers[X_GOOG_CHANNEL_ID]
38
39 # Retrieve the channel by id.
40 channel = ...
41
42 # Parse notification from the headers, including validating the id.
43 n = notification_from_headers(channel, self.request.headers)
44
45 # Do app specific stuff with the notification here.
46 if n.resource_state == 'sync':
47 # Code to handle sync state.
48 elif n.resource_state == 'exists':
49 # Code to handle the exists state.
50 elif n.resource_state == 'not_exists':
51 # Code to handle the not exists state.
52
53
54 Example of unsubscribing.
55
56 service.channels().stop(channel.body())
57 """
58
59 import datetime
60 import uuid
61
62 from googleapiclient import errors
63 from ...oauth2client import util
64
65
66 # The unix time epoch starts at midnight 1970.
67 EPOCH = datetime.datetime.utcfromtimestamp(0)
68
69 # Map the names of the parameters in the JSON channel description to
70 # the parameter names we use in the Channel class.
71 CHANNEL_PARAMS = {
72 'address': 'address',
73 'id': 'id',
74 'expiration': 'expiration',
75 'params': 'params',
76 'resourceId': 'resource_id',
77 'resourceUri': 'resource_uri',
78 'type': 'type',
79 'token': 'token',
80 }
81
82 X_GOOG_CHANNEL_ID = 'X-GOOG-CHANNEL-ID'
83 X_GOOG_MESSAGE_NUMBER = 'X-GOOG-MESSAGE-NUMBER'
84 X_GOOG_RESOURCE_STATE = 'X-GOOG-RESOURCE-STATE'
85 X_GOOG_RESOURCE_URI = 'X-GOOG-RESOURCE-URI'
86 X_GOOG_RESOURCE_ID = 'X-GOOG-RESOURCE-ID'
87
88
89 def _upper_header_keys(headers):
90 new_headers = {}
91 for k, v in headers.iteritems():
92 new_headers[k.upper()] = v
93 return new_headers
94
95
96 class Notification(object):
97 """A Notification from a Channel.
98
99 Notifications are not usually constructed directly, but are returned
100 from functions like notification_from_headers().
101
102 Attributes:
103 message_number: int, The unique id number of this notification.
104 state: str, The state of the resource being monitored.
105 uri: str, The address of the resource being monitored.
106 resource_id: str, The unique identifier of the version of the resource at
107 this event.
108 """
109 @util.positional(5)
110 def __init__(self, message_number, state, resource_uri, resource_id):
111 """Notification constructor.
112
113 Args:
114 message_number: int, The unique id number of this notification.
115 state: str, The state of the resource being monitored. Can be one
116 of "exists", "not_exists", or "sync".
117 resource_uri: str, The address of the resource being monitored.
118 resource_id: str, The identifier of the watched resource.
119 """
120 self.message_number = message_number
121 self.state = state
122 self.resource_uri = resource_uri
123 self.resource_id = resource_id
124
125
126 class Channel(object):
127 """A Channel for notifications.
128
129 Usually not constructed directly, instead it is returned from helper
130 functions like new_webhook_channel().
131
132 Attributes:
133 type: str, The type of delivery mechanism used by this channel. For
134 example, 'web_hook'.
135 id: str, A UUID for the channel.
136 token: str, An arbitrary string associated with the channel that
137 is delivered to the target address with each event delivered
138 over this channel.
139 address: str, The address of the receiving entity where events are
140 delivered. Specific to the channel type.
141 expiration: int, The time, in milliseconds from the epoch, when this
142 channel will expire.
143 params: dict, A dictionary of string to string, with additional parameters
144 controlling delivery channel behavior.
145 resource_id: str, An opaque id that identifies the resource that is
146 being watched. Stable across different API versions.
147 resource_uri: str, The canonicalized ID of the watched resource.
148 """
149
150 @util.positional(5)
151 def __init__(self, type, id, token, address, expiration=None,
152 params=None, resource_id="", resource_uri=""):
153 """Create a new Channel.
154
155 In user code, this Channel constructor will not typically be called
156 manually since there are functions for creating channels for each specific
157 type with a more customized set of arguments to pass.
158
159 Args:
160 type: str, The type of delivery mechanism used by this channel. For
161 example, 'web_hook'.
162 id: str, A UUID for the channel.
163 token: str, An arbitrary string associated with the channel that
164 is delivered to the target address with each event delivered
165 over this channel.
166 address: str, The address of the receiving entity where events are
167 delivered. Specific to the channel type.
168 expiration: int, The time, in milliseconds from the epoch, when this
169 channel will expire.
170 params: dict, A dictionary of string to string, with additional parameters
171 controlling delivery channel behavior.
172 resource_id: str, An opaque id that identifies the resource that is
173 being watched. Stable across different API versions.
174 resource_uri: str, The canonicalized ID of the watched resource.
175 """
176 self.type = type
177 self.id = id
178 self.token = token
179 self.address = address
180 self.expiration = expiration
181 self.params = params
182 self.resource_id = resource_id
183 self.resource_uri = resource_uri
184
185 def body(self):
186 """Build a body from the Channel.
187
188 Constructs a dictionary that's appropriate for passing into watch()
189 methods as the value of body argument.
190
191 Returns:
192 A dictionary representation of the channel.
193 """
194 result = {
195 'id': self.id,
196 'token': self.token,
197 'type': self.type,
198 'address': self.address
199 }
200 if self.params:
201 result['params'] = self.params
202 if self.resource_id:
203 result['resourceId'] = self.resource_id
204 if self.resource_uri:
205 result['resourceUri'] = self.resource_uri
206 if self.expiration:
207 result['expiration'] = self.expiration
208
209 return result
210
211 def update(self, resp):
212 """Update a channel with information from the response of watch().
213
214 When a request is sent to watch() a resource, the response returned
215 from the watch() request is a dictionary with updated channel information,
216 such as the resource_id, which is needed when stopping a subscription.
217
218 Args:
219 resp: dict, The response from a watch() method.
220 """
221 for json_name, param_name in CHANNEL_PARAMS.iteritems():
222 value = resp.get(json_name)
223 if value is not None:
224 setattr(self, param_name, value)
225
226
227 def notification_from_headers(channel, headers):
228 """Parse a notification from the webhook request headers, validate
229 the notification, and return a Notification object.
230
231 Args:
232 channel: Channel, The channel that the notification is associated with.
233 headers: dict, A dictionary like object that contains the request headers
234 from the webhook HTTP request.
235
236 Returns:
237 A Notification object.
238
239 Raises:
240 errors.InvalidNotificationError if the notification is invalid.
241 ValueError if the X-GOOG-MESSAGE-NUMBER can't be converted to an int.
242 """
243 headers = _upper_header_keys(headers)
244 channel_id = headers[X_GOOG_CHANNEL_ID]
245 if channel.id != channel_id:
246 raise errors.InvalidNotificationError(
247 'Channel id mismatch: %s != %s' % (channel.id, channel_id))
248 else:
249 message_number = int(headers[X_GOOG_MESSAGE_NUMBER])
250 state = headers[X_GOOG_RESOURCE_STATE]
251 resource_uri = headers[X_GOOG_RESOURCE_URI]
252 resource_id = headers[X_GOOG_RESOURCE_ID]
253 return Notification(message_number, state, resource_uri, resource_id)
254
255
256 @util.positional(2)
257 def new_webhook_channel(url, token=None, expiration=None, params=None):
258 """Create a new webhook Channel.
259
260 Args:
261 url: str, URL to post notifications to.
262 token: str, An arbitrary string associated with the channel that
263 is delivered to the target address with each notification delivered
264 over this channel.
265 expiration: datetime.datetime, A time in the future when the channel
266 should expire. Can also be None if the subscription should use the
267 default expiration. Note that different services may have different
268 limits on how long a subscription lasts. Check the response from the
269 watch() method to see the value the service has set for an expiration
270 time.
271 params: dict, Extra parameters to pass on channel creation. Currently
272 not used for webhook channels.
273 """
274 expiration_ms = 0
275 if expiration:
276 delta = expiration - EPOCH
277 expiration_ms = delta.microseconds/1000 + (
278 delta.seconds + delta.days*24*3600)*1000
279 if expiration_ms < 0:
280 expiration_ms = 0
281
282 return Channel('web_hook', str(uuid.uuid4()),
283 token, url, expiration=expiration_ms,
284 params=params)
285
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698