Skip to content

Commit

Permalink
models: bucket tags
Browse files Browse the repository at this point in the history
* Adds support for adding tags (i.e. key/value-pairs) to buckets.

Signed-off-by: Lars Holm Nielsen <lars.holm.nielsen@cern.ch>
  • Loading branch information
lnielsen committed Mar 3, 2016
1 parent 328175f commit d11c0bc
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 2 deletions.
74 changes: 74 additions & 0 deletions invenio_files_rest/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
The entities of this module consists of:
* Buckets - Identified by UUIDs, and contains objects.
* Buckets tags - Identified uniquely with a bucket by a key. Used to store
extra metadata for a bucket.
* Objects - Identified uniquely within a bucket by string keys. Each object
can have multiple object versions.
* Object versions - Identified by UUIDs and belongs to one specific object
Expand Down Expand Up @@ -234,6 +236,10 @@ def snapshot(self, lock=False):

return b

def get_tags(self):
"""Get tags for bucket as dictionary."""
return {t.key: t.value for t in self.tags}

@classmethod
def create(cls, location=None, storage_class=None):
"""Create a bucket.
Expand Down Expand Up @@ -277,6 +283,74 @@ def all(cls):
)


class BucketTag(db.Model):
"""Model for storing tags associated to buckets.
This is useful to store extra information for a bucket.
"""

__tablename__ = 'files_buckettags'

bucket_id = db.Column(
UUIDType,
db.ForeignKey(Bucket.id, ondelete='CASCADE'),
default=uuid.uuid4,
primary_key=True, )

key = db.Column(db.String(255), primary_key=True)
"""Tag key."""

value = db.Column(db.Text, nullable=False)
"""Tag value."""

bucket = db.relationship(Bucket, backref='tags')
"""Relationship to buckets."""

@classmethod
def get(cls, bucket, key):
"""Get tag object."""
return cls.query.filter_by(
bucket_id=bucket.id if isinstance(bucket, Bucket) else bucket,
key=key,
).one_or_none()

@classmethod
def create(cls, bucket, key, value):
"""Create a new tag for bucket."""
with db.session.begin_nested():
obj = cls(
bucket_id=bucket.id if isinstance(bucket, Bucket) else bucket,
key=key,
value=value
)
db.session.add(obj)
return obj

@classmethod
def create_or_update(cls, bucket, key, value):
"""Create a new tag for bucket."""
obj = cls.get(bucket, key)
if obj:
obj.value = value
else:
cls.create(bucket, key, value)

@classmethod
def get_value(cls, bucket, key):
"""Get tag value."""
obj = cls.get(bucket, key)
return obj.value if obj else None

@classmethod
def delete(cls, bucket, key):
"""Delete a tag."""
with db.session.begin_nested():
cls.query.filter_by(
bucket_id=bucket.id if isinstance(bucket, Bucket) else bucket,
key=key,
).delete()


class FileInstance(db.Model, Timestamp):
"""Model for storing files.
Expand Down
45 changes: 43 additions & 2 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@

from invenio_files_rest.errors import FileInstanceAlreadySetError, \
InvalidOperationError
from invenio_files_rest.models import Bucket, FileInstance, Location, \
ObjectVersion
from invenio_files_rest.models import Bucket, BucketTag, FileInstance, \
Location, ObjectVersion


def test_location(app, db):
Expand Down Expand Up @@ -475,3 +475,44 @@ def test_object_restore(app, db, dummy_location):
assert obj_new.key == obj1.key
assert obj_new.file_id == obj1.file_id
assert obj_new.bucket == obj1.bucket


def test_bucket_tags(app, db, dummy_location):
"""Test bucket tags."""
b = Bucket.create()
BucketTag.create(b, "mykey", "testvalue")
BucketTag.create(b, "another_key", "another value")
db.session.commit()

# Duplicate key
pytest.raises(Exception, BucketTag.create, b, "mykey", "newvalue")

# Test get
assert BucketTag.query.count() == 2
assert BucketTag.get(b.id, "mykey").value == "testvalue"
assert BucketTag.get_value(b, "another_key") == "another value"
assert BucketTag.get_value(b.id, "invalid") is None

# Test delete
BucketTag.delete(b, "mykey")
assert BucketTag.query.count() == 1
BucketTag.delete(b, "invalid")
assert BucketTag.query.count() == 1

# Create or update
BucketTag.create_or_update(b, "another_key", "newval")
BucketTag.create_or_update(b, "newkey", "testval")
db.session.commit()
assert BucketTag.get_value(b, "another_key") == "newval"
assert BucketTag.get_value(b, "newkey") == "testval"

# Get tags as dictionary
assert b.get_tags() == dict(another_key="newval", newkey="testval")

b2 = Bucket.create()
assert b2.get_tags() == dict()

# Test cascading delete.
Bucket.query.delete()
db.session.commit()
assert BucketTag.query.count() == 0

0 comments on commit d11c0bc

Please sign in to comment.