Skip to content

Commit 9dc8773

Browse files
committed
Small tweaks to Connection in storage; towards a cleaner API.
- Making Connection.make_request non-public - Making Connection.build_api_url a class method
1 parent 9518db7 commit 9dc8773

4 files changed

Lines changed: 81 additions & 48 deletions

File tree

gcloud/storage/blob.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,14 +314,15 @@ def upload_from_file(self, file_obj, rewind=False, size=None,
314314

315315
# Temporary URL, until we know simple vs. resumable.
316316
upload_url = conn.build_api_url(
317-
path=self.bucket.path + '/o', upload=True)
317+
project=conn.project, path=self.bucket.path + '/o', upload=True)
318318

319319
# Use apitools 'Upload' facility.
320320
request = http_wrapper.Request(upload_url, 'POST', headers)
321321

322322
upload.ConfigureRequest(upload_config, request, url_builder)
323323
query_params = url_builder.query_params
324-
request.url = conn.build_api_url(path=self.bucket.path + '/o',
324+
request.url = conn.build_api_url(project=conn.project,
325+
path=self.bucket.path + '/o',
325326
query_params=query_params,
326327
upload=True)
327328
upload.InitializeUpload(request, conn.http)

gcloud/storage/connection.py

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ class Connection(_Base):
6666
6767
>>> print 'my-bucket-name' in connection
6868
True
69+
70+
:type project: string
71+
:param project: The project name to connect to.
6972
"""
7073

7174
API_VERSION = 'v1'
@@ -75,10 +78,6 @@ class Connection(_Base):
7578
"""A template for the URL of a particular API call."""
7679

7780
def __init__(self, project, *args, **kwargs):
78-
""":type project: string
79-
80-
:param project: The project name to connect to.
81-
"""
8281
super(Connection, self).__init__(*args, **kwargs)
8382
self.project = project
8483

@@ -88,12 +87,16 @@ def __iter__(self):
8887
def __contains__(self, bucket_name):
8988
return self.lookup(bucket_name) is not None
9089

91-
def build_api_url(self, path, query_params=None, api_base_url=None,
90+
@classmethod
91+
def build_api_url(cls, project, path, query_params=None, api_base_url=None,
9292
api_version=None, upload=False):
9393
"""Construct an API url given a few components, some optional.
9494
9595
Typically, you shouldn't need to use this method.
9696
97+
:type project: string
98+
:param project: The project name to connect to.
99+
97100
:type path: string
98101
:param path: The path to the resource (ie, ``'/b/bucket-name'``).
99102
@@ -116,23 +119,23 @@ def build_api_url(self, path, query_params=None, api_base_url=None,
116119
:rtype: string
117120
:returns: The URL assembled from the pieces provided.
118121
"""
119-
api_base_url = api_base_url or self.API_BASE_URL
122+
api_base_url = api_base_url or cls.API_BASE_URL
120123
if upload:
121124
api_base_url += '/upload'
122125

123-
url = self.API_URL_TEMPLATE.format(
124-
api_base_url=(api_base_url or self.API_BASE_URL),
125-
api_version=(api_version or self.API_VERSION),
126+
url = cls.API_URL_TEMPLATE.format(
127+
api_base_url=(api_base_url or cls.API_BASE_URL),
128+
api_version=(api_version or cls.API_VERSION),
126129
path=path)
127130

128131
query_params = query_params or {}
129-
query_params.update({'project': self.project})
132+
query_params.update({'project': project})
130133
url += '?' + urlencode(query_params)
131134

132135
return url
133136

134-
def make_request(self, method, url, data=None, content_type=None,
135-
headers=None):
137+
def _make_request(self, method, url, data=None, content_type=None,
138+
headers=None):
136139
"""A low level method to send a request to the API.
137140
138141
Typically, you shouldn't need to use this method.
@@ -224,7 +227,8 @@ def api_request(self, method, path, query_params=None,
224227
225228
:raises: Exception if the response code is not 200 OK.
226229
"""
227-
url = self.build_api_url(path=path, query_params=query_params,
230+
url = self.build_api_url(project=self.project, path=path,
231+
query_params=query_params,
228232
api_base_url=api_base_url,
229233
api_version=api_version)
230234

@@ -234,7 +238,7 @@ def api_request(self, method, path, query_params=None,
234238
data = json.dumps(data)
235239
content_type = 'application/json'
236240

237-
response, content = self.make_request(
241+
response, content = self._make_request(
238242
method=method, url=url, data=data, content_type=content_type)
239243

240244
if not 200 <= response.status < 300:

gcloud/storage/test_blob.py

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,12 @@ def test_upload_from_file_simple(self):
332332
self.assertEqual(scheme, 'http')
333333
self.assertEqual(netloc, 'example.com')
334334
self.assertEqual(path, '/b/name/o')
335-
self.assertEqual(dict(parse_qsl(qs)),
336-
{'uploadType': 'media', 'name': BLOB_NAME})
335+
query_params = {
336+
'uploadType': 'media',
337+
'name': BLOB_NAME,
338+
'project': 'PROJECT',
339+
}
340+
self.assertEqual(dict(parse_qsl(qs)), query_params)
337341
headers = dict(
338342
[(x.title(), str(y)) for x, y in rq[0]['headers'].items()])
339343
self.assertEqual(headers['Content-Length'], '6')
@@ -376,8 +380,12 @@ def test_upload_from_file_resumable(self):
376380
self.assertEqual(scheme, 'http')
377381
self.assertEqual(netloc, 'example.com')
378382
self.assertEqual(path, '/b/name/o')
379-
self.assertEqual(dict(parse_qsl(qs)),
380-
{'uploadType': 'resumable', 'name': BLOB_NAME})
383+
query_params = {
384+
'uploadType': 'resumable',
385+
'name': BLOB_NAME,
386+
'project': 'PROJECT',
387+
}
388+
self.assertEqual(dict(parse_qsl(qs)), query_params)
381389
headers = dict(
382390
[(x.title(), str(y)) for x, y in rq[0]['headers'].items()])
383391
self.assertEqual(headers['X-Upload-Content-Length'], '6')
@@ -431,8 +439,12 @@ def test_upload_from_file_w_slash_in_name(self):
431439
self.assertEqual(scheme, 'http')
432440
self.assertEqual(netloc, 'example.com')
433441
self.assertEqual(path, '/b/name/o')
434-
self.assertEqual(dict(parse_qsl(qs)),
435-
{'uploadType': 'media', 'name': 'parent/child'})
442+
query_params = {
443+
'uploadType': 'media',
444+
'name': 'parent/child',
445+
'project': 'PROJECT',
446+
}
447+
self.assertEqual(dict(parse_qsl(qs)), query_params)
436448
headers = dict(
437449
[(x.title(), str(y)) for x, y in rq[0]['headers'].items()])
438450
self.assertEqual(headers['Content-Length'], '6')
@@ -471,8 +483,12 @@ def test_upload_from_filename(self):
471483
self.assertEqual(scheme, 'http')
472484
self.assertEqual(netloc, 'example.com')
473485
self.assertEqual(path, '/b/name/o')
474-
self.assertEqual(dict(parse_qsl(qs)),
475-
{'uploadType': 'media', 'name': BLOB_NAME})
486+
query_params = {
487+
'uploadType': 'media',
488+
'name': BLOB_NAME,
489+
'project': 'PROJECT',
490+
}
491+
self.assertEqual(dict(parse_qsl(qs)), query_params)
476492
headers = dict(
477493
[(x.title(), str(y)) for x, y in rq[0]['headers'].items()])
478494
self.assertEqual(headers['Content-Length'], '6')
@@ -507,8 +523,12 @@ def test_upload_from_string_w_bytes(self):
507523
self.assertEqual(scheme, 'http')
508524
self.assertEqual(netloc, 'example.com')
509525
self.assertEqual(path, '/b/name/o')
510-
self.assertEqual(dict(parse_qsl(qs)),
511-
{'uploadType': 'media', 'name': BLOB_NAME})
526+
query_params = {
527+
'project': 'PROJECT',
528+
'uploadType': 'media',
529+
'name': BLOB_NAME,
530+
}
531+
self.assertEqual(dict(parse_qsl(qs)), query_params)
512532
headers = dict(
513533
[(x.title(), str(y)) for x, y in rq[0]['headers'].items()])
514534
self.assertEqual(headers['Content-Length'], '6')
@@ -545,8 +565,12 @@ def test_upload_from_string_w_text(self):
545565
self.assertEqual(scheme, 'http')
546566
self.assertEqual(netloc, 'example.com')
547567
self.assertEqual(path, '/b/name/o')
548-
self.assertEqual(dict(parse_qsl(qs)),
549-
{'uploadType': 'media', 'name': BLOB_NAME})
568+
query_params = {
569+
'uploadType': 'media',
570+
'name': BLOB_NAME,
571+
'project': 'PROJECT',
572+
}
573+
self.assertEqual(dict(parse_qsl(qs)), query_params)
550574
headers = dict(
551575
[(x.title(), str(y)) for x, y in rq[0]['headers'].items()])
552576
self.assertEqual(headers['Content-Length'], str(len(ENCODED)))
@@ -906,6 +930,7 @@ class _Connection(_Responder):
906930
API_BASE_URL = 'http://example.com'
907931
USER_AGENT = 'testing 1.2.3'
908932
credentials = object()
933+
project = 'PROJECT'
909934

910935
def __init__(self, *responses):
911936
super(_Connection, self).__init__(*responses)
@@ -915,15 +940,17 @@ def __init__(self, *responses):
915940
def api_request(self, **kw):
916941
return self._respond(**kw)
917942

918-
def build_api_url(self, path, query_params=None,
943+
def build_api_url(self, project, path, query_params=None,
919944
api_base_url=API_BASE_URL, upload=False):
920945
from six.moves.urllib.parse import urlencode
921946
from six.moves.urllib.parse import urlsplit
922947
from six.moves.urllib.parse import urlunsplit
923948
# mimic the build_api_url interface, but avoid unused param and
924949
# missed coverage errors
925950
upload = not upload # pragma NO COVER
926-
qs = urlencode(query_params or {})
951+
query_params = query_params or {}
952+
query_params['project'] = project
953+
qs = urlencode(query_params)
927954
scheme, netloc, _, _, _ = urlsplit(api_base_url)
928955
return urlunsplit((scheme, netloc, path, qs, ''))
929956

gcloud/storage/test_connection.py

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -141,50 +141,51 @@ def test___contains___hit(self):
141141

142142
def test_build_api_url_no_extra_query_params(self):
143143
PROJECT = 'project'
144-
conn = self._makeOne(PROJECT)
144+
klass = self._getTargetClass()
145145
URI = '/'.join([
146-
conn.API_BASE_URL,
146+
klass.API_BASE_URL,
147147
'storage',
148-
conn.API_VERSION,
148+
klass.API_VERSION,
149149
'foo?project=%s' % PROJECT,
150150
])
151-
self.assertEqual(conn.build_api_url('/foo'), URI)
151+
self.assertEqual(klass.build_api_url(PROJECT, '/foo'), URI)
152152

153153
def test_build_api_url_w_extra_query_params(self):
154154
from six.moves.urllib.parse import parse_qsl
155155
from six.moves.urllib.parse import urlsplit
156156
PROJECT = 'project'
157-
conn = self._makeOne(PROJECT)
158-
uri = conn.build_api_url('/foo', {'bar': 'baz'})
157+
klass = self._getTargetClass()
158+
uri = klass.build_api_url(PROJECT, '/foo', query_params={'bar': 'baz'})
159159
scheme, netloc, path, qs, _ = urlsplit(uri)
160-
self.assertEqual('%s://%s' % (scheme, netloc), conn.API_BASE_URL)
160+
self.assertEqual('%s://%s' % (scheme, netloc), klass.API_BASE_URL)
161161
self.assertEqual(path,
162-
'/'.join(['', 'storage', conn.API_VERSION, 'foo']))
162+
'/'.join(['', 'storage', klass.API_VERSION, 'foo']))
163163
parms = dict(parse_qsl(qs))
164164
self.assertEqual(parms['project'], PROJECT)
165165
self.assertEqual(parms['bar'], 'baz')
166166

167167
def test_build_api_url_w_upload(self):
168168
PROJECT = 'project'
169-
conn = self._makeOne(PROJECT)
169+
klass = self._getTargetClass()
170170
URI = '/'.join([
171-
conn.API_BASE_URL,
171+
klass.API_BASE_URL,
172172
'upload',
173173
'storage',
174-
conn.API_VERSION,
174+
klass.API_VERSION,
175175
'foo?project=%s' % PROJECT,
176176
])
177-
self.assertEqual(conn.build_api_url('/foo', upload=True), URI)
177+
self.assertEqual(klass.build_api_url(PROJECT, '/foo', upload=True),
178+
URI)
178179

179-
def test_make_request_no_data_no_content_type_no_headers(self):
180+
def test__make_request_no_data_no_content_type_no_headers(self):
180181
PROJECT = 'project'
181182
conn = self._makeOne(PROJECT)
182183
URI = 'http://example.com/test'
183184
http = conn._http = Http(
184185
{'status': '200', 'content-type': 'text/plain'},
185186
'',
186187
)
187-
headers, content = conn.make_request('GET', URI)
188+
headers, content = conn._make_request('GET', URI)
188189
self.assertEqual(headers['status'], '200')
189190
self.assertEqual(headers['content-type'], 'text/plain')
190191
self.assertEqual(content, '')
@@ -198,15 +199,15 @@ def test_make_request_no_data_no_content_type_no_headers(self):
198199
}
199200
self.assertEqual(http._called_with['headers'], expected_headers)
200201

201-
def test_make_request_w_data_no_extra_headers(self):
202+
def test__make_request_w_data_no_extra_headers(self):
202203
PROJECT = 'project'
203204
conn = self._makeOne(PROJECT)
204205
URI = 'http://example.com/test'
205206
http = conn._http = Http(
206207
{'status': '200', 'content-type': 'text/plain'},
207208
'',
208209
)
209-
conn.make_request('GET', URI, {}, 'application/json')
210+
conn._make_request('GET', URI, {}, 'application/json')
210211
self.assertEqual(http._called_with['method'], 'GET')
211212
self.assertEqual(http._called_with['uri'], URI)
212213
self.assertEqual(http._called_with['body'], {})
@@ -218,15 +219,15 @@ def test_make_request_w_data_no_extra_headers(self):
218219
}
219220
self.assertEqual(http._called_with['headers'], expected_headers)
220221

221-
def test_make_request_w_extra_headers(self):
222+
def test__make_request_w_extra_headers(self):
222223
PROJECT = 'project'
223224
conn = self._makeOne(PROJECT)
224225
URI = 'http://example.com/test'
225226
http = conn._http = Http(
226227
{'status': '200', 'content-type': 'text/plain'},
227228
'',
228229
)
229-
conn.make_request('GET', URI, headers={'X-Foo': 'foo'})
230+
conn._make_request('GET', URI, headers={'X-Foo': 'foo'})
230231
self.assertEqual(http._called_with['method'], 'GET')
231232
self.assertEqual(http._called_with['uri'], URI)
232233
self.assertEqual(http._called_with['body'], None)

0 commit comments

Comments
 (0)