Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions renga/api/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,15 @@ def get(self, *args, **kwargs):

@check_status_code
def post(self, *args, **kwargs):
"""Perform the ``GET`` request and check its status code."""
"""Perform the ``POST`` request and check its status code."""
return super(APIClient, self).post(*args, **kwargs)

@check_status_code
def put(self, *args, **kwargs):
"""Perform the ``PUT`` request and check its status code."""
return super(APIClient, self).put(*args, **kwargs)

@check_status_code
def delete(self, *args, **kwargs):
"""Perform the ``GET`` request and check its status code."""
"""Perform the ``DELETE`` request and check its status code."""
return super(APIClient, self).delete(*args, **kwargs)
10 changes: 9 additions & 1 deletion renga/api/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ def create_bucket(self, **kwargs):
self._url('/api/storage/authorize/create_bucket'), json=kwargs)
return resp.json()

def storage_bucket_metadata_replace(self, resource_id, data):
"""Replace resource metadata."""
return self.put(
self._url('/api/storage/bucket/{0}', resource_id),
json=data,
expected_status_code=200, ).json()


class FilesApiMixin(object):
"""Client for handling file objects in a bucket."""
Expand Down Expand Up @@ -76,7 +83,8 @@ def storage_file_metadata_replace(self, resource_id, data):
"""Replace resource metadata."""
return self.put(
self._url('/api/storage/file/{0}', resource_id),
json=data, ).json()
json=data,
expected_status_code=200, ).json()

def storage_io_write(self, data):
"""Write data to the file.
Expand Down
73 changes: 49 additions & 24 deletions renga/models/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,24 @@ def name(self):
"""The bucket name."""
return self._properties.get('resource:bucket_name')

@name.setter
def name(self, value):
"""Modify the name."""
# FIXME check when the API is fixed
self._client.api.storage_bucket_metadata_replace(
self.id, {
'file_name': value,
})

# Update if the service replace works
self._properties['resource:bucket_name'] = value

@property
def _properties(self):
"""The internal bucket properties."""
if self._response.get('properties') is None:
self._response = self._client.api.get_bucket(self.id)
assert self._response['properties']
return self._response.get('properties')

@property
Expand All @@ -72,6 +87,12 @@ def backends(self):
def create(self, name=None, backend=None, **kwargs):
"""Create a new :class:`~renga.models.storage.Bucket` instance.

**Example**

>>> bucket = client.buckets.create('bucket1')
>>> bucket.name
'bucket1'

:param name: Bucket name.
:param backend: Name of backend used to store data in the bucket.
Defaults to the :envvar:`RENGA_STORAGE_BUCKET_BACKEND`
Expand Down Expand Up @@ -139,27 +160,27 @@ def access_token(self):
self._client.api.access_token)

@property
def filename(self):
"""Filename of the file."""
def name(self):
"""Name of the file."""
return self._properties.get('resource:file_name')

@filename.setter
def filename(self, value):
"""Modify the filename value."""
labels = self._properties.get('resource:labels', [])
@name.setter
def name(self, value):
"""Modify the name."""
self._client.api.storage_file_metadata_replace(self.id, {
'file_name': value,
'labels': labels,
})

# Update if the service replace works
self._properties['resource:file_name'] = value

def clone(self, filename=None, bucket=None):
filename = name

def clone(self, name=None, filename=None, bucket=None):
"""Create an instance of the file for independent version tracking."""
resp = self._client.api.storage_copy_file(
resource_id=self.id,
file_name=filename or 'clone_' + self.filename,
file_name=name or filename or 'clone_' + self.name,
bucket_id=bucket.id if isinstance(bucket, Bucket) else bucket, )
return self.__class__(
LazyResponse(lambda: self._client.api.get_file(resp['id']), resp),
Expand All @@ -184,7 +205,7 @@ class Meta:

model = File

headers = ('id', 'filename')
headers = ('id', 'name')

def __init__(self, bucket, **kwargs):
"""Initialize collection of files in the bucket."""
Expand All @@ -203,14 +224,14 @@ def __iter__(self):
return (self.Meta.model(f, client=self._client, collection=self)
for f in self._client.api.get_bucket_files(self.bucket.id))

def open(self, filename=None, mode='w'):
def open(self, name=None, filename=None, mode='w'):
"""Create an empty file in this bucket."""
if mode != 'w':
raise NotImplemented('Only mode "w" is currently supported')

resp = self._client.api.create_file(
bucket_id=self.bucket.id,
file_name=filename,
file_name=name or filename,
request_type='create_file', )

access_token = resp.pop('access_token')
Expand All @@ -228,31 +249,32 @@ def open(self, filename=None, mode='w'):
}
return FileHandle(file_handle, client=client)

def create(self, filename=None):
def create(self, name=None, filename=None):
"""Create an empty file in this bucket."""
resp = self._client.api.create_file(
bucket_id=self.bucket.id,
file_name=filename,
file_name=name or filename,
request_type='create_file', )
return self.Meta.model(
LazyResponse(lambda: self._client.api.get_file(resp['id']), resp),
client=self._client,
collection=self)

