1
+ """Utility classes and functions"""
2
+
3
+ from __future__ import absolute_import , unicode_literals
4
+
5
+ import contextlib
6
+ import json
7
+ import requests
8
+ from requests_oauthlib import OAuth2Session
9
+ import time
10
+ import jwt
11
+ from http .server import HTTPServer , BaseHTTPRequestHandler
12
+ import socketserver
13
+ from threading import Thread
14
+ from urllib .parse import urlparse , urlencode , quote
15
+ import webbrowser
16
+ import os
17
+
18
+ class ApiClient (object ):
19
+ """Simple wrapper for REST API requests"""
20
+
21
+ def __init__ (self , base_uri = None , timeout = 15 , ** kwargs ):
22
+ """Setup a new API Client
23
+
24
+ :param base_uri: The base URI to the API
25
+ :param timeout: The timeout to use for requests
26
+ :param kwargs: Any other attributes. These will be added as
27
+ attributes to the ApiClient object.
28
+ """
29
+ self .base_uri = base_uri
30
+ self .timeout = timeout
31
+ for k , v in kwargs .items ():
32
+ setattr (self , k , v )
33
+
34
+ @property
35
+ def timeout (self ):
36
+ """The timeout"""
37
+ return self ._timeout
38
+
39
+ @timeout .setter
40
+ def timeout (self , value ):
41
+ """The default timeout"""
42
+ if value is not None :
43
+ try :
44
+ value = int (value )
45
+ except ValueError :
46
+ raise ValueError ("timeout value must be an integer" )
47
+ self ._timeout = value
48
+
49
+ @property
50
+ def base_uri (self ):
51
+ """The base_uri"""
52
+ return self ._base_uri
53
+
54
+ @base_uri .setter
55
+ def base_uri (self , value ):
56
+ """The default base_uri"""
57
+ if value and value .endswith ("/" ):
58
+ value = value [:- 1 ]
59
+ self ._base_uri = value
60
+
61
+ def url_for (self , endpoint ):
62
+ """Get the URL for the given endpoint
63
+
64
+ :param endpoint: The endpoint
65
+ :return: The full URL for the endpoint
66
+ """
67
+ if not endpoint .startswith ("/" ):
68
+ endpoint = "/{}" .format (endpoint )
69
+ if endpoint .endswith ("/" ):
70
+ endpoint = endpoint [:- 1 ]
71
+ return self .base_uri + endpoint
72
+
73
+ def get_request (self , endpoint , params = None , headers = None ):
74
+ """Helper function for GET requests
75
+
76
+ :param endpoint: The endpoint
77
+ :param params: The URL parameters
78
+ :param headers: request headers
79
+ :return: The :class:``requests.Response`` object for this request
80
+ """
81
+ if headers is None :
82
+ headers = {"Authorization" : "Bearer {}" .format (self .config .get ("token" ))}
83
+ return requests .get (
84
+ self .url_for (endpoint ), params = params , headers = headers , timeout = self .timeout
85
+ )
86
+
87
+ def post_request (
88
+ self , endpoint , params = None , data = None , headers = None , cookies = None
89
+ ):
90
+ """Helper function for POST requests
91
+
92
+ :param endpoint: The endpoint
93
+ :param params: The URL parameters
94
+ :param data: The data (either as a dict or dumped JSON string) to
95
+ include with the POST
96
+ :param headers: request headers
97
+ :param cookies: request cookies
98
+ :return: The :class:``requests.Response`` object for this request
99
+ """
100
+ if data and not is_str_type (data ):
101
+ data = json .dumps (data )
102
+ if headers is None :
103
+ headers = {"Authorization" : "Bearer {}" .format (self .config .get ("token" ))}
104
+ headers ["Content-type" ] = "application/json"
105
+ return requests .post (
106
+ self .url_for (endpoint ),
107
+ params = params ,
108
+ data = data ,
109
+ headers = headers ,
110
+ cookies = cookies ,
111
+ timeout = self .timeout ,
112
+ )
113
+
114
+ def patch_request (
115
+ self , endpoint , params = None , data = None , headers = None , cookies = None
116
+ ):
117
+ """Helper function for PATCH requests
118
+
119
+ :param endpoint: The endpoint
120
+ :param params: The URL parameters
121
+ :param data: The data (either as a dict or dumped JSON string) to
122
+ include with the PATCH
123
+ :param headers: request headers
124
+ :param cookies: request cookies
125
+ :return: The :class:``requests.Response`` object for this request
126
+ """
127
+ if data and not is_str_type (data ):
128
+ data = json .dumps (data )
129
+ if headers is None :
130
+ headers = {"Authorization" : "Bearer {}" .format (self .config .get ("token" ))}
131
+ return requests .patch (
132
+ self .url_for (endpoint ),
133
+ params = params ,
134
+ data = data ,
135
+ headers = headers ,
136
+ cookies = cookies ,
137
+ timeout = self .timeout ,
138
+ )
139
+
140
+ def delete_request (
141
+ self , endpoint , params = None , data = None , headers = None , cookies = None
142
+ ):
143
+ """Helper function for DELETE requests
144
+
145
+ :param endpoint: The endpoint
146
+ :param params: The URL parameters
147
+ :param data: The data (either as a dict or dumped JSON string) to
148
+ include with the DELETE
149
+ :param headers: request headers
150
+ :param cookies: request cookies
151
+ :return: The :class:``requests.Response`` object for this request
152
+ """
153
+ if data and not is_str_type (data ):
154
+ data = json .dumps (data )
155
+ if headers is None :
156
+ headers = {"Authorization" : "Bearer {}" .format (self .config .get ("token" ))}
157
+ return requests .delete (
158
+ self .url_for (endpoint ),
159
+ params = params ,
160
+ data = data ,
161
+ headers = headers ,
162
+ cookies = cookies ,
163
+ timeout = self .timeout ,
164
+ )
165
+
166
+ def put_request (self , endpoint , params = None , data = None , headers = None , cookies = None ):
167
+ """Helper function for PUT requests
168
+
169
+ :param endpoint: The endpoint
170
+ :param params: The URL paramaters
171
+ :param data: The data (either as a dict or dumped JSON string) to
172
+ include with the PUT
173
+ :param headers: request headers
174
+ :param cookies: request cookies
175
+ :return: The :class:``requests.Response`` object for this request
176
+ """
177
+ if data and not is_str_type (data ):
178
+ data = json .dumps (data )
179
+ if headers is None :
180
+ headers = {"Authorization" : "Bearer {}" .format (self .config .get ("token" ))}
181
+ return requests .put (
182
+ self .url_for (endpoint ),
183
+ params = params ,
184
+ data = data ,
185
+ headers = headers ,
186
+ cookies = cookies ,
187
+ timeout = self .timeout ,
188
+ )
189
+
190
+
191
+ @contextlib .contextmanager
192
+ def ignored (* exceptions ):
193
+ """Simple context manager to ignore expected Exceptions
194
+
195
+ :param \*exceptions: The exceptions to safely ignore
196
+ """
197
+ try :
198
+ yield
199
+ except exceptions :
200
+ pass
201
+
202
+
203
+ def is_str_type (val ):
204
+ """Check whether the input is of a string type.
205
+
206
+ We use this method to ensure python 2-3 capatibility.
207
+
208
+ :param val: The value to check wither it is a string
209
+ :return: In python2 it will return ``True`` if :attr:`val` is either an
210
+ instance of str or unicode. In python3 it will return ``True`` if
211
+ it is an instance of str
212
+ """
213
+ with ignored (NameError ):
214
+ return isinstance (val , basestring )
215
+ return isinstance (val , str )
216
+
217
+
218
+ def require_keys (d , keys , allow_none = True ):
219
+ """Require that the object have the given keys
220
+
221
+ :param d: The dict the check
222
+ :param keys: The keys to check :attr:`obj` for. This can either be a single
223
+ string, or an iterable of strings
224
+
225
+ :param allow_none: Whether ``None`` values are allowed
226
+ :raises:
227
+ :ValueError: If any of the keys are missing from the obj
228
+ """
229
+ if is_str_type (keys ):
230
+ keys = [keys ]
231
+ for k in keys :
232
+ if k not in d :
233
+ raise ValueError ("'{}' must be set" .format (k ))
234
+ if not allow_none and d [k ] is None :
235
+ raise ValueError ("'{}' cannot be None" .format (k ))
236
+ return True
237
+
238
+
239
+ def date_to_str (d ):
240
+ """Convert date and datetime objects to a string
241
+
242
+ Note, this does not do any timezone conversion.
243
+
244
+ :param d: The :class:`datetime.date` or :class:`datetime.datetime` to
245
+ convert to a string
246
+ :returns: The string representation of the date
247
+ """
248
+ return d .strftime ("%Y-%m-%dT%H:%M:%SZ" )
249
+
250
+
251
+ def generate_jwt (key , secret ):
252
+ header = {"alg" : "HS256" , "typ" : "JWT" }
253
+
254
+ payload = {"iss" : key , "exp" : int (time .time () + 3600 )}
255
+
256
+ token = jwt .encode (payload , secret , algorithm = "HS256" , headers = header )
257
+ return token .decode ("utf-8" )
258
+
259
+ class TokenHandler (BaseHTTPRequestHandler ):
260
+ code = None
261
+ def do_GET (self ):
262
+ print ("GET request to " + self .path )
263
+ query = urlparse (self .path ).query
264
+ if len (query ) > 0 :
265
+ print ("query is " + query )
266
+ query_components = dict (qc .split ("=" ) for qc in query .split ("&" ))
267
+ TokenHandler .code = query_components ["code" ]
268
+ print ("***" + TokenHandler .code + "***" )
269
+ self .send_response (200 )
270
+ self .end_headers ()
271
+
272
+ def http_receiver ():
273
+ with socketserver .TCPServer (("" , 4000 ), TokenHandler ) as httpd :
274
+ print ("serving at port" , 4000 )
275
+ while TokenHandler .code == None :
276
+ httpd .handle_request ()
277
+ print ("End of http receiver" )
278
+
279
+ def get_oauth_token (cid , client_secret , redirect_url ):
280
+
281
+ oauth = OAuth2Session (client_id = cid , redirect_uri = redirect_url )
282
+ authorization_url , state = oauth .authorization_url (
283
+ 'https://zoom.us/oauth/authorize' )
284
+
285
+ print ('Going to %s to authorize access.' % authorization_url )
286
+ chrome_path = r'/mnt/c/Program\ Files\ \(x86\)/Google/Chrome/Application/chrome.exe'
287
+ authorization_url = authorization_url .replace ('&' , '\&' )
288
+ print (authorization_url )
289
+ os .system (chrome_path + " " + authorization_url )
290
+
291
+ http_receiver ()
292
+
293
+ token = oauth .fetch_token (
294
+ 'https://zoom.us/oauth/token' ,
295
+ code = TokenHandler .code ,
296
+ client_secret = client_secret )
297
+ resp = dict (token )
298
+ return resp ["access_token" ]
0 commit comments