blob: 9500fbf9f3525d08f71fb1975180b570fc3b08c5 [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
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040024
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070025__author__ = "jcgregorio@google.com (Joe Gregorio)"
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040026
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -040027from collections import defaultdict
Daniel Hermesc2113242013-02-27 10:16:13 -080028import copy
Joe Gregoriodc106fc2012-11-20 14:30:14 -050029import datetime
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040030import httplib2
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -040031import io
Craig Citro7ee535d2015-02-23 10:11:14 -080032import itertools
Craig Citro6ae34d72014-08-18 23:10:09 -070033import json
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040034import os
Joe Gregoriodc106fc2012-11-20 14:30:14 -050035import pickle
Phil Ruffwind26178fc2015-10-13 19:00:33 -040036import re
Joe Gregorioc80ac9d2012-08-21 14:09:09 -040037import sys
Pat Ferate497a90f2015-03-09 09:52:54 -070038import unittest2 as unittest
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -040039import urllib
Joe Gregoriodc106fc2012-11-20 14:30:14 -050040
arithmetic1728981eadf2020-06-02 10:20:10 -070041from parameterized import parameterized
Takashi Matsuo30125122015-08-19 11:42:32 -070042import mock
43
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -080044import google.auth.credentials
arithmetic1728981eadf2020-06-02 10:20:10 -070045from google.auth.transport import mtls
46from google.auth.exceptions import MutualTLSChannelError
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -080047import google_auth_httplib2
Bu Sun Kim790e7022020-09-11 20:18:06 -060048import google.api_core.exceptions
49
John Asmuth864311d2014-04-24 15:46:08 -040050from googleapiclient.discovery import _fix_up_media_upload
51from googleapiclient.discovery import _fix_up_method_description
52from googleapiclient.discovery import _fix_up_parameters
Craig Citro7ee535d2015-02-23 10:11:14 -080053from googleapiclient.discovery import _urljoin
John Asmuth864311d2014-04-24 15:46:08 -040054from googleapiclient.discovery import build
55from googleapiclient.discovery import build_from_document
56from googleapiclient.discovery import DISCOVERY_URI
57from googleapiclient.discovery import key2param
58from googleapiclient.discovery import MEDIA_BODY_PARAMETER_DEFAULT_VALUE
Brian J. Watson38051ac2016-10-25 07:53:08 -070059from googleapiclient.discovery import MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE
John Asmuth864311d2014-04-24 15:46:08 -040060from googleapiclient.discovery import ResourceMethodParameters
61from googleapiclient.discovery import STACK_QUERY_PARAMETERS
62from googleapiclient.discovery import STACK_QUERY_PARAMETER_DEFAULT_VALUE
Dmitry Frenkelf3348f92020-07-15 13:05:58 -070063from googleapiclient.discovery import V1_DISCOVERY_URI
64from googleapiclient.discovery import V2_DISCOVERY_URI
Takashi Matsuo30125122015-08-19 11:42:32 -070065from googleapiclient.discovery_cache import DISCOVERY_DOC_MAX_AGE
66from googleapiclient.discovery_cache.base import Cache
John Asmuth864311d2014-04-24 15:46:08 -040067from googleapiclient.errors import HttpError
68from googleapiclient.errors import InvalidJsonError
69from googleapiclient.errors import MediaUploadSizeError
70from googleapiclient.errors import ResumableUploadError
71from googleapiclient.errors import UnacceptableMimeTypeError
Takashi Matsuo3772f9d2015-09-04 12:25:55 -070072from googleapiclient.errors import UnknownApiNameOrVersion
Brian J. Watson38051ac2016-10-25 07:53:08 -070073from googleapiclient.errors import UnknownFileType
Igor Maravić22435292017-01-19 22:28:22 +010074from googleapiclient.http import build_http
Pepper Lebeck-Jobe860836f2015-06-12 20:42:23 -040075from googleapiclient.http import BatchHttpRequest
John Asmuth864311d2014-04-24 15:46:08 -040076from googleapiclient.http import HttpMock
77from googleapiclient.http import HttpMockSequence
78from googleapiclient.http import MediaFileUpload
79from googleapiclient.http import MediaIoBaseUpload
80from googleapiclient.http import MediaUpload
81from googleapiclient.http import MediaUploadProgress
82from googleapiclient.http import tunnel_patch
Thomas Coffee20af04d2017-02-10 15:24:44 -080083from googleapiclient.model import JsonModel
Jean-Loup Roussel-Clouet0c0c8972018-04-28 05:42:43 +090084from googleapiclient.schema import Schemas
dhermes@google.coma9eb0bb2013-02-06 09:19:01 -080085from oauth2client import GOOGLE_TOKEN_URI
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -080086from oauth2client.client import OAuth2Credentials, GoogleCredentials
Joe Gregorio79daca02013-03-29 16:25:52 -040087
Bu Sun Kima9583f72021-03-15 09:12:02 -060088
Helen Koikede13e3b2018-04-26 16:05:16 -030089from googleapiclient import _helpers as util
Jon Wayne Parrott36d4e1b2016-10-17 13:31:33 -070090
Joe Gregoriodc106fc2012-11-20 14:30:14 -050091import uritemplate
92
Joe Gregoriocb8103d2011-02-11 23:20:52 -050093
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070094DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
Joe Gregoriocb8103d2011-02-11 23:20:52 -050095
Joe Gregorioa98733f2011-09-16 10:12:28 -040096
Joe Gregoriof1ba7f12012-11-16 15:10:47 -050097def assertUrisEqual(testcase, expected, actual):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070098 """Test that URIs are the same, up to reordering of query parameters."""
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -040099 expected = urllib.parse.urlparse(expected)
100 actual = urllib.parse.urlparse(actual)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700101 testcase.assertEqual(expected.scheme, actual.scheme)
102 testcase.assertEqual(expected.netloc, actual.netloc)
103 testcase.assertEqual(expected.path, actual.path)
104 testcase.assertEqual(expected.params, actual.params)
105 testcase.assertEqual(expected.fragment, actual.fragment)
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -0400106 expected_query = urllib.parse.parse_qs(expected.query)
107 actual_query = urllib.parse.parse_qs(actual.query)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700108 for name in list(expected_query.keys()):
109 testcase.assertEqual(expected_query[name], actual_query[name])
110 for name in list(actual_query.keys()):
111 testcase.assertEqual(expected_query[name], actual_query[name])
Joe Gregoriof1ba7f12012-11-16 15:10:47 -0500112
113
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700114def assert_discovery_uri(testcase, actual, service_name, version, discovery):
115 """Assert that discovery URI used was the one that was expected
116 for a given service and version."""
117 params = {"api": service_name, "apiVersion": version}
118 expanded_requested_uri = uritemplate.expand(discovery, params)
119 assertUrisEqual(testcase, expanded_requested_uri, actual)
120
121
Bu Sun Kim790e7022020-09-11 20:18:06 -0600122def validate_discovery_requests(testcase, http_mock, service_name, version, discovery):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700123 """Validates that there have > 0 calls to Http Discovery
124 and that LAST discovery URI used was the one that was expected
125 for a given service and version."""
126 testcase.assertTrue(len(http_mock.request_sequence) > 0)
127 if len(http_mock.request_sequence) > 0:
128 actual_uri = http_mock.request_sequence[-1][0]
Bu Sun Kim790e7022020-09-11 20:18:06 -0600129 assert_discovery_uri(testcase, actual_uri, service_name, version, discovery)
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700130
131
Joe Gregoriocb8103d2011-02-11 23:20:52 -0500132def datafile(filename):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700133 return os.path.join(DATA_DIR, filename)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400134
135
Bu Sun Kim790e7022020-09-11 20:18:06 -0600136def read_datafile(filename, mode="r"):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700137 with open(datafile(filename), mode=mode) as f:
138 return f.read()
139
140
Joe Gregorio504a17f2012-12-07 14:14:26 -0500141class SetupHttplib2(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700142 def test_retries(self):
143 # Merely loading googleapiclient.discovery should set the RETRIES to 1.
144 self.assertEqual(1, httplib2.RETRIES)
Joe Gregorio504a17f2012-12-07 14:14:26 -0500145
146
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400147class Utilities(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700148 def setUp(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700149 self.zoo_root_desc = json.loads(read_datafile("zoo.json", "r"))
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700150 self.zoo_get_method_desc = self.zoo_root_desc["methods"]["query"]
151 self.zoo_animals_resource = self.zoo_root_desc["resources"]["animals"]
152 self.zoo_insert_method_desc = self.zoo_animals_resource["methods"]["insert"]
153 self.zoo_schema = Schemas(self.zoo_root_desc)
Daniel Hermesc2113242013-02-27 10:16:13 -0800154
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700155 def test_key2param(self):
156 self.assertEqual("max_results", key2param("max-results"))
157 self.assertEqual("x007_bond", key2param("007-bond"))
Daniel Hermesc2113242013-02-27 10:16:13 -0800158
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700159 def _base_fix_up_parameters_test(self, method_desc, http_method, root_desc, schema):
160 self.assertEqual(method_desc["httpMethod"], http_method)
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400161
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700162 method_desc_copy = copy.deepcopy(method_desc)
163 self.assertEqual(method_desc, method_desc_copy)
Daniel Hermesc2113242013-02-27 10:16:13 -0800164
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700165 parameters = _fix_up_parameters(
166 method_desc_copy, root_desc, http_method, schema
167 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800168
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700169 self.assertNotEqual(method_desc, method_desc_copy)
Daniel Hermesc2113242013-02-27 10:16:13 -0800170
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700171 for param_name in STACK_QUERY_PARAMETERS:
172 self.assertEqual(
173 STACK_QUERY_PARAMETER_DEFAULT_VALUE, parameters[param_name]
174 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800175
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -0400176 for param_name, value in root_desc.get("parameters", {}).items():
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700177 self.assertEqual(value, parameters[param_name])
Daniel Hermesc2113242013-02-27 10:16:13 -0800178
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700179 return parameters
Daniel Hermesc2113242013-02-27 10:16:13 -0800180
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700181 def test_fix_up_parameters_get(self):
182 parameters = self._base_fix_up_parameters_test(
183 self.zoo_get_method_desc, "GET", self.zoo_root_desc, self.zoo_schema
184 )
185 # Since http_method is 'GET'
186 self.assertFalse("body" in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800187
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700188 def test_fix_up_parameters_insert(self):
189 parameters = self._base_fix_up_parameters_test(
190 self.zoo_insert_method_desc, "POST", self.zoo_root_desc, self.zoo_schema
191 )
192 body = {"description": "The request body.", "type": "object", "$ref": "Animal"}
193 self.assertEqual(parameters["body"], body)
Daniel Hermesc2113242013-02-27 10:16:13 -0800194
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700195 def test_fix_up_parameters_check_body(self):
196 dummy_root_desc = {}
197 dummy_schema = {
198 "Request": {
199 "properties": {
200 "description": "Required. Dummy parameter.",
201 "type": "string",
202 }
203 }
Jean-Loup Roussel-Clouet0c0c8972018-04-28 05:42:43 +0900204 }
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700205 no_payload_http_method = "DELETE"
206 with_payload_http_method = "PUT"
Daniel Hermesc2113242013-02-27 10:16:13 -0800207
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700208 invalid_method_desc = {"response": "Who cares"}
209 valid_method_desc = {
210 "request": {"key1": "value1", "key2": "value2", "$ref": "Request"}
211 }
Daniel Hermesc2113242013-02-27 10:16:13 -0800212
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700213 parameters = _fix_up_parameters(
214 invalid_method_desc, dummy_root_desc, no_payload_http_method, dummy_schema
215 )
216 self.assertFalse("body" in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800217
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700218 parameters = _fix_up_parameters(
219 valid_method_desc, dummy_root_desc, no_payload_http_method, dummy_schema
220 )
221 self.assertFalse("body" in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800222
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700223 parameters = _fix_up_parameters(
224 invalid_method_desc, dummy_root_desc, with_payload_http_method, dummy_schema
225 )
226 self.assertFalse("body" in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800227
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700228 parameters = _fix_up_parameters(
229 valid_method_desc, dummy_root_desc, with_payload_http_method, dummy_schema
230 )
231 body = {
232 "description": "The request body.",
233 "type": "object",
234 "$ref": "Request",
235 "key1": "value1",
236 "key2": "value2",
237 }
238 self.assertEqual(parameters["body"], body)
Daniel Hermesc2113242013-02-27 10:16:13 -0800239
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700240 def test_fix_up_parameters_optional_body(self):
241 # Request with no parameters
242 dummy_schema = {"Request": {"properties": {}}}
243 method_desc = {"request": {"$ref": "Request"}}
Jean-Loup Roussel-Clouet0c0c8972018-04-28 05:42:43 +0900244
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700245 parameters = _fix_up_parameters(method_desc, {}, "POST", dummy_schema)
Jean-Loup Roussel-Clouet0c0c8972018-04-28 05:42:43 +0900246
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700247 def _base_fix_up_method_description_test(
248 self,
249 method_desc,
250 initial_parameters,
251 final_parameters,
252 final_accept,
253 final_max_size,
254 final_media_path_url,
255 ):
arithmetic1728981eadf2020-06-02 10:20:10 -0700256 fake_root_desc = {
257 "rootUrl": "http://root/",
258 "servicePath": "fake/",
259 "mtlsRootUrl": "http://root/",
260 }
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700261 fake_path_url = "fake-path/"
Daniel Hermesc2113242013-02-27 10:16:13 -0800262
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700263 accept, max_size, media_path_url = _fix_up_media_upload(
264 method_desc, fake_root_desc, fake_path_url, initial_parameters
265 )
266 self.assertEqual(accept, final_accept)
267 self.assertEqual(max_size, final_max_size)
268 self.assertEqual(media_path_url, final_media_path_url)
269 self.assertEqual(initial_parameters, final_parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800270
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700271 def test_fix_up_media_upload_no_initial_invalid(self):
272 invalid_method_desc = {"response": "Who cares"}
273 self._base_fix_up_method_description_test(
274 invalid_method_desc, {}, {}, [], 0, None
275 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800276
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700277 def test_fix_up_media_upload_no_initial_valid_minimal(self):
278 valid_method_desc = {"mediaUpload": {"accept": []}}
279 final_parameters = {
280 "media_body": MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
281 "media_mime_type": MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE,
282 }
283 self._base_fix_up_method_description_test(
284 valid_method_desc,
285 {},
286 final_parameters,
287 [],
288 0,
289 "http://root/upload/fake/fake-path/",
290 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800291
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700292 def test_fix_up_media_upload_no_initial_valid_full(self):
293 valid_method_desc = {"mediaUpload": {"accept": ["*/*"], "maxSize": "10GB"}}
294 final_parameters = {
295 "media_body": MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
296 "media_mime_type": MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE,
297 }
298 ten_gb = 10 * 2 ** 30
299 self._base_fix_up_method_description_test(
300 valid_method_desc,
301 {},
302 final_parameters,
303 ["*/*"],
304 ten_gb,
305 "http://root/upload/fake/fake-path/",
306 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800307
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700308 def test_fix_up_media_upload_with_initial_invalid(self):
309 invalid_method_desc = {"response": "Who cares"}
310 initial_parameters = {"body": {}}
311 self._base_fix_up_method_description_test(
312 invalid_method_desc, initial_parameters, initial_parameters, [], 0, None
313 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800314
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700315 def test_fix_up_media_upload_with_initial_valid_minimal(self):
316 valid_method_desc = {"mediaUpload": {"accept": []}}
317 initial_parameters = {"body": {}}
318 final_parameters = {
319 "body": {},
320 "media_body": MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
321 "media_mime_type": MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE,
322 }
323 self._base_fix_up_method_description_test(
324 valid_method_desc,
325 initial_parameters,
326 final_parameters,
327 [],
328 0,
329 "http://root/upload/fake/fake-path/",
330 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800331
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700332 def test_fix_up_media_upload_with_initial_valid_full(self):
333 valid_method_desc = {"mediaUpload": {"accept": ["*/*"], "maxSize": "10GB"}}
334 initial_parameters = {"body": {}}
335 final_parameters = {
336 "body": {},
337 "media_body": MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
338 "media_mime_type": MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE,
339 }
340 ten_gb = 10 * 2 ** 30
341 self._base_fix_up_method_description_test(
342 valid_method_desc,
343 initial_parameters,
344 final_parameters,
345 ["*/*"],
346 ten_gb,
347 "http://root/upload/fake/fake-path/",
348 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800349
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700350 def test_fix_up_method_description_get(self):
351 result = _fix_up_method_description(
352 self.zoo_get_method_desc, self.zoo_root_desc, self.zoo_schema
353 )
354 path_url = "query"
355 http_method = "GET"
356 method_id = "bigquery.query"
357 accept = []
358 max_size = 0
359 media_path_url = None
360 self.assertEqual(
361 result, (path_url, http_method, method_id, accept, max_size, media_path_url)
362 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800363
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700364 def test_fix_up_method_description_insert(self):
365 result = _fix_up_method_description(
366 self.zoo_insert_method_desc, self.zoo_root_desc, self.zoo_schema
367 )
368 path_url = "animals"
369 http_method = "POST"
370 method_id = "zoo.animals.insert"
371 accept = ["image/png"]
372 max_size = 1024
373 media_path_url = "https://www.googleapis.com/upload/zoo/v1/animals"
374 self.assertEqual(
375 result, (path_url, http_method, method_id, accept, max_size, media_path_url)
376 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800377
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700378 def test_urljoin(self):
379 # We want to exhaustively test various URL combinations.
380 simple_bases = ["https://www.googleapis.com", "https://www.googleapis.com/"]
381 long_urls = ["foo/v1/bar:custom?alt=json", "/foo/v1/bar:custom?alt=json"]
Craig Citro7ee535d2015-02-23 10:11:14 -0800382
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700383 long_bases = [
384 "https://www.googleapis.com/foo/v1",
385 "https://www.googleapis.com/foo/v1/",
386 ]
387 simple_urls = ["bar:custom?alt=json", "/bar:custom?alt=json"]
Craig Citro7ee535d2015-02-23 10:11:14 -0800388
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700389 final_url = "https://www.googleapis.com/foo/v1/bar:custom?alt=json"
390 for base, url in itertools.product(simple_bases, long_urls):
391 self.assertEqual(final_url, _urljoin(base, url))
392 for base, url in itertools.product(long_bases, simple_urls):
393 self.assertEqual(final_url, _urljoin(base, url))
Craig Citro7ee535d2015-02-23 10:11:14 -0800394
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700395 def test_ResourceMethodParameters_zoo_get(self):
396 parameters = ResourceMethodParameters(self.zoo_get_method_desc)
Craig Citro7ee535d2015-02-23 10:11:14 -0800397
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700398 param_types = {
399 "a": "any",
400 "b": "boolean",
401 "e": "string",
402 "er": "string",
403 "i": "integer",
404 "n": "number",
405 "o": "object",
406 "q": "string",
407 "rr": "string",
408 }
409 keys = list(param_types.keys())
410 self.assertEqual(parameters.argmap, dict((key, key) for key in keys))
411 self.assertEqual(parameters.required_params, [])
412 self.assertEqual(sorted(parameters.repeated_params), ["er", "rr"])
413 self.assertEqual(parameters.pattern_params, {"rr": "[a-z]+"})
414 self.assertEqual(
415 sorted(parameters.query_params),
416 ["a", "b", "e", "er", "i", "n", "o", "q", "rr"],
417 )
418 self.assertEqual(parameters.path_params, set())
419 self.assertEqual(parameters.param_types, param_types)
420 enum_params = {"e": ["foo", "bar"], "er": ["one", "two", "three"]}
421 self.assertEqual(parameters.enum_params, enum_params)
Daniel Hermes954e1242013-02-28 09:28:37 -0800422
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700423 def test_ResourceMethodParameters_zoo_animals_patch(self):
424 method_desc = self.zoo_animals_resource["methods"]["patch"]
425 parameters = ResourceMethodParameters(method_desc)
Daniel Hermes954e1242013-02-28 09:28:37 -0800426
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700427 param_types = {"name": "string"}
428 keys = list(param_types.keys())
429 self.assertEqual(parameters.argmap, dict((key, key) for key in keys))
430 self.assertEqual(parameters.required_params, ["name"])
431 self.assertEqual(parameters.repeated_params, [])
432 self.assertEqual(parameters.pattern_params, {})
433 self.assertEqual(parameters.query_params, [])
434 self.assertEqual(parameters.path_params, set(["name"]))
435 self.assertEqual(parameters.param_types, param_types)
436 self.assertEqual(parameters.enum_params, {})
Daniel Hermes954e1242013-02-28 09:28:37 -0800437
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400438
Bu Sun Kim98888da2020-09-23 11:10:39 -0600439class Discovery(unittest.TestCase):
440 def test_discovery_http_is_closed(self):
441 http = HttpMock(datafile("malformed.json"), {"status": "200"})
442 service = build("plus", "v1", credentials=mock.sentinel.credentials)
443 http.close.assert_called_once()
444
445
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500446class DiscoveryErrors(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700447 def test_tests_should_be_run_with_strict_positional_enforcement(self):
448 try:
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500449 plus = build("plus", "v1", None, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700450 self.fail("should have raised a TypeError exception over missing http=.")
451 except TypeError:
452 pass
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500453
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700454 def test_failed_to_parse_discovery_json(self):
455 self.http = HttpMock(datafile("malformed.json"), {"status": "200"})
456 try:
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500457 plus = build("plus", "v1", http=self.http, cache_discovery=False, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700458 self.fail("should have raised an exception over malformed JSON.")
459 except InvalidJsonError:
460 pass
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400461
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700462 def test_unknown_api_name_or_version(self):
463 http = HttpMockSequence(
464 [
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700465 ({"status": "404"}, read_datafile("zoo.json", "rb")),
466 ({"status": "404"}, read_datafile("zoo.json", "rb")),
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700467 ]
468 )
469 with self.assertRaises(UnknownApiNameOrVersion):
470 plus = build("plus", "v1", http=http, cache_discovery=False)
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500471
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700472 def test_credentials_and_http_mutually_exclusive(self):
473 http = HttpMock(datafile("plus.json"), {"status": "200"})
474 with self.assertRaises(ValueError):
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500475 build("plus", "v1", http=http, credentials=mock.sentinel.credentials, static_discovery=False)
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -0800476
Bu Sun Kim790e7022020-09-11 20:18:06 -0600477 def test_credentials_file_and_http_mutually_exclusive(self):
478 http = HttpMock(datafile("plus.json"), {"status": "200"})
479 with self.assertRaises(ValueError):
480 build(
481 "plus",
482 "v1",
483 http=http,
484 client_options=google.api_core.client_options.ClientOptions(
485 credentials_file="credentials.json"
486 ),
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500487 static_discovery=False,
Bu Sun Kim790e7022020-09-11 20:18:06 -0600488 )
489
490 def test_credentials_and_credentials_file_mutually_exclusive(self):
491 with self.assertRaises(google.api_core.exceptions.DuplicateCredentialArgs):
492 build(
493 "plus",
494 "v1",
495 credentials=mock.sentinel.credentials,
496 client_options=google.api_core.client_options.ClientOptions(
497 credentials_file="credentials.json"
498 ),
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500499 static_discovery=False,
Bu Sun Kim790e7022020-09-11 20:18:06 -0600500 )
501
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500502
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100503class DiscoveryFromDocument(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700504 MOCK_CREDENTIALS = mock.Mock(spec=google.auth.credentials.Credentials)
Joe Gregorioa98733f2011-09-16 10:12:28 -0400505
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700506 def test_can_build_from_local_document(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700507 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700508 plus = build_from_document(
509 discovery,
510 base="https://www.googleapis.com/",
511 credentials=self.MOCK_CREDENTIALS,
512 )
arithmetic1728981eadf2020-06-02 10:20:10 -0700513 self.assertIsNotNone(plus)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700514 self.assertTrue(hasattr(plus, "activities"))
Joe Gregorio4772f3d2012-12-10 10:22:37 -0500515
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700516 def test_can_build_from_local_deserialized_document(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700517 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700518 discovery = json.loads(discovery)
519 plus = build_from_document(
520 discovery,
521 base="https://www.googleapis.com/",
522 credentials=self.MOCK_CREDENTIALS,
523 )
arithmetic1728981eadf2020-06-02 10:20:10 -0700524 self.assertIsNotNone(plus)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700525 self.assertTrue(hasattr(plus, "activities"))
Joe Gregorioa98733f2011-09-16 10:12:28 -0400526
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700527 def test_building_with_base_remembers_base(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700528 discovery = read_datafile("plus.json")
Joe Gregorioa98733f2011-09-16 10:12:28 -0400529
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700530 base = "https://www.example.com/"
531 plus = build_from_document(
532 discovery, base=base, credentials=self.MOCK_CREDENTIALS
533 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700534 self.assertEqual("https://www.googleapis.com/plus/v1/", plus._baseUrl)
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100535
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700536 def test_building_with_optional_http_with_authorization(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700537 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700538 plus = build_from_document(
539 discovery,
540 base="https://www.googleapis.com/",
541 credentials=self.MOCK_CREDENTIALS,
542 )
Igor Maravić22435292017-01-19 22:28:22 +0100543
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700544 # plus service requires Authorization, hence we expect to see AuthorizedHttp object here
545 self.assertIsInstance(plus._http, google_auth_httplib2.AuthorizedHttp)
546 self.assertIsInstance(plus._http.http, httplib2.Http)
547 self.assertIsInstance(plus._http.http.timeout, int)
548 self.assertGreater(plus._http.http.timeout, 0)
Igor Maravić22435292017-01-19 22:28:22 +0100549
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700550 def test_building_with_optional_http_with_no_authorization(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700551 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700552 # Cleanup auth field, so we would use plain http client
553 discovery = json.loads(discovery)
554 discovery["auth"] = {}
555 discovery = json.dumps(discovery)
Igor Maravić22435292017-01-19 22:28:22 +0100556
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700557 plus = build_from_document(
558 discovery, base="https://www.googleapis.com/", credentials=None
559 )
560 # plus service requires Authorization
561 self.assertIsInstance(plus._http, httplib2.Http)
562 self.assertIsInstance(plus._http.timeout, int)
563 self.assertGreater(plus._http.timeout, 0)
Jonathan Wayne Parrotta6e6fbd2015-07-16 15:33:57 -0700564
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700565 def test_building_with_explicit_http(self):
566 http = HttpMock()
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700567 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700568 plus = build_from_document(
569 discovery, base="https://www.googleapis.com/", http=http
570 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700571 self.assertEqual(plus._http, http)
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100572
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700573 def test_building_with_developer_key_skips_adc(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700574 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700575 plus = build_from_document(
576 discovery, base="https://www.googleapis.com/", developerKey="123"
577 )
578 self.assertIsInstance(plus._http, httplib2.Http)
579 # It should not be an AuthorizedHttp, because that would indicate that
580 # application default credentials were used.
581 self.assertNotIsInstance(plus._http, google_auth_httplib2.AuthorizedHttp)
Jon Wayne Parrott068eb352017-02-08 10:13:06 -0800582
Bu Sun Kim98888da2020-09-23 11:10:39 -0600583 def test_building_with_context_manager(self):
584 discovery = read_datafile("plus.json")
585 with mock.patch("httplib2.Http") as http:
586 with build_from_document(discovery, base="https://www.googleapis.com/", credentials=self.MOCK_CREDENTIALS) as plus:
587 self.assertIsNotNone(plus)
588 self.assertTrue(hasattr(plus, "activities"))
589 plus._http.http.close.assert_called_once()
590
591 def test_resource_close(self):
592 discovery = read_datafile("plus.json")
Anthonios Partheniou3b4f2e22021-03-19 11:36:01 -0400593
Bu Sun Kima9583f72021-03-15 09:12:02 -0600594 with mock.patch("httplib2.Http", autospec=True) as httplib2_http:
595 http = httplib2_http()
596 plus = build_from_document(
597 discovery,
598 base="https://www.googleapis.com/",
599 http=http,
600 )
601 plus.close()
602 http.close.assert_called_once()
603
604 def test_resource_close_authorized_http(self):
605 discovery = read_datafile("plus.json")
606 with mock.patch("google_auth_httplib2.AuthorizedHttp", autospec=True):
Bu Sun Kim98888da2020-09-23 11:10:39 -0600607 plus = build_from_document(
608 discovery,
609 base="https://www.googleapis.com/",
610 credentials=self.MOCK_CREDENTIALS,
611 )
612 plus.close()
Bu Sun Kima9583f72021-03-15 09:12:02 -0600613 plus._http.close.assert_called_once()
Bu Sun Kim98888da2020-09-23 11:10:39 -0600614
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700615 def test_api_endpoint_override_from_client_options(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700616 discovery = read_datafile("plus.json")
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700617 api_endpoint = "https://foo.googleapis.com/"
618 options = google.api_core.client_options.ClientOptions(
619 api_endpoint=api_endpoint
620 )
Bu Sun Kim8ed1dcd2020-05-14 00:42:22 -0700621 plus = build_from_document(
arithmetic1728981eadf2020-06-02 10:20:10 -0700622 discovery, client_options=options, credentials=self.MOCK_CREDENTIALS
Bu Sun Kim8ed1dcd2020-05-14 00:42:22 -0700623 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700624
625 self.assertEqual(plus._baseUrl, api_endpoint)
626
Pavel Kiselev21af37b2020-06-18 19:50:03 +0300627 def test_api_endpoint_override_from_client_options_mapping_object(self):
628
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700629 discovery = read_datafile("plus.json")
Pavel Kiselev21af37b2020-06-18 19:50:03 +0300630 api_endpoint = "https://foo.googleapis.com/"
631 mapping_object = defaultdict(str)
Bu Sun Kim790e7022020-09-11 20:18:06 -0600632 mapping_object["api_endpoint"] = api_endpoint
Bu Sun Kimd500e832021-04-27 16:13:05 -0600633 plus = build_from_document(
634 discovery, client_options=mapping_object, credentials=self.MOCK_CREDENTIALS
635 )
Pavel Kiselev21af37b2020-06-18 19:50:03 +0300636
637 self.assertEqual(plus._baseUrl, api_endpoint)
638
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700639 def test_api_endpoint_override_from_client_options_dict(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700640 discovery = read_datafile("plus.json")
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700641 api_endpoint = "https://foo.googleapis.com/"
642 plus = build_from_document(
arithmetic1728981eadf2020-06-02 10:20:10 -0700643 discovery,
Bu Sun Kim8ed1dcd2020-05-14 00:42:22 -0700644 client_options={"api_endpoint": api_endpoint},
arithmetic1728981eadf2020-06-02 10:20:10 -0700645 credentials=self.MOCK_CREDENTIALS,
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700646 )
647
648 self.assertEqual(plus._baseUrl, api_endpoint)
649
Bu Sun Kim790e7022020-09-11 20:18:06 -0600650 def test_scopes_from_client_options(self):
651 discovery = read_datafile("plus.json")
652
653 with mock.patch("googleapiclient._auth.default_credentials") as default:
654 plus = build_from_document(
655 discovery, client_options={"scopes": ["1", "2"]},
656 )
657
658 default.assert_called_once_with(scopes=["1", "2"], quota_project_id=None)
659
660 def test_quota_project_from_client_options(self):
661 discovery = read_datafile("plus.json")
662
663 with mock.patch("googleapiclient._auth.default_credentials") as default:
664 plus = build_from_document(
665 discovery,
666 client_options=google.api_core.client_options.ClientOptions(
667 quota_project_id="my-project"
668 ),
669 )
670
671 default.assert_called_once_with(scopes=None, quota_project_id="my-project")
672
673 def test_credentials_file_from_client_options(self):
674 discovery = read_datafile("plus.json")
675
676 with mock.patch("googleapiclient._auth.credentials_from_file") as default:
677 plus = build_from_document(
678 discovery,
679 client_options=google.api_core.client_options.ClientOptions(
680 credentials_file="credentials.json"
681 ),
682 )
683
684 default.assert_called_once_with(
685 "credentials.json", scopes=None, quota_project_id=None
686 )
687
Jon Wayne Parrott068eb352017-02-08 10:13:06 -0800688
arithmetic1728981eadf2020-06-02 10:20:10 -0700689REGULAR_ENDPOINT = "https://www.googleapis.com/plus/v1/"
690MTLS_ENDPOINT = "https://www.mtls.googleapis.com/plus/v1/"
691
692
693class DiscoveryFromDocumentMutualTLS(unittest.TestCase):
694 MOCK_CREDENTIALS = mock.Mock(spec=google.auth.credentials.Credentials)
695 ADC_CERT_PATH = "adc_cert_path"
696 ADC_KEY_PATH = "adc_key_path"
697 ADC_PASSPHRASE = "adc_passphrase"
698
arithmetic17282fc5ca12020-08-27 14:08:12 -0700699 def check_http_client_cert(self, resource, has_client_cert="false"):
arithmetic1728981eadf2020-06-02 10:20:10 -0700700 if isinstance(resource._http, google_auth_httplib2.AuthorizedHttp):
701 certs = list(resource._http.http.certificates.iter(""))
702 else:
703 certs = list(resource._http.certificates.iter(""))
arithmetic17282fc5ca12020-08-27 14:08:12 -0700704 if has_client_cert == "true":
arithmetic1728981eadf2020-06-02 10:20:10 -0700705 self.assertEqual(len(certs), 1)
706 self.assertEqual(
707 certs[0], (self.ADC_KEY_PATH, self.ADC_CERT_PATH, self.ADC_PASSPHRASE)
708 )
709 else:
710 self.assertEqual(len(certs), 0)
711
712 def client_encrypted_cert_source(self):
713 return self.ADC_CERT_PATH, self.ADC_KEY_PATH, self.ADC_PASSPHRASE
714
arithmetic17282fc5ca12020-08-27 14:08:12 -0700715 @parameterized.expand(
716 [
717 ("never", "true"),
718 ("auto", "true"),
719 ("always", "true"),
720 ("never", "false"),
721 ("auto", "false"),
722 ("always", "false"),
723 ]
724 )
725 def test_mtls_not_trigger_if_http_provided(self, use_mtls_env, use_client_cert):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700726 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700727
arithmetic17282fc5ca12020-08-27 14:08:12 -0700728 with mock.patch.dict(
729 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
730 ):
731 with mock.patch.dict(
732 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
733 ):
734 plus = build_from_document(discovery, http=httplib2.Http())
735 self.assertIsNotNone(plus)
736 self.assertEqual(plus._baseUrl, REGULAR_ENDPOINT)
737 self.check_http_client_cert(plus, has_client_cert="false")
arithmetic1728981eadf2020-06-02 10:20:10 -0700738
739 @parameterized.expand(
740 [
arithmetic17282fc5ca12020-08-27 14:08:12 -0700741 ("never", "true"),
742 ("auto", "true"),
743 ("always", "true"),
744 ("never", "false"),
745 ("auto", "false"),
746 ("always", "false"),
arithmetic1728981eadf2020-06-02 10:20:10 -0700747 ]
748 )
arithmetic17282fc5ca12020-08-27 14:08:12 -0700749 def test_exception_with_client_cert_source(self, use_mtls_env, use_client_cert):
750 discovery = read_datafile("plus.json")
751 with mock.patch.dict(
752 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
753 ):
754 with mock.patch.dict(
755 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
756 ):
757 with self.assertRaises(MutualTLSChannelError):
758 build_from_document(
759 discovery,
760 credentials=self.MOCK_CREDENTIALS,
761 client_options={"client_cert_source": mock.Mock()},
762 )
763
764 @parameterized.expand(
765 [
766 ("never", "true", REGULAR_ENDPOINT),
767 ("auto", "true", MTLS_ENDPOINT),
768 ("always", "true", MTLS_ENDPOINT),
769 ("never", "false", REGULAR_ENDPOINT),
770 ("auto", "false", REGULAR_ENDPOINT),
771 ("always", "false", MTLS_ENDPOINT),
772 ]
773 )
774 def test_mtls_with_provided_client_cert(
775 self, use_mtls_env, use_client_cert, base_url
776 ):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700777 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700778
arithmetic17282fc5ca12020-08-27 14:08:12 -0700779 with mock.patch.dict(
780 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
781 ):
782 with mock.patch.dict(
783 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
784 ):
785 plus = build_from_document(
786 discovery,
787 credentials=self.MOCK_CREDENTIALS,
788 client_options={
789 "client_encrypted_cert_source": self.client_encrypted_cert_source
790 },
791 )
792 self.assertIsNotNone(plus)
793 self.check_http_client_cert(plus, has_client_cert=use_client_cert)
794 self.assertEqual(plus._baseUrl, base_url)
arithmetic1728981eadf2020-06-02 10:20:10 -0700795
arithmetic17282fc5ca12020-08-27 14:08:12 -0700796 @parameterized.expand(
797 [
798 ("never", "true"),
799 ("auto", "true"),
800 ("always", "true"),
801 ("never", "false"),
802 ("auto", "false"),
803 ("always", "false"),
804 ]
805 )
806 def test_endpoint_not_switch(self, use_mtls_env, use_client_cert):
arithmetic1728981eadf2020-06-02 10:20:10 -0700807 # Test endpoint is not switched if user provided api endpoint
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700808 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700809
arithmetic17282fc5ca12020-08-27 14:08:12 -0700810 with mock.patch.dict(
811 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
812 ):
813 with mock.patch.dict(
814 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
815 ):
816 plus = build_from_document(
817 discovery,
818 credentials=self.MOCK_CREDENTIALS,
819 client_options={
820 "api_endpoint": "https://foo.googleapis.com",
821 "client_encrypted_cert_source": self.client_encrypted_cert_source,
822 },
823 )
824 self.assertIsNotNone(plus)
825 self.check_http_client_cert(plus, has_client_cert=use_client_cert)
826 self.assertEqual(plus._baseUrl, "https://foo.googleapis.com")
arithmetic1728981eadf2020-06-02 10:20:10 -0700827
828 @parameterized.expand(
829 [
arithmetic17282fc5ca12020-08-27 14:08:12 -0700830 ("never", "true", REGULAR_ENDPOINT),
831 ("auto", "true", MTLS_ENDPOINT),
832 ("always", "true", MTLS_ENDPOINT),
833 ("never", "false", REGULAR_ENDPOINT),
834 ("auto", "false", REGULAR_ENDPOINT),
835 ("always", "false", MTLS_ENDPOINT),
arithmetic1728981eadf2020-06-02 10:20:10 -0700836 ]
837 )
838 @mock.patch(
839 "google.auth.transport.mtls.has_default_client_cert_source", autospec=True
840 )
841 @mock.patch(
842 "google.auth.transport.mtls.default_client_encrypted_cert_source", autospec=True
843 )
844 def test_mtls_with_default_client_cert(
845 self,
846 use_mtls_env,
arithmetic17282fc5ca12020-08-27 14:08:12 -0700847 use_client_cert,
arithmetic1728981eadf2020-06-02 10:20:10 -0700848 base_url,
849 default_client_encrypted_cert_source,
850 has_default_client_cert_source,
851 ):
852 has_default_client_cert_source.return_value = True
853 default_client_encrypted_cert_source.return_value = (
854 self.client_encrypted_cert_source
855 )
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700856 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700857
arithmetic17282fc5ca12020-08-27 14:08:12 -0700858 with mock.patch.dict(
859 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
860 ):
861 with mock.patch.dict(
862 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
863 ):
864 plus = build_from_document(
865 discovery,
866 credentials=self.MOCK_CREDENTIALS,
867 adc_cert_path=self.ADC_CERT_PATH,
868 adc_key_path=self.ADC_KEY_PATH,
869 )
870 self.assertIsNotNone(plus)
871 self.check_http_client_cert(plus, has_client_cert=use_client_cert)
872 self.assertEqual(plus._baseUrl, base_url)
arithmetic1728981eadf2020-06-02 10:20:10 -0700873
874 @parameterized.expand(
875 [
arithmetic17282fc5ca12020-08-27 14:08:12 -0700876 ("never", "true", REGULAR_ENDPOINT),
877 ("auto", "true", REGULAR_ENDPOINT),
878 ("always", "true", MTLS_ENDPOINT),
879 ("never", "false", REGULAR_ENDPOINT),
880 ("auto", "false", REGULAR_ENDPOINT),
881 ("always", "false", MTLS_ENDPOINT),
arithmetic1728981eadf2020-06-02 10:20:10 -0700882 ]
883 )
884 @mock.patch(
885 "google.auth.transport.mtls.has_default_client_cert_source", autospec=True
886 )
887 def test_mtls_with_no_client_cert(
arithmetic17282fc5ca12020-08-27 14:08:12 -0700888 self, use_mtls_env, use_client_cert, base_url, has_default_client_cert_source
arithmetic1728981eadf2020-06-02 10:20:10 -0700889 ):
890 has_default_client_cert_source.return_value = False
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700891 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700892
arithmetic17282fc5ca12020-08-27 14:08:12 -0700893 with mock.patch.dict(
894 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
895 ):
896 with mock.patch.dict(
897 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
898 ):
899 plus = build_from_document(
900 discovery,
901 credentials=self.MOCK_CREDENTIALS,
902 adc_cert_path=self.ADC_CERT_PATH,
903 adc_key_path=self.ADC_KEY_PATH,
904 )
905 self.assertIsNotNone(plus)
906 self.check_http_client_cert(plus, has_client_cert="false")
907 self.assertEqual(plus._baseUrl, base_url)
arithmetic1728981eadf2020-06-02 10:20:10 -0700908
909
Joe Gregorioa98733f2011-09-16 10:12:28 -0400910class DiscoveryFromHttp(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700911 def setUp(self):
912 self.old_environ = os.environ.copy()
Joe Gregorioa98733f2011-09-16 10:12:28 -0400913
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700914 def tearDown(self):
915 os.environ = self.old_environ
Joe Gregorio583d9e42011-09-16 15:54:15 -0400916
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700917 def test_userip_is_added_to_discovery_uri(self):
918 # build() will raise an HttpError on a 400, use this to pick the request uri
919 # out of the raised exception.
920 os.environ["REMOTE_ADDR"] = "10.0.0.1"
921 try:
922 http = HttpMockSequence(
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700923 [({"status": "400"}, read_datafile("zoo.json", "rb"))]
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700924 )
925 zoo = build(
926 "zoo",
927 "v1",
928 http=http,
929 developerKey=None,
Anthonios Partheniou3b4f2e22021-03-19 11:36:01 -0400930 discoveryServiceUrl="http://example.com"
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700931 )
932 self.fail("Should have raised an exception.")
933 except HttpError as e:
934 self.assertEqual(e.uri, "http://example.com?userIp=10.0.0.1")
Joe Gregorioa98733f2011-09-16 10:12:28 -0400935
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700936 def test_userip_missing_is_not_added_to_discovery_uri(self):
937 # build() will raise an HttpError on a 400, use this to pick the request uri
938 # out of the raised exception.
939 try:
940 http = HttpMockSequence(
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700941 [({"status": "400"}, read_datafile("zoo.json", "rb"))]
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700942 )
943 zoo = build(
944 "zoo",
945 "v1",
946 http=http,
947 developerKey=None,
Anthonios Partheniou3b4f2e22021-03-19 11:36:01 -0400948 discoveryServiceUrl="http://example.com"
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700949 )
950 self.fail("Should have raised an exception.")
951 except HttpError as e:
952 self.assertEqual(e.uri, "http://example.com")
Joe Gregorioa98733f2011-09-16 10:12:28 -0400953
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700954 def test_key_is_added_to_discovery_uri(self):
955 # build() will raise an HttpError on a 400, use this to pick the request uri
956 # out of the raised exception.
957 try:
958 http = HttpMockSequence(
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700959 [({"status": "400"}, read_datafile("zoo.json", "rb"))]
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700960 )
961 zoo = build(
962 "zoo",
963 "v1",
964 http=http,
965 developerKey="foo",
966 discoveryServiceUrl="http://example.com",
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500967 static_discovery=False,
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700968 )
969 self.fail("Should have raised an exception.")
970 except HttpError as e:
971 self.assertEqual(e.uri, "http://example.com?key=foo")
Arunpn9d779cc2018-11-30 10:25:01 -0800972
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700973 def test_discovery_loading_from_v2_discovery_uri(self):
974 http = HttpMockSequence(
975 [
976 ({"status": "404"}, "Not found"),
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700977 ({"status": "200"}, read_datafile("zoo.json", "rb")),
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700978 ]
979 )
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500980 zoo = build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700981 self.assertTrue(hasattr(zoo, "animals"))
982
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700983 def test_api_endpoint_override_from_client_options(self):
984 http = HttpMockSequence(
985 [
986 ({"status": "404"}, "Not found"),
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700987 ({"status": "200"}, read_datafile("zoo.json", "rb")),
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700988 ]
989 )
990 api_endpoint = "https://foo.googleapis.com/"
991 options = google.api_core.client_options.ClientOptions(
992 api_endpoint=api_endpoint
993 )
994 zoo = build(
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500995 "zoo", "v1", http=http, cache_discovery=False, client_options=options, static_discovery=False
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700996 )
997 self.assertEqual(zoo._baseUrl, api_endpoint)
998
999 def test_api_endpoint_override_from_client_options_dict(self):
1000 http = HttpMockSequence(
1001 [
1002 ({"status": "404"}, "Not found"),
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001003 ({"status": "200"}, read_datafile("zoo.json", "rb")),
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001004 ]
1005 )
1006 api_endpoint = "https://foo.googleapis.com/"
1007 zoo = build(
1008 "zoo",
1009 "v1",
1010 http=http,
1011 cache_discovery=False,
1012 client_options={"api_endpoint": api_endpoint},
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001013 static_discovery=False,
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001014 )
1015 self.assertEqual(zoo._baseUrl, api_endpoint)
1016
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -07001017 def test_discovery_with_empty_version_uses_v2(self):
Bu Sun Kim790e7022020-09-11 20:18:06 -06001018 http = HttpMockSequence([({"status": "200"}, read_datafile("zoo.json", "rb")),])
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001019 build("zoo", version=None, http=http, cache_discovery=False, static_discovery=False)
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -07001020 validate_discovery_requests(self, http, "zoo", None, V2_DISCOVERY_URI)
1021
1022 def test_discovery_with_empty_version_preserves_custom_uri(self):
Bu Sun Kim790e7022020-09-11 20:18:06 -06001023 http = HttpMockSequence([({"status": "200"}, read_datafile("zoo.json", "rb")),])
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -07001024 custom_discovery_uri = "https://foo.bar/$discovery"
1025 build(
Bu Sun Kim790e7022020-09-11 20:18:06 -06001026 "zoo",
1027 version=None,
1028 http=http,
1029 cache_discovery=False,
1030 discoveryServiceUrl=custom_discovery_uri,
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001031 static_discovery=False,
Bu Sun Kim790e7022020-09-11 20:18:06 -06001032 )
1033 validate_discovery_requests(self, http, "zoo", None, custom_discovery_uri)
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -07001034
1035 def test_discovery_with_valid_version_uses_v1(self):
Bu Sun Kim790e7022020-09-11 20:18:06 -06001036 http = HttpMockSequence([({"status": "200"}, read_datafile("zoo.json", "rb")),])
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001037 build("zoo", version="v123", http=http, cache_discovery=False, static_discovery=False)
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -07001038 validate_discovery_requests(self, http, "zoo", "v123", V1_DISCOVERY_URI)
1039
Joe Gregorioa98733f2011-09-16 10:12:28 -04001040
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001041class DiscoveryRetryFromHttp(unittest.TestCase):
1042 def test_repeated_500_retries_and_fails(self):
1043 http = HttpMockSequence(
1044 [
1045 ({"status": "500"}, read_datafile("500.json", "rb")),
1046 ({"status": "503"}, read_datafile("503.json", "rb")),
1047 ]
1048 )
1049 with self.assertRaises(HttpError):
1050 with mock.patch("time.sleep") as mocked_sleep:
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001051 build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False)
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001052
1053 mocked_sleep.assert_called_once()
1054 # We also want to verify that we stayed with v1 discovery
1055 validate_discovery_requests(self, http, "zoo", "v1", V1_DISCOVERY_URI)
1056
1057 def test_v2_repeated_500_retries_and_fails(self):
1058 http = HttpMockSequence(
1059 [
1060 ({"status": "404"}, "Not found"), # last v1 discovery call
1061 ({"status": "500"}, read_datafile("500.json", "rb")),
1062 ({"status": "503"}, read_datafile("503.json", "rb")),
1063 ]
1064 )
1065 with self.assertRaises(HttpError):
1066 with mock.patch("time.sleep") as mocked_sleep:
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001067 build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False)
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001068
1069 mocked_sleep.assert_called_once()
1070 # We also want to verify that we switched to v2 discovery
1071 validate_discovery_requests(self, http, "zoo", "v1", V2_DISCOVERY_URI)
1072
1073 def test_single_500_retries_and_succeeds(self):
1074 http = HttpMockSequence(
1075 [
1076 ({"status": "500"}, read_datafile("500.json", "rb")),
1077 ({"status": "200"}, read_datafile("zoo.json", "rb")),
1078 ]
1079 )
1080 with mock.patch("time.sleep") as mocked_sleep:
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001081 zoo = build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False)
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001082
1083 self.assertTrue(hasattr(zoo, "animals"))
1084 mocked_sleep.assert_called_once()
1085 # We also want to verify that we stayed with v1 discovery
1086 validate_discovery_requests(self, http, "zoo", "v1", V1_DISCOVERY_URI)
1087
1088 def test_single_500_then_404_retries_and_succeeds(self):
1089 http = HttpMockSequence(
1090 [
1091 ({"status": "500"}, read_datafile("500.json", "rb")),
1092 ({"status": "404"}, "Not found"), # last v1 discovery call
1093 ({"status": "200"}, read_datafile("zoo.json", "rb")),
1094 ]
1095 )
1096 with mock.patch("time.sleep") as mocked_sleep:
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001097 zoo = build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False)
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001098
1099 self.assertTrue(hasattr(zoo, "animals"))
1100 mocked_sleep.assert_called_once()
1101 # We also want to verify that we switched to v2 discovery
1102 validate_discovery_requests(self, http, "zoo", "v1", V2_DISCOVERY_URI)
1103
1104
Takashi Matsuo30125122015-08-19 11:42:32 -07001105class DiscoveryFromAppEngineCache(unittest.TestCase):
Zev Goldstein09e64472020-05-14 16:29:20 -04001106 def setUp(self):
1107 self.old_environ = os.environ.copy()
1108 os.environ["APPENGINE_RUNTIME"] = "python27"
1109
1110 def tearDown(self):
1111 os.environ = self.old_environ
1112
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001113 def test_appengine_memcache(self):
1114 # Hack module import
1115 self.orig_import = __import__
1116 self.mocked_api = mock.MagicMock()
Takashi Matsuo30125122015-08-19 11:42:32 -07001117
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001118 def import_mock(name, *args, **kwargs):
1119 if name == "google.appengine.api":
1120 return self.mocked_api
1121 return self.orig_import(name, *args, **kwargs)
Takashi Matsuo30125122015-08-19 11:42:32 -07001122
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001123 import_fullname = "__builtin__.__import__"
1124 if sys.version_info[0] >= 3:
1125 import_fullname = "builtins.__import__"
Takashi Matsuo30125122015-08-19 11:42:32 -07001126
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001127 with mock.patch(import_fullname, side_effect=import_mock):
1128 namespace = "google-api-client"
1129 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -07001130
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001131 self.mocked_api.memcache.get.return_value = None
Takashi Matsuo30125122015-08-19 11:42:32 -07001132
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001133 plus = build("plus", "v1", http=self.http, static_discovery=False)
Takashi Matsuo30125122015-08-19 11:42:32 -07001134
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001135 # memcache.get is called once
1136 url = "https://www.googleapis.com/discovery/v1/apis/plus/v1/rest"
1137 self.mocked_api.memcache.get.assert_called_once_with(
1138 url, namespace=namespace
1139 )
Takashi Matsuo30125122015-08-19 11:42:32 -07001140
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001141 # memcache.set is called once
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001142 content = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001143 self.mocked_api.memcache.set.assert_called_once_with(
1144 url, content, time=DISCOVERY_DOC_MAX_AGE, namespace=namespace
1145 )
Takashi Matsuo30125122015-08-19 11:42:32 -07001146
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001147 # Returns the cached content this time.
1148 self.mocked_api.memcache.get.return_value = content
Takashi Matsuo30125122015-08-19 11:42:32 -07001149
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001150 # Make sure the contents are returned from the cache.
1151 # (Otherwise it should through an error)
1152 self.http = HttpMock(None, {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -07001153
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001154 plus = build("plus", "v1", http=self.http, static_discovery=False)
Takashi Matsuo30125122015-08-19 11:42:32 -07001155
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001156 # memcache.get is called twice
1157 self.mocked_api.memcache.get.assert_has_calls(
1158 [
1159 mock.call(url, namespace=namespace),
1160 mock.call(url, namespace=namespace),
1161 ]
1162 )
Takashi Matsuo30125122015-08-19 11:42:32 -07001163
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001164 # memcahce.set is called just once
1165 self.mocked_api.memcache.set.assert_called_once_with(
1166 url, content, time=DISCOVERY_DOC_MAX_AGE, namespace=namespace
1167 )
Takashi Matsuo30125122015-08-19 11:42:32 -07001168
1169
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001170class DiscoveryFromStaticDocument(unittest.TestCase):
1171 def test_retrieve_from_local_when_static_discovery_true(self):
1172 http = HttpMockSequence([({"status": "400"}, "")])
1173 drive = build("drive", "v3", http=http, cache_discovery=False,
1174 static_discovery=True)
1175 self.assertIsNotNone(drive)
1176 self.assertTrue(hasattr(drive, "files"))
1177
1178 def test_retrieve_from_internet_when_static_discovery_false(self):
1179 http = HttpMockSequence([({"status": "400"}, "")])
1180 with self.assertRaises(HttpError):
1181 build("drive", "v3", http=http, cache_discovery=False,
1182 static_discovery=False)
1183
1184 def test_unknown_api_when_static_discovery_true(self):
1185 with self.assertRaises(UnknownApiNameOrVersion):
1186 build("doesnotexist", "v3", cache_discovery=False,
1187 static_discovery=True)
1188
1189
Takashi Matsuo30125122015-08-19 11:42:32 -07001190class DictCache(Cache):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001191 def __init__(self):
1192 self.d = {}
1193
1194 def get(self, url):
1195 return self.d.get(url, None)
1196
1197 def set(self, url, content):
1198 self.d[url] = content
1199
1200 def contains(self, url):
1201 return url in self.d
Takashi Matsuo30125122015-08-19 11:42:32 -07001202
1203
1204class DiscoveryFromFileCache(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001205 def test_file_based_cache(self):
1206 cache = mock.Mock(wraps=DictCache())
1207 with mock.patch(
1208 "googleapiclient.discovery_cache.autodetect", return_value=cache
1209 ):
1210 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -07001211
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001212 plus = build("plus", "v1", http=self.http, static_discovery=False)
Takashi Matsuo30125122015-08-19 11:42:32 -07001213
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001214 # cache.get is called once
1215 url = "https://www.googleapis.com/discovery/v1/apis/plus/v1/rest"
1216 cache.get.assert_called_once_with(url)
Takashi Matsuo30125122015-08-19 11:42:32 -07001217
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001218 # cache.set is called once
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001219 content = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001220 cache.set.assert_called_once_with(url, content)
Takashi Matsuo30125122015-08-19 11:42:32 -07001221
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001222 # Make sure there is a cache entry for the plus v1 discovery doc.
1223 self.assertTrue(cache.contains(url))
Takashi Matsuo30125122015-08-19 11:42:32 -07001224
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001225 # Make sure the contents are returned from the cache.
1226 # (Otherwise it should through an error)
1227 self.http = HttpMock(None, {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -07001228
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001229 plus = build("plus", "v1", http=self.http, static_discovery=False)
Takashi Matsuo30125122015-08-19 11:42:32 -07001230
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001231 # cache.get is called twice
1232 cache.get.assert_has_calls([mock.call(url), mock.call(url)])
Takashi Matsuo30125122015-08-19 11:42:32 -07001233
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001234 # cahce.set is called just once
1235 cache.set.assert_called_once_with(url, content)
Takashi Matsuo30125122015-08-19 11:42:32 -07001236
1237
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001238class Discovery(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001239 def test_method_error_checking(self):
1240 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001241 plus = build("plus", "v1", http=self.http, static_discovery=False)
Joe Gregorio2379ecc2010-10-26 10:51:28 -04001242
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001243 # Missing required parameters
1244 try:
1245 plus.activities().list()
1246 self.fail()
1247 except TypeError as e:
1248 self.assertTrue("Missing" in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001249
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001250 # Missing required parameters even if supplied as None.
1251 try:
1252 plus.activities().list(collection=None, userId=None)
1253 self.fail()
1254 except TypeError as e:
1255 self.assertTrue("Missing" in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001256
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001257 # Parameter doesn't match regex
1258 try:
1259 plus.activities().list(collection="not_a_collection_name", userId="me")
1260 self.fail()
1261 except TypeError as e:
1262 self.assertTrue("not an allowed value" in str(e))
Joe Gregorio2467afa2012-06-20 12:21:25 -04001263
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001264 # Unexpected parameter
1265 try:
1266 plus.activities().list(flubber=12)
1267 self.fail()
1268 except TypeError as e:
1269 self.assertTrue("unexpected" in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001270
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001271 def _check_query_types(self, request):
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -04001272 parsed = urllib.parse.urlparse(request.uri)
1273 q = urllib.parse.parse_qs(parsed.query)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001274 self.assertEqual(q["q"], ["foo"])
1275 self.assertEqual(q["i"], ["1"])
1276 self.assertEqual(q["n"], ["1.0"])
1277 self.assertEqual(q["b"], ["false"])
1278 self.assertEqual(q["a"], ["[1, 2, 3]"])
1279 self.assertEqual(q["o"], ["{'a': 1}"])
1280 self.assertEqual(q["e"], ["bar"])
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001281
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001282 def test_type_coercion(self):
1283 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001284 zoo = build("zoo", "v1", http=http, static_discovery=False)
Joe Gregoriobee86832011-02-22 10:00:19 -05001285
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001286 request = zoo.query(
1287 q="foo", i=1.0, n=1.0, b=0, a=[1, 2, 3], o={"a": 1}, e="bar"
1288 )
1289 self._check_query_types(request)
1290 request = zoo.query(
1291 q="foo", i=1, n=1, b=False, a=[1, 2, 3], o={"a": 1}, e="bar"
1292 )
1293 self._check_query_types(request)
Joe Gregoriobee86832011-02-22 10:00:19 -05001294
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001295 request = zoo.query(
1296 q="foo", i="1", n="1", b="", a=[1, 2, 3], o={"a": 1}, e="bar", er="two"
1297 )
Joe Gregoriof863f7a2011-02-24 03:24:44 -05001298
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001299 request = zoo.query(
1300 q="foo",
1301 i="1",
1302 n="1",
1303 b="",
1304 a=[1, 2, 3],
1305 o={"a": 1},
1306 e="bar",
1307 er=["one", "three"],
1308 rr=["foo", "bar"],
1309 )
1310 self._check_query_types(request)
Joe Gregorio6804c7a2011-11-18 14:30:32 -05001311
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001312 # Five is right out.
1313 self.assertRaises(TypeError, zoo.query, er=["one", "five"])
Joe Gregoriobee86832011-02-22 10:00:19 -05001314
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001315 def test_optional_stack_query_parameters(self):
1316 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001317 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001318 request = zoo.query(trace="html", fields="description")
Craig Citro1e742822012-03-01 12:59:22 -08001319
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -04001320 parsed = urllib.parse.urlparse(request.uri)
1321 q = urllib.parse.parse_qs(parsed.query)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001322 self.assertEqual(q["trace"], ["html"])
1323 self.assertEqual(q["fields"], ["description"])
Joe Gregorio13217952011-02-22 15:37:38 -05001324
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001325 def test_string_params_value_of_none_get_dropped(self):
1326 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001327 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001328 request = zoo.query(trace=None, fields="description")
Joe Gregoriof4153422011-03-18 22:45:18 -04001329
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -04001330 parsed = urllib.parse.urlparse(request.uri)
1331 q = urllib.parse.parse_qs(parsed.query)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001332 self.assertFalse("trace" in q)
Joe Gregorio2467afa2012-06-20 12:21:25 -04001333
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001334 def test_model_added_query_parameters(self):
1335 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001336 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001337 request = zoo.animals().get(name="Lion")
Joe Gregorio4b4002f2012-06-14 15:41:01 -04001338
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -04001339 parsed = urllib.parse.urlparse(request.uri)
1340 q = urllib.parse.parse_qs(parsed.query)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001341 self.assertEqual(q["alt"], ["json"])
1342 self.assertEqual(request.headers["accept"], "application/json")
Joe Gregorioe08a1662011-12-07 09:48:22 -05001343
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001344 def test_fallback_to_raw_model(self):
1345 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001346 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001347 request = zoo.animals().getmedia(name="Lion")
Joe Gregorioe08a1662011-12-07 09:48:22 -05001348
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -04001349 parsed = urllib.parse.urlparse(request.uri)
1350 q = urllib.parse.parse_qs(parsed.query)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001351 self.assertTrue("alt" not in q)
1352 self.assertEqual(request.headers["accept"], "*/*")
Joe Gregorioe08a1662011-12-07 09:48:22 -05001353
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001354 def test_patch(self):
1355 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001356 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001357 request = zoo.animals().patch(name="lion", body='{"description": "foo"}')
Joe Gregorioe08a1662011-12-07 09:48:22 -05001358
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001359 self.assertEqual(request.method, "PATCH")
Joe Gregoriof4153422011-03-18 22:45:18 -04001360
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001361 def test_batch_request_from_discovery(self):
1362 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1363 # zoo defines a batchPath
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001364 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001365 batch_request = zoo.new_batch_http_request()
1366 self.assertEqual(
1367 batch_request._batch_uri, "https://www.googleapis.com/batchZoo"
1368 )
Joe Gregoriof4153422011-03-18 22:45:18 -04001369
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001370 def test_batch_request_from_default(self):
1371 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
1372 # plus does not define a batchPath
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001373 plus = build("plus", "v1", http=self.http, cache_discovery=False, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001374 batch_request = plus.new_batch_http_request()
1375 self.assertEqual(batch_request._batch_uri, "https://www.googleapis.com/batch")
Pepper Lebeck-Jobe860836f2015-06-12 20:42:23 -04001376
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001377 def test_tunnel_patch(self):
1378 http = HttpMockSequence(
1379 [
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001380 ({"status": "200"}, read_datafile("zoo.json", "rb")),
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001381 ({"status": "200"}, "echo_request_headers_as_json"),
1382 ]
1383 )
1384 http = tunnel_patch(http)
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001385 zoo = build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001386 resp = zoo.animals().patch(name="lion", body='{"description": "foo"}').execute()
Pepper Lebeck-Jobe860836f2015-06-12 20:42:23 -04001387
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001388 self.assertTrue("x-http-method-override" in resp)
Joe Gregoriof4153422011-03-18 22:45:18 -04001389
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001390 def test_plus_resources(self):
1391 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001392 plus = build("plus", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001393 self.assertTrue(getattr(plus, "activities"))
1394 self.assertTrue(getattr(plus, "people"))
Joe Gregorioca876e42011-02-22 19:39:42 -05001395
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001396 def test_oauth2client_credentials(self):
1397 credentials = mock.Mock(spec=GoogleCredentials)
1398 credentials.create_scoped_required.return_value = False
Joe Gregorioc5c5a372010-09-22 11:42:32 -04001399
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001400 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001401 service = build_from_document(discovery, credentials=credentials)
1402 self.assertEqual(service._http, credentials.authorize.return_value)
Orest Bolohane92c9002014-05-30 11:15:43 -07001403
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001404 def test_google_auth_credentials(self):
1405 credentials = mock.Mock(spec=google.auth.credentials.Credentials)
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001406 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001407 service = build_from_document(discovery, credentials=credentials)
Orest Bolohane92c9002014-05-30 11:15:43 -07001408
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001409 self.assertIsInstance(service._http, google_auth_httplib2.AuthorizedHttp)
1410 self.assertEqual(service._http.credentials, credentials)
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -08001411
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001412 def test_no_scopes_no_credentials(self):
1413 # Zoo doesn't have scopes
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001414 discovery = read_datafile("zoo.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001415 service = build_from_document(discovery)
1416 # Should be an ordinary httplib2.Http instance and not AuthorizedHttp.
1417 self.assertIsInstance(service._http, httplib2.Http)
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -08001418
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001419 def test_full_featured(self):
1420 # Zoo should exercise all discovery facets
1421 # and should also have no future.json file.
1422 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001423 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001424 self.assertTrue(getattr(zoo, "animals"))
Orest Bolohane92c9002014-05-30 11:15:43 -07001425
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001426 request = zoo.animals().list(name="bat", projection="full")
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -04001427 parsed = urllib.parse.urlparse(request.uri)
1428 q = urllib.parse.parse_qs(parsed.query)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001429 self.assertEqual(q["name"], ["bat"])
1430 self.assertEqual(q["projection"], ["full"])
Joe Gregoriof863f7a2011-02-24 03:24:44 -05001431
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001432 def test_nested_resources(self):
1433 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001434 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001435 self.assertTrue(getattr(zoo, "animals"))
1436 request = zoo.my().favorites().list(max_results="5")
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -04001437 parsed = urllib.parse.urlparse(request.uri)
1438 q = urllib.parse.parse_qs(parsed.query)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001439 self.assertEqual(q["max-results"], ["5"])
Joe Gregorio2379ecc2010-10-26 10:51:28 -04001440
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001441 def test_top_level_functions(self):
1442 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001443 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001444 self.assertTrue(getattr(zoo, "query"))
1445 request = zoo.query(q="foo")
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -04001446 parsed = urllib.parse.urlparse(request.uri)
1447 q = urllib.parse.parse_qs(parsed.query)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001448 self.assertEqual(q["q"], ["foo"])
Joe Gregoriod92897c2011-07-07 11:44:56 -04001449
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001450 def test_simple_media_uploads(self):
1451 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001452 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001453 doc = getattr(zoo.animals().insert, "__doc__")
1454 self.assertTrue("media_body" in doc)
Joe Gregorio2379ecc2010-10-26 10:51:28 -04001455
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001456 def test_simple_media_upload_no_max_size_provided(self):
1457 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001458 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001459 request = zoo.animals().crossbreed(media_body=datafile("small.png"))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001460 self.assertEqual("image/png", request.headers["content-type"])
1461 self.assertEqual(b"PNG", request.body[1:4])
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001462
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001463 def test_simple_media_raise_correct_exceptions(self):
1464 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001465 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorio84d3c1f2011-07-25 10:39:45 -04001466
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001467 try:
1468 zoo.animals().insert(media_body=datafile("smiley.png"))
1469 self.fail("should throw exception if media is too large.")
1470 except MediaUploadSizeError:
1471 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001472
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001473 try:
1474 zoo.animals().insert(media_body=datafile("small.jpg"))
1475 self.fail("should throw exception if mimetype is unacceptable.")
1476 except UnacceptableMimeTypeError:
1477 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001478
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001479 def test_simple_media_good_upload(self):
1480 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001481 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001482
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001483 request = zoo.animals().insert(media_body=datafile("small.png"))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001484 self.assertEqual("image/png", request.headers["content-type"])
1485 self.assertEqual(b"PNG", request.body[1:4])
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001486 assertUrisEqual(
1487 self,
1488 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json",
1489 request.uri,
1490 )
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001491
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001492 def test_simple_media_unknown_mimetype(self):
1493 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001494 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001495
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001496 try:
1497 zoo.animals().insert(media_body=datafile("small-png"))
1498 self.fail("should throw exception if mimetype is unknown.")
1499 except UnknownFileType:
1500 pass
Brian J. Watson38051ac2016-10-25 07:53:08 -07001501
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001502 request = zoo.animals().insert(
1503 media_body=datafile("small-png"), media_mime_type="image/png"
1504 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001505 self.assertEqual("image/png", request.headers["content-type"])
1506 self.assertEqual(b"PNG", request.body[1:4])
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001507 assertUrisEqual(
1508 self,
1509 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json",
1510 request.uri,
1511 )
Brian J. Watson38051ac2016-10-25 07:53:08 -07001512
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001513 def test_multipart_media_raise_correct_exceptions(self):
1514 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001515 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Brian J. Watson38051ac2016-10-25 07:53:08 -07001516
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001517 try:
1518 zoo.animals().insert(media_body=datafile("smiley.png"), body={})
1519 self.fail("should throw exception if media is too large.")
1520 except MediaUploadSizeError:
1521 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001522
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001523 try:
1524 zoo.animals().insert(media_body=datafile("small.jpg"), body={})
1525 self.fail("should throw exception if mimetype is unacceptable.")
1526 except UnacceptableMimeTypeError:
1527 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001528
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001529 def test_multipart_media_good_upload(self, static_discovery=False):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001530 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001531 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001532
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001533 request = zoo.animals().insert(media_body=datafile("small.png"), body={})
1534 self.assertTrue(request.headers["content-type"].startswith("multipart/related"))
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001535 contents = read_datafile("small.png", "rb")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001536 boundary = re.match(b"--=+([^=]+)", request.body).group(1)
1537 self.assertEqual(
1538 request.body.rstrip(b"\n"), # Python 2.6 does not add a trailing \n
1539 b"--==============="
1540 + boundary
1541 + b"==\n"
1542 + b"Content-Type: application/json\n"
1543 + b"MIME-Version: 1.0\n\n"
1544 + b'{"data": {}}\n'
1545 + b"--==============="
1546 + boundary
1547 + b"==\n"
1548 + b"Content-Type: image/png\n"
1549 + b"MIME-Version: 1.0\n"
1550 + b"Content-Transfer-Encoding: binary\n\n"
1551 + contents
1552 + b"\n--==============="
1553 + boundary
1554 + b"==--",
1555 )
1556 assertUrisEqual(
1557 self,
1558 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=multipart&alt=json",
1559 request.uri,
1560 )
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001561
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001562 def test_media_capable_method_without_media(self):
1563 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001564 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001565
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001566 request = zoo.animals().insert(body={})
1567 self.assertTrue(request.headers["content-type"], "application/json")
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001568
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001569 def test_resumable_multipart_media_good_upload(self):
1570 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001571 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorioc5c5a372010-09-22 11:42:32 -04001572
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001573 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1574 request = zoo.animals().insert(media_body=media_upload, body={})
1575 self.assertTrue(request.headers["content-type"].startswith("application/json"))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001576 self.assertEqual('{"data": {}}', request.body)
1577 self.assertEqual(media_upload, request.resumable)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001578
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001579 self.assertEqual("image/png", request.resumable.mimetype())
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001580
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001581 self.assertNotEqual(request.body, None)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001582 self.assertEqual(request.resumable_uri, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001583
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001584 http = HttpMockSequence(
1585 [
1586 ({"status": "200", "location": "http://upload.example.com"}, ""),
1587 ({"status": "308", "location": "http://upload.example.com/2"}, ""),
1588 (
1589 {
1590 "status": "308",
1591 "location": "http://upload.example.com/3",
1592 "range": "0-12",
1593 },
1594 "",
1595 ),
1596 (
1597 {
1598 "status": "308",
1599 "location": "http://upload.example.com/4",
1600 "range": "0-%d" % (media_upload.size() - 2),
1601 },
1602 "",
1603 ),
1604 ({"status": "200"}, '{"foo": "bar"}'),
1605 ]
1606 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001607
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001608 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001609 self.assertEqual(None, body)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001610 self.assertTrue(isinstance(status, MediaUploadProgress))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001611 self.assertEqual(0, status.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001612
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001613 # Two requests should have been made and the resumable_uri should have been
1614 # updated for each one.
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001615 self.assertEqual(request.resumable_uri, "http://upload.example.com/2")
1616 self.assertEqual(media_upload, request.resumable)
1617 self.assertEqual(0, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001618
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001619 # This next chuck call should upload the first chunk
1620 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001621 self.assertEqual(request.resumable_uri, "http://upload.example.com/3")
1622 self.assertEqual(media_upload, request.resumable)
1623 self.assertEqual(13, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001624
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001625 # This call will upload the next chunk
1626 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001627 self.assertEqual(request.resumable_uri, "http://upload.example.com/4")
1628 self.assertEqual(media_upload.size() - 1, request.resumable_progress)
1629 self.assertEqual('{"data": {}}', request.body)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001630
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001631 # Final call to next_chunk should complete the upload.
1632 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001633 self.assertEqual(body, {"foo": "bar"})
1634 self.assertEqual(status, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001635
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001636 def test_resumable_media_good_upload(self):
1637 """Not a multipart upload."""
1638 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001639 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001640
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001641 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1642 request = zoo.animals().insert(media_body=media_upload, body=None)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001643 self.assertEqual(media_upload, request.resumable)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001644
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001645 self.assertEqual("image/png", request.resumable.mimetype())
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001646
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001647 self.assertEqual(request.body, None)
1648 self.assertEqual(request.resumable_uri, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001649
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001650 http = HttpMockSequence(
1651 [
1652 ({"status": "200", "location": "http://upload.example.com"}, ""),
1653 (
1654 {
1655 "status": "308",
1656 "location": "http://upload.example.com/2",
1657 "range": "0-12",
1658 },
1659 "",
1660 ),
1661 (
1662 {
1663 "status": "308",
1664 "location": "http://upload.example.com/3",
1665 "range": "0-%d" % (media_upload.size() - 2),
1666 },
1667 "",
1668 ),
1669 ({"status": "200"}, '{"foo": "bar"}'),
1670 ]
1671 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001672
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001673 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001674 self.assertEqual(None, body)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001675 self.assertTrue(isinstance(status, MediaUploadProgress))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001676 self.assertEqual(13, status.resumable_progress)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001677
1678 # Two requests should have been made and the resumable_uri should have been
1679 # updated for each one.
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001680 self.assertEqual(request.resumable_uri, "http://upload.example.com/2")
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001681
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001682 self.assertEqual(media_upload, request.resumable)
1683 self.assertEqual(13, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001684
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001685 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001686 self.assertEqual(request.resumable_uri, "http://upload.example.com/3")
1687 self.assertEqual(media_upload.size() - 1, request.resumable_progress)
1688 self.assertEqual(request.body, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001689
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001690 # Final call to next_chunk should complete the upload.
1691 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001692 self.assertEqual(body, {"foo": "bar"})
1693 self.assertEqual(status, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001694
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001695 def test_resumable_media_good_upload_from_execute(self):
1696 """Not a multipart upload."""
1697 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001698 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001699
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001700 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1701 request = zoo.animals().insert(media_body=media_upload, body=None)
1702 assertUrisEqual(
1703 self,
1704 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=resumable&alt=json",
1705 request.uri,
1706 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001707
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001708 http = HttpMockSequence(
1709 [
1710 ({"status": "200", "location": "http://upload.example.com"}, ""),
1711 (
1712 {
1713 "status": "308",
1714 "location": "http://upload.example.com/2",
1715 "range": "0-12",
1716 },
1717 "",
1718 ),
1719 (
1720 {
1721 "status": "308",
1722 "location": "http://upload.example.com/3",
1723 "range": "0-%d" % media_upload.size(),
1724 },
1725 "",
1726 ),
1727 ({"status": "200"}, '{"foo": "bar"}'),
1728 ]
1729 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001730
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001731 body = request.execute(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001732 self.assertEqual(body, {"foo": "bar"})
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001733
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001734 def test_resumable_media_fail_unknown_response_code_first_request(self):
1735 """Not a multipart upload."""
1736 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001737 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001738
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001739 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1740 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001741
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001742 http = HttpMockSequence(
1743 [({"status": "400", "location": "http://upload.example.com"}, "")]
1744 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001745
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001746 try:
1747 request.execute(http=http)
1748 self.fail("Should have raised ResumableUploadError.")
1749 except ResumableUploadError as e:
1750 self.assertEqual(400, e.resp.status)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001751
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001752 def test_resumable_media_fail_unknown_response_code_subsequent_request(self):
1753 """Not a multipart upload."""
1754 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001755 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001756
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001757 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1758 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001759
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001760 http = HttpMockSequence(
1761 [
1762 ({"status": "200", "location": "http://upload.example.com"}, ""),
1763 ({"status": "400"}, ""),
1764 ]
1765 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001766
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001767 self.assertRaises(HttpError, request.execute, http=http)
1768 self.assertTrue(request._in_error_state)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001769
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001770 http = HttpMockSequence(
1771 [
1772 ({"status": "308", "range": "0-5"}, ""),
1773 ({"status": "308", "range": "0-6"}, ""),
1774 ]
1775 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001776
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001777 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001778 self.assertEqual(
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001779 status.resumable_progress,
1780 7,
1781 "Should have first checked length and then tried to PUT more.",
1782 )
1783 self.assertFalse(request._in_error_state)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001784
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001785 # Put it back in an error state.
1786 http = HttpMockSequence([({"status": "400"}, "")])
1787 self.assertRaises(HttpError, request.execute, http=http)
1788 self.assertTrue(request._in_error_state)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001789
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001790 # Pretend the last request that 400'd actually succeeded.
1791 http = HttpMockSequence([({"status": "200"}, '{"foo": "bar"}')])
1792 status, body = request.next_chunk(http=http)
1793 self.assertEqual(body, {"foo": "bar"})
Joe Gregorio910b9b12012-06-12 09:36:30 -04001794
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001795 def test_media_io_base_stream_unlimited_chunksize_resume(self):
1796 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001797 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001798
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001799 # Set up a seekable stream and try to upload in single chunk.
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -04001800 fd = io.BytesIO(b'01234"56789"')
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001801 media_upload = MediaIoBaseUpload(
1802 fd=fd, mimetype="text/plain", chunksize=-1, resumable=True
1803 )
Joe Gregorio910b9b12012-06-12 09:36:30 -04001804
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001805 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001806
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001807 # The single chunk fails, restart at the right point.
1808 http = HttpMockSequence(
1809 [
1810 ({"status": "200", "location": "http://upload.example.com"}, ""),
1811 (
1812 {
1813 "status": "308",
1814 "location": "http://upload.example.com/2",
1815 "range": "0-4",
1816 },
1817 "",
1818 ),
1819 ({"status": "200"}, "echo_request_body"),
1820 ]
1821 )
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001822
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001823 body = request.execute(http=http)
1824 self.assertEqual("56789", body)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001825
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001826 def test_media_io_base_stream_chunksize_resume(self):
1827 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001828 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001829
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001830 # Set up a seekable stream and try to upload in chunks.
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -04001831 fd = io.BytesIO(b"0123456789")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001832 media_upload = MediaIoBaseUpload(
1833 fd=fd, mimetype="text/plain", chunksize=5, resumable=True
1834 )
Joe Gregorio5c120db2012-08-23 09:13:55 -04001835
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001836 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregorio5c120db2012-08-23 09:13:55 -04001837
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001838 # The single chunk fails, pull the content sent out of the exception.
1839 http = HttpMockSequence(
1840 [
1841 ({"status": "200", "location": "http://upload.example.com"}, ""),
1842 ({"status": "400"}, "echo_request_body"),
1843 ]
1844 )
Pat Ferateed9affd2015-03-03 16:03:15 -08001845
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001846 try:
1847 body = request.execute(http=http)
1848 except HttpError as e:
1849 self.assertEqual(b"01234", e.content)
Pat Ferateed9affd2015-03-03 16:03:15 -08001850
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001851 def test_resumable_media_handle_uploads_of_unknown_size(self):
1852 http = HttpMockSequence(
1853 [
1854 ({"status": "200", "location": "http://upload.example.com"}, ""),
1855 ({"status": "200"}, "echo_request_headers_as_json"),
1856 ]
1857 )
Pat Ferateed9affd2015-03-03 16:03:15 -08001858
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001859 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001860 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorio5c120db2012-08-23 09:13:55 -04001861
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001862 # Create an upload that doesn't know the full size of the media.
1863 class IoBaseUnknownLength(MediaUpload):
1864 def chunksize(self):
1865 return 10
Joe Gregorio910b9b12012-06-12 09:36:30 -04001866
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001867 def mimetype(self):
1868 return "image/png"
Joe Gregorio910b9b12012-06-12 09:36:30 -04001869
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001870 def size(self):
1871 return None
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001872
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001873 def resumable(self):
1874 return True
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001875
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001876 def getbytes(self, begin, length):
1877 return "0123456789"
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001878
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001879 upload = IoBaseUnknownLength()
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001880
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001881 request = zoo.animals().insert(media_body=upload, body=None)
1882 status, body = request.next_chunk(http=http)
1883 self.assertEqual(body, {"Content-Range": "bytes 0-9/*", "Content-Length": "10"})
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001884
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001885 def test_resumable_media_no_streaming_on_unsupported_platforms(self):
1886 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001887 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001888
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001889 class IoBaseHasStream(MediaUpload):
1890 def chunksize(self):
1891 return 10
Joe Gregorio44454e42012-06-15 08:38:53 -04001892
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001893 def mimetype(self):
1894 return "image/png"
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001895
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001896 def size(self):
1897 return None
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001898
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001899 def resumable(self):
1900 return True
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001901
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001902 def getbytes(self, begin, length):
1903 return "0123456789"
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001904
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001905 def has_stream(self):
1906 return True
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001907
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001908 def stream(self):
1909 raise NotImplementedError()
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001910
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001911 upload = IoBaseHasStream()
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001912
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001913 orig_version = sys.version_info
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001914
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001915 sys.version_info = (2, 6, 5, "final", 0)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001916
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001917 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001918
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001919 # This should raise an exception because stream() will be called.
1920 http = HttpMockSequence(
1921 [
1922 ({"status": "200", "location": "http://upload.example.com"}, ""),
1923 ({"status": "200"}, "echo_request_headers_as_json"),
1924 ]
1925 )
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001926
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001927 self.assertRaises(NotImplementedError, request.next_chunk, http=http)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001928
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001929 sys.version_info = orig_version
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001930
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001931 def test_resumable_media_handle_uploads_of_unknown_size_eof(self):
1932 http = HttpMockSequence(
1933 [
1934 ({"status": "200", "location": "http://upload.example.com"}, ""),
1935 ({"status": "200"}, "echo_request_headers_as_json"),
1936 ]
1937 )
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001938
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001939 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001940 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001941
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -04001942 fd = io.BytesIO(b"data goes here")
Joe Gregorio44454e42012-06-15 08:38:53 -04001943
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001944 # Create an upload that doesn't know the full size of the media.
1945 upload = MediaIoBaseUpload(
1946 fd=fd, mimetype="image/png", chunksize=15, resumable=True
1947 )
Joe Gregorio44454e42012-06-15 08:38:53 -04001948
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001949 request = zoo.animals().insert(media_body=upload, body=None)
1950 status, body = request.next_chunk(http=http)
1951 self.assertEqual(
1952 body, {"Content-Range": "bytes 0-13/14", "Content-Length": "14"}
1953 )
Joe Gregorio44454e42012-06-15 08:38:53 -04001954
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001955 def test_resumable_media_handle_resume_of_upload_of_unknown_size(self):
1956 http = HttpMockSequence(
1957 [
1958 ({"status": "200", "location": "http://upload.example.com"}, ""),
1959 ({"status": "400"}, ""),
1960 ]
1961 )
Joe Gregorio44454e42012-06-15 08:38:53 -04001962
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001963 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001964 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001965
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001966 # Create an upload that doesn't know the full size of the media.
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -04001967 fd = io.BytesIO(b"data goes here")
Joe Gregorio910b9b12012-06-12 09:36:30 -04001968
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001969 upload = MediaIoBaseUpload(
1970 fd=fd, mimetype="image/png", chunksize=500, resumable=True
1971 )
Joe Gregorio910b9b12012-06-12 09:36:30 -04001972
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001973 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001974
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001975 # Put it in an error state.
1976 self.assertRaises(HttpError, request.next_chunk, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001977
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001978 http = HttpMockSequence(
1979 [({"status": "400", "range": "0-5"}, "echo_request_headers_as_json")]
1980 )
1981 try:
1982 # Should resume the upload by first querying the status of the upload.
1983 request.next_chunk(http=http)
1984 except HttpError as e:
1985 expected = {"Content-Range": "bytes */14", "content-length": "0"}
1986 self.assertEqual(
1987 expected,
1988 json.loads(e.content.decode("utf-8")),
1989 "Should send an empty body when requesting the current upload status.",
1990 )
Joe Gregorio910b9b12012-06-12 09:36:30 -04001991
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001992 def test_pickle(self):
1993 sorted_resource_keys = [
1994 "_baseUrl",
1995 "_developerKey",
1996 "_dynamic_attrs",
1997 "_http",
1998 "_model",
1999 "_requestBuilder",
2000 "_resourceDesc",
2001 "_rootDesc",
2002 "_schema",
2003 "animals",
2004 "global_",
2005 "load",
2006 "loadNoTemplate",
2007 "my",
2008 "new_batch_http_request",
2009 "query",
2010 "scopedAnimals",
2011 ]
Joe Gregorio910b9b12012-06-12 09:36:30 -04002012
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002013 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05002014 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002015 self.assertEqual(sorted(zoo.__dict__.keys()), sorted_resource_keys)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05002016
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002017 pickled_zoo = pickle.dumps(zoo)
2018 new_zoo = pickle.loads(pickled_zoo)
2019 self.assertEqual(sorted(new_zoo.__dict__.keys()), sorted_resource_keys)
2020 self.assertTrue(hasattr(new_zoo, "animals"))
2021 self.assertTrue(callable(new_zoo.animals))
2022 self.assertTrue(hasattr(new_zoo, "global_"))
2023 self.assertTrue(callable(new_zoo.global_))
2024 self.assertTrue(hasattr(new_zoo, "load"))
2025 self.assertTrue(callable(new_zoo.load))
2026 self.assertTrue(hasattr(new_zoo, "loadNoTemplate"))
2027 self.assertTrue(callable(new_zoo.loadNoTemplate))
2028 self.assertTrue(hasattr(new_zoo, "my"))
2029 self.assertTrue(callable(new_zoo.my))
2030 self.assertTrue(hasattr(new_zoo, "query"))
2031 self.assertTrue(callable(new_zoo.query))
2032 self.assertTrue(hasattr(new_zoo, "scopedAnimals"))
2033 self.assertTrue(callable(new_zoo.scopedAnimals))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002034
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002035 self.assertEqual(sorted(zoo._dynamic_attrs), sorted(new_zoo._dynamic_attrs))
2036 self.assertEqual(zoo._baseUrl, new_zoo._baseUrl)
2037 self.assertEqual(zoo._developerKey, new_zoo._developerKey)
2038 self.assertEqual(zoo._requestBuilder, new_zoo._requestBuilder)
2039 self.assertEqual(zoo._resourceDesc, new_zoo._resourceDesc)
2040 self.assertEqual(zoo._rootDesc, new_zoo._rootDesc)
2041 # _http, _model and _schema won't be equal since we will get new
2042 # instances upon un-pickling
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002043
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002044 def _dummy_zoo_request(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07002045 zoo_contents = read_datafile("zoo.json")
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002046
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002047 zoo_uri = uritemplate.expand(DISCOVERY_URI, {"api": "zoo", "apiVersion": "v1"})
2048 if "REMOTE_ADDR" in os.environ:
2049 zoo_uri = util._add_query_parameter(
2050 zoo_uri, "userIp", os.environ["REMOTE_ADDR"]
2051 )
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002052
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002053 http = build_http()
2054 original_request = http.request
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002055
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002056 def wrapped_request(uri, method="GET", *args, **kwargs):
2057 if uri == zoo_uri:
2058 return httplib2.Response({"status": "200"}), zoo_contents
2059 return original_request(uri, method=method, *args, **kwargs)
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002060
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002061 http.request = wrapped_request
2062 return http
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002063
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002064 def _dummy_token(self):
2065 access_token = "foo"
2066 client_id = "some_client_id"
2067 client_secret = "cOuDdkfjxxnv+"
2068 refresh_token = "1/0/a.df219fjls0"
2069 token_expiry = datetime.datetime.utcnow()
2070 user_agent = "refresh_checker/1.0"
2071 return OAuth2Credentials(
2072 access_token,
2073 client_id,
2074 client_secret,
2075 refresh_token,
2076 token_expiry,
2077 GOOGLE_TOKEN_URI,
2078 user_agent,
2079 )
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002080
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002081 def test_pickle_with_credentials(self):
2082 credentials = self._dummy_token()
2083 http = self._dummy_zoo_request()
2084 http = credentials.authorize(http)
2085 self.assertTrue(hasattr(http.request, "credentials"))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002086
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05002087 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002088 pickled_zoo = pickle.dumps(zoo)
2089 new_zoo = pickle.loads(pickled_zoo)
2090 self.assertEqual(sorted(zoo.__dict__.keys()), sorted(new_zoo.__dict__.keys()))
2091 new_http = new_zoo._http
2092 self.assertFalse(hasattr(new_http.request, "credentials"))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002093
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002094 def test_resumable_media_upload_no_content(self):
2095 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05002096 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
andrewnestera4a44cf2017-03-31 16:09:31 +03002097
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002098 media_upload = MediaFileUpload(datafile("empty"), resumable=True)
2099 request = zoo.animals().insert(media_body=media_upload, body=None)
andrewnestera4a44cf2017-03-31 16:09:31 +03002100
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07002101 self.assertEqual(media_upload, request.resumable)
2102 self.assertEqual(request.body, None)
2103 self.assertEqual(request.resumable_uri, None)
andrewnestera4a44cf2017-03-31 16:09:31 +03002104
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002105 http = HttpMockSequence(
2106 [
2107 ({"status": "200", "location": "http://upload.example.com"}, ""),
2108 (
2109 {
2110 "status": "308",
2111 "location": "http://upload.example.com/2",
2112 "range": "0-0",
2113 },
2114 "",
2115 ),
2116 ]
2117 )
andrewnestera4a44cf2017-03-31 16:09:31 +03002118
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002119 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07002120 self.assertEqual(None, body)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002121 self.assertTrue(isinstance(status, MediaUploadProgress))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07002122 self.assertEqual(0, status.progress())
andrewnestera4a44cf2017-03-31 16:09:31 +03002123
Joe Gregorio708388c2012-06-15 13:43:04 -04002124
Joe Gregorioc5c5a372010-09-22 11:42:32 -04002125class Next(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002126 def test_next_successful_none_on_no_next_page_token(self):
2127 self.http = HttpMock(datafile("tasks.json"), {"status": "200"})
2128 tasks = build("tasks", "v1", http=self.http)
2129 request = tasks.tasklists().list()
2130 self.assertEqual(None, tasks.tasklists().list_next(request, {}))
Joe Gregorio00cf1d92010-09-27 09:22:03 -04002131
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002132 def test_next_successful_none_on_empty_page_token(self):
2133 self.http = HttpMock(datafile("tasks.json"), {"status": "200"})
2134 tasks = build("tasks", "v1", http=self.http)
2135 request = tasks.tasklists().list()
2136 next_request = tasks.tasklists().list_next(request, {"nextPageToken": ""})
2137 self.assertEqual(None, next_request)
Joe Gregorio3c676f92011-07-25 10:38:14 -04002138
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002139 def test_next_successful_with_next_page_token(self):
2140 self.http = HttpMock(datafile("tasks.json"), {"status": "200"})
2141 tasks = build("tasks", "v1", http=self.http)
2142 request = tasks.tasklists().list()
2143 next_request = tasks.tasklists().list_next(request, {"nextPageToken": "123abc"})
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -04002144 parsed = urllib.parse.urlparse(next_request.uri)
2145 q = urllib.parse.parse_qs(parsed.query)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002146 self.assertEqual(q["pageToken"][0], "123abc")
Son Dinh2a9a2132015-07-23 16:30:56 +00002147
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002148 def test_next_successful_with_next_page_token_alternate_name(self):
2149 self.http = HttpMock(datafile("bigquery.json"), {"status": "200"})
2150 bigquery = build("bigquery", "v2", http=self.http)
2151 request = bigquery.tabledata().list(datasetId="", projectId="", tableId="")
2152 next_request = bigquery.tabledata().list_next(request, {"pageToken": "123abc"})
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -04002153 parsed = urllib.parse.urlparse(next_request.uri)
2154 q = urllib.parse.parse_qs(parsed.query)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002155 self.assertEqual(q["pageToken"][0], "123abc")
Joe Gregorio3c676f92011-07-25 10:38:14 -04002156
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002157 def test_next_successful_with_next_page_token_in_body(self):
2158 self.http = HttpMock(datafile("logging.json"), {"status": "200"})
2159 logging = build("logging", "v2", http=self.http)
2160 request = logging.entries().list(body={})
2161 next_request = logging.entries().list_next(request, {"nextPageToken": "123abc"})
2162 body = JsonModel().deserialize(next_request.body)
2163 self.assertEqual(body["pageToken"], "123abc")
David Schweikert8019f2f2021-06-08 16:54:44 +02002164 # The body is changed, make sure that body_length is changed too (see
2165 # github #1403)
2166 self.assertEqual(next_request.body_size, len(next_request.body))
Thomas Coffee20af04d2017-02-10 15:24:44 -08002167
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002168 def test_next_with_method_with_no_properties(self):
2169 self.http = HttpMock(datafile("latitude.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05002170 service = build("latitude", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002171 service.currentLocation().get()
Thomas Coffee20af04d2017-02-10 15:24:44 -08002172
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002173 def test_next_nonexistent_with_no_next_page_token(self):
2174 self.http = HttpMock(datafile("drive.json"), {"status": "200"})
2175 drive = build("drive", "v3", http=self.http)
2176 drive.changes().watch(body={})
2177 self.assertFalse(callable(getattr(drive.changes(), "watch_next", None)))
Thomas Coffee20af04d2017-02-10 15:24:44 -08002178
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002179 def test_next_successful_with_next_page_token_required(self):
2180 self.http = HttpMock(datafile("drive.json"), {"status": "200"})
2181 drive = build("drive", "v3", http=self.http)
2182 request = drive.changes().list(pageToken="startPageToken")
2183 next_request = drive.changes().list_next(request, {"nextPageToken": "123abc"})
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -04002184 parsed = urllib.parse.urlparse(next_request.uri)
2185 q = urllib.parse.parse_qs(parsed.query)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002186 self.assertEqual(q["pageToken"][0], "123abc")
Joe Gregorio00cf1d92010-09-27 09:22:03 -04002187
Joe Gregorioa98733f2011-09-16 10:12:28 -04002188
Joe Gregorio708388c2012-06-15 13:43:04 -04002189class MediaGet(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002190 def test_get_media(self):
2191 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05002192 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002193 request = zoo.animals().get_media(name="Lion")
Joe Gregorio708388c2012-06-15 13:43:04 -04002194
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -04002195 parsed = urllib.parse.urlparse(request.uri)
2196 q = urllib.parse.parse_qs(parsed.query)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002197 self.assertEqual(q["alt"], ["media"])
2198 self.assertEqual(request.headers["accept"], "*/*")
Joe Gregorio708388c2012-06-15 13:43:04 -04002199
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002200 http = HttpMockSequence([({"status": "200"}, "standing in for media")])
2201 response = request.execute(http=http)
2202 self.assertEqual(b"standing in for media", response)
Joe Gregorio708388c2012-06-15 13:43:04 -04002203
2204
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002205if __name__ == "__main__":
2206 unittest.main()