blob: 26820555007001d4bf293b49a9fff2e53a7b2375 [file] [log] [blame]
Craig Citro15744b12015-03-02 13:34:32 -08001#!/usr/bin/env python
Joe Gregoriof863f7a2011-02-24 03:24:44 -05002# -*- coding: utf-8 -*-
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04003#
Craig Citro751b7fb2014-09-23 11:20:38 -07004# Copyright 2014 Google Inc. All Rights Reserved.
Joe Gregorio6d5e94f2010-08-25 23:49:30 -04005#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040018
19"""Discovery document tests
20
21Unit tests for objects created from discovery documents.
22"""
INADA Naokid898a372015-03-04 03:52:46 +090023from __future__ import absolute_import
24import six
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040025
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070026__author__ = "jcgregorio@google.com (Joe Gregorio)"
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040027
Pat Ferateed9affd2015-03-03 16:03:15 -080028from six import BytesIO, StringIO
Pat Ferated5b61bd2015-03-03 16:04:11 -080029from six.moves.urllib.parse import urlparse, parse_qs
Pat Ferateed9affd2015-03-03 16:03:15 -080030
Daniel Hermesc2113242013-02-27 10:16:13 -080031import copy
Joe Gregoriodc106fc2012-11-20 14:30:14 -050032import datetime
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040033import httplib2
Craig Citro7ee535d2015-02-23 10:11:14 -080034import itertools
Craig Citro6ae34d72014-08-18 23:10:09 -070035import json
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040036import os
Joe Gregoriodc106fc2012-11-20 14:30:14 -050037import pickle
Phil Ruffwind26178fc2015-10-13 19:00:33 -040038import re
Joe Gregorioc80ac9d2012-08-21 14:09:09 -040039import sys
Pat Ferate497a90f2015-03-09 09:52:54 -070040import unittest2 as unittest
Pavel Kiselev21af37b2020-06-18 19:50:03 +030041from collections import defaultdict
Joe Gregoriodc106fc2012-11-20 14:30:14 -050042
arithmetic1728981eadf2020-06-02 10:20:10 -070043from parameterized import parameterized
Takashi Matsuo30125122015-08-19 11:42:32 -070044import mock
45
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -080046import google.auth.credentials
arithmetic1728981eadf2020-06-02 10:20:10 -070047from google.auth.transport import mtls
48from google.auth.exceptions import MutualTLSChannelError
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -080049import google_auth_httplib2
Bu Sun Kim790e7022020-09-11 20:18:06 -060050import google.api_core.exceptions
51
John Asmuth864311d2014-04-24 15:46:08 -040052from googleapiclient.discovery import _fix_up_media_upload
53from googleapiclient.discovery import _fix_up_method_description
54from googleapiclient.discovery import _fix_up_parameters
Craig Citro7ee535d2015-02-23 10:11:14 -080055from googleapiclient.discovery import _urljoin
John Asmuth864311d2014-04-24 15:46:08 -040056from googleapiclient.discovery import build
57from googleapiclient.discovery import build_from_document
58from googleapiclient.discovery import DISCOVERY_URI
59from googleapiclient.discovery import key2param
60from googleapiclient.discovery import MEDIA_BODY_PARAMETER_DEFAULT_VALUE
Brian J. Watson38051ac2016-10-25 07:53:08 -070061from googleapiclient.discovery import MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE
John Asmuth864311d2014-04-24 15:46:08 -040062from googleapiclient.discovery import ResourceMethodParameters
63from googleapiclient.discovery import STACK_QUERY_PARAMETERS
64from googleapiclient.discovery import STACK_QUERY_PARAMETER_DEFAULT_VALUE
Dmitry Frenkelf3348f92020-07-15 13:05:58 -070065from googleapiclient.discovery import V1_DISCOVERY_URI
66from googleapiclient.discovery import V2_DISCOVERY_URI
Takashi Matsuo30125122015-08-19 11:42:32 -070067from googleapiclient.discovery_cache import DISCOVERY_DOC_MAX_AGE
68from googleapiclient.discovery_cache.base import Cache
John Asmuth864311d2014-04-24 15:46:08 -040069from googleapiclient.errors import HttpError
70from googleapiclient.errors import InvalidJsonError
71from googleapiclient.errors import MediaUploadSizeError
72from googleapiclient.errors import ResumableUploadError
73from googleapiclient.errors import UnacceptableMimeTypeError
Takashi Matsuo3772f9d2015-09-04 12:25:55 -070074from googleapiclient.errors import UnknownApiNameOrVersion
Brian J. Watson38051ac2016-10-25 07:53:08 -070075from googleapiclient.errors import UnknownFileType
Igor Maravić22435292017-01-19 22:28:22 +010076from googleapiclient.http import build_http
Pepper Lebeck-Jobe860836f2015-06-12 20:42:23 -040077from googleapiclient.http import BatchHttpRequest
John Asmuth864311d2014-04-24 15:46:08 -040078from googleapiclient.http import HttpMock
79from googleapiclient.http import HttpMockSequence
80from googleapiclient.http import MediaFileUpload
81from googleapiclient.http import MediaIoBaseUpload
82from googleapiclient.http import MediaUpload
83from googleapiclient.http import MediaUploadProgress
84from googleapiclient.http import tunnel_patch
Thomas Coffee20af04d2017-02-10 15:24:44 -080085from googleapiclient.model import JsonModel
Jean-Loup Roussel-Clouet0c0c8972018-04-28 05:42:43 +090086from googleapiclient.schema import Schemas
dhermes@google.coma9eb0bb2013-02-06 09:19:01 -080087from oauth2client import GOOGLE_TOKEN_URI
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -080088from oauth2client.client import OAuth2Credentials, GoogleCredentials
Joe Gregorio79daca02013-03-29 16:25:52 -040089
Bu Sun Kima9583f72021-03-15 09:12:02 -060090
Helen Koikede13e3b2018-04-26 16:05:16 -030091from googleapiclient import _helpers as util
Jon Wayne Parrott36d4e1b2016-10-17 13:31:33 -070092
Joe Gregoriodc106fc2012-11-20 14:30:14 -050093import uritemplate
94
Joe Gregoriocb8103d2011-02-11 23:20:52 -050095
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070096DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
Joe Gregoriocb8103d2011-02-11 23:20:52 -050097
Joe Gregorioa98733f2011-09-16 10:12:28 -040098
Joe Gregoriof1ba7f12012-11-16 15:10:47 -050099def assertUrisEqual(testcase, expected, actual):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700100 """Test that URIs are the same, up to reordering of query parameters."""
101 expected = urlparse(expected)
102 actual = urlparse(actual)
103 testcase.assertEqual(expected.scheme, actual.scheme)
104 testcase.assertEqual(expected.netloc, actual.netloc)
105 testcase.assertEqual(expected.path, actual.path)
106 testcase.assertEqual(expected.params, actual.params)
107 testcase.assertEqual(expected.fragment, actual.fragment)
108 expected_query = parse_qs(expected.query)
109 actual_query = parse_qs(actual.query)
110 for name in list(expected_query.keys()):
111 testcase.assertEqual(expected_query[name], actual_query[name])
112 for name in list(actual_query.keys()):
113 testcase.assertEqual(expected_query[name], actual_query[name])
Joe Gregoriof1ba7f12012-11-16 15:10:47 -0500114
115
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700116def assert_discovery_uri(testcase, actual, service_name, version, discovery):
117 """Assert that discovery URI used was the one that was expected
118 for a given service and version."""
119 params = {"api": service_name, "apiVersion": version}
120 expanded_requested_uri = uritemplate.expand(discovery, params)
121 assertUrisEqual(testcase, expanded_requested_uri, actual)
122
123
Bu Sun Kim790e7022020-09-11 20:18:06 -0600124def validate_discovery_requests(testcase, http_mock, service_name, version, discovery):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700125 """Validates that there have > 0 calls to Http Discovery
126 and that LAST discovery URI used was the one that was expected
127 for a given service and version."""
128 testcase.assertTrue(len(http_mock.request_sequence) > 0)
129 if len(http_mock.request_sequence) > 0:
130 actual_uri = http_mock.request_sequence[-1][0]
Bu Sun Kim790e7022020-09-11 20:18:06 -0600131 assert_discovery_uri(testcase, actual_uri, service_name, version, discovery)
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700132
133
Joe Gregoriocb8103d2011-02-11 23:20:52 -0500134def datafile(filename):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700135 return os.path.join(DATA_DIR, filename)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400136
137
Bu Sun Kim790e7022020-09-11 20:18:06 -0600138def read_datafile(filename, mode="r"):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700139 with open(datafile(filename), mode=mode) as f:
140 return f.read()
141
142
Joe Gregorio504a17f2012-12-07 14:14:26 -0500143class SetupHttplib2(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700144 def test_retries(self):
145 # Merely loading googleapiclient.discovery should set the RETRIES to 1.
146 self.assertEqual(1, httplib2.RETRIES)
Joe Gregorio504a17f2012-12-07 14:14:26 -0500147
148
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400149class Utilities(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700150 def setUp(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700151 self.zoo_root_desc = json.loads(read_datafile("zoo.json", "r"))
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700152 self.zoo_get_method_desc = self.zoo_root_desc["methods"]["query"]
153 self.zoo_animals_resource = self.zoo_root_desc["resources"]["animals"]
154 self.zoo_insert_method_desc = self.zoo_animals_resource["methods"]["insert"]
155 self.zoo_schema = Schemas(self.zoo_root_desc)
Daniel Hermesc2113242013-02-27 10:16:13 -0800156
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700157 def test_key2param(self):
158 self.assertEqual("max_results", key2param("max-results"))
159 self.assertEqual("x007_bond", key2param("007-bond"))
Daniel Hermesc2113242013-02-27 10:16:13 -0800160
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700161 def _base_fix_up_parameters_test(self, method_desc, http_method, root_desc, schema):
162 self.assertEqual(method_desc["httpMethod"], http_method)
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400163
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700164 method_desc_copy = copy.deepcopy(method_desc)
165 self.assertEqual(method_desc, method_desc_copy)
Daniel Hermesc2113242013-02-27 10:16:13 -0800166
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700167 parameters = _fix_up_parameters(
168 method_desc_copy, root_desc, http_method, schema
169 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800170
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700171 self.assertNotEqual(method_desc, method_desc_copy)
Daniel Hermesc2113242013-02-27 10:16:13 -0800172
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700173 for param_name in STACK_QUERY_PARAMETERS:
174 self.assertEqual(
175 STACK_QUERY_PARAMETER_DEFAULT_VALUE, parameters[param_name]
176 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800177
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700178 for param_name, value in six.iteritems(root_desc.get("parameters", {})):
179 self.assertEqual(value, parameters[param_name])
Daniel Hermesc2113242013-02-27 10:16:13 -0800180
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700181 return parameters
Daniel Hermesc2113242013-02-27 10:16:13 -0800182
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700183 def test_fix_up_parameters_get(self):
184 parameters = self._base_fix_up_parameters_test(
185 self.zoo_get_method_desc, "GET", self.zoo_root_desc, self.zoo_schema
186 )
187 # Since http_method is 'GET'
188 self.assertFalse("body" in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800189
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700190 def test_fix_up_parameters_insert(self):
191 parameters = self._base_fix_up_parameters_test(
192 self.zoo_insert_method_desc, "POST", self.zoo_root_desc, self.zoo_schema
193 )
194 body = {"description": "The request body.", "type": "object", "$ref": "Animal"}
195 self.assertEqual(parameters["body"], body)
Daniel Hermesc2113242013-02-27 10:16:13 -0800196
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700197 def test_fix_up_parameters_check_body(self):
198 dummy_root_desc = {}
199 dummy_schema = {
200 "Request": {
201 "properties": {
202 "description": "Required. Dummy parameter.",
203 "type": "string",
204 }
205 }
Jean-Loup Roussel-Clouet0c0c8972018-04-28 05:42:43 +0900206 }
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700207 no_payload_http_method = "DELETE"
208 with_payload_http_method = "PUT"
Daniel Hermesc2113242013-02-27 10:16:13 -0800209
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700210 invalid_method_desc = {"response": "Who cares"}
211 valid_method_desc = {
212 "request": {"key1": "value1", "key2": "value2", "$ref": "Request"}
213 }
Daniel Hermesc2113242013-02-27 10:16:13 -0800214
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700215 parameters = _fix_up_parameters(
216 invalid_method_desc, dummy_root_desc, no_payload_http_method, dummy_schema
217 )
218 self.assertFalse("body" in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800219
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700220 parameters = _fix_up_parameters(
221 valid_method_desc, dummy_root_desc, no_payload_http_method, dummy_schema
222 )
223 self.assertFalse("body" in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800224
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700225 parameters = _fix_up_parameters(
226 invalid_method_desc, dummy_root_desc, with_payload_http_method, dummy_schema
227 )
228 self.assertFalse("body" in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800229
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700230 parameters = _fix_up_parameters(
231 valid_method_desc, dummy_root_desc, with_payload_http_method, dummy_schema
232 )
233 body = {
234 "description": "The request body.",
235 "type": "object",
236 "$ref": "Request",
237 "key1": "value1",
238 "key2": "value2",
239 }
240 self.assertEqual(parameters["body"], body)
Daniel Hermesc2113242013-02-27 10:16:13 -0800241
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700242 def test_fix_up_parameters_optional_body(self):
243 # Request with no parameters
244 dummy_schema = {"Request": {"properties": {}}}
245 method_desc = {"request": {"$ref": "Request"}}
Jean-Loup Roussel-Clouet0c0c8972018-04-28 05:42:43 +0900246
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700247 parameters = _fix_up_parameters(method_desc, {}, "POST", dummy_schema)
Jean-Loup Roussel-Clouet0c0c8972018-04-28 05:42:43 +0900248
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700249 def _base_fix_up_method_description_test(
250 self,
251 method_desc,
252 initial_parameters,
253 final_parameters,
254 final_accept,
255 final_max_size,
256 final_media_path_url,
257 ):
arithmetic1728981eadf2020-06-02 10:20:10 -0700258 fake_root_desc = {
259 "rootUrl": "http://root/",
260 "servicePath": "fake/",
261 "mtlsRootUrl": "http://root/",
262 }
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700263 fake_path_url = "fake-path/"
Daniel Hermesc2113242013-02-27 10:16:13 -0800264
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700265 accept, max_size, media_path_url = _fix_up_media_upload(
266 method_desc, fake_root_desc, fake_path_url, initial_parameters
267 )
268 self.assertEqual(accept, final_accept)
269 self.assertEqual(max_size, final_max_size)
270 self.assertEqual(media_path_url, final_media_path_url)
271 self.assertEqual(initial_parameters, final_parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800272
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700273 def test_fix_up_media_upload_no_initial_invalid(self):
274 invalid_method_desc = {"response": "Who cares"}
275 self._base_fix_up_method_description_test(
276 invalid_method_desc, {}, {}, [], 0, None
277 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800278
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700279 def test_fix_up_media_upload_no_initial_valid_minimal(self):
280 valid_method_desc = {"mediaUpload": {"accept": []}}
281 final_parameters = {
282 "media_body": MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
283 "media_mime_type": MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE,
284 }
285 self._base_fix_up_method_description_test(
286 valid_method_desc,
287 {},
288 final_parameters,
289 [],
290 0,
291 "http://root/upload/fake/fake-path/",
292 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800293
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700294 def test_fix_up_media_upload_no_initial_valid_full(self):
295 valid_method_desc = {"mediaUpload": {"accept": ["*/*"], "maxSize": "10GB"}}
296 final_parameters = {
297 "media_body": MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
298 "media_mime_type": MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE,
299 }
300 ten_gb = 10 * 2 ** 30
301 self._base_fix_up_method_description_test(
302 valid_method_desc,
303 {},
304 final_parameters,
305 ["*/*"],
306 ten_gb,
307 "http://root/upload/fake/fake-path/",
308 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800309
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700310 def test_fix_up_media_upload_with_initial_invalid(self):
311 invalid_method_desc = {"response": "Who cares"}
312 initial_parameters = {"body": {}}
313 self._base_fix_up_method_description_test(
314 invalid_method_desc, initial_parameters, initial_parameters, [], 0, None
315 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800316
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700317 def test_fix_up_media_upload_with_initial_valid_minimal(self):
318 valid_method_desc = {"mediaUpload": {"accept": []}}
319 initial_parameters = {"body": {}}
320 final_parameters = {
321 "body": {},
322 "media_body": MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
323 "media_mime_type": MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE,
324 }
325 self._base_fix_up_method_description_test(
326 valid_method_desc,
327 initial_parameters,
328 final_parameters,
329 [],
330 0,
331 "http://root/upload/fake/fake-path/",
332 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800333
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700334 def test_fix_up_media_upload_with_initial_valid_full(self):
335 valid_method_desc = {"mediaUpload": {"accept": ["*/*"], "maxSize": "10GB"}}
336 initial_parameters = {"body": {}}
337 final_parameters = {
338 "body": {},
339 "media_body": MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
340 "media_mime_type": MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE,
341 }
342 ten_gb = 10 * 2 ** 30
343 self._base_fix_up_method_description_test(
344 valid_method_desc,
345 initial_parameters,
346 final_parameters,
347 ["*/*"],
348 ten_gb,
349 "http://root/upload/fake/fake-path/",
350 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800351
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700352 def test_fix_up_method_description_get(self):
353 result = _fix_up_method_description(
354 self.zoo_get_method_desc, self.zoo_root_desc, self.zoo_schema
355 )
356 path_url = "query"
357 http_method = "GET"
358 method_id = "bigquery.query"
359 accept = []
360 max_size = 0
361 media_path_url = None
362 self.assertEqual(
363 result, (path_url, http_method, method_id, accept, max_size, media_path_url)
364 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800365
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700366 def test_fix_up_method_description_insert(self):
367 result = _fix_up_method_description(
368 self.zoo_insert_method_desc, self.zoo_root_desc, self.zoo_schema
369 )
370 path_url = "animals"
371 http_method = "POST"
372 method_id = "zoo.animals.insert"
373 accept = ["image/png"]
374 max_size = 1024
375 media_path_url = "https://www.googleapis.com/upload/zoo/v1/animals"
376 self.assertEqual(
377 result, (path_url, http_method, method_id, accept, max_size, media_path_url)
378 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800379
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700380 def test_urljoin(self):
381 # We want to exhaustively test various URL combinations.
382 simple_bases = ["https://www.googleapis.com", "https://www.googleapis.com/"]
383 long_urls = ["foo/v1/bar:custom?alt=json", "/foo/v1/bar:custom?alt=json"]
Craig Citro7ee535d2015-02-23 10:11:14 -0800384
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700385 long_bases = [
386 "https://www.googleapis.com/foo/v1",
387 "https://www.googleapis.com/foo/v1/",
388 ]
389 simple_urls = ["bar:custom?alt=json", "/bar:custom?alt=json"]
Craig Citro7ee535d2015-02-23 10:11:14 -0800390
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700391 final_url = "https://www.googleapis.com/foo/v1/bar:custom?alt=json"
392 for base, url in itertools.product(simple_bases, long_urls):
393 self.assertEqual(final_url, _urljoin(base, url))
394 for base, url in itertools.product(long_bases, simple_urls):
395 self.assertEqual(final_url, _urljoin(base, url))
Craig Citro7ee535d2015-02-23 10:11:14 -0800396
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700397 def test_ResourceMethodParameters_zoo_get(self):
398 parameters = ResourceMethodParameters(self.zoo_get_method_desc)
Craig Citro7ee535d2015-02-23 10:11:14 -0800399
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700400 param_types = {
401 "a": "any",
402 "b": "boolean",
403 "e": "string",
404 "er": "string",
405 "i": "integer",
406 "n": "number",
407 "o": "object",
408 "q": "string",
409 "rr": "string",
410 }
411 keys = list(param_types.keys())
412 self.assertEqual(parameters.argmap, dict((key, key) for key in keys))
413 self.assertEqual(parameters.required_params, [])
414 self.assertEqual(sorted(parameters.repeated_params), ["er", "rr"])
415 self.assertEqual(parameters.pattern_params, {"rr": "[a-z]+"})
416 self.assertEqual(
417 sorted(parameters.query_params),
418 ["a", "b", "e", "er", "i", "n", "o", "q", "rr"],
419 )
420 self.assertEqual(parameters.path_params, set())
421 self.assertEqual(parameters.param_types, param_types)
422 enum_params = {"e": ["foo", "bar"], "er": ["one", "two", "three"]}
423 self.assertEqual(parameters.enum_params, enum_params)
Daniel Hermes954e1242013-02-28 09:28:37 -0800424
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700425 def test_ResourceMethodParameters_zoo_animals_patch(self):
426 method_desc = self.zoo_animals_resource["methods"]["patch"]
427 parameters = ResourceMethodParameters(method_desc)
Daniel Hermes954e1242013-02-28 09:28:37 -0800428
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700429 param_types = {"name": "string"}
430 keys = list(param_types.keys())
431 self.assertEqual(parameters.argmap, dict((key, key) for key in keys))
432 self.assertEqual(parameters.required_params, ["name"])
433 self.assertEqual(parameters.repeated_params, [])
434 self.assertEqual(parameters.pattern_params, {})
435 self.assertEqual(parameters.query_params, [])
436 self.assertEqual(parameters.path_params, set(["name"]))
437 self.assertEqual(parameters.param_types, param_types)
438 self.assertEqual(parameters.enum_params, {})
Daniel Hermes954e1242013-02-28 09:28:37 -0800439
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400440
Bu Sun Kim98888da2020-09-23 11:10:39 -0600441class Discovery(unittest.TestCase):
442 def test_discovery_http_is_closed(self):
443 http = HttpMock(datafile("malformed.json"), {"status": "200"})
444 service = build("plus", "v1", credentials=mock.sentinel.credentials)
445 http.close.assert_called_once()
446
447
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500448class DiscoveryErrors(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700449 def test_tests_should_be_run_with_strict_positional_enforcement(self):
450 try:
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500451 plus = build("plus", "v1", None, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700452 self.fail("should have raised a TypeError exception over missing http=.")
453 except TypeError:
454 pass
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500455
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700456 def test_failed_to_parse_discovery_json(self):
457 self.http = HttpMock(datafile("malformed.json"), {"status": "200"})
458 try:
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500459 plus = build("plus", "v1", http=self.http, cache_discovery=False, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700460 self.fail("should have raised an exception over malformed JSON.")
461 except InvalidJsonError:
462 pass
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400463
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700464 def test_unknown_api_name_or_version(self):
465 http = HttpMockSequence(
466 [
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700467 ({"status": "404"}, read_datafile("zoo.json", "rb")),
468 ({"status": "404"}, read_datafile("zoo.json", "rb")),
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700469 ]
470 )
471 with self.assertRaises(UnknownApiNameOrVersion):
472 plus = build("plus", "v1", http=http, cache_discovery=False)
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500473
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700474 def test_credentials_and_http_mutually_exclusive(self):
475 http = HttpMock(datafile("plus.json"), {"status": "200"})
476 with self.assertRaises(ValueError):
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500477 build("plus", "v1", http=http, credentials=mock.sentinel.credentials, static_discovery=False)
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -0800478
Bu Sun Kim790e7022020-09-11 20:18:06 -0600479 def test_credentials_file_and_http_mutually_exclusive(self):
480 http = HttpMock(datafile("plus.json"), {"status": "200"})
481 with self.assertRaises(ValueError):
482 build(
483 "plus",
484 "v1",
485 http=http,
486 client_options=google.api_core.client_options.ClientOptions(
487 credentials_file="credentials.json"
488 ),
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500489 static_discovery=False,
Bu Sun Kim790e7022020-09-11 20:18:06 -0600490 )
491
492 def test_credentials_and_credentials_file_mutually_exclusive(self):
493 with self.assertRaises(google.api_core.exceptions.DuplicateCredentialArgs):
494 build(
495 "plus",
496 "v1",
497 credentials=mock.sentinel.credentials,
498 client_options=google.api_core.client_options.ClientOptions(
499 credentials_file="credentials.json"
500 ),
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500501 static_discovery=False,
Bu Sun Kim790e7022020-09-11 20:18:06 -0600502 )
503
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500504
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100505class DiscoveryFromDocument(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700506 MOCK_CREDENTIALS = mock.Mock(spec=google.auth.credentials.Credentials)
Joe Gregorioa98733f2011-09-16 10:12:28 -0400507
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700508 def test_can_build_from_local_document(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700509 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700510 plus = build_from_document(
511 discovery,
512 base="https://www.googleapis.com/",
513 credentials=self.MOCK_CREDENTIALS,
514 )
arithmetic1728981eadf2020-06-02 10:20:10 -0700515 self.assertIsNotNone(plus)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700516 self.assertTrue(hasattr(plus, "activities"))
Joe Gregorio4772f3d2012-12-10 10:22:37 -0500517
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700518 def test_can_build_from_local_deserialized_document(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700519 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700520 discovery = json.loads(discovery)
521 plus = build_from_document(
522 discovery,
523 base="https://www.googleapis.com/",
524 credentials=self.MOCK_CREDENTIALS,
525 )
arithmetic1728981eadf2020-06-02 10:20:10 -0700526 self.assertIsNotNone(plus)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700527 self.assertTrue(hasattr(plus, "activities"))
Joe Gregorioa98733f2011-09-16 10:12:28 -0400528
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700529 def test_building_with_base_remembers_base(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700530 discovery = read_datafile("plus.json")
Joe Gregorioa98733f2011-09-16 10:12:28 -0400531
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700532 base = "https://www.example.com/"
533 plus = build_from_document(
534 discovery, base=base, credentials=self.MOCK_CREDENTIALS
535 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700536 self.assertEqual("https://www.googleapis.com/plus/v1/", plus._baseUrl)
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100537
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700538 def test_building_with_optional_http_with_authorization(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700539 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700540 plus = build_from_document(
541 discovery,
542 base="https://www.googleapis.com/",
543 credentials=self.MOCK_CREDENTIALS,
544 )
Igor Maravić22435292017-01-19 22:28:22 +0100545
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700546 # plus service requires Authorization, hence we expect to see AuthorizedHttp object here
547 self.assertIsInstance(plus._http, google_auth_httplib2.AuthorizedHttp)
548 self.assertIsInstance(plus._http.http, httplib2.Http)
549 self.assertIsInstance(plus._http.http.timeout, int)
550 self.assertGreater(plus._http.http.timeout, 0)
Igor Maravić22435292017-01-19 22:28:22 +0100551
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700552 def test_building_with_optional_http_with_no_authorization(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700553 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700554 # Cleanup auth field, so we would use plain http client
555 discovery = json.loads(discovery)
556 discovery["auth"] = {}
557 discovery = json.dumps(discovery)
Igor Maravić22435292017-01-19 22:28:22 +0100558
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700559 plus = build_from_document(
560 discovery, base="https://www.googleapis.com/", credentials=None
561 )
562 # plus service requires Authorization
563 self.assertIsInstance(plus._http, httplib2.Http)
564 self.assertIsInstance(plus._http.timeout, int)
565 self.assertGreater(plus._http.timeout, 0)
Jonathan Wayne Parrotta6e6fbd2015-07-16 15:33:57 -0700566
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700567 def test_building_with_explicit_http(self):
568 http = HttpMock()
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700569 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700570 plus = build_from_document(
571 discovery, base="https://www.googleapis.com/", http=http
572 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700573 self.assertEqual(plus._http, http)
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100574
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700575 def test_building_with_developer_key_skips_adc(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700576 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700577 plus = build_from_document(
578 discovery, base="https://www.googleapis.com/", developerKey="123"
579 )
580 self.assertIsInstance(plus._http, httplib2.Http)
581 # It should not be an AuthorizedHttp, because that would indicate that
582 # application default credentials were used.
583 self.assertNotIsInstance(plus._http, google_auth_httplib2.AuthorizedHttp)
Jon Wayne Parrott068eb352017-02-08 10:13:06 -0800584
Bu Sun Kim98888da2020-09-23 11:10:39 -0600585 def test_building_with_context_manager(self):
586 discovery = read_datafile("plus.json")
587 with mock.patch("httplib2.Http") as http:
588 with build_from_document(discovery, base="https://www.googleapis.com/", credentials=self.MOCK_CREDENTIALS) as plus:
589 self.assertIsNotNone(plus)
590 self.assertTrue(hasattr(plus, "activities"))
591 plus._http.http.close.assert_called_once()
592
593 def test_resource_close(self):
594 discovery = read_datafile("plus.json")
Anthonios Partheniou3b4f2e22021-03-19 11:36:01 -0400595
Bu Sun Kima9583f72021-03-15 09:12:02 -0600596 with mock.patch("httplib2.Http", autospec=True) as httplib2_http:
597 http = httplib2_http()
598 plus = build_from_document(
599 discovery,
600 base="https://www.googleapis.com/",
601 http=http,
602 )
603 plus.close()
604 http.close.assert_called_once()
605
606 def test_resource_close_authorized_http(self):
607 discovery = read_datafile("plus.json")
608 with mock.patch("google_auth_httplib2.AuthorizedHttp", autospec=True):
Bu Sun Kim98888da2020-09-23 11:10:39 -0600609 plus = build_from_document(
610 discovery,
611 base="https://www.googleapis.com/",
612 credentials=self.MOCK_CREDENTIALS,
613 )
614 plus.close()
Bu Sun Kima9583f72021-03-15 09:12:02 -0600615 plus._http.close.assert_called_once()
Bu Sun Kim98888da2020-09-23 11:10:39 -0600616
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700617 def test_api_endpoint_override_from_client_options(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700618 discovery = read_datafile("plus.json")
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700619 api_endpoint = "https://foo.googleapis.com/"
620 options = google.api_core.client_options.ClientOptions(
621 api_endpoint=api_endpoint
622 )
Bu Sun Kim8ed1dcd2020-05-14 00:42:22 -0700623 plus = build_from_document(
arithmetic1728981eadf2020-06-02 10:20:10 -0700624 discovery, client_options=options, credentials=self.MOCK_CREDENTIALS
Bu Sun Kim8ed1dcd2020-05-14 00:42:22 -0700625 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700626
627 self.assertEqual(plus._baseUrl, api_endpoint)
628
Pavel Kiselev21af37b2020-06-18 19:50:03 +0300629 def test_api_endpoint_override_from_client_options_mapping_object(self):
630
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700631 discovery = read_datafile("plus.json")
Pavel Kiselev21af37b2020-06-18 19:50:03 +0300632 api_endpoint = "https://foo.googleapis.com/"
633 mapping_object = defaultdict(str)
Bu Sun Kim790e7022020-09-11 20:18:06 -0600634 mapping_object["api_endpoint"] = api_endpoint
Bu Sun Kimd500e832021-04-27 16:13:05 -0600635 plus = build_from_document(
636 discovery, client_options=mapping_object, credentials=self.MOCK_CREDENTIALS
637 )
Pavel Kiselev21af37b2020-06-18 19:50:03 +0300638
639 self.assertEqual(plus._baseUrl, api_endpoint)
640
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700641 def test_api_endpoint_override_from_client_options_dict(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700642 discovery = read_datafile("plus.json")
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700643 api_endpoint = "https://foo.googleapis.com/"
644 plus = build_from_document(
arithmetic1728981eadf2020-06-02 10:20:10 -0700645 discovery,
Bu Sun Kim8ed1dcd2020-05-14 00:42:22 -0700646 client_options={"api_endpoint": api_endpoint},
arithmetic1728981eadf2020-06-02 10:20:10 -0700647 credentials=self.MOCK_CREDENTIALS,
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700648 )
649
650 self.assertEqual(plus._baseUrl, api_endpoint)
651
Bu Sun Kim790e7022020-09-11 20:18:06 -0600652 def test_scopes_from_client_options(self):
653 discovery = read_datafile("plus.json")
654
655 with mock.patch("googleapiclient._auth.default_credentials") as default:
656 plus = build_from_document(
657 discovery, client_options={"scopes": ["1", "2"]},
658 )
659
660 default.assert_called_once_with(scopes=["1", "2"], quota_project_id=None)
661
662 def test_quota_project_from_client_options(self):
663 discovery = read_datafile("plus.json")
664
665 with mock.patch("googleapiclient._auth.default_credentials") as default:
666 plus = build_from_document(
667 discovery,
668 client_options=google.api_core.client_options.ClientOptions(
669 quota_project_id="my-project"
670 ),
671 )
672
673 default.assert_called_once_with(scopes=None, quota_project_id="my-project")
674
675 def test_credentials_file_from_client_options(self):
676 discovery = read_datafile("plus.json")
677
678 with mock.patch("googleapiclient._auth.credentials_from_file") as default:
679 plus = build_from_document(
680 discovery,
681 client_options=google.api_core.client_options.ClientOptions(
682 credentials_file="credentials.json"
683 ),
684 )
685
686 default.assert_called_once_with(
687 "credentials.json", scopes=None, quota_project_id=None
688 )
689
Jon Wayne Parrott068eb352017-02-08 10:13:06 -0800690
arithmetic1728981eadf2020-06-02 10:20:10 -0700691REGULAR_ENDPOINT = "https://www.googleapis.com/plus/v1/"
692MTLS_ENDPOINT = "https://www.mtls.googleapis.com/plus/v1/"
693
694
695class DiscoveryFromDocumentMutualTLS(unittest.TestCase):
696 MOCK_CREDENTIALS = mock.Mock(spec=google.auth.credentials.Credentials)
697 ADC_CERT_PATH = "adc_cert_path"
698 ADC_KEY_PATH = "adc_key_path"
699 ADC_PASSPHRASE = "adc_passphrase"
700
arithmetic17282fc5ca12020-08-27 14:08:12 -0700701 def check_http_client_cert(self, resource, has_client_cert="false"):
arithmetic1728981eadf2020-06-02 10:20:10 -0700702 if isinstance(resource._http, google_auth_httplib2.AuthorizedHttp):
703 certs = list(resource._http.http.certificates.iter(""))
704 else:
705 certs = list(resource._http.certificates.iter(""))
arithmetic17282fc5ca12020-08-27 14:08:12 -0700706 if has_client_cert == "true":
arithmetic1728981eadf2020-06-02 10:20:10 -0700707 self.assertEqual(len(certs), 1)
708 self.assertEqual(
709 certs[0], (self.ADC_KEY_PATH, self.ADC_CERT_PATH, self.ADC_PASSPHRASE)
710 )
711 else:
712 self.assertEqual(len(certs), 0)
713
714 def client_encrypted_cert_source(self):
715 return self.ADC_CERT_PATH, self.ADC_KEY_PATH, self.ADC_PASSPHRASE
716
arithmetic17282fc5ca12020-08-27 14:08:12 -0700717 @parameterized.expand(
718 [
719 ("never", "true"),
720 ("auto", "true"),
721 ("always", "true"),
722 ("never", "false"),
723 ("auto", "false"),
724 ("always", "false"),
725 ]
726 )
727 def test_mtls_not_trigger_if_http_provided(self, use_mtls_env, use_client_cert):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700728 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700729
arithmetic17282fc5ca12020-08-27 14:08:12 -0700730 with mock.patch.dict(
731 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
732 ):
733 with mock.patch.dict(
734 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
735 ):
736 plus = build_from_document(discovery, http=httplib2.Http())
737 self.assertIsNotNone(plus)
738 self.assertEqual(plus._baseUrl, REGULAR_ENDPOINT)
739 self.check_http_client_cert(plus, has_client_cert="false")
arithmetic1728981eadf2020-06-02 10:20:10 -0700740
741 @parameterized.expand(
742 [
arithmetic17282fc5ca12020-08-27 14:08:12 -0700743 ("never", "true"),
744 ("auto", "true"),
745 ("always", "true"),
746 ("never", "false"),
747 ("auto", "false"),
748 ("always", "false"),
arithmetic1728981eadf2020-06-02 10:20:10 -0700749 ]
750 )
arithmetic17282fc5ca12020-08-27 14:08:12 -0700751 def test_exception_with_client_cert_source(self, use_mtls_env, use_client_cert):
752 discovery = read_datafile("plus.json")
753 with mock.patch.dict(
754 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
755 ):
756 with mock.patch.dict(
757 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
758 ):
759 with self.assertRaises(MutualTLSChannelError):
760 build_from_document(
761 discovery,
762 credentials=self.MOCK_CREDENTIALS,
763 client_options={"client_cert_source": mock.Mock()},
764 )
765
766 @parameterized.expand(
767 [
768 ("never", "true", REGULAR_ENDPOINT),
769 ("auto", "true", MTLS_ENDPOINT),
770 ("always", "true", MTLS_ENDPOINT),
771 ("never", "false", REGULAR_ENDPOINT),
772 ("auto", "false", REGULAR_ENDPOINT),
773 ("always", "false", MTLS_ENDPOINT),
774 ]
775 )
776 def test_mtls_with_provided_client_cert(
777 self, use_mtls_env, use_client_cert, base_url
778 ):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700779 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700780
arithmetic17282fc5ca12020-08-27 14:08:12 -0700781 with mock.patch.dict(
782 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
783 ):
784 with mock.patch.dict(
785 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
786 ):
787 plus = build_from_document(
788 discovery,
789 credentials=self.MOCK_CREDENTIALS,
790 client_options={
791 "client_encrypted_cert_source": self.client_encrypted_cert_source
792 },
793 )
794 self.assertIsNotNone(plus)
795 self.check_http_client_cert(plus, has_client_cert=use_client_cert)
796 self.assertEqual(plus._baseUrl, base_url)
arithmetic1728981eadf2020-06-02 10:20:10 -0700797
arithmetic17282fc5ca12020-08-27 14:08:12 -0700798 @parameterized.expand(
799 [
800 ("never", "true"),
801 ("auto", "true"),
802 ("always", "true"),
803 ("never", "false"),
804 ("auto", "false"),
805 ("always", "false"),
806 ]
807 )
808 def test_endpoint_not_switch(self, use_mtls_env, use_client_cert):
arithmetic1728981eadf2020-06-02 10:20:10 -0700809 # Test endpoint is not switched if user provided api endpoint
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700810 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700811
arithmetic17282fc5ca12020-08-27 14:08:12 -0700812 with mock.patch.dict(
813 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
814 ):
815 with mock.patch.dict(
816 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
817 ):
818 plus = build_from_document(
819 discovery,
820 credentials=self.MOCK_CREDENTIALS,
821 client_options={
822 "api_endpoint": "https://foo.googleapis.com",
823 "client_encrypted_cert_source": self.client_encrypted_cert_source,
824 },
825 )
826 self.assertIsNotNone(plus)
827 self.check_http_client_cert(plus, has_client_cert=use_client_cert)
828 self.assertEqual(plus._baseUrl, "https://foo.googleapis.com")
arithmetic1728981eadf2020-06-02 10:20:10 -0700829
830 @parameterized.expand(
831 [
arithmetic17282fc5ca12020-08-27 14:08:12 -0700832 ("never", "true", REGULAR_ENDPOINT),
833 ("auto", "true", MTLS_ENDPOINT),
834 ("always", "true", MTLS_ENDPOINT),
835 ("never", "false", REGULAR_ENDPOINT),
836 ("auto", "false", REGULAR_ENDPOINT),
837 ("always", "false", MTLS_ENDPOINT),
arithmetic1728981eadf2020-06-02 10:20:10 -0700838 ]
839 )
840 @mock.patch(
841 "google.auth.transport.mtls.has_default_client_cert_source", autospec=True
842 )
843 @mock.patch(
844 "google.auth.transport.mtls.default_client_encrypted_cert_source", autospec=True
845 )
846 def test_mtls_with_default_client_cert(
847 self,
848 use_mtls_env,
arithmetic17282fc5ca12020-08-27 14:08:12 -0700849 use_client_cert,
arithmetic1728981eadf2020-06-02 10:20:10 -0700850 base_url,
851 default_client_encrypted_cert_source,
852 has_default_client_cert_source,
853 ):
854 has_default_client_cert_source.return_value = True
855 default_client_encrypted_cert_source.return_value = (
856 self.client_encrypted_cert_source
857 )
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700858 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700859
arithmetic17282fc5ca12020-08-27 14:08:12 -0700860 with mock.patch.dict(
861 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
862 ):
863 with mock.patch.dict(
864 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
865 ):
866 plus = build_from_document(
867 discovery,
868 credentials=self.MOCK_CREDENTIALS,
869 adc_cert_path=self.ADC_CERT_PATH,
870 adc_key_path=self.ADC_KEY_PATH,
871 )
872 self.assertIsNotNone(plus)
873 self.check_http_client_cert(plus, has_client_cert=use_client_cert)
874 self.assertEqual(plus._baseUrl, base_url)
arithmetic1728981eadf2020-06-02 10:20:10 -0700875
876 @parameterized.expand(
877 [
arithmetic17282fc5ca12020-08-27 14:08:12 -0700878 ("never", "true", REGULAR_ENDPOINT),
879 ("auto", "true", REGULAR_ENDPOINT),
880 ("always", "true", MTLS_ENDPOINT),
881 ("never", "false", REGULAR_ENDPOINT),
882 ("auto", "false", REGULAR_ENDPOINT),
883 ("always", "false", MTLS_ENDPOINT),
arithmetic1728981eadf2020-06-02 10:20:10 -0700884 ]
885 )
886 @mock.patch(
887 "google.auth.transport.mtls.has_default_client_cert_source", autospec=True
888 )
889 def test_mtls_with_no_client_cert(
arithmetic17282fc5ca12020-08-27 14:08:12 -0700890 self, use_mtls_env, use_client_cert, base_url, has_default_client_cert_source
arithmetic1728981eadf2020-06-02 10:20:10 -0700891 ):
892 has_default_client_cert_source.return_value = False
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700893 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700894
arithmetic17282fc5ca12020-08-27 14:08:12 -0700895 with mock.patch.dict(
896 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
897 ):
898 with mock.patch.dict(
899 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
900 ):
901 plus = build_from_document(
902 discovery,
903 credentials=self.MOCK_CREDENTIALS,
904 adc_cert_path=self.ADC_CERT_PATH,
905 adc_key_path=self.ADC_KEY_PATH,
906 )
907 self.assertIsNotNone(plus)
908 self.check_http_client_cert(plus, has_client_cert="false")
909 self.assertEqual(plus._baseUrl, base_url)
arithmetic1728981eadf2020-06-02 10:20:10 -0700910
911
Joe Gregorioa98733f2011-09-16 10:12:28 -0400912class DiscoveryFromHttp(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700913 def setUp(self):
914 self.old_environ = os.environ.copy()
Joe Gregorioa98733f2011-09-16 10:12:28 -0400915
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700916 def tearDown(self):
917 os.environ = self.old_environ
Joe Gregorio583d9e42011-09-16 15:54:15 -0400918
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700919 def test_userip_is_added_to_discovery_uri(self):
920 # build() will raise an HttpError on a 400, use this to pick the request uri
921 # out of the raised exception.
922 os.environ["REMOTE_ADDR"] = "10.0.0.1"
923 try:
924 http = HttpMockSequence(
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700925 [({"status": "400"}, read_datafile("zoo.json", "rb"))]
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700926 )
927 zoo = build(
928 "zoo",
929 "v1",
930 http=http,
931 developerKey=None,
Anthonios Partheniou3b4f2e22021-03-19 11:36:01 -0400932 discoveryServiceUrl="http://example.com"
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700933 )
934 self.fail("Should have raised an exception.")
935 except HttpError as e:
936 self.assertEqual(e.uri, "http://example.com?userIp=10.0.0.1")
Joe Gregorioa98733f2011-09-16 10:12:28 -0400937
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700938 def test_userip_missing_is_not_added_to_discovery_uri(self):
939 # build() will raise an HttpError on a 400, use this to pick the request uri
940 # out of the raised exception.
941 try:
942 http = HttpMockSequence(
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700943 [({"status": "400"}, read_datafile("zoo.json", "rb"))]
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700944 )
945 zoo = build(
946 "zoo",
947 "v1",
948 http=http,
949 developerKey=None,
Anthonios Partheniou3b4f2e22021-03-19 11:36:01 -0400950 discoveryServiceUrl="http://example.com"
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700951 )
952 self.fail("Should have raised an exception.")
953 except HttpError as e:
954 self.assertEqual(e.uri, "http://example.com")
Joe Gregorioa98733f2011-09-16 10:12:28 -0400955
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700956 def test_key_is_added_to_discovery_uri(self):
957 # build() will raise an HttpError on a 400, use this to pick the request uri
958 # out of the raised exception.
959 try:
960 http = HttpMockSequence(
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700961 [({"status": "400"}, read_datafile("zoo.json", "rb"))]
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700962 )
963 zoo = build(
964 "zoo",
965 "v1",
966 http=http,
967 developerKey="foo",
968 discoveryServiceUrl="http://example.com",
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500969 static_discovery=False,
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700970 )
971 self.fail("Should have raised an exception.")
972 except HttpError as e:
973 self.assertEqual(e.uri, "http://example.com?key=foo")
Arunpn9d779cc2018-11-30 10:25:01 -0800974
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700975 def test_discovery_loading_from_v2_discovery_uri(self):
976 http = HttpMockSequence(
977 [
978 ({"status": "404"}, "Not found"),
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700979 ({"status": "200"}, read_datafile("zoo.json", "rb")),
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700980 ]
981 )
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500982 zoo = build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700983 self.assertTrue(hasattr(zoo, "animals"))
984
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700985 def test_api_endpoint_override_from_client_options(self):
986 http = HttpMockSequence(
987 [
988 ({"status": "404"}, "Not found"),
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700989 ({"status": "200"}, read_datafile("zoo.json", "rb")),
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700990 ]
991 )
992 api_endpoint = "https://foo.googleapis.com/"
993 options = google.api_core.client_options.ClientOptions(
994 api_endpoint=api_endpoint
995 )
996 zoo = build(
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500997 "zoo", "v1", http=http, cache_discovery=False, client_options=options, static_discovery=False
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700998 )
999 self.assertEqual(zoo._baseUrl, api_endpoint)
1000
1001 def test_api_endpoint_override_from_client_options_dict(self):
1002 http = HttpMockSequence(
1003 [
1004 ({"status": "404"}, "Not found"),
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001005 ({"status": "200"}, read_datafile("zoo.json", "rb")),
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001006 ]
1007 )
1008 api_endpoint = "https://foo.googleapis.com/"
1009 zoo = build(
1010 "zoo",
1011 "v1",
1012 http=http,
1013 cache_discovery=False,
1014 client_options={"api_endpoint": api_endpoint},
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001015 static_discovery=False,
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001016 )
1017 self.assertEqual(zoo._baseUrl, api_endpoint)
1018
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -07001019 def test_discovery_with_empty_version_uses_v2(self):
Bu Sun Kim790e7022020-09-11 20:18:06 -06001020 http = HttpMockSequence([({"status": "200"}, read_datafile("zoo.json", "rb")),])
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001021 build("zoo", version=None, http=http, cache_discovery=False, static_discovery=False)
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -07001022 validate_discovery_requests(self, http, "zoo", None, V2_DISCOVERY_URI)
1023
1024 def test_discovery_with_empty_version_preserves_custom_uri(self):
Bu Sun Kim790e7022020-09-11 20:18:06 -06001025 http = HttpMockSequence([({"status": "200"}, read_datafile("zoo.json", "rb")),])
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -07001026 custom_discovery_uri = "https://foo.bar/$discovery"
1027 build(
Bu Sun Kim790e7022020-09-11 20:18:06 -06001028 "zoo",
1029 version=None,
1030 http=http,
1031 cache_discovery=False,
1032 discoveryServiceUrl=custom_discovery_uri,
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001033 static_discovery=False,
Bu Sun Kim790e7022020-09-11 20:18:06 -06001034 )
1035 validate_discovery_requests(self, http, "zoo", None, custom_discovery_uri)
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -07001036
1037 def test_discovery_with_valid_version_uses_v1(self):
Bu Sun Kim790e7022020-09-11 20:18:06 -06001038 http = HttpMockSequence([({"status": "200"}, read_datafile("zoo.json", "rb")),])
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001039 build("zoo", version="v123", http=http, cache_discovery=False, static_discovery=False)
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -07001040 validate_discovery_requests(self, http, "zoo", "v123", V1_DISCOVERY_URI)
1041
Joe Gregorioa98733f2011-09-16 10:12:28 -04001042
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001043class DiscoveryRetryFromHttp(unittest.TestCase):
1044 def test_repeated_500_retries_and_fails(self):
1045 http = HttpMockSequence(
1046 [
1047 ({"status": "500"}, read_datafile("500.json", "rb")),
1048 ({"status": "503"}, read_datafile("503.json", "rb")),
1049 ]
1050 )
1051 with self.assertRaises(HttpError):
1052 with mock.patch("time.sleep") as mocked_sleep:
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001053 build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False)
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001054
1055 mocked_sleep.assert_called_once()
1056 # We also want to verify that we stayed with v1 discovery
1057 validate_discovery_requests(self, http, "zoo", "v1", V1_DISCOVERY_URI)
1058
1059 def test_v2_repeated_500_retries_and_fails(self):
1060 http = HttpMockSequence(
1061 [
1062 ({"status": "404"}, "Not found"), # last v1 discovery call
1063 ({"status": "500"}, read_datafile("500.json", "rb")),
1064 ({"status": "503"}, read_datafile("503.json", "rb")),
1065 ]
1066 )
1067 with self.assertRaises(HttpError):
1068 with mock.patch("time.sleep") as mocked_sleep:
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001069 build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False)
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001070
1071 mocked_sleep.assert_called_once()
1072 # We also want to verify that we switched to v2 discovery
1073 validate_discovery_requests(self, http, "zoo", "v1", V2_DISCOVERY_URI)
1074
1075 def test_single_500_retries_and_succeeds(self):
1076 http = HttpMockSequence(
1077 [
1078 ({"status": "500"}, read_datafile("500.json", "rb")),
1079 ({"status": "200"}, read_datafile("zoo.json", "rb")),
1080 ]
1081 )
1082 with mock.patch("time.sleep") as mocked_sleep:
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001083 zoo = build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False)
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001084
1085 self.assertTrue(hasattr(zoo, "animals"))
1086 mocked_sleep.assert_called_once()
1087 # We also want to verify that we stayed with v1 discovery
1088 validate_discovery_requests(self, http, "zoo", "v1", V1_DISCOVERY_URI)
1089
1090 def test_single_500_then_404_retries_and_succeeds(self):
1091 http = HttpMockSequence(
1092 [
1093 ({"status": "500"}, read_datafile("500.json", "rb")),
1094 ({"status": "404"}, "Not found"), # last v1 discovery call
1095 ({"status": "200"}, read_datafile("zoo.json", "rb")),
1096 ]
1097 )
1098 with mock.patch("time.sleep") as mocked_sleep:
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001099 zoo = build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False)
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001100
1101 self.assertTrue(hasattr(zoo, "animals"))
1102 mocked_sleep.assert_called_once()
1103 # We also want to verify that we switched to v2 discovery
1104 validate_discovery_requests(self, http, "zoo", "v1", V2_DISCOVERY_URI)
1105
1106
Takashi Matsuo30125122015-08-19 11:42:32 -07001107class DiscoveryFromAppEngineCache(unittest.TestCase):
Zev Goldstein09e64472020-05-14 16:29:20 -04001108 def setUp(self):
1109 self.old_environ = os.environ.copy()
1110 os.environ["APPENGINE_RUNTIME"] = "python27"
1111
1112 def tearDown(self):
1113 os.environ = self.old_environ
1114
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001115 def test_appengine_memcache(self):
1116 # Hack module import
1117 self.orig_import = __import__
1118 self.mocked_api = mock.MagicMock()
Takashi Matsuo30125122015-08-19 11:42:32 -07001119
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001120 def import_mock(name, *args, **kwargs):
1121 if name == "google.appengine.api":
1122 return self.mocked_api
1123 return self.orig_import(name, *args, **kwargs)
Takashi Matsuo30125122015-08-19 11:42:32 -07001124
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001125 import_fullname = "__builtin__.__import__"
1126 if sys.version_info[0] >= 3:
1127 import_fullname = "builtins.__import__"
Takashi Matsuo30125122015-08-19 11:42:32 -07001128
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001129 with mock.patch(import_fullname, side_effect=import_mock):
1130 namespace = "google-api-client"
1131 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -07001132
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001133 self.mocked_api.memcache.get.return_value = None
Takashi Matsuo30125122015-08-19 11:42:32 -07001134
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001135 plus = build("plus", "v1", http=self.http, static_discovery=False)
Takashi Matsuo30125122015-08-19 11:42:32 -07001136
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001137 # memcache.get is called once
1138 url = "https://www.googleapis.com/discovery/v1/apis/plus/v1/rest"
1139 self.mocked_api.memcache.get.assert_called_once_with(
1140 url, namespace=namespace
1141 )
Takashi Matsuo30125122015-08-19 11:42:32 -07001142
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001143 # memcache.set is called once
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001144 content = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001145 self.mocked_api.memcache.set.assert_called_once_with(
1146 url, content, time=DISCOVERY_DOC_MAX_AGE, namespace=namespace
1147 )
Takashi Matsuo30125122015-08-19 11:42:32 -07001148
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001149 # Returns the cached content this time.
1150 self.mocked_api.memcache.get.return_value = content
Takashi Matsuo30125122015-08-19 11:42:32 -07001151
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001152 # Make sure the contents are returned from the cache.
1153 # (Otherwise it should through an error)
1154 self.http = HttpMock(None, {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -07001155
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001156 plus = build("plus", "v1", http=self.http, static_discovery=False)
Takashi Matsuo30125122015-08-19 11:42:32 -07001157
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001158 # memcache.get is called twice
1159 self.mocked_api.memcache.get.assert_has_calls(
1160 [
1161 mock.call(url, namespace=namespace),
1162 mock.call(url, namespace=namespace),
1163 ]
1164 )
Takashi Matsuo30125122015-08-19 11:42:32 -07001165
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001166 # memcahce.set is called just once
1167 self.mocked_api.memcache.set.assert_called_once_with(
1168 url, content, time=DISCOVERY_DOC_MAX_AGE, namespace=namespace
1169 )
Takashi Matsuo30125122015-08-19 11:42:32 -07001170
1171
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001172class DiscoveryFromStaticDocument(unittest.TestCase):
1173 def test_retrieve_from_local_when_static_discovery_true(self):
1174 http = HttpMockSequence([({"status": "400"}, "")])
1175 drive = build("drive", "v3", http=http, cache_discovery=False,
1176 static_discovery=True)
1177 self.assertIsNotNone(drive)
1178 self.assertTrue(hasattr(drive, "files"))
1179
1180 def test_retrieve_from_internet_when_static_discovery_false(self):
1181 http = HttpMockSequence([({"status": "400"}, "")])
1182 with self.assertRaises(HttpError):
1183 build("drive", "v3", http=http, cache_discovery=False,
1184 static_discovery=False)
1185
1186 def test_unknown_api_when_static_discovery_true(self):
1187 with self.assertRaises(UnknownApiNameOrVersion):
1188 build("doesnotexist", "v3", cache_discovery=False,
1189 static_discovery=True)
1190
1191
Takashi Matsuo30125122015-08-19 11:42:32 -07001192class DictCache(Cache):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001193 def __init__(self):
1194 self.d = {}
1195
1196 def get(self, url):
1197 return self.d.get(url, None)
1198
1199 def set(self, url, content):
1200 self.d[url] = content
1201
1202 def contains(self, url):
1203 return url in self.d
Takashi Matsuo30125122015-08-19 11:42:32 -07001204
1205
1206class DiscoveryFromFileCache(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001207 def test_file_based_cache(self):
1208 cache = mock.Mock(wraps=DictCache())
1209 with mock.patch(
1210 "googleapiclient.discovery_cache.autodetect", return_value=cache
1211 ):
1212 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -07001213
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001214 plus = build("plus", "v1", http=self.http, static_discovery=False)
Takashi Matsuo30125122015-08-19 11:42:32 -07001215
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001216 # cache.get is called once
1217 url = "https://www.googleapis.com/discovery/v1/apis/plus/v1/rest"
1218 cache.get.assert_called_once_with(url)
Takashi Matsuo30125122015-08-19 11:42:32 -07001219
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001220 # cache.set is called once
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001221 content = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001222 cache.set.assert_called_once_with(url, content)
Takashi Matsuo30125122015-08-19 11:42:32 -07001223
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001224 # Make sure there is a cache entry for the plus v1 discovery doc.
1225 self.assertTrue(cache.contains(url))
Takashi Matsuo30125122015-08-19 11:42:32 -07001226
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001227 # Make sure the contents are returned from the cache.
1228 # (Otherwise it should through an error)
1229 self.http = HttpMock(None, {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -07001230
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001231 plus = build("plus", "v1", http=self.http, static_discovery=False)
Takashi Matsuo30125122015-08-19 11:42:32 -07001232
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001233 # cache.get is called twice
1234 cache.get.assert_has_calls([mock.call(url), mock.call(url)])
Takashi Matsuo30125122015-08-19 11:42:32 -07001235
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001236 # cahce.set is called just once
1237 cache.set.assert_called_once_with(url, content)
Takashi Matsuo30125122015-08-19 11:42:32 -07001238
1239
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001240class Discovery(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001241 def test_method_error_checking(self):
1242 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001243 plus = build("plus", "v1", http=self.http, static_discovery=False)
Joe Gregorio2379ecc2010-10-26 10:51:28 -04001244
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001245 # Missing required parameters
1246 try:
1247 plus.activities().list()
1248 self.fail()
1249 except TypeError as e:
1250 self.assertTrue("Missing" in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001251
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001252 # Missing required parameters even if supplied as None.
1253 try:
1254 plus.activities().list(collection=None, userId=None)
1255 self.fail()
1256 except TypeError as e:
1257 self.assertTrue("Missing" in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001258
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001259 # Parameter doesn't match regex
1260 try:
1261 plus.activities().list(collection="not_a_collection_name", userId="me")
1262 self.fail()
1263 except TypeError as e:
1264 self.assertTrue("not an allowed value" in str(e))
Joe Gregorio2467afa2012-06-20 12:21:25 -04001265
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001266 # Unexpected parameter
1267 try:
1268 plus.activities().list(flubber=12)
1269 self.fail()
1270 except TypeError as e:
1271 self.assertTrue("unexpected" in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001272
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001273 def _check_query_types(self, request):
1274 parsed = urlparse(request.uri)
1275 q = parse_qs(parsed[4])
1276 self.assertEqual(q["q"], ["foo"])
1277 self.assertEqual(q["i"], ["1"])
1278 self.assertEqual(q["n"], ["1.0"])
1279 self.assertEqual(q["b"], ["false"])
1280 self.assertEqual(q["a"], ["[1, 2, 3]"])
1281 self.assertEqual(q["o"], ["{'a': 1}"])
1282 self.assertEqual(q["e"], ["bar"])
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001283
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001284 def test_type_coercion(self):
1285 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001286 zoo = build("zoo", "v1", http=http, static_discovery=False)
Joe Gregoriobee86832011-02-22 10:00:19 -05001287
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001288 request = zoo.query(
1289 q="foo", i=1.0, n=1.0, b=0, a=[1, 2, 3], o={"a": 1}, e="bar"
1290 )
1291 self._check_query_types(request)
1292 request = zoo.query(
1293 q="foo", i=1, n=1, b=False, a=[1, 2, 3], o={"a": 1}, e="bar"
1294 )
1295 self._check_query_types(request)
Joe Gregoriobee86832011-02-22 10:00:19 -05001296
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001297 request = zoo.query(
1298 q="foo", i="1", n="1", b="", a=[1, 2, 3], o={"a": 1}, e="bar", er="two"
1299 )
Joe Gregoriof863f7a2011-02-24 03:24:44 -05001300
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001301 request = zoo.query(
1302 q="foo",
1303 i="1",
1304 n="1",
1305 b="",
1306 a=[1, 2, 3],
1307 o={"a": 1},
1308 e="bar",
1309 er=["one", "three"],
1310 rr=["foo", "bar"],
1311 )
1312 self._check_query_types(request)
Joe Gregorio6804c7a2011-11-18 14:30:32 -05001313
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001314 # Five is right out.
1315 self.assertRaises(TypeError, zoo.query, er=["one", "five"])
Joe Gregoriobee86832011-02-22 10:00:19 -05001316
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001317 def test_optional_stack_query_parameters(self):
1318 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001319 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001320 request = zoo.query(trace="html", fields="description")
Craig Citro1e742822012-03-01 12:59:22 -08001321
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001322 parsed = urlparse(request.uri)
1323 q = parse_qs(parsed[4])
1324 self.assertEqual(q["trace"], ["html"])
1325 self.assertEqual(q["fields"], ["description"])
Joe Gregorio13217952011-02-22 15:37:38 -05001326
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001327 def test_string_params_value_of_none_get_dropped(self):
1328 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001329 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001330 request = zoo.query(trace=None, fields="description")
Joe Gregoriof4153422011-03-18 22:45:18 -04001331
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001332 parsed = urlparse(request.uri)
1333 q = parse_qs(parsed[4])
1334 self.assertFalse("trace" in q)
Joe Gregorio2467afa2012-06-20 12:21:25 -04001335
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001336 def test_model_added_query_parameters(self):
1337 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001338 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001339 request = zoo.animals().get(name="Lion")
Joe Gregorio4b4002f2012-06-14 15:41:01 -04001340
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001341 parsed = urlparse(request.uri)
1342 q = parse_qs(parsed[4])
1343 self.assertEqual(q["alt"], ["json"])
1344 self.assertEqual(request.headers["accept"], "application/json")
Joe Gregorioe08a1662011-12-07 09:48:22 -05001345
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001346 def test_fallback_to_raw_model(self):
1347 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001348 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001349 request = zoo.animals().getmedia(name="Lion")
Joe Gregorioe08a1662011-12-07 09:48:22 -05001350
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001351 parsed = urlparse(request.uri)
1352 q = parse_qs(parsed[4])
1353 self.assertTrue("alt" not in q)
1354 self.assertEqual(request.headers["accept"], "*/*")
Joe Gregorioe08a1662011-12-07 09:48:22 -05001355
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001356 def test_patch(self):
1357 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001358 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001359 request = zoo.animals().patch(name="lion", body='{"description": "foo"}')
Joe Gregorioe08a1662011-12-07 09:48:22 -05001360
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001361 self.assertEqual(request.method, "PATCH")
Joe Gregoriof4153422011-03-18 22:45:18 -04001362
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001363 def test_batch_request_from_discovery(self):
1364 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1365 # zoo defines a batchPath
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001366 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001367 batch_request = zoo.new_batch_http_request()
1368 self.assertEqual(
1369 batch_request._batch_uri, "https://www.googleapis.com/batchZoo"
1370 )
Joe Gregoriof4153422011-03-18 22:45:18 -04001371
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001372 def test_batch_request_from_default(self):
1373 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
1374 # plus does not define a batchPath
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001375 plus = build("plus", "v1", http=self.http, cache_discovery=False, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001376 batch_request = plus.new_batch_http_request()
1377 self.assertEqual(batch_request._batch_uri, "https://www.googleapis.com/batch")
Pepper Lebeck-Jobe860836f2015-06-12 20:42:23 -04001378
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001379 def test_tunnel_patch(self):
1380 http = HttpMockSequence(
1381 [
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001382 ({"status": "200"}, read_datafile("zoo.json", "rb")),
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001383 ({"status": "200"}, "echo_request_headers_as_json"),
1384 ]
1385 )
1386 http = tunnel_patch(http)
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001387 zoo = build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001388 resp = zoo.animals().patch(name="lion", body='{"description": "foo"}').execute()
Pepper Lebeck-Jobe860836f2015-06-12 20:42:23 -04001389
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001390 self.assertTrue("x-http-method-override" in resp)
Joe Gregoriof4153422011-03-18 22:45:18 -04001391
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001392 def test_plus_resources(self):
1393 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001394 plus = build("plus", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001395 self.assertTrue(getattr(plus, "activities"))
1396 self.assertTrue(getattr(plus, "people"))
Joe Gregorioca876e42011-02-22 19:39:42 -05001397
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001398 def test_oauth2client_credentials(self):
1399 credentials = mock.Mock(spec=GoogleCredentials)
1400 credentials.create_scoped_required.return_value = False
Joe Gregorioc5c5a372010-09-22 11:42:32 -04001401
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001402 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001403 service = build_from_document(discovery, credentials=credentials)
1404 self.assertEqual(service._http, credentials.authorize.return_value)
Orest Bolohane92c9002014-05-30 11:15:43 -07001405
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001406 def test_google_auth_credentials(self):
1407 credentials = mock.Mock(spec=google.auth.credentials.Credentials)
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001408 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001409 service = build_from_document(discovery, credentials=credentials)
Orest Bolohane92c9002014-05-30 11:15:43 -07001410
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001411 self.assertIsInstance(service._http, google_auth_httplib2.AuthorizedHttp)
1412 self.assertEqual(service._http.credentials, credentials)
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -08001413
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001414 def test_no_scopes_no_credentials(self):
1415 # Zoo doesn't have scopes
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001416 discovery = read_datafile("zoo.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001417 service = build_from_document(discovery)
1418 # Should be an ordinary httplib2.Http instance and not AuthorizedHttp.
1419 self.assertIsInstance(service._http, httplib2.Http)
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -08001420
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001421 def test_full_featured(self):
1422 # Zoo should exercise all discovery facets
1423 # and should also have no future.json file.
1424 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001425 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001426 self.assertTrue(getattr(zoo, "animals"))
Orest Bolohane92c9002014-05-30 11:15:43 -07001427
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001428 request = zoo.animals().list(name="bat", projection="full")
1429 parsed = urlparse(request.uri)
1430 q = parse_qs(parsed[4])
1431 self.assertEqual(q["name"], ["bat"])
1432 self.assertEqual(q["projection"], ["full"])
Joe Gregoriof863f7a2011-02-24 03:24:44 -05001433
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001434 def test_nested_resources(self):
1435 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001436 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001437 self.assertTrue(getattr(zoo, "animals"))
1438 request = zoo.my().favorites().list(max_results="5")
1439 parsed = urlparse(request.uri)
1440 q = parse_qs(parsed[4])
1441 self.assertEqual(q["max-results"], ["5"])
Joe Gregorio2379ecc2010-10-26 10:51:28 -04001442
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001443 @unittest.skipIf(six.PY3, "print is not a reserved name in Python 3")
1444 def test_methods_with_reserved_names(self):
1445 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1446 zoo = build("zoo", "v1", http=self.http)
1447 self.assertTrue(getattr(zoo, "animals"))
1448 request = zoo.global_().print_().assert_(max_results="5")
1449 parsed = urlparse(request.uri)
1450 self.assertEqual(parsed[2], "/zoo/v1/global/print/assert")
Joe Gregorio3fada332011-01-07 17:07:45 -05001451
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001452 def test_top_level_functions(self):
1453 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001454 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001455 self.assertTrue(getattr(zoo, "query"))
1456 request = zoo.query(q="foo")
1457 parsed = urlparse(request.uri)
1458 q = parse_qs(parsed[4])
1459 self.assertEqual(q["q"], ["foo"])
Joe Gregoriod92897c2011-07-07 11:44:56 -04001460
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001461 def test_simple_media_uploads(self):
1462 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001463 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001464 doc = getattr(zoo.animals().insert, "__doc__")
1465 self.assertTrue("media_body" in doc)
Joe Gregorio2379ecc2010-10-26 10:51:28 -04001466
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001467 def test_simple_media_upload_no_max_size_provided(self):
1468 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001469 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001470 request = zoo.animals().crossbreed(media_body=datafile("small.png"))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001471 self.assertEqual("image/png", request.headers["content-type"])
1472 self.assertEqual(b"PNG", request.body[1:4])
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001473
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001474 def test_simple_media_raise_correct_exceptions(self):
1475 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001476 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorio84d3c1f2011-07-25 10:39:45 -04001477
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001478 try:
1479 zoo.animals().insert(media_body=datafile("smiley.png"))
1480 self.fail("should throw exception if media is too large.")
1481 except MediaUploadSizeError:
1482 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001483
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001484 try:
1485 zoo.animals().insert(media_body=datafile("small.jpg"))
1486 self.fail("should throw exception if mimetype is unacceptable.")
1487 except UnacceptableMimeTypeError:
1488 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001489
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001490 def test_simple_media_good_upload(self):
1491 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001492 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001493
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001494 request = zoo.animals().insert(media_body=datafile("small.png"))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001495 self.assertEqual("image/png", request.headers["content-type"])
1496 self.assertEqual(b"PNG", request.body[1:4])
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001497 assertUrisEqual(
1498 self,
1499 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json",
1500 request.uri,
1501 )
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001502
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001503 def test_simple_media_unknown_mimetype(self):
1504 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001505 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001506
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001507 try:
1508 zoo.animals().insert(media_body=datafile("small-png"))
1509 self.fail("should throw exception if mimetype is unknown.")
1510 except UnknownFileType:
1511 pass
Brian J. Watson38051ac2016-10-25 07:53:08 -07001512
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001513 request = zoo.animals().insert(
1514 media_body=datafile("small-png"), media_mime_type="image/png"
1515 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001516 self.assertEqual("image/png", request.headers["content-type"])
1517 self.assertEqual(b"PNG", request.body[1:4])
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001518 assertUrisEqual(
1519 self,
1520 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json",
1521 request.uri,
1522 )
Brian J. Watson38051ac2016-10-25 07:53:08 -07001523
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001524 def test_multipart_media_raise_correct_exceptions(self):
1525 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001526 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Brian J. Watson38051ac2016-10-25 07:53:08 -07001527
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001528 try:
1529 zoo.animals().insert(media_body=datafile("smiley.png"), body={})
1530 self.fail("should throw exception if media is too large.")
1531 except MediaUploadSizeError:
1532 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001533
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001534 try:
1535 zoo.animals().insert(media_body=datafile("small.jpg"), body={})
1536 self.fail("should throw exception if mimetype is unacceptable.")
1537 except UnacceptableMimeTypeError:
1538 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001539
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001540 def test_multipart_media_good_upload(self, static_discovery=False):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001541 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001542 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001543
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001544 request = zoo.animals().insert(media_body=datafile("small.png"), body={})
1545 self.assertTrue(request.headers["content-type"].startswith("multipart/related"))
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001546 contents = read_datafile("small.png", "rb")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001547 boundary = re.match(b"--=+([^=]+)", request.body).group(1)
1548 self.assertEqual(
1549 request.body.rstrip(b"\n"), # Python 2.6 does not add a trailing \n
1550 b"--==============="
1551 + boundary
1552 + b"==\n"
1553 + b"Content-Type: application/json\n"
1554 + b"MIME-Version: 1.0\n\n"
1555 + b'{"data": {}}\n'
1556 + b"--==============="
1557 + boundary
1558 + b"==\n"
1559 + b"Content-Type: image/png\n"
1560 + b"MIME-Version: 1.0\n"
1561 + b"Content-Transfer-Encoding: binary\n\n"
1562 + contents
1563 + b"\n--==============="
1564 + boundary
1565 + b"==--",
1566 )
1567 assertUrisEqual(
1568 self,
1569 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=multipart&alt=json",
1570 request.uri,
1571 )
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001572
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001573 def test_media_capable_method_without_media(self):
1574 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001575 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001576
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001577 request = zoo.animals().insert(body={})
1578 self.assertTrue(request.headers["content-type"], "application/json")
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001579
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001580 def test_resumable_multipart_media_good_upload(self):
1581 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001582 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorioc5c5a372010-09-22 11:42:32 -04001583
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001584 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1585 request = zoo.animals().insert(media_body=media_upload, body={})
1586 self.assertTrue(request.headers["content-type"].startswith("application/json"))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001587 self.assertEqual('{"data": {}}', request.body)
1588 self.assertEqual(media_upload, request.resumable)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001589
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001590 self.assertEqual("image/png", request.resumable.mimetype())
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001591
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001592 self.assertNotEqual(request.body, None)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001593 self.assertEqual(request.resumable_uri, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001594
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001595 http = HttpMockSequence(
1596 [
1597 ({"status": "200", "location": "http://upload.example.com"}, ""),
1598 ({"status": "308", "location": "http://upload.example.com/2"}, ""),
1599 (
1600 {
1601 "status": "308",
1602 "location": "http://upload.example.com/3",
1603 "range": "0-12",
1604 },
1605 "",
1606 ),
1607 (
1608 {
1609 "status": "308",
1610 "location": "http://upload.example.com/4",
1611 "range": "0-%d" % (media_upload.size() - 2),
1612 },
1613 "",
1614 ),
1615 ({"status": "200"}, '{"foo": "bar"}'),
1616 ]
1617 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001618
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001619 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001620 self.assertEqual(None, body)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001621 self.assertTrue(isinstance(status, MediaUploadProgress))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001622 self.assertEqual(0, status.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001623
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001624 # Two requests should have been made and the resumable_uri should have been
1625 # updated for each one.
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001626 self.assertEqual(request.resumable_uri, "http://upload.example.com/2")
1627 self.assertEqual(media_upload, request.resumable)
1628 self.assertEqual(0, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001629
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001630 # This next chuck call should upload the first chunk
1631 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001632 self.assertEqual(request.resumable_uri, "http://upload.example.com/3")
1633 self.assertEqual(media_upload, request.resumable)
1634 self.assertEqual(13, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001635
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001636 # This call will upload the next chunk
1637 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001638 self.assertEqual(request.resumable_uri, "http://upload.example.com/4")
1639 self.assertEqual(media_upload.size() - 1, request.resumable_progress)
1640 self.assertEqual('{"data": {}}', request.body)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001641
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001642 # Final call to next_chunk should complete the upload.
1643 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001644 self.assertEqual(body, {"foo": "bar"})
1645 self.assertEqual(status, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001646
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001647 def test_resumable_media_good_upload(self):
1648 """Not a multipart upload."""
1649 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001650 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001651
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001652 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1653 request = zoo.animals().insert(media_body=media_upload, body=None)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001654 self.assertEqual(media_upload, request.resumable)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001655
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001656 self.assertEqual("image/png", request.resumable.mimetype())
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001657
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001658 self.assertEqual(request.body, None)
1659 self.assertEqual(request.resumable_uri, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001660
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001661 http = HttpMockSequence(
1662 [
1663 ({"status": "200", "location": "http://upload.example.com"}, ""),
1664 (
1665 {
1666 "status": "308",
1667 "location": "http://upload.example.com/2",
1668 "range": "0-12",
1669 },
1670 "",
1671 ),
1672 (
1673 {
1674 "status": "308",
1675 "location": "http://upload.example.com/3",
1676 "range": "0-%d" % (media_upload.size() - 2),
1677 },
1678 "",
1679 ),
1680 ({"status": "200"}, '{"foo": "bar"}'),
1681 ]
1682 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001683
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001684 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001685 self.assertEqual(None, body)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001686 self.assertTrue(isinstance(status, MediaUploadProgress))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001687 self.assertEqual(13, status.resumable_progress)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001688
1689 # Two requests should have been made and the resumable_uri should have been
1690 # updated for each one.
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001691 self.assertEqual(request.resumable_uri, "http://upload.example.com/2")
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001692
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001693 self.assertEqual(media_upload, request.resumable)
1694 self.assertEqual(13, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001695
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001696 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001697 self.assertEqual(request.resumable_uri, "http://upload.example.com/3")
1698 self.assertEqual(media_upload.size() - 1, request.resumable_progress)
1699 self.assertEqual(request.body, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001700
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001701 # Final call to next_chunk should complete the upload.
1702 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001703 self.assertEqual(body, {"foo": "bar"})
1704 self.assertEqual(status, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001705
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001706 def test_resumable_media_good_upload_from_execute(self):
1707 """Not a multipart upload."""
1708 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001709 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001710
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001711 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1712 request = zoo.animals().insert(media_body=media_upload, body=None)
1713 assertUrisEqual(
1714 self,
1715 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=resumable&alt=json",
1716 request.uri,
1717 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001718
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001719 http = HttpMockSequence(
1720 [
1721 ({"status": "200", "location": "http://upload.example.com"}, ""),
1722 (
1723 {
1724 "status": "308",
1725 "location": "http://upload.example.com/2",
1726 "range": "0-12",
1727 },
1728 "",
1729 ),
1730 (
1731 {
1732 "status": "308",
1733 "location": "http://upload.example.com/3",
1734 "range": "0-%d" % media_upload.size(),
1735 },
1736 "",
1737 ),
1738 ({"status": "200"}, '{"foo": "bar"}'),
1739 ]
1740 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001741
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001742 body = request.execute(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001743 self.assertEqual(body, {"foo": "bar"})
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001744
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001745 def test_resumable_media_fail_unknown_response_code_first_request(self):
1746 """Not a multipart upload."""
1747 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001748 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001749
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001750 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1751 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001752
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001753 http = HttpMockSequence(
1754 [({"status": "400", "location": "http://upload.example.com"}, "")]
1755 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001756
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001757 try:
1758 request.execute(http=http)
1759 self.fail("Should have raised ResumableUploadError.")
1760 except ResumableUploadError as e:
1761 self.assertEqual(400, e.resp.status)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001762
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001763 def test_resumable_media_fail_unknown_response_code_subsequent_request(self):
1764 """Not a multipart upload."""
1765 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001766 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001767
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001768 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1769 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001770
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001771 http = HttpMockSequence(
1772 [
1773 ({"status": "200", "location": "http://upload.example.com"}, ""),
1774 ({"status": "400"}, ""),
1775 ]
1776 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001777
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001778 self.assertRaises(HttpError, request.execute, http=http)
1779 self.assertTrue(request._in_error_state)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001780
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001781 http = HttpMockSequence(
1782 [
1783 ({"status": "308", "range": "0-5"}, ""),
1784 ({"status": "308", "range": "0-6"}, ""),
1785 ]
1786 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001787
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001788 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001789 self.assertEqual(
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001790 status.resumable_progress,
1791 7,
1792 "Should have first checked length and then tried to PUT more.",
1793 )
1794 self.assertFalse(request._in_error_state)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001795
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001796 # Put it back in an error state.
1797 http = HttpMockSequence([({"status": "400"}, "")])
1798 self.assertRaises(HttpError, request.execute, http=http)
1799 self.assertTrue(request._in_error_state)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001800
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001801 # Pretend the last request that 400'd actually succeeded.
1802 http = HttpMockSequence([({"status": "200"}, '{"foo": "bar"}')])
1803 status, body = request.next_chunk(http=http)
1804 self.assertEqual(body, {"foo": "bar"})
Joe Gregorio910b9b12012-06-12 09:36:30 -04001805
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001806 def test_media_io_base_stream_unlimited_chunksize_resume(self):
1807 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001808 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001809
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001810 # Set up a seekable stream and try to upload in single chunk.
1811 fd = BytesIO(b'01234"56789"')
1812 media_upload = MediaIoBaseUpload(
1813 fd=fd, mimetype="text/plain", chunksize=-1, resumable=True
1814 )
Joe Gregorio910b9b12012-06-12 09:36:30 -04001815
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001816 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001817
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001818 # The single chunk fails, restart at the right point.
1819 http = HttpMockSequence(
1820 [
1821 ({"status": "200", "location": "http://upload.example.com"}, ""),
1822 (
1823 {
1824 "status": "308",
1825 "location": "http://upload.example.com/2",
1826 "range": "0-4",
1827 },
1828 "",
1829 ),
1830 ({"status": "200"}, "echo_request_body"),
1831 ]
1832 )
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001833
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001834 body = request.execute(http=http)
1835 self.assertEqual("56789", body)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001836
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001837 def test_media_io_base_stream_chunksize_resume(self):
1838 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001839 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001840
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001841 # Set up a seekable stream and try to upload in chunks.
1842 fd = BytesIO(b"0123456789")
1843 media_upload = MediaIoBaseUpload(
1844 fd=fd, mimetype="text/plain", chunksize=5, resumable=True
1845 )
Joe Gregorio5c120db2012-08-23 09:13:55 -04001846
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001847 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregorio5c120db2012-08-23 09:13:55 -04001848
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001849 # The single chunk fails, pull the content sent out of the exception.
1850 http = HttpMockSequence(
1851 [
1852 ({"status": "200", "location": "http://upload.example.com"}, ""),
1853 ({"status": "400"}, "echo_request_body"),
1854 ]
1855 )
Pat Ferateed9affd2015-03-03 16:03:15 -08001856
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001857 try:
1858 body = request.execute(http=http)
1859 except HttpError as e:
1860 self.assertEqual(b"01234", e.content)
Pat Ferateed9affd2015-03-03 16:03:15 -08001861
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001862 def test_resumable_media_handle_uploads_of_unknown_size(self):
1863 http = HttpMockSequence(
1864 [
1865 ({"status": "200", "location": "http://upload.example.com"}, ""),
1866 ({"status": "200"}, "echo_request_headers_as_json"),
1867 ]
1868 )
Pat Ferateed9affd2015-03-03 16:03:15 -08001869
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001870 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001871 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorio5c120db2012-08-23 09:13:55 -04001872
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001873 # Create an upload that doesn't know the full size of the media.
1874 class IoBaseUnknownLength(MediaUpload):
1875 def chunksize(self):
1876 return 10
Joe Gregorio910b9b12012-06-12 09:36:30 -04001877
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001878 def mimetype(self):
1879 return "image/png"
Joe Gregorio910b9b12012-06-12 09:36:30 -04001880
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001881 def size(self):
1882 return None
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001883
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001884 def resumable(self):
1885 return True
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001886
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001887 def getbytes(self, begin, length):
1888 return "0123456789"
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001889
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001890 upload = IoBaseUnknownLength()
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001891
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001892 request = zoo.animals().insert(media_body=upload, body=None)
1893 status, body = request.next_chunk(http=http)
1894 self.assertEqual(body, {"Content-Range": "bytes 0-9/*", "Content-Length": "10"})
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001895
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001896 def test_resumable_media_no_streaming_on_unsupported_platforms(self):
1897 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001898 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001899
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001900 class IoBaseHasStream(MediaUpload):
1901 def chunksize(self):
1902 return 10
Joe Gregorio44454e42012-06-15 08:38:53 -04001903
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001904 def mimetype(self):
1905 return "image/png"
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001906
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001907 def size(self):
1908 return None
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001909
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001910 def resumable(self):
1911 return True
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001912
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001913 def getbytes(self, begin, length):
1914 return "0123456789"
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001915
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001916 def has_stream(self):
1917 return True
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001918
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001919 def stream(self):
1920 raise NotImplementedError()
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001921
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001922 upload = IoBaseHasStream()
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001923
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001924 orig_version = sys.version_info
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001925
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001926 sys.version_info = (2, 6, 5, "final", 0)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001927
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001928 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001929
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001930 # This should raise an exception because stream() will be called.
1931 http = HttpMockSequence(
1932 [
1933 ({"status": "200", "location": "http://upload.example.com"}, ""),
1934 ({"status": "200"}, "echo_request_headers_as_json"),
1935 ]
1936 )
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001937
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001938 self.assertRaises(NotImplementedError, request.next_chunk, http=http)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001939
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001940 sys.version_info = orig_version
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001941
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001942 def test_resumable_media_handle_uploads_of_unknown_size_eof(self):
1943 http = HttpMockSequence(
1944 [
1945 ({"status": "200", "location": "http://upload.example.com"}, ""),
1946 ({"status": "200"}, "echo_request_headers_as_json"),
1947 ]
1948 )
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001949
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001950 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001951 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001952
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001953 fd = BytesIO(b"data goes here")
Joe Gregorio44454e42012-06-15 08:38:53 -04001954
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001955 # Create an upload that doesn't know the full size of the media.
1956 upload = MediaIoBaseUpload(
1957 fd=fd, mimetype="image/png", chunksize=15, resumable=True
1958 )
Joe Gregorio44454e42012-06-15 08:38:53 -04001959
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001960 request = zoo.animals().insert(media_body=upload, body=None)
1961 status, body = request.next_chunk(http=http)
1962 self.assertEqual(
1963 body, {"Content-Range": "bytes 0-13/14", "Content-Length": "14"}
1964 )
Joe Gregorio44454e42012-06-15 08:38:53 -04001965
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001966 def test_resumable_media_handle_resume_of_upload_of_unknown_size(self):
1967 http = HttpMockSequence(
1968 [
1969 ({"status": "200", "location": "http://upload.example.com"}, ""),
1970 ({"status": "400"}, ""),
1971 ]
1972 )
Joe Gregorio44454e42012-06-15 08:38:53 -04001973
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001974 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001975 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001976
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001977 # Create an upload that doesn't know the full size of the media.
1978 fd = BytesIO(b"data goes here")
Joe Gregorio910b9b12012-06-12 09:36:30 -04001979
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001980 upload = MediaIoBaseUpload(
1981 fd=fd, mimetype="image/png", chunksize=500, resumable=True
1982 )
Joe Gregorio910b9b12012-06-12 09:36:30 -04001983
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001984 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001985
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001986 # Put it in an error state.
1987 self.assertRaises(HttpError, request.next_chunk, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001988
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001989 http = HttpMockSequence(
1990 [({"status": "400", "range": "0-5"}, "echo_request_headers_as_json")]
1991 )
1992 try:
1993 # Should resume the upload by first querying the status of the upload.
1994 request.next_chunk(http=http)
1995 except HttpError as e:
1996 expected = {"Content-Range": "bytes */14", "content-length": "0"}
1997 self.assertEqual(
1998 expected,
1999 json.loads(e.content.decode("utf-8")),
2000 "Should send an empty body when requesting the current upload status.",
2001 )
Joe Gregorio910b9b12012-06-12 09:36:30 -04002002
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002003 def test_pickle(self):
2004 sorted_resource_keys = [
2005 "_baseUrl",
2006 "_developerKey",
2007 "_dynamic_attrs",
2008 "_http",
2009 "_model",
2010 "_requestBuilder",
2011 "_resourceDesc",
2012 "_rootDesc",
2013 "_schema",
2014 "animals",
2015 "global_",
2016 "load",
2017 "loadNoTemplate",
2018 "my",
2019 "new_batch_http_request",
2020 "query",
2021 "scopedAnimals",
2022 ]
Joe Gregorio910b9b12012-06-12 09:36:30 -04002023
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002024 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05002025 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002026 self.assertEqual(sorted(zoo.__dict__.keys()), sorted_resource_keys)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05002027
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002028 pickled_zoo = pickle.dumps(zoo)
2029 new_zoo = pickle.loads(pickled_zoo)
2030 self.assertEqual(sorted(new_zoo.__dict__.keys()), sorted_resource_keys)
2031 self.assertTrue(hasattr(new_zoo, "animals"))
2032 self.assertTrue(callable(new_zoo.animals))
2033 self.assertTrue(hasattr(new_zoo, "global_"))
2034 self.assertTrue(callable(new_zoo.global_))
2035 self.assertTrue(hasattr(new_zoo, "load"))
2036 self.assertTrue(callable(new_zoo.load))
2037 self.assertTrue(hasattr(new_zoo, "loadNoTemplate"))
2038 self.assertTrue(callable(new_zoo.loadNoTemplate))
2039 self.assertTrue(hasattr(new_zoo, "my"))
2040 self.assertTrue(callable(new_zoo.my))
2041 self.assertTrue(hasattr(new_zoo, "query"))
2042 self.assertTrue(callable(new_zoo.query))
2043 self.assertTrue(hasattr(new_zoo, "scopedAnimals"))
2044 self.assertTrue(callable(new_zoo.scopedAnimals))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002045
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002046 self.assertEqual(sorted(zoo._dynamic_attrs), sorted(new_zoo._dynamic_attrs))
2047 self.assertEqual(zoo._baseUrl, new_zoo._baseUrl)
2048 self.assertEqual(zoo._developerKey, new_zoo._developerKey)
2049 self.assertEqual(zoo._requestBuilder, new_zoo._requestBuilder)
2050 self.assertEqual(zoo._resourceDesc, new_zoo._resourceDesc)
2051 self.assertEqual(zoo._rootDesc, new_zoo._rootDesc)
2052 # _http, _model and _schema won't be equal since we will get new
2053 # instances upon un-pickling
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002054
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002055 def _dummy_zoo_request(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07002056 zoo_contents = read_datafile("zoo.json")
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002057
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002058 zoo_uri = uritemplate.expand(DISCOVERY_URI, {"api": "zoo", "apiVersion": "v1"})
2059 if "REMOTE_ADDR" in os.environ:
2060 zoo_uri = util._add_query_parameter(
2061 zoo_uri, "userIp", os.environ["REMOTE_ADDR"]
2062 )
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002063
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002064 http = build_http()
2065 original_request = http.request
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002066
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002067 def wrapped_request(uri, method="GET", *args, **kwargs):
2068 if uri == zoo_uri:
2069 return httplib2.Response({"status": "200"}), zoo_contents
2070 return original_request(uri, method=method, *args, **kwargs)
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002071
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002072 http.request = wrapped_request
2073 return http
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002074
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002075 def _dummy_token(self):
2076 access_token = "foo"
2077 client_id = "some_client_id"
2078 client_secret = "cOuDdkfjxxnv+"
2079 refresh_token = "1/0/a.df219fjls0"
2080 token_expiry = datetime.datetime.utcnow()
2081 user_agent = "refresh_checker/1.0"
2082 return OAuth2Credentials(
2083 access_token,
2084 client_id,
2085 client_secret,
2086 refresh_token,
2087 token_expiry,
2088 GOOGLE_TOKEN_URI,
2089 user_agent,
2090 )
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002091
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002092 def test_pickle_with_credentials(self):
2093 credentials = self._dummy_token()
2094 http = self._dummy_zoo_request()
2095 http = credentials.authorize(http)
2096 self.assertTrue(hasattr(http.request, "credentials"))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002097
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05002098 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002099 pickled_zoo = pickle.dumps(zoo)
2100 new_zoo = pickle.loads(pickled_zoo)
2101 self.assertEqual(sorted(zoo.__dict__.keys()), sorted(new_zoo.__dict__.keys()))
2102 new_http = new_zoo._http
2103 self.assertFalse(hasattr(new_http.request, "credentials"))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002104
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002105 def test_resumable_media_upload_no_content(self):
2106 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05002107 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
andrewnestera4a44cf2017-03-31 16:09:31 +03002108
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002109 media_upload = MediaFileUpload(datafile("empty"), resumable=True)
2110 request = zoo.animals().insert(media_body=media_upload, body=None)
andrewnestera4a44cf2017-03-31 16:09:31 +03002111
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07002112 self.assertEqual(media_upload, request.resumable)
2113 self.assertEqual(request.body, None)
2114 self.assertEqual(request.resumable_uri, None)
andrewnestera4a44cf2017-03-31 16:09:31 +03002115
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002116 http = HttpMockSequence(
2117 [
2118 ({"status": "200", "location": "http://upload.example.com"}, ""),
2119 (
2120 {
2121 "status": "308",
2122 "location": "http://upload.example.com/2",
2123 "range": "0-0",
2124 },
2125 "",
2126 ),
2127 ]
2128 )
andrewnestera4a44cf2017-03-31 16:09:31 +03002129
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002130 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07002131 self.assertEqual(None, body)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002132 self.assertTrue(isinstance(status, MediaUploadProgress))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07002133 self.assertEqual(0, status.progress())
andrewnestera4a44cf2017-03-31 16:09:31 +03002134
Joe Gregorio708388c2012-06-15 13:43:04 -04002135
Joe Gregorioc5c5a372010-09-22 11:42:32 -04002136class Next(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002137 def test_next_successful_none_on_no_next_page_token(self):
2138 self.http = HttpMock(datafile("tasks.json"), {"status": "200"})
2139 tasks = build("tasks", "v1", http=self.http)
2140 request = tasks.tasklists().list()
2141 self.assertEqual(None, tasks.tasklists().list_next(request, {}))
Joe Gregorio00cf1d92010-09-27 09:22:03 -04002142
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002143 def test_next_successful_none_on_empty_page_token(self):
2144 self.http = HttpMock(datafile("tasks.json"), {"status": "200"})
2145 tasks = build("tasks", "v1", http=self.http)
2146 request = tasks.tasklists().list()
2147 next_request = tasks.tasklists().list_next(request, {"nextPageToken": ""})
2148 self.assertEqual(None, next_request)
Joe Gregorio3c676f92011-07-25 10:38:14 -04002149
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002150 def test_next_successful_with_next_page_token(self):
2151 self.http = HttpMock(datafile("tasks.json"), {"status": "200"})
2152 tasks = build("tasks", "v1", http=self.http)
2153 request = tasks.tasklists().list()
2154 next_request = tasks.tasklists().list_next(request, {"nextPageToken": "123abc"})
2155 parsed = list(urlparse(next_request.uri))
2156 q = parse_qs(parsed[4])
2157 self.assertEqual(q["pageToken"][0], "123abc")
Son Dinh2a9a2132015-07-23 16:30:56 +00002158
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002159 def test_next_successful_with_next_page_token_alternate_name(self):
2160 self.http = HttpMock(datafile("bigquery.json"), {"status": "200"})
2161 bigquery = build("bigquery", "v2", http=self.http)
2162 request = bigquery.tabledata().list(datasetId="", projectId="", tableId="")
2163 next_request = bigquery.tabledata().list_next(request, {"pageToken": "123abc"})
2164 parsed = list(urlparse(next_request.uri))
2165 q = parse_qs(parsed[4])
2166 self.assertEqual(q["pageToken"][0], "123abc")
Joe Gregorio3c676f92011-07-25 10:38:14 -04002167
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002168 def test_next_successful_with_next_page_token_in_body(self):
2169 self.http = HttpMock(datafile("logging.json"), {"status": "200"})
2170 logging = build("logging", "v2", http=self.http)
2171 request = logging.entries().list(body={})
2172 next_request = logging.entries().list_next(request, {"nextPageToken": "123abc"})
2173 body = JsonModel().deserialize(next_request.body)
2174 self.assertEqual(body["pageToken"], "123abc")
Thomas Coffee20af04d2017-02-10 15:24:44 -08002175
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002176 def test_next_with_method_with_no_properties(self):
2177 self.http = HttpMock(datafile("latitude.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05002178 service = build("latitude", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002179 service.currentLocation().get()
Thomas Coffee20af04d2017-02-10 15:24:44 -08002180
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002181 def test_next_nonexistent_with_no_next_page_token(self):
2182 self.http = HttpMock(datafile("drive.json"), {"status": "200"})
2183 drive = build("drive", "v3", http=self.http)
2184 drive.changes().watch(body={})
2185 self.assertFalse(callable(getattr(drive.changes(), "watch_next", None)))
Thomas Coffee20af04d2017-02-10 15:24:44 -08002186
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002187 def test_next_successful_with_next_page_token_required(self):
2188 self.http = HttpMock(datafile("drive.json"), {"status": "200"})
2189 drive = build("drive", "v3", http=self.http)
2190 request = drive.changes().list(pageToken="startPageToken")
2191 next_request = drive.changes().list_next(request, {"nextPageToken": "123abc"})
2192 parsed = list(urlparse(next_request.uri))
2193 q = parse_qs(parsed[4])
2194 self.assertEqual(q["pageToken"][0], "123abc")
Joe Gregorio00cf1d92010-09-27 09:22:03 -04002195
Joe Gregorioa98733f2011-09-16 10:12:28 -04002196
Joe Gregorio708388c2012-06-15 13:43:04 -04002197class MediaGet(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002198 def test_get_media(self):
2199 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05002200 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002201 request = zoo.animals().get_media(name="Lion")
Joe Gregorio708388c2012-06-15 13:43:04 -04002202
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002203 parsed = urlparse(request.uri)
2204 q = parse_qs(parsed[4])
2205 self.assertEqual(q["alt"], ["media"])
2206 self.assertEqual(request.headers["accept"], "*/*")
Joe Gregorio708388c2012-06-15 13:43:04 -04002207
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002208 http = HttpMockSequence([({"status": "200"}, "standing in for media")])
2209 response = request.execute(http=http)
2210 self.assertEqual(b"standing in for media", response)
Joe Gregorio708388c2012-06-15 13:43:04 -04002211
2212
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002213if __name__ == "__main__":
2214 unittest.main()