diff options
Diffstat (limited to 'docs/client.html')
-rw-r--r-- | docs/client.html | 1201 |
1 files changed, 1201 insertions, 0 deletions
diff --git a/docs/client.html b/docs/client.html new file mode 100644 index 0000000..4e69b89 --- /dev/null +++ b/docs/client.html | |||
@@ -0,0 +1,1201 @@ | |||
1 | <!DOCTYPE html> | ||
2 | <html> | ||
3 | <head> | ||
4 | <meta http-equiv="content-type" content="text/html;charset=utf-8"> | ||
5 | <title>client.py</title> | ||
6 | <link rel="stylesheet" href="pycco.css"> | ||
7 | </head> | ||
8 | <body> | ||
9 | <div id='container'> | ||
10 | <div id="background"></div> | ||
11 | <div class='section'> | ||
12 | <div class='docs'><h1>client.py</h1></div> | ||
13 | </div> | ||
14 | <div class='clearall'> | ||
15 | <div class='section' id='section-0'> | ||
16 | <div class='docs'> | ||
17 | <div class='octowrap'> | ||
18 | <a class='octothorpe' href='#section-0'>#</a> | ||
19 | </div> | ||
20 | |||
21 | </div> | ||
22 | <div class='code'> | ||
23 | <div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span><span class="p">,</span> <span class="n">timedelta</span> | ||
24 | <span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">OrderedDict</span> | ||
25 | <span class="kn">import</span> <span class="nn">backoff</span> | ||
26 | <span class="kn">import</span> <span class="nn">requests</span> | ||
27 | <span class="kn">import</span> <span class="nn">singer</span> | ||
28 | <span class="kn">from</span> <span class="nn">singer</span> <span class="kn">import</span> <span class="n">metrics</span> | ||
29 | <span class="kn">from</span> <span class="nn">singer</span> <span class="kn">import</span> <span class="n">utils</span> | ||
30 | |||
31 | <span class="n">BASE_URL</span> <span class="o">=</span> <span class="s1">'https://www.googleapis.com'</span> | ||
32 | <span class="n">GOOGLE_TOKEN_URI</span> <span class="o">=</span> <span class="s1">'https://oauth2.googleapis.com/token'</span> | ||
33 | <span class="n">LOGGER</span> <span class="o">=</span> <span class="n">singer</span><span class="o">.</span><span class="n">get_logger</span><span class="p">()</span></pre></div> | ||
34 | </div> | ||
35 | </div> | ||
36 | <div class='clearall'></div> | ||
37 | <div class='section' id='section-1'> | ||
38 | <div class='docs'> | ||
39 | <div class='octowrap'> | ||
40 | <a class='octothorpe' href='#section-1'>#</a> | ||
41 | </div> | ||
42 | |||
43 | </div> | ||
44 | <div class='code'> | ||
45 | <div class="highlight"><pre><span class="k">class</span> <span class="nc">Server5xxError</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span> | ||
46 | <span class="k">pass</span></pre></div> | ||
47 | </div> | ||
48 | </div> | ||
49 | <div class='clearall'></div> | ||
50 | <div class='section' id='section-2'> | ||
51 | <div class='docs'> | ||
52 | <div class='octowrap'> | ||
53 | <a class='octothorpe' href='#section-2'>#</a> | ||
54 | </div> | ||
55 | |||
56 | </div> | ||
57 | <div class='code'> | ||
58 | <div class="highlight"><pre><span class="k">class</span> <span class="nc">Server429Error</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span> | ||
59 | <span class="k">pass</span></pre></div> | ||
60 | </div> | ||
61 | </div> | ||
62 | <div class='clearall'></div> | ||
63 | <div class='section' id='section-3'> | ||
64 | <div class='docs'> | ||
65 | <div class='octowrap'> | ||
66 | <a class='octothorpe' href='#section-3'>#</a> | ||
67 | </div> | ||
68 | |||
69 | </div> | ||
70 | <div class='code'> | ||
71 | <div class="highlight"><pre><span class="k">class</span> <span class="nc">GoogleError</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span> | ||
72 | <span class="k">pass</span></pre></div> | ||
73 | </div> | ||
74 | </div> | ||
75 | <div class='clearall'></div> | ||
76 | <div class='section' id='section-4'> | ||
77 | <div class='docs'> | ||
78 | <div class='octowrap'> | ||
79 | <a class='octothorpe' href='#section-4'>#</a> | ||
80 | </div> | ||
81 | |||
82 | </div> | ||
83 | <div class='code'> | ||
84 | <div class="highlight"><pre><span class="k">class</span> <span class="nc">GoogleBadRequestError</span><span class="p">(</span><span class="n">GoogleError</span><span class="p">):</span> | ||
85 | <span class="k">pass</span></pre></div> | ||
86 | </div> | ||
87 | </div> | ||
88 | <div class='clearall'></div> | ||
89 | <div class='section' id='section-5'> | ||
90 | <div class='docs'> | ||
91 | <div class='octowrap'> | ||
92 | <a class='octothorpe' href='#section-5'>#</a> | ||
93 | </div> | ||
94 | |||
95 | </div> | ||
96 | <div class='code'> | ||
97 | <div class="highlight"><pre><span class="k">class</span> <span class="nc">GoogleUnauthorizedError</span><span class="p">(</span><span class="n">GoogleError</span><span class="p">):</span> | ||
98 | <span class="k">pass</span></pre></div> | ||
99 | </div> | ||
100 | </div> | ||
101 | <div class='clearall'></div> | ||
102 | <div class='section' id='section-6'> | ||
103 | <div class='docs'> | ||
104 | <div class='octowrap'> | ||
105 | <a class='octothorpe' href='#section-6'>#</a> | ||
106 | </div> | ||
107 | |||
108 | </div> | ||
109 | <div class='code'> | ||
110 | <div class="highlight"><pre><span class="k">class</span> <span class="nc">GooglePaymentRequiredError</span><span class="p">(</span><span class="n">GoogleError</span><span class="p">):</span> | ||
111 | <span class="k">pass</span></pre></div> | ||
112 | </div> | ||
113 | </div> | ||
114 | <div class='clearall'></div> | ||
115 | <div class='section' id='section-7'> | ||
116 | <div class='docs'> | ||
117 | <div class='octowrap'> | ||
118 | <a class='octothorpe' href='#section-7'>#</a> | ||
119 | </div> | ||
120 | |||
121 | </div> | ||
122 | <div class='code'> | ||
123 | <div class="highlight"><pre><span class="k">class</span> <span class="nc">GoogleNotFoundError</span><span class="p">(</span><span class="n">GoogleError</span><span class="p">):</span> | ||
124 | <span class="k">pass</span></pre></div> | ||
125 | </div> | ||
126 | </div> | ||
127 | <div class='clearall'></div> | ||
128 | <div class='section' id='section-8'> | ||
129 | <div class='docs'> | ||
130 | <div class='octowrap'> | ||
131 | <a class='octothorpe' href='#section-8'>#</a> | ||
132 | </div> | ||
133 | |||
134 | </div> | ||
135 | <div class='code'> | ||
136 | <div class="highlight"><pre><span class="k">class</span> <span class="nc">GoogleMethodNotAllowedError</span><span class="p">(</span><span class="n">GoogleError</span><span class="p">):</span> | ||
137 | <span class="k">pass</span></pre></div> | ||
138 | </div> | ||
139 | </div> | ||
140 | <div class='clearall'></div> | ||
141 | <div class='section' id='section-9'> | ||
142 | <div class='docs'> | ||
143 | <div class='octowrap'> | ||
144 | <a class='octothorpe' href='#section-9'>#</a> | ||
145 | </div> | ||
146 | |||
147 | </div> | ||
148 | <div class='code'> | ||
149 | <div class="highlight"><pre><span class="k">class</span> <span class="nc">GoogleConflictError</span><span class="p">(</span><span class="n">GoogleError</span><span class="p">):</span> | ||
150 | <span class="k">pass</span></pre></div> | ||
151 | </div> | ||
152 | </div> | ||
153 | <div class='clearall'></div> | ||
154 | <div class='section' id='section-10'> | ||
155 | <div class='docs'> | ||
156 | <div class='octowrap'> | ||
157 | <a class='octothorpe' href='#section-10'>#</a> | ||
158 | </div> | ||
159 | |||
160 | </div> | ||
161 | <div class='code'> | ||
162 | <div class="highlight"><pre><span class="k">class</span> <span class="nc">GoogleGoneError</span><span class="p">(</span><span class="n">GoogleError</span><span class="p">):</span> | ||
163 | <span class="k">pass</span></pre></div> | ||
164 | </div> | ||
165 | </div> | ||
166 | <div class='clearall'></div> | ||
167 | <div class='section' id='section-11'> | ||
168 | <div class='docs'> | ||
169 | <div class='octowrap'> | ||
170 | <a class='octothorpe' href='#section-11'>#</a> | ||
171 | </div> | ||
172 | |||
173 | </div> | ||
174 | <div class='code'> | ||
175 | <div class="highlight"><pre><span class="k">class</span> <span class="nc">GooglePreconditionFailedError</span><span class="p">(</span><span class="n">GoogleError</span><span class="p">):</span> | ||
176 | <span class="k">pass</span></pre></div> | ||
177 | </div> | ||
178 | </div> | ||
179 | <div class='clearall'></div> | ||
180 | <div class='section' id='section-12'> | ||
181 | <div class='docs'> | ||
182 | <div class='octowrap'> | ||
183 | <a class='octothorpe' href='#section-12'>#</a> | ||
184 | </div> | ||
185 | |||
186 | </div> | ||
187 | <div class='code'> | ||
188 | <div class="highlight"><pre><span class="k">class</span> <span class="nc">GoogleRequestEntityTooLargeError</span><span class="p">(</span><span class="n">GoogleError</span><span class="p">):</span> | ||
189 | <span class="k">pass</span></pre></div> | ||
190 | </div> | ||
191 | </div> | ||
192 | <div class='clearall'></div> | ||
193 | <div class='section' id='section-13'> | ||
194 | <div class='docs'> | ||
195 | <div class='octowrap'> | ||
196 | <a class='octothorpe' href='#section-13'>#</a> | ||
197 | </div> | ||
198 | |||
199 | </div> | ||
200 | <div class='code'> | ||
201 | <div class="highlight"><pre><span class="k">class</span> <span class="nc">GoogleRequestedRangeNotSatisfiableError</span><span class="p">(</span><span class="n">GoogleError</span><span class="p">):</span> | ||
202 | <span class="k">pass</span></pre></div> | ||
203 | </div> | ||
204 | </div> | ||
205 | <div class='clearall'></div> | ||
206 | <div class='section' id='section-14'> | ||
207 | <div class='docs'> | ||
208 | <div class='octowrap'> | ||
209 | <a class='octothorpe' href='#section-14'>#</a> | ||
210 | </div> | ||
211 | |||
212 | </div> | ||
213 | <div class='code'> | ||
214 | <div class="highlight"><pre><span class="k">class</span> <span class="nc">GoogleExpectationFailedError</span><span class="p">(</span><span class="n">GoogleError</span><span class="p">):</span> | ||
215 | <span class="k">pass</span></pre></div> | ||
216 | </div> | ||
217 | </div> | ||
218 | <div class='clearall'></div> | ||
219 | <div class='section' id='section-15'> | ||
220 | <div class='docs'> | ||
221 | <div class='octowrap'> | ||
222 | <a class='octothorpe' href='#section-15'>#</a> | ||
223 | </div> | ||
224 | |||
225 | </div> | ||
226 | <div class='code'> | ||
227 | <div class="highlight"><pre><span class="k">class</span> <span class="nc">GoogleForbiddenError</span><span class="p">(</span><span class="n">GoogleError</span><span class="p">):</span> | ||
228 | <span class="k">pass</span></pre></div> | ||
229 | </div> | ||
230 | </div> | ||
231 | <div class='clearall'></div> | ||
232 | <div class='section' id='section-16'> | ||
233 | <div class='docs'> | ||
234 | <div class='octowrap'> | ||
235 | <a class='octothorpe' href='#section-16'>#</a> | ||
236 | </div> | ||
237 | |||
238 | </div> | ||
239 | <div class='code'> | ||
240 | <div class="highlight"><pre><span class="k">class</span> <span class="nc">GoogleUnprocessableEntityError</span><span class="p">(</span><span class="n">GoogleError</span><span class="p">):</span> | ||
241 | <span class="k">pass</span></pre></div> | ||
242 | </div> | ||
243 | </div> | ||
244 | <div class='clearall'></div> | ||
245 | <div class='section' id='section-17'> | ||
246 | <div class='docs'> | ||
247 | <div class='octowrap'> | ||
248 | <a class='octothorpe' href='#section-17'>#</a> | ||
249 | </div> | ||
250 | |||
251 | </div> | ||
252 | <div class='code'> | ||
253 | <div class="highlight"><pre><span class="k">class</span> <span class="nc">GooglePreconditionRequiredError</span><span class="p">(</span><span class="n">GoogleError</span><span class="p">):</span> | ||
254 | <span class="k">pass</span></pre></div> | ||
255 | </div> | ||
256 | </div> | ||
257 | <div class='clearall'></div> | ||
258 | <div class='section' id='section-18'> | ||
259 | <div class='docs'> | ||
260 | <div class='octowrap'> | ||
261 | <a class='octothorpe' href='#section-18'>#</a> | ||
262 | </div> | ||
263 | |||
264 | </div> | ||
265 | <div class='code'> | ||
266 | <div class="highlight"><pre><span class="k">class</span> <span class="nc">GoogleInternalServiceError</span><span class="p">(</span><span class="n">GoogleError</span><span class="p">):</span> | ||
267 | <span class="k">pass</span></pre></div> | ||
268 | </div> | ||
269 | </div> | ||
270 | <div class='clearall'></div> | ||
271 | <div class='section' id='section-19'> | ||
272 | <div class='docs'> | ||
273 | <div class='octowrap'> | ||
274 | <a class='octothorpe' href='#section-19'>#</a> | ||
275 | </div> | ||
276 | <p>Error Codes: https://developers.google.com/webmaster-tools/search-console-api-original/v3/errors</p> | ||
277 | </div> | ||
278 | <div class='code'> | ||
279 | <div class="highlight"><pre><span class="n">ERROR_CODE_EXCEPTION_MAPPING</span> <span class="o">=</span> <span class="p">{</span> | ||
280 | <span class="mi">400</span><span class="p">:</span> <span class="n">GoogleBadRequestError</span><span class="p">,</span> | ||
281 | <span class="mi">401</span><span class="p">:</span> <span class="n">GoogleUnauthorizedError</span><span class="p">,</span> | ||
282 | <span class="mi">402</span><span class="p">:</span> <span class="n">GooglePaymentRequiredError</span><span class="p">,</span> | ||
283 | <span class="mi">403</span><span class="p">:</span> <span class="n">GoogleForbiddenError</span><span class="p">,</span> | ||
284 | <span class="mi">404</span><span class="p">:</span> <span class="n">GoogleNotFoundError</span><span class="p">,</span> | ||
285 | <span class="mi">405</span><span class="p">:</span> <span class="n">GoogleMethodNotAllowedError</span><span class="p">,</span> | ||
286 | <span class="mi">409</span><span class="p">:</span> <span class="n">GoogleConflictError</span><span class="p">,</span> | ||
287 | <span class="mi">410</span><span class="p">:</span> <span class="n">GoogleGoneError</span><span class="p">,</span> | ||
288 | <span class="mi">412</span><span class="p">:</span> <span class="n">GooglePreconditionFailedError</span><span class="p">,</span> | ||
289 | <span class="mi">413</span><span class="p">:</span> <span class="n">GoogleRequestEntityTooLargeError</span><span class="p">,</span> | ||
290 | <span class="mi">416</span><span class="p">:</span> <span class="n">GoogleRequestedRangeNotSatisfiableError</span><span class="p">,</span> | ||
291 | <span class="mi">417</span><span class="p">:</span> <span class="n">GoogleExpectationFailedError</span><span class="p">,</span> | ||
292 | <span class="mi">422</span><span class="p">:</span> <span class="n">GoogleUnprocessableEntityError</span><span class="p">,</span> | ||
293 | <span class="mi">428</span><span class="p">:</span> <span class="n">GooglePreconditionRequiredError</span><span class="p">,</span> | ||
294 | <span class="mi">500</span><span class="p">:</span> <span class="n">GoogleInternalServiceError</span><span class="p">}</span></pre></div> | ||
295 | </div> | ||
296 | </div> | ||
297 | <div class='clearall'></div> | ||
298 | <div class='section' id='section-20'> | ||
299 | <div class='docs'> | ||
300 | <div class='octowrap'> | ||
301 | <a class='octothorpe' href='#section-20'>#</a> | ||
302 | </div> | ||
303 | |||
304 | </div> | ||
305 | <div class='code'> | ||
306 | <div class="highlight"><pre><span class="k">def</span> <span class="nf">get_exception_for_error_code</span><span class="p">(</span><span class="n">error_code</span><span class="p">):</span> | ||
307 | <span class="k">return</span> <span class="n">ERROR_CODE_EXCEPTION_MAPPING</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">error_code</span><span class="p">,</span> <span class="n">GoogleError</span><span class="p">)</span></pre></div> | ||
308 | </div> | ||
309 | </div> | ||
310 | <div class='clearall'></div> | ||
311 | <div class='section' id='section-21'> | ||
312 | <div class='docs'> | ||
313 | <div class='octowrap'> | ||
314 | <a class='octothorpe' href='#section-21'>#</a> | ||
315 | </div> | ||
316 | <hr /> | ||
317 | </div> | ||
318 | <div class='code'> | ||
319 | <div class="highlight"><pre></pre></div> | ||
320 | </div> | ||
321 | </div> | ||
322 | <div class='clearall'></div> | ||
323 | <div class='section' id='section-22'> | ||
324 | <div class='docs'> | ||
325 | <div class='octowrap'> | ||
326 | <a class='octothorpe' href='#section-22'>#</a> | ||
327 | </div> | ||
328 | <p><code>client.py:raise_for_error()</code> calls the <code>raise_for_status()</code> function from the <code>requests</code> library. | ||
329 | and catches all <code>requests.HTTPError</code> and <code>requests.ConnectionError</code>. Note the name difference.</p> | ||
330 | </div> | ||
331 | <div class='code'> | ||
332 | <div class="highlight"><pre></pre></div> | ||
333 | </div> | ||
334 | </div> | ||
335 | <div class='clearall'></div> | ||
336 | <div class='section' id='section-23'> | ||
337 | <div class='docs'> | ||
338 | <div class='octowrap'> | ||
339 | <a class='octothorpe' href='#section-23'>#</a> | ||
340 | </div> | ||
341 | <h5>Thoughts</h5> | ||
342 | <p>I believe there are 5 ways to leave this function. It’s worth skimming this just to understand the | ||
343 | structure. I’ll note below, but I think there’s just two ways to leave this function.</p> | ||
344 | </div> | ||
345 | <div class='code'> | ||
346 | <div class="highlight"><pre></pre></div> | ||
347 | </div> | ||
348 | </div> | ||
349 | <div class='clearall'></div> | ||
350 | <div class='section' id='section-24'> | ||
351 | <div class='docs'> | ||
352 | <div class='octowrap'> | ||
353 | <a class='octothorpe' href='#section-24'>#</a> | ||
354 | </div> | ||
355 | <ol> | ||
356 | <li>If the length of the response content is 0, then we just leave<ul> | ||
357 | <li>I believe this results in us swallowing the <code>requests.HTTPError</code> and successfully returns to | ||
358 | the calling function</li> | ||
359 | <li>I believe it’s possible to leave the function this way</li> | ||
360 | </ul> | ||
361 | </li> | ||
362 | <li>If you can call <code>response.json()</code>, then we attempt to create a specific error message via | ||
363 | <code>client.py:get_exception_for_error_code()</code>, which just looks up a code found in | ||
364 | <code>response.json()</code><ul> | ||
365 | <li>I am not convinced this ever works for this tap because my understanding of | ||
366 | <code>raise_for_status()</code> is if you <code>raise_for_status()</code> is unsuccessful then <code>response.json()</code> | ||
367 | will also be unsuccessful. So, because we are in the exception handling for | ||
368 | <code>raise_for_status()</code> I think we never make it past <code>response = response.json()</code> on | ||
369 | <code>client.py:118</code></li> | ||
370 | <li>I believe it’s possible to leave the function this way</li> | ||
371 | </ul> | ||
372 | </li> | ||
373 | <li>Assuming <code>response.json()</code> does fail, then that function will raise a | ||
374 | <code>simplejson.scanner.JSONDecodeError</code> with an error message like <code>"Expecting value: line 1 | ||
375 | column 1 (char 0)"</code></li> | ||
376 | <li>Assuming <code>response.json()</code> worked, but it’s lacks an <code>error</code> key and lacks an <code>errorCode</code> key, | ||
377 | we re-raise whatever was caught from <code>raise_for_status()</code></li> | ||
378 | <li>We also re-raise whatever was caught from <code>raise_for_status()</code> if a <code>ValueError</code> or <code>TypeError</code> | ||
379 | occurs in trying to handle the <code>raise_for_status()</code> error</li> | ||
380 | </ol> | ||
381 | </div> | ||
382 | <div class='code'> | ||
383 | <div class="highlight"><pre></pre></div> | ||
384 | </div> | ||
385 | </div> | ||
386 | <div class='clearall'></div> | ||
387 | <div class='section' id='section-25'> | ||
388 | <div class='docs'> | ||
389 | <div class='octowrap'> | ||
390 | <a class='octothorpe' href='#section-25'>#</a> | ||
391 | </div> | ||
392 | <hr /> | ||
393 | </div> | ||
394 | <div class='code'> | ||
395 | <div class="highlight"><pre></pre></div> | ||
396 | </div> | ||
397 | </div> | ||
398 | <div class='clearall'></div> | ||
399 | <div class='section' id='section-26'> | ||
400 | <div class='docs'> | ||
401 | <div class='octowrap'> | ||
402 | <a class='octothorpe' href='#section-26'>#</a> | ||
403 | </div> | ||
404 | <p>Try to catch API errors to rethrow as tap specific errors</p> | ||
405 | </div> | ||
406 | <div class='code'> | ||
407 | <div class="highlight"><pre><span class="k">def</span> <span class="nf">raise_for_error</span><span class="p">(</span><span class="n">response</span><span class="p">):</span></pre></div> | ||
408 | </div> | ||
409 | </div> | ||
410 | <div class='clearall'></div> | ||
411 | <div class='section' id='section-27'> | ||
412 | <div class='docs'> | ||
413 | <div class='octowrap'> | ||
414 | <a class='octothorpe' href='#section-27'>#</a> | ||
415 | </div> | ||
416 | <p>Inputs:</p> | ||
417 | <ul> | ||
418 | <li><code>response</code>: A requests.Response object</li> | ||
419 | </ul> | ||
420 | <p>Returns:</p> | ||
421 | <ul> | ||
422 | <li>None</li> | ||
423 | </ul> | ||
424 | <p>Side Effects:</p> | ||
425 | <ul> | ||
426 | <li>Raises a GoogleError</li> | ||
427 | </ul> | ||
428 | </div> | ||
429 | <div class='code'> | ||
430 | <div class="highlight"><pre> <span class="k">try</span><span class="p">:</span> | ||
431 | <span class="n">response</span><span class="o">.</span><span class="n">raise_for_status</span><span class="p">()</span> | ||
432 | <span class="k">except</span> <span class="p">(</span><span class="n">requests</span><span class="o">.</span><span class="n">HTTPError</span><span class="p">,</span> <span class="n">requests</span><span class="o">.</span><span class="n">ConnectionError</span><span class="p">)</span> <span class="k">as</span> <span class="n">error</span><span class="p">:</span> | ||
433 | <span class="k">try</span><span class="p">:</span> | ||
434 | <span class="n">content_length</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">content</span><span class="p">)</span> | ||
435 | <span class="k">if</span> <span class="n">content_length</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span> | ||
436 | <span class="k">return</span> | ||
437 | <span class="n">response</span> <span class="o">=</span> <span class="n">response</span><span class="o">.</span><span class="n">json</span><span class="p">()</span> | ||
438 | <span class="k">if</span> <span class="p">(</span><span class="s1">'error'</span> <span class="ow">in</span> <span class="n">response</span><span class="p">)</span> <span class="ow">or</span> <span class="p">(</span><span class="s1">'errorCode'</span> <span class="ow">in</span> <span class="n">response</span><span class="p">):</span> | ||
439 | <span class="n">message</span> <span class="o">=</span> <span class="s1">'</span><span class="si">%s</span><span class="s1">: </span><span class="si">%s</span><span class="s1">'</span> <span class="o">%</span> <span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'error'</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">error</span><span class="p">)),</span> | ||
440 | <span class="n">response</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'message'</span><span class="p">,</span> <span class="s1">'Unknown Error'</span><span class="p">))</span> | ||
441 | <span class="n">error_code</span> <span class="o">=</span> <span class="n">response</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'error'</span><span class="p">,</span> <span class="p">{})</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'code'</span><span class="p">)</span> | ||
442 | <span class="n">ex</span> <span class="o">=</span> <span class="n">get_exception_for_error_code</span><span class="p">(</span><span class="n">error_code</span><span class="p">)</span> | ||
443 | <span class="k">raise</span> <span class="n">ex</span><span class="p">(</span><span class="n">message</span><span class="p">)</span> | ||
444 | <span class="k">raise</span> <span class="n">GoogleError</span><span class="p">(</span><span class="n">error</span><span class="p">)</span> | ||
445 | <span class="k">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">TypeError</span><span class="p">):</span> | ||
446 | <span class="k">raise</span> <span class="n">GoogleError</span><span class="p">(</span><span class="n">error</span><span class="p">)</span></pre></div> | ||
447 | </div> | ||
448 | </div> | ||
449 | <div class='clearall'></div> | ||
450 | <div class='section' id='section-28'> | ||
451 | <div class='docs'> | ||
452 | <div class='octowrap'> | ||
453 | <a class='octothorpe' href='#section-28'>#</a> | ||
454 | </div> | ||
455 | <hr /> | ||
456 | </div> | ||
457 | <div class='code'> | ||
458 | <div class="highlight"><pre></pre></div> | ||
459 | </div> | ||
460 | </div> | ||
461 | <div class='clearall'></div> | ||
462 | <div class='section' id='section-29'> | ||
463 | <div class='docs'> | ||
464 | <div class='octowrap'> | ||
465 | <a class='octothorpe' href='#section-29'>#</a> | ||
466 | </div> | ||
467 | <h3>Handling a successful response</h3> | ||
468 | </div> | ||
469 | <div class='code'> | ||
470 | <div class="highlight"><pre></pre></div> | ||
471 | </div> | ||
472 | </div> | ||
473 | <div class='clearall'></div> | ||
474 | <div class='section' id='section-30'> | ||
475 | <div class='docs'> | ||
476 | <div class='octowrap'> | ||
477 | <a class='octothorpe' href='#section-30'>#</a> | ||
478 | </div> | ||
479 | <p>A successful response is defined as anything that returns a <code>HTTP 200</code>.</p> | ||
480 | </div> | ||
481 | <div class='code'> | ||
482 | <div class="highlight"><pre></pre></div> | ||
483 | </div> | ||
484 | </div> | ||
485 | <div class='clearall'></div> | ||
486 | <div class='section' id='section-31'> | ||
487 | <div class='docs'> | ||
488 | <div class='octowrap'> | ||
489 | <a class='octothorpe' href='#section-31'>#</a> | ||
490 | </div> | ||
491 | <p>On a successful response, we store the <code>access_token</code> returned on a private field, | ||
492 | <code>GoogleClient.__access_token</code>, and we update <code>GoogleClient.__expires</code> to be the time this | ||
493 | <code>access_token</code> expires. <code>GoogleClient.__expires</code> is a <code>datetime</code> object in UTC.</p> | ||
494 | </div> | ||
495 | <div class='code'> | ||
496 | <div class="highlight"><pre></pre></div> | ||
497 | </div> | ||
498 | </div> | ||
499 | <div class='clearall'></div> | ||
500 | <div class='section' id='section-32'> | ||
501 | <div class='docs'> | ||
502 | <div class='octowrap'> | ||
503 | <a class='octothorpe' href='#section-32'>#</a> | ||
504 | </div> | ||
505 | <h3>Handling an unsuccessful response</h3> | ||
506 | </div> | ||
507 | <div class='code'> | ||
508 | <div class="highlight"><pre></pre></div> | ||
509 | </div> | ||
510 | </div> | ||
511 | <div class='clearall'></div> | ||
512 | <div class='section' id='section-33'> | ||
513 | <div class='docs'> | ||
514 | <div class='octowrap'> | ||
515 | <a class='octothorpe' href='#section-33'>#</a> | ||
516 | </div> | ||
517 | <p>To handle unsuccessful requests, the tap has the following pattern</p> | ||
518 | </div> | ||
519 | <div class='code'> | ||
520 | <div class="highlight"><pre></pre></div> | ||
521 | </div> | ||
522 | </div> | ||
523 | <div class='clearall'></div> | ||
524 | <div class='section' id='section-34'> | ||
525 | <div class='docs'> | ||
526 | <div class='octowrap'> | ||
527 | <a class='octothorpe' href='#section-34'>#</a> | ||
528 | </div> | ||
529 | <pre><code class="language-Python">if response.status_code >= 500: | ||
530 | raise Server5xxError() | ||
531 | |||
532 | if response.status_code != 200: | ||
533 | raise_for_error(response) | ||
534 | </code></pre> | ||
535 | </div> | ||
536 | <div class='code'> | ||
537 | <div class="highlight"><pre></pre></div> | ||
538 | </div> | ||
539 | </div> | ||
540 | <div class='clearall'></div> | ||
541 | <div class='section' id='section-35'> | ||
542 | <div class='docs'> | ||
543 | <div class='octowrap'> | ||
544 | <a class='octothorpe' href='#section-35'>#</a> | ||
545 | </div> | ||
546 | <p>The <code>client.py:Server5xxError</code> is caught by <code>backoff</code> and we exponentially backoff the request.</p> | ||
547 | </div> | ||
548 | <div class='code'> | ||
549 | <div class="highlight"><pre></pre></div> | ||
550 | </div> | ||
551 | </div> | ||
552 | <div class='clearall'></div> | ||
553 | <div class='section' id='section-36'> | ||
554 | <div class='docs'> | ||
555 | <div class='octowrap'> | ||
556 | <a class='octothorpe' href='#section-36'>#</a> | ||
557 | </div> | ||
558 | <hr /> | ||
559 | </div> | ||
560 | <div class='code'> | ||
561 | <div class="highlight"><pre></pre></div> | ||
562 | </div> | ||
563 | </div> | ||
564 | <div class='clearall'></div> | ||
565 | <div class='section' id='section-37'> | ||
566 | <div class='docs'> | ||
567 | <div class='octowrap'> | ||
568 | <a class='octothorpe' href='#section-37'>#</a> | ||
569 | </div> | ||
570 | <p>This is a class implemented in the tap in <code>client.py</code>. We initialize it once in <code>__init__.py</code> as | ||
571 | a context manager in <code>__init__.py:main()</code></p> | ||
572 | </div> | ||
573 | <div class='code'> | ||
574 | <div class="highlight"><pre><span class="k">class</span> <span class="nc">GoogleClient</span><span class="p">:</span> <span class="c1"># pylint: disable=too-many-instance-attributes</span></pre></div> | ||
575 | </div> | ||
576 | </div> | ||
577 | <div class='clearall'></div> | ||
578 | <div class='section' id='section-38'> | ||
579 | <div class='docs'> | ||
580 | <div class='octowrap'> | ||
581 | <a class='octothorpe' href='#section-38'>#</a> | ||
582 | </div> | ||
583 | |||
584 | </div> | ||
585 | <div class='code'> | ||
586 | <div class="highlight"><pre></pre></div> | ||
587 | </div> | ||
588 | </div> | ||
589 | <div class='clearall'></div> | ||
590 | <div class='section' id='section-39'> | ||
591 | <div class='docs'> | ||
592 | <div class='octowrap'> | ||
593 | <a class='octothorpe' href='#section-39'>#</a> | ||
594 | </div> | ||
595 | <p>To create the <code>GoogleClient</code> object, we have to pass in the three OAuth2 variables. Optionally we | ||
596 | can include the <code>user_agent</code>.</p> | ||
597 | <p>Side Effects:</p> | ||
598 | <ul> | ||
599 | <li>All of this gets stored in private fields by the constructor.</li> | ||
600 | <li>The constructor also initializes a <code>requests.Session</code>.</li> | ||
601 | </ul> | ||
602 | </div> | ||
603 | <div class='code'> | ||
604 | <div class="highlight"><pre> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">client_id</span><span class="p">,</span> <span class="n">client_secret</span><span class="p">,</span> <span class="n">refresh_token</span><span class="p">,</span> <span class="n">access_token</span><span class="p">,</span> <span class="n">user_agent</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span></pre></div> | ||
605 | </div> | ||
606 | </div> | ||
607 | <div class='clearall'></div> | ||
608 | <div class='section' id='section-40'> | ||
609 | <div class='docs'> | ||
610 | <div class='octowrap'> | ||
611 | <a class='octothorpe' href='#section-40'>#</a> | ||
612 | </div> | ||
613 | |||
614 | </div> | ||
615 | <div class='code'> | ||
616 | <div class="highlight"><pre> <span class="bp">self</span><span class="o">.</span><span class="n">__client_id</span> <span class="o">=</span> <span class="n">client_id</span> | ||
617 | <span class="bp">self</span><span class="o">.</span><span class="n">__client_secret</span> <span class="o">=</span> <span class="n">client_secret</span> | ||
618 | <span class="bp">self</span><span class="o">.</span><span class="n">__refresh_token</span> <span class="o">=</span> <span class="n">refresh_token</span> | ||
619 | <span class="bp">self</span><span class="o">.</span><span class="n">__user_agent</span> <span class="o">=</span> <span class="n">user_agent</span> | ||
620 | <span class="bp">self</span><span class="o">.</span><span class="n">__access_token</span> <span class="o">=</span> <span class="n">access_token</span> | ||
621 | <span class="bp">self</span><span class="o">.</span><span class="n">__session</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">Session</span><span class="p">()</span> | ||
622 | <span class="bp">self</span><span class="o">.</span><span class="n">base_url</span> <span class="o">=</span> <span class="kc">None</span></pre></div> | ||
623 | </div> | ||
624 | </div> | ||
625 | <div class='clearall'></div> | ||
626 | <div class='section' id='section-41'> | ||
627 | <div class='docs'> | ||
628 | <div class='octowrap'> | ||
629 | <a class='octothorpe' href='#section-41'>#</a> | ||
630 | </div> | ||
631 | <p>On enter, get a new access token</p> | ||
632 | </div> | ||
633 | <div class='code'> | ||
634 | <div class="highlight"><pre> <span class="k">def</span> <span class="fm">__enter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span></pre></div> | ||
635 | </div> | ||
636 | </div> | ||
637 | <div class='clearall'></div> | ||
638 | <div class='section' id='section-42'> | ||
639 | <div class='docs'> | ||
640 | <div class='octowrap'> | ||
641 | <a class='octothorpe' href='#section-42'>#</a> | ||
642 | </div> | ||
643 | |||
644 | </div> | ||
645 | <div class='code'> | ||
646 | <div class="highlight"><pre> <span class="bp">self</span><span class="o">.</span><span class="n">get_access_token</span><span class="p">()</span> | ||
647 | <span class="k">return</span> <span class="bp">self</span></pre></div> | ||
648 | </div> | ||
649 | </div> | ||
650 | <div class='clearall'></div> | ||
651 | <div class='section' id='section-43'> | ||
652 | <div class='docs'> | ||
653 | <div class='octowrap'> | ||
654 | <a class='octothorpe' href='#section-43'>#</a> | ||
655 | </div> | ||
656 | <p>On exit, close the Requests Session</p> | ||
657 | </div> | ||
658 | <div class='code'> | ||
659 | <div class="highlight"><pre> <span class="k">def</span> <span class="fm">__exit__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">exception_type</span><span class="p">,</span> <span class="n">exception_value</span><span class="p">,</span> <span class="n">traceback</span><span class="p">):</span></pre></div> | ||
660 | </div> | ||
661 | </div> | ||
662 | <div class='clearall'></div> | ||
663 | <div class='section' id='section-44'> | ||
664 | <div class='docs'> | ||
665 | <div class='octowrap'> | ||
666 | <a class='octothorpe' href='#section-44'>#</a> | ||
667 | </div> | ||
668 | |||
669 | </div> | ||
670 | <div class='code'> | ||
671 | <div class="highlight"><pre> <span class="bp">self</span><span class="o">.</span><span class="n">__session</span><span class="o">.</span><span class="n">close</span><span class="p">()</span></pre></div> | ||
672 | </div> | ||
673 | </div> | ||
674 | <div class='clearall'></div> | ||
675 | <div class='section' id='section-45'> | ||
676 | <div class='docs'> | ||
677 | <div class='octowrap'> | ||
678 | <a class='octothorpe' href='#section-45'>#</a> | ||
679 | </div> | ||
680 | <p><code>get_access_token()</code> will <code>POST</code> to <code>client.py:GOOGLE_TOKEN_URI</code> which is just | ||
681 | <code>https://oauth2.googleapis.com/token</code>. The body of the <code>POST</code> looks like</p> | ||
682 | <pre><code class="language-JSON">{ | ||
683 | "grant_type": "refresh_token", | ||
684 | "client_id": my_client_id, | ||
685 | "client_secret": my_client_secret, | ||
686 | "refresh_token": my_refresh_token | ||
687 | } | ||
688 | </code></pre> | ||
689 | <p>Side Effects:</p> | ||
690 | <ul> | ||
691 | <li>Store the access token and time it expires in private fields on the Client object</li> | ||
692 | </ul> | ||
693 | </div> | ||
694 | <div class='code'> | ||
695 | <div class="highlight"><pre> <span class="nd">@backoff</span><span class="o">.</span><span class="n">on_exception</span><span class="p">(</span><span class="n">backoff</span><span class="o">.</span><span class="n">expo</span><span class="p">,</span> | ||
696 | <span class="n">Server5xxError</span><span class="p">,</span> | ||
697 | <span class="n">max_tries</span><span class="o">=</span><span class="mi">5</span><span class="p">,</span> | ||
698 | <span class="n">factor</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span> | ||
699 | <span class="k">def</span> <span class="nf">get_access_token</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span></pre></div> | ||
700 | </div> | ||
701 | </div> | ||
702 | <div class='clearall'></div> | ||
703 | <div class='section' id='section-46'> | ||
704 | <div class='docs'> | ||
705 | <div class='octowrap'> | ||
706 | <a class='octothorpe' href='#section-46'>#</a> | ||
707 | </div> | ||
708 | |||
709 | </div> | ||
710 | <div class='code'> | ||
711 | <div class="highlight"><pre> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">__access_token</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span> | ||
712 | <span class="k">return</span> | ||
713 | |||
714 | <span class="n">headers</span> <span class="o">=</span> <span class="p">{}</span> | ||
715 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">__user_agent</span><span class="p">:</span> | ||
716 | <span class="n">headers</span><span class="p">[</span><span class="s1">'User-Agent'</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__user_agent</span> | ||
717 | |||
718 | <span class="n">response</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__session</span><span class="o">.</span><span class="n">post</span><span class="p">(</span> | ||
719 | <span class="n">url</span><span class="o">=</span><span class="n">GOOGLE_TOKEN_URI</span><span class="p">,</span> | ||
720 | <span class="n">headers</span><span class="o">=</span><span class="n">headers</span><span class="p">,</span> | ||
721 | <span class="n">data</span><span class="o">=</span><span class="p">{</span> | ||
722 | <span class="s1">'grant_type'</span><span class="p">:</span> <span class="s1">'refresh_token'</span><span class="p">,</span> | ||
723 | <span class="s1">'client_id'</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">__client_id</span><span class="p">,</span> | ||
724 | <span class="s1">'client_secret'</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">__client_secret</span><span class="p">,</span> | ||
725 | <span class="s1">'refresh_token'</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">__refresh_token</span><span class="p">,</span> | ||
726 | <span class="p">})</span> | ||
727 | |||
728 | <span class="k">if</span> <span class="n">response</span><span class="o">.</span><span class="n">status_code</span> <span class="o">>=</span> <span class="mi">500</span><span class="p">:</span> | ||
729 | <span class="k">raise</span> <span class="n">Server5xxError</span><span class="p">()</span> | ||
730 | |||
731 | <span class="k">if</span> <span class="n">response</span><span class="o">.</span><span class="n">status_code</span> <span class="o">!=</span> <span class="mi">200</span><span class="p">:</span> | ||
732 | <span class="n">raise_for_error</span><span class="p">(</span><span class="n">response</span><span class="p">)</span> | ||
733 | |||
734 | <span class="n">data</span> <span class="o">=</span> <span class="n">response</span><span class="o">.</span><span class="n">json</span><span class="p">()</span> | ||
735 | <span class="bp">self</span><span class="o">.</span><span class="n">__access_token</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="s1">'access_token'</span><span class="p">]</span> | ||
736 | <span class="bp">self</span><span class="o">.</span><span class="n">__expires</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">utcnow</span><span class="p">()</span> <span class="o">+</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">seconds</span><span class="o">=</span><span class="n">data</span><span class="p">[</span><span class="s1">'expires_in'</span><span class="p">])</span> | ||
737 | <span class="n">LOGGER</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'Authorized, token expires = </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__expires</span><span class="p">))</span></pre></div> | ||
738 | </div> | ||
739 | </div> | ||
740 | <div class='clearall'></div> | ||
741 | <div class='section' id='section-47'> | ||
742 | <div class='docs'> | ||
743 | <div class='octowrap'> | ||
744 | <a class='octothorpe' href='#section-47'>#</a> | ||
745 | </div> | ||
746 | <hr /> | ||
747 | </div> | ||
748 | <div class='code'> | ||
749 | <div class="highlight"><pre></pre></div> | ||
750 | </div> | ||
751 | </div> | ||
752 | <div class='clearall'></div> | ||
753 | <div class='section' id='section-48'> | ||
754 | <div class='docs'> | ||
755 | <div class='octowrap'> | ||
756 | <a class='octothorpe' href='#section-48'>#</a> | ||
757 | </div> | ||
758 | <p>This function starts with a call to <code>GoogleClient.get_access_token()</code> which likely returns | ||
759 | immediately most of the time.</p> | ||
760 | </div> | ||
761 | <div class='code'> | ||
762 | <div class="highlight"><pre></pre></div> | ||
763 | </div> | ||
764 | </div> | ||
765 | <div class='clearall'></div> | ||
766 | <div class='section' id='section-49'> | ||
767 | <div class='docs'> | ||
768 | <div class='octowrap'> | ||
769 | <a class='octothorpe' href='#section-49'>#</a> | ||
770 | </div> | ||
771 | <p>Then we decide what url we are sending the request to. Sometimes it’s | ||
772 | <code>https://sheets.googleapis.com/v4</code> and sometimes it’s <code>https://www.googleapis.com/drive/v3</code>.</p> | ||
773 | </div> | ||
774 | <div class='code'> | ||
775 | <div class="highlight"><pre></pre></div> | ||
776 | </div> | ||
777 | </div> | ||
778 | <div class='clearall'></div> | ||
779 | <div class='section' id='section-50'> | ||
780 | <div class='docs'> | ||
781 | <div class='octowrap'> | ||
782 | <a class='octothorpe' href='#section-50'>#</a> | ||
783 | </div> | ||
784 | <ul> | ||
785 | <li>It seems like a mistake to decide this so deep into the code. Why doesn’t the caller decide | ||
786 | where the request goes?</li> | ||
787 | </ul> | ||
788 | </div> | ||
789 | <div class='code'> | ||
790 | <div class="highlight"><pre></pre></div> | ||
791 | </div> | ||
792 | </div> | ||
793 | <div class='clearall'></div> | ||
794 | <div class='section' id='section-51'> | ||
795 | <div class='docs'> | ||
796 | <div class='octowrap'> | ||
797 | <a class='octothorpe' href='#section-51'>#</a> | ||
798 | </div> | ||
799 | <p>Then we set up the request headers. The <code>authorization</code>, <code>user-agent</code>, and <code>content-type</code> keys come | ||
800 | into play here</p> | ||
801 | </div> | ||
802 | <div class='code'> | ||
803 | <div class="highlight"><pre></pre></div> | ||
804 | </div> | ||
805 | </div> | ||
806 | <div class='clearall'></div> | ||
807 | <div class='section' id='section-52'> | ||
808 | <div class='docs'> | ||
809 | <div class='octowrap'> | ||
810 | <a class='octothorpe' href='#section-52'>#</a> | ||
811 | </div> | ||
812 | <ul> | ||
813 | <li>One benefit of a a <code>requests.Session</code> is that you can set the headers for the session. I’m not | ||
814 | sure why we don’t do that here</li> | ||
815 | <li>If we did that, we wouldn’t have to think about the access_token making it into the headers | ||
816 | here. They would just already be there</li> | ||
817 | </ul> | ||
818 | </div> | ||
819 | <div class='code'> | ||
820 | <div class="highlight"><pre></pre></div> | ||
821 | </div> | ||
822 | </div> | ||
823 | <div class='clearall'></div> | ||
824 | <div class='section' id='section-53'> | ||
825 | <div class='docs'> | ||
826 | <div class='octowrap'> | ||
827 | <a class='octothorpe' href='#section-53'>#</a> | ||
828 | </div> | ||
829 | <p>Then we make the request, timing how long it takes with a <code>singer.metrics.http_request_timer</code>.</p> | ||
830 | </div> | ||
831 | <div class='code'> | ||
832 | <div class="highlight"><pre></pre></div> | ||
833 | </div> | ||
834 | </div> | ||
835 | <div class='clearall'></div> | ||
836 | <div class='section' id='section-54'> | ||
837 | <div class='docs'> | ||
838 | <div class='octowrap'> | ||
839 | <a class='octothorpe' href='#section-54'>#</a> | ||
840 | </div> | ||
841 | <p>The chunk of code after making the request handles an unsuccessful response. We will retry <code>HTTP | ||
842 | 500</code> and <code>HTTP 429</code> errors, and <code>client.py:raise_for_error</code> for everything else.</p> | ||
843 | </div> | ||
844 | <div class='code'> | ||
845 | <div class="highlight"><pre></pre></div> | ||
846 | </div> | ||
847 | </div> | ||
848 | <div class='clearall'></div> | ||
849 | <div class='section' id='section-55'> | ||
850 | <div class='docs'> | ||
851 | <div class='octowrap'> | ||
852 | <a class='octothorpe' href='#section-55'>#</a> | ||
853 | </div> | ||
854 | <p>The most unique thing of this tap happens here: we return an <code>OrderedDict</code> of the response with this | ||
855 | line</p> | ||
856 | </div> | ||
857 | <div class='code'> | ||
858 | <div class="highlight"><pre></pre></div> | ||
859 | </div> | ||
860 | </div> | ||
861 | <div class='clearall'></div> | ||
862 | <div class='section' id='section-56'> | ||
863 | <div class='docs'> | ||
864 | <div class='octowrap'> | ||
865 | <a class='octothorpe' href='#section-56'>#</a> | ||
866 | </div> | ||
867 | <pre><code class="language-Python">return response.json(object_pairs_hook=OrderedDict) | ||
868 | </code></pre> | ||
869 | </div> | ||
870 | <div class='code'> | ||
871 | <div class="highlight"><pre></pre></div> | ||
872 | </div> | ||
873 | </div> | ||
874 | <div class='clearall'></div> | ||
875 | <div class='section' id='section-57'> | ||
876 | <div class='docs'> | ||
877 | <div class='octowrap'> | ||
878 | <a class='octothorpe' href='#section-57'>#</a> | ||
879 | </div> | ||
880 | <p>where <code>object_pairs_hook</code> is a <code>kwarg</code> passed to the JSON parser used by <code>requests</code>.</p> | ||
881 | </div> | ||
882 | <div class='code'> | ||
883 | <div class="highlight"><pre></pre></div> | ||
884 | </div> | ||
885 | </div> | ||
886 | <div class='clearall'></div> | ||
887 | <div class='section' id='section-58'> | ||
888 | <div class='docs'> | ||
889 | <div class='octowrap'> | ||
890 | <a class='octothorpe' href='#section-58'>#</a> | ||
891 | </div> | ||
892 | <p>This turns every key-value pair in the JSON response into a <code>OrderedDict</code>.</p> | ||
893 | </div> | ||
894 | <div class='code'> | ||
895 | <div class="highlight"><pre></pre></div> | ||
896 | </div> | ||
897 | </div> | ||
898 | <div class='clearall'></div> | ||
899 | <div class='section' id='section-59'> | ||
900 | <div class='docs'> | ||
901 | <div class='octowrap'> | ||
902 | <a class='octothorpe' href='#section-59'>#</a> | ||
903 | </div> | ||
904 | <p>Why do we do this? I don’t know. See the footnote for code examples</p> | ||
905 | </div> | ||
906 | <div class='code'> | ||
907 | <div class="highlight"><pre></pre></div> | ||
908 | </div> | ||
909 | </div> | ||
910 | <div class='clearall'></div> | ||
911 | <div class='section' id='section-60'> | ||
912 | <div class='docs'> | ||
913 | <div class='octowrap'> | ||
914 | <a class='octothorpe' href='#section-60'>#</a> | ||
915 | </div> | ||
916 | <hr /> | ||
917 | </div> | ||
918 | <div class='code'> | ||
919 | <div class="highlight"><pre></pre></div> | ||
920 | </div> | ||
921 | </div> | ||
922 | <div class='clearall'></div> | ||
923 | <div class='section' id='section-61'> | ||
924 | <div class='docs'> | ||
925 | <div class='octowrap'> | ||
926 | <a class='octothorpe' href='#section-61'>#</a> | ||
927 | </div> | ||
928 | <p>Rate Limit: https://developers.google.com/sheets/api/limits | ||
929 | 100 request per 100 seconds per User</p> | ||
930 | </div> | ||
931 | <div class='code'> | ||
932 | <div class="highlight"><pre> <span class="nd">@backoff</span><span class="o">.</span><span class="n">on_exception</span><span class="p">(</span><span class="n">backoff</span><span class="o">.</span><span class="n">expo</span><span class="p">,</span> | ||
933 | <span class="p">(</span><span class="n">Server5xxError</span><span class="p">,</span> <span class="ne">ConnectionError</span><span class="p">,</span> <span class="n">Server429Error</span><span class="p">),</span> | ||
934 | <span class="n">max_tries</span><span class="o">=</span><span class="mi">7</span><span class="p">,</span> | ||
935 | <span class="n">factor</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span> | ||
936 | <span class="nd">@utils</span><span class="o">.</span><span class="n">ratelimit</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span> | ||
937 | <span class="k">def</span> <span class="nf">request</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">method</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">url</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">api</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span></pre></div> | ||
938 | </div> | ||
939 | </div> | ||
940 | <div class='clearall'></div> | ||
941 | <div class='section' id='section-62'> | ||
942 | <div class='docs'> | ||
943 | <div class='octowrap'> | ||
944 | <a class='octothorpe' href='#section-62'>#</a> | ||
945 | </div> | ||
946 | <p>Make a request to the API</p> | ||
947 | <p>Inputs:</p> | ||
948 | <ul> | ||
949 | <li>method: “GET” or “POST”</li> | ||
950 | <li>url: The start of the url to make the request to</li> | ||
951 | <li>path:</li> | ||
952 | </ul> | ||
953 | <p>Returns:</p> | ||
954 | <ul> | ||
955 | <li>A requests.Reponse</li> | ||
956 | </ul> | ||
957 | <p>Side Effects:</p> | ||
958 | <ul> | ||
959 | <li>Might store a new access token</li> | ||
960 | </ul> | ||
961 | </div> | ||
962 | <div class='code'> | ||
963 | <div class="highlight"><pre> <span class="bp">self</span><span class="o">.</span><span class="n">get_access_token</span><span class="p">()</span></pre></div> | ||
964 | </div> | ||
965 | </div> | ||
966 | <div class='clearall'></div> | ||
967 | <div class='section' id='section-63'> | ||
968 | <div class='docs'> | ||
969 | <div class='octowrap'> | ||
970 | <a class='octothorpe' href='#section-63'>#</a> | ||
971 | </div> | ||
972 | <p>Construct the URL to make a request to</p> | ||
973 | </div> | ||
974 | <div class='code'> | ||
975 | <div class="highlight"><pre> <span class="bp">self</span><span class="o">.</span><span class="n">base_url</span> <span class="o">=</span> <span class="s1">'https://sheets.googleapis.com/v4'</span> | ||
976 | <span class="k">if</span> <span class="n">api</span> <span class="o">==</span> <span class="s1">'files'</span><span class="p">:</span> | ||
977 | <span class="bp">self</span><span class="o">.</span><span class="n">base_url</span> <span class="o">=</span> <span class="s1">'https://www.googleapis.com/drive/v3'</span> | ||
978 | |||
979 | <span class="k">if</span> <span class="ow">not</span> <span class="n">url</span> <span class="ow">and</span> <span class="n">path</span><span class="p">:</span> | ||
980 | <span class="n">url</span> <span class="o">=</span> <span class="s1">'</span><span class="si">{}</span><span class="s1">/</span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">base_url</span><span class="p">,</span> <span class="n">path</span><span class="p">)</span> | ||
981 | |||
982 | <span class="k">if</span> <span class="s1">'endpoint'</span> <span class="ow">in</span> <span class="n">kwargs</span><span class="p">:</span> | ||
983 | <span class="n">endpoint</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s1">'endpoint'</span><span class="p">]</span> | ||
984 | <span class="k">del</span> <span class="n">kwargs</span><span class="p">[</span><span class="s1">'endpoint'</span><span class="p">]</span> | ||
985 | <span class="k">else</span><span class="p">:</span> | ||
986 | <span class="n">endpoint</span> <span class="o">=</span> <span class="kc">None</span> | ||
987 | <span class="n">LOGGER</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'</span><span class="si">{}</span><span class="s1"> URL = </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="n">url</span><span class="p">))</span></pre></div> | ||
988 | </div> | ||
989 | </div> | ||
990 | <div class='clearall'></div> | ||
991 | <div class='section' id='section-64'> | ||
992 | <div class='docs'> | ||
993 | <div class='octowrap'> | ||
994 | <a class='octothorpe' href='#section-64'>#</a> | ||
995 | </div> | ||
996 | <p>Contruct the <code>headers</code> arg for <code>requests.request()</code></p> | ||
997 | </div> | ||
998 | <div class='code'> | ||
999 | <div class="highlight"><pre> <span class="k">if</span> <span class="s1">'headers'</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">kwargs</span><span class="p">:</span> | ||
1000 | <span class="n">kwargs</span><span class="p">[</span><span class="s1">'headers'</span><span class="p">]</span> <span class="o">=</span> <span class="p">{}</span> | ||
1001 | <span class="n">kwargs</span><span class="p">[</span><span class="s1">'headers'</span><span class="p">][</span><span class="s1">'Authorization'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'Bearer </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__access_token</span><span class="p">)</span> | ||
1002 | |||
1003 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">__user_agent</span><span class="p">:</span> | ||
1004 | <span class="n">kwargs</span><span class="p">[</span><span class="s1">'headers'</span><span class="p">][</span><span class="s1">'User-Agent'</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__user_agent</span> | ||
1005 | |||
1006 | <span class="k">if</span> <span class="n">method</span> <span class="o">==</span> <span class="s1">'POST'</span><span class="p">:</span> | ||
1007 | <span class="n">kwargs</span><span class="p">[</span><span class="s1">'headers'</span><span class="p">][</span><span class="s1">'Content-Type'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'application/json'</span></pre></div> | ||
1008 | </div> | ||
1009 | </div> | ||
1010 | <div class='clearall'></div> | ||
1011 | <div class='section' id='section-65'> | ||
1012 | <div class='docs'> | ||
1013 | <div class='octowrap'> | ||
1014 | <a class='octothorpe' href='#section-65'>#</a> | ||
1015 | </div> | ||
1016 | <p>Make request</p> | ||
1017 | </div> | ||
1018 | <div class='code'> | ||
1019 | <div class="highlight"><pre> <span class="k">with</span> <span class="n">metrics</span><span class="o">.</span><span class="n">http_request_timer</span><span class="p">(</span><span class="n">endpoint</span><span class="p">)</span> <span class="k">as</span> <span class="n">timer</span><span class="p">:</span> | ||
1020 | <span class="n">response</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__session</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="n">method</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> | ||
1021 | <span class="n">timer</span><span class="o">.</span><span class="n">tags</span><span class="p">[</span><span class="n">metrics</span><span class="o">.</span><span class="n">Tag</span><span class="o">.</span><span class="n">http_status_code</span><span class="p">]</span> <span class="o">=</span> <span class="n">response</span><span class="o">.</span><span class="n">status_code</span></pre></div> | ||
1022 | </div> | ||
1023 | </div> | ||
1024 | <div class='clearall'></div> | ||
1025 | <div class='section' id='section-66'> | ||
1026 | <div class='docs'> | ||
1027 | <div class='octowrap'> | ||
1028 | <a class='octothorpe' href='#section-66'>#</a> | ||
1029 | </div> | ||
1030 | <p>Start backoff logic</p> | ||
1031 | </div> | ||
1032 | <div class='code'> | ||
1033 | <div class="highlight"><pre> <span class="k">if</span> <span class="n">response</span><span class="o">.</span><span class="n">status_code</span> <span class="o">>=</span> <span class="mi">500</span><span class="p">:</span> | ||
1034 | <span class="k">raise</span> <span class="n">Server5xxError</span><span class="p">()</span> | ||
1035 | |||
1036 | <span class="k">if</span> <span class="n">response</span><span class="o">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="mi">429</span><span class="p">:</span> | ||
1037 | <span class="k">raise</span> <span class="n">Server429Error</span><span class="p">()</span> | ||
1038 | |||
1039 | <span class="k">if</span> <span class="n">response</span><span class="o">.</span><span class="n">status_code</span> <span class="o">!=</span> <span class="mi">200</span><span class="p">:</span> | ||
1040 | <span class="n">raise_for_error</span><span class="p">(</span><span class="n">response</span><span class="p">)</span></pre></div> | ||
1041 | </div> | ||
1042 | </div> | ||
1043 | <div class='clearall'></div> | ||
1044 | <div class='section' id='section-67'> | ||
1045 | <div class='docs'> | ||
1046 | <div class='octowrap'> | ||
1047 | <a class='octothorpe' href='#section-67'>#</a> | ||
1048 | </div> | ||
1049 | <p>Ensure keys and rows are ordered as received from API. | ||
1050 | QUESITON: But why??</p> | ||
1051 | </div> | ||
1052 | <div class='code'> | ||
1053 | <div class="highlight"><pre> <span class="k">return</span> <span class="n">response</span><span class="o">.</span><span class="n">json</span><span class="p">(</span><span class="n">object_pairs_hook</span><span class="o">=</span><span class="n">OrderedDict</span><span class="p">)</span></pre></div> | ||
1054 | </div> | ||
1055 | </div> | ||
1056 | <div class='clearall'></div> | ||
1057 | <div class='section' id='section-68'> | ||
1058 | <div class='docs'> | ||
1059 | <div class='octowrap'> | ||
1060 | <a class='octothorpe' href='#section-68'>#</a> | ||
1061 | </div> | ||
1062 | <h3>Syntactic Sugar</h3> | ||
1063 | </div> | ||
1064 | <div class='code'> | ||
1065 | <div class="highlight"><pre> <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">path</span><span class="p">,</span> <span class="n">api</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> | ||
1066 | <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="n">method</span><span class="o">=</span><span class="s1">'GET'</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="n">path</span><span class="p">,</span> <span class="n">api</span><span class="o">=</span><span class="n">api</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span></pre></div> | ||
1067 | </div> | ||
1068 | </div> | ||
1069 | <div class='clearall'></div> | ||
1070 | <div class='section' id='section-69'> | ||
1071 | <div class='docs'> | ||
1072 | <div class='octowrap'> | ||
1073 | <a class='octothorpe' href='#section-69'>#</a> | ||
1074 | </div> | ||
1075 | |||
1076 | </div> | ||
1077 | <div class='code'> | ||
1078 | <div class="highlight"><pre> <span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">path</span><span class="p">,</span> <span class="n">api</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> | ||
1079 | <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="n">method</span><span class="o">=</span><span class="s1">'POST'</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="n">path</span><span class="p">,</span> <span class="n">api</span><span class="o">=</span><span class="n">api</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span></pre></div> | ||
1080 | </div> | ||
1081 | </div> | ||
1082 | <div class='clearall'></div> | ||
1083 | <div class='section' id='section-70'> | ||
1084 | <div class='docs'> | ||
1085 | <div class='octowrap'> | ||
1086 | <a class='octothorpe' href='#section-70'>#</a> | ||
1087 | </div> | ||
1088 | <hr /> | ||
1089 | </div> | ||
1090 | <div class='code'> | ||
1091 | <div class="highlight"><pre></pre></div> | ||
1092 | </div> | ||
1093 | </div> | ||
1094 | <div class='clearall'></div> | ||
1095 | <div class='section' id='section-71'> | ||
1096 | <div class='docs'> | ||
1097 | <div class='octowrap'> | ||
1098 | <a class='octothorpe' href='#section-71'>#</a> | ||
1099 | </div> | ||
1100 | <h1>Footnotes</h1> | ||
1101 | </div> | ||
1102 | <div class='code'> | ||
1103 | <div class="highlight"><pre></pre></div> | ||
1104 | </div> | ||
1105 | </div> | ||
1106 | <div class='clearall'></div> | ||
1107 | <div class='section' id='section-72'> | ||
1108 | <div class='docs'> | ||
1109 | <div class='octowrap'> | ||
1110 | <a class='octothorpe' href='#section-72'>#</a> | ||
1111 | </div> | ||
1112 | <p>Here’s a normal <code>.json()</code>‘s output</p> | ||
1113 | <pre><code class="language-Python">{"file": "this is my file"} | ||
1114 | </code></pre> | ||
1115 | </div> | ||
1116 | <div class='code'> | ||
1117 | <div class="highlight"><pre></pre></div> | ||
1118 | </div> | ||
1119 | </div> | ||
1120 | <div class='clearall'></div> | ||
1121 | <div class='section' id='section-73'> | ||
1122 | <div class='docs'> | ||
1123 | <div class='octowrap'> | ||
1124 | <a class='octothorpe' href='#section-73'>#</a> | ||
1125 | </div> | ||
1126 | <p>Here’s the weird one’s <code>.json(object_pairs_hook=OrderedDict)</code> output</p> | ||
1127 | <pre><code class="language-Python">OrderedDict([('file', 'this is my file')]) | ||
1128 | </code></pre> | ||
1129 | </div> | ||
1130 | <div class='code'> | ||
1131 | <div class="highlight"><pre></pre></div> | ||
1132 | </div> | ||
1133 | </div> | ||
1134 | <div class='clearall'></div> | ||
1135 | <div class='section' id='section-74'> | ||
1136 | <div class='docs'> | ||
1137 | <div class='octowrap'> | ||
1138 | <a class='octothorpe' href='#section-74'>#</a> | ||
1139 | </div> | ||
1140 | <p>Here’s a more complex example:</p> | ||
1141 | </div> | ||
1142 | <div class='code'> | ||
1143 | <div class="highlight"><pre></pre></div> | ||
1144 | </div> | ||
1145 | </div> | ||
1146 | <div class='clearall'></div> | ||
1147 | <div class='section' id='section-75'> | ||
1148 | <div class='docs'> | ||
1149 | <div class='octowrap'> | ||
1150 | <a class='octothorpe' href='#section-75'>#</a> | ||
1151 | </div> | ||
1152 | <pre><code class="language-python">{ "deleted": false, | ||
1153 | "__v": 0, | ||
1154 | "_id": "5887e1d85c873e0011036889", | ||
1155 | "text": "Cats make about 100 different sounds. Dogs make only about 10.", | ||
1156 | "createdAt": "2018-01-15T21:20:00.003Z", | ||
1157 | "updatedAt": "2020-09-03T16:39:39.578Z", | ||
1158 | "used": true, | ||
1159 | "status": { | ||
1160 | "sentCount": 1, | ||
1161 | "feedback": "", | ||
1162 | "verified": true | ||
1163 | }, | ||
1164 | "type": "cat", | ||
1165 | "user": "5a9ac18c7478810ea6c06381", | ||
1166 | "source": "user"} | ||
1167 | </code></pre> | ||
1168 | </div> | ||
1169 | <div class='code'> | ||
1170 | <div class="highlight"><pre></pre></div> | ||
1171 | </div> | ||
1172 | </div> | ||
1173 | <div class='clearall'></div> | ||
1174 | <div class='section' id='section-76'> | ||
1175 | <div class='docs'> | ||
1176 | <div class='octowrap'> | ||
1177 | <a class='octothorpe' href='#section-76'>#</a> | ||
1178 | </div> | ||
1179 | <p>Versus <code>.json(object_pairs_hook=OrderedDict)</code></p> | ||
1180 | <pre><code class="language-python">OrderedDict([('status', OrderedDict([('verified', True), | ||
1181 | ('sentCount', 1), | ||
1182 | ('feedback', '')])), | ||
1183 | ('type', 'cat'), | ||
1184 | ('deleted', False), | ||
1185 | ('_id', '5887e1d85c873e0011036889'), | ||
1186 | ('user', '5a9ac18c7478810ea6c06381'), | ||
1187 | ('text', 'Cats make about 100 different sounds. Dogs make only about 10.'), | ||
1188 | ('__v', 0), | ||
1189 | ('source', 'user'), | ||
1190 | ('updatedAt', '2020-09-03T16:39:39.578Z'), | ||
1191 | ('createdAt', '2018-01-15T21:20:00.003Z'), | ||
1192 | ('used', True)]) | ||
1193 | </code></pre> | ||
1194 | </div> | ||
1195 | <div class='code'> | ||
1196 | <div class="highlight"><pre></pre></div> | ||
1197 | </div> | ||
1198 | </div> | ||
1199 | <div class='clearall'></div> | ||
1200 | </div> | ||
1201 | </body> | ||