Skip to content

Commit f0395d2

Browse files
committed
fix api endpoints for storage upload/download
1 parent 41286a8 commit f0395d2

File tree

4 files changed

+46
-29
lines changed

4 files changed

+46
-29
lines changed

gcloud/storage/connection.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def __contains__(self, bucket_name):
9191
return self.lookup(bucket_name) is not None
9292

9393
def build_api_url(self, path, query_params=None, api_base_url=None,
94-
api_version=None):
94+
api_version=None, upload=False):
9595
"""Construct an API url given a few components, some optional.
9696
9797
Typically, you shouldn't need to use this method.
@@ -112,9 +112,16 @@ def build_api_url(self, path, query_params=None, api_base_url=None,
112112
Typically you shouldn't provide this and instead
113113
use the default for the library.
114114
115+
:type upload: boolean
116+
:param upload: True if the URL is for uploading purposes.
117+
115118
:rtype: string
116119
:returns: The URL assembled from the pieces provided.
117120
"""
121+
api_base_url = api_base_url or self.API_BASE_URL
122+
if upload:
123+
api_base_url += '/upload'
124+
118125
url = self.API_URL_TEMPLATE.format(
119126
api_base_url=(api_base_url or self.API_BASE_URL),
120127
api_version=(api_version or self.API_VERSION),

gcloud/storage/key.py

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -209,13 +209,14 @@ def download_to_file(self, file_obj):
209209
210210
:raises: :class:`gcloud.storage.exceptions.NotFound`
211211
"""
212+
213+
download_url = self.media_link
214+
212215
# Use apitools 'Download' facility.
213216
download = transfer.Download.FromStream(file_obj, auto_transfer=False)
214217
download.chunksize = self.CHUNK_SIZE
215-
download_url = self.connection.build_api_url(
216-
path=self.path, query_params={'alt': 'media'})
217218
headers = {'Range': 'bytes=0-%d' % (self.CHUNK_SIZE - 1)}
218-
request = http_wrapper.Request(download_url, 'POST', headers)
219+
request = http_wrapper.Request(download_url, 'GET', headers)
219220

220221
download.InitializeDownload(request, self.connection.http)
221222

@@ -308,15 +309,16 @@ def upload_from_file(self, file_obj, rewind=False, size=None,
308309

309310
# Temporary URL, until we know simple vs. resumable.
310311
upload_url = conn.build_api_url(
311-
path=self.bucket.path + '/o')
312+
path=self.bucket.path + '/o', upload=True)
312313

313314
# Use apitools 'Upload' facility.
314315
request = http_wrapper.Request(upload_url, 'POST', headers)
315316

316317
upload.ConfigureRequest(upload_config, request, url_builder)
317-
path = url_builder.relative_path.format(bucket=self.bucket.name)
318318
query_params = url_builder.query_params
319-
request.url = conn.build_api_url(path=path, query_params=query_params)
319+
request.url = conn.build_api_url(path=self.bucket.path + '/o',
320+
query_params=query_params,
321+
upload=True)
320322
upload.InitializeUpload(request, conn.http)
321323

322324
# Should we be passing callbacks through from caller? We can't
@@ -628,16 +630,3 @@ def __init__(self, bucket_name, object_name):
628630
self.query_params = {'name': object_name}
629631
self._bucket_name = bucket_name
630632
self._relative_path = ''
631-
632-
@property
633-
def relative_path(self):
634-
"""Inject bucket name into path."""
635-
return self._relative_path.format(bucket=self._bucket_name)
636-
637-
@relative_path.setter
638-
def relative_path(self, value):
639-
"""Allow update of path template.
640-
641-
``value`` should be a string template taking ``{bucket}``.
642-
"""
643-
self._relative_path = value

gcloud/storage/test_connection.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,18 @@ def test_build_api_url_w_extra_query_params(self):
150150
self.assertEqual(parms['project'], PROJECT)
151151
self.assertEqual(parms['bar'], 'baz')
152152

153+
def test_build_api_url_w_upload(self):
154+
PROJECT = 'project'
155+
conn = self._makeOne(PROJECT)
156+
URI = '/'.join([
157+
conn.API_BASE_URL,
158+
'upload',
159+
'storage',
160+
conn.API_VERSION,
161+
'foo?project=%s' % PROJECT,
162+
])
163+
self.assertEqual(conn.build_api_url('/foo', upload=True), URI)
164+
153165
def test_make_request_no_data_no_content_type_no_headers(self):
154166
PROJECT = 'project'
155167
conn = self._makeOne(PROJECT)

gcloud/storage/test_key.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,9 @@ def test_download_to_file(self):
191191
(chunk2_response, 'def'),
192192
)
193193
bucket = _Bucket(connection)
194-
key = self._makeOne(bucket, KEY)
194+
MEDIA_LINK = 'http://example.com/media/'
195+
properties = {'mediaLink': MEDIA_LINK}
196+
key = self._makeOne(bucket, KEY, properties)
195197
key.CHUNK_SIZE = 3
196198
fh = StringIO()
197199
key.download_to_file(fh)
@@ -210,7 +212,9 @@ def test_download_to_filename(self):
210212
(chunk2_response, 'def'),
211213
)
212214
bucket = _Bucket(connection)
213-
key = self._makeOne(bucket, KEY)
215+
MEDIA_LINK = 'http://example.com/media/'
216+
properties = {'mediaLink': MEDIA_LINK}
217+
key = self._makeOne(bucket, KEY, properties)
214218
key.CHUNK_SIZE = 3
215219
with NamedTemporaryFile() as f:
216220
key.download_to_filename(f.name)
@@ -231,7 +235,9 @@ def test_download_as_string(self):
231235
(chunk2_response, 'def'),
232236
)
233237
bucket = _Bucket(connection)
234-
key = self._makeOne(bucket, KEY)
238+
MEDIA_LINK = 'http://example.com/media/'
239+
properties = {'mediaLink': MEDIA_LINK}
240+
key = self._makeOne(bucket, KEY, properties)
235241
key.CHUNK_SIZE = 3
236242
fetched = key.download_as_string()
237243
self.assertEqual(fetched, 'abcdef')
@@ -261,7 +267,7 @@ def test_upload_from_file_simple(self):
261267
scheme, netloc, path, qs, _ = urlsplit(uri)
262268
self.assertEqual(scheme, 'http')
263269
self.assertEqual(netloc, 'example.com')
264-
self.assertEqual(path, '/upload/storage/v1/b/name/o')
270+
self.assertEqual(path, '/b/name/o')
265271
self.assertEqual(dict(parse_qsl(qs)),
266272
{'uploadType': 'media', 'name': 'key'})
267273
headers = dict(
@@ -305,7 +311,7 @@ def test_upload_from_file_resumable(self):
305311
scheme, netloc, path, qs, _ = urlsplit(uri)
306312
self.assertEqual(scheme, 'http')
307313
self.assertEqual(netloc, 'example.com')
308-
self.assertEqual(path, '/resumable/upload/storage/v1/b/name/o')
314+
self.assertEqual(path, '/b/name/o')
309315
self.assertEqual(dict(parse_qsl(qs)),
310316
{'uploadType': 'resumable', 'name': 'key'})
311317
headers = dict(
@@ -360,7 +366,7 @@ def test_upload_from_file_w_slash_in_name(self):
360366
scheme, netloc, path, qs, _ = urlsplit(uri)
361367
self.assertEqual(scheme, 'http')
362368
self.assertEqual(netloc, 'example.com')
363-
self.assertEqual(path, '/upload/storage/v1/b/name/o')
369+
self.assertEqual(path, '/b/name/o')
364370
self.assertEqual(dict(parse_qsl(qs)),
365371
{'uploadType': 'media', 'name': 'parent/child'})
366372
headers = dict(
@@ -400,7 +406,7 @@ def test_upload_from_filename(self):
400406
scheme, netloc, path, qs, _ = urlsplit(uri)
401407
self.assertEqual(scheme, 'http')
402408
self.assertEqual(netloc, 'example.com')
403-
self.assertEqual(path, '/upload/storage/v1/b/name/o')
409+
self.assertEqual(path, '/b/name/o')
404410
self.assertEqual(dict(parse_qsl(qs)),
405411
{'uploadType': 'media', 'name': 'key'})
406412
headers = dict(
@@ -436,7 +442,7 @@ def test_upload_from_string(self):
436442
scheme, netloc, path, qs, _ = urlsplit(uri)
437443
self.assertEqual(scheme, 'http')
438444
self.assertEqual(netloc, 'example.com')
439-
self.assertEqual(path, '/upload/storage/v1/b/name/o')
445+
self.assertEqual(path, '/b/name/o')
440446
self.assertEqual(dict(parse_qsl(qs)),
441447
{'uploadType': 'media', 'name': 'key'})
442448
headers = dict(
@@ -806,10 +812,13 @@ def api_request(self, **kw):
806812
return self._respond(**kw)
807813

808814
def build_api_url(self, path, query_params=None,
809-
api_base_url=API_BASE_URL):
815+
api_base_url=API_BASE_URL, upload=False):
810816
from urllib import urlencode
811817
from urlparse import urlsplit
812818
from urlparse import urlunsplit
819+
# mimic the build_api_url interface, but avoid unused param and
820+
# missed coverage errors
821+
upload = not upload # pragma NO COVER
813822
qs = urlencode(query_params or {})
814823
scheme, netloc, _, _, _ = urlsplit(api_base_url)
815824
return urlunsplit((scheme, netloc, path, qs, ''))

0 commit comments

Comments
 (0)