def from_url(self, url, filename=None):
def from_url(self, url, name=None, filename=None):
"""Create a file with data from the streamed GET response.

**Example**

>>> file_ = client.buckets[1234].files.from_url(
... 'https://example.com/tests/data', filename='hello')
>>> bucket = client.buckets.create('bucket1')
>>> file_ = bucket.files.from_url(
... 'https://example.com/tests/data', name='hello')
>>> file_.id
9876
>>> client.buckets[1234].files[9876].open('r').read()
>>> client.buckets[bucket.id].files[file_.id].open('r').read()
b'hello world'

"""
with self.open(filename=filename or url, mode='w') as fp:
with self.open(name=name or filename or url, mode='w') as fp:
fp.from_url(url)
return self.__getitem__(fp.id)

Expand Down Expand Up @@ -300,7 +322,8 @@ def from_url(self, url):

**Example**

>>> with client.buckets[1234].files[9876].open('w') as fp:
>>> file_ = client.buckets.create('bucket1').files.create('data')
>>> with file_.open('w') as fp:
... fp.from_url('https://example.com/tests/data')

"""
Expand All @@ -321,10 +344,12 @@ class FileVersion(Model, FileMixin):
IDENTIFIER_KEY = 'id'

@property
def filename(self):
"""Filename of the file."""
def name(self):
"""Name of the file."""
return self._properties.get('resource:file_name',
self._collection.file.filename)
self._collection.file.name)

filename = name

@property
def created(self):
Expand All @@ -342,7 +367,7 @@ class Meta:

model = FileVersion

headers = ('id', 'filename', 'created')
headers = ('id', 'name', 'created')

def __init__(self, file_, **kwargs):
"""Initialize a collection of file versions."""
Expand Down
144 changes: 68 additions & 76 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,15 +311,58 @@ def deployer_responses(auth_responses, renga_client):
def storage_responses(auth_responses, renga_client):
"""Monkeypatch requests to immitate the storage service."""
rsps = auth_responses
rsps.add(

buckets = rsps.buckets = []
bucket_ = {'id': 1234}

def create_bucket(request):
"""Create a new instance of bucket."""
resp = json.loads(request.body.decode('utf-8'))
id = bucket_['id']
bucket_['id'] += 4444
name = resp['name']
buckets.append({
"id":
id,
"types": ["resource:bucket"],
"properties": [{
"key":
"resource:bucket_backend",
"data_type":
"string",
"cardinality":
"single",
"values": [{
"key": "resource:bucket_backend",
"data_type": "string",
"value": "local",
"properties": []
}]
}, {
"key":
"resource:bucket_name",
"data_type":
"string",
"cardinality":
"single",
"values": [{
"key": "resource:bucket_name",
"data_type": "string",
"value": name,
"properties": []
}]
}]
})
return (201, {}, json.dumps({
'id': id,
'name': name,
'backend': 'local',
}))

rsps.add_callback(
responses.POST,
renga_client.api._url('/api/storage/authorize/create_bucket'),
status=201,
json={
'id': 1234,
'name': 'hello',
'backend': 'local',
})
callback=create_bucket, )

file_ = {'id': 9876}
data = {}
Expand Down Expand Up @@ -490,81 +533,30 @@ def io_read(request):
def explorer_responses(auth_responses, renga_client):
"""Monkeypatch requests to immitate the explorer service."""
rsps = auth_responses
buckets = [{
"id":
1234,
"types": ["resource:bucket"],
"properties": [{
"key":
"resource:bucket_backend",
"data_type":
"string",
"cardinality":
"single",
"values": [{
"key": "resource:bucket_backend",
"data_type": "string",
"value": "local",
"properties": []
}]
}, {
"key":
"resource:bucket_name",
"data_type":
"string",
"cardinality":
"single",
"values": [{
"key": "resource:bucket_name",
"data_type": "string",
"value": "bucket1",
"properties": []
}]
}]
}, {
"id":
5678,
"types": ["resource:bucket"],
"properties": [{
"key":
"resource:bucket_backend",
"data_type":
"string",
"cardinality":
"single",
"values": [{
"key": "resource:bucket_backend",
"data_type": "string",
"value": "local",
"properties": []
}]
}, {
"key":
"resource:bucket_name",
"data_type":
"string",
"cardinality":
"single",
"values": [{
"key": "resource:bucket_name",
"data_type": "string",
"value": "bucket2",
"properties": []
}]
}]
}]
buckets = rsps.buckets

rsps.add(
rsps.add_callback(
responses.GET,
renga_client.api._url('/api/explorer/storage/bucket'),
status=200,
json=buckets)
callback=lambda request: (200, {}, json.dumps(buckets)), )

rsps.add(
def rename_bucket(request):
"""Rename a bucket."""
payload = json.loads(request.body.decode('utf-8'))
buckets[0]['properties'][1]['values'][0]['value'] = payload[
'file_name']
return (200, {}, '{}')

rsps.add_callback(
responses.PUT,
renga_client.api._url('/api/storage/bucket/1234'),
callback=rename_bucket,
content_type='application/json', )

rsps.add_callback(
responses.GET,
renga_client.api._url('/api/explorer/storage/bucket/1234'),
status=200,
json=buckets[0])
callback=lambda request: (200, {}, json.dumps(buckets[0])))

def new_file(id, name):
"""Create new file."""
Expand Down
Loading