Skip to content
This repository has been archived by the owner on Jul 1, 2022. It is now read-only.

Commit

Permalink
Implement Rights registration from existing source Rights (#26)
Browse files Browse the repository at this point in the history
* test(coalaip): small cosmetic changes for testing manifestaiton registration

* test(models): add test for ensuring models' formatted data keeps original data

* feature(coalaip): implement Right derivation for current holder of an existing Right

* test(coalaip): add test for derive_right() throwing if no allowedBy or source_right is given
  • Loading branch information
sohkai authored Sep 16, 2016
1 parent 9069598 commit 0d512fe
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 25 deletions.
72 changes: 57 additions & 15 deletions coalaip/coalaip.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from collections import namedtuple
from coalaip.exceptions import EntityNotYetPersistedError
from coalaip.models import Copyright, Manifestation, Work
from coalaip.models import Copyright, Right, Manifestation, Work
from coalaip.plugin import AbstractPlugin


Expand Down Expand Up @@ -152,27 +152,69 @@ def register_manifestation(self, manifestation_data, *, user,

return RegistrationResult(manifestation_copyright, manifestation, work)

def derive_right(self, right_data, from_copyright, *, user,
data_format=None):
"""Derive a new Right from a Manifestation's Copyright.
def derive_right(self, right_data, *, current_holder, source_right=None,
right_model_cls=Right, **kwargs):
"""Derive a new Right from an existing :attr:`source_right` (a
:class:`~coalaip.models.Right` or subclass) for the
:attr:`current_holder` of the :attr:`source_right`. The newly
registered Right can then be transferred to other Parties.
Args:
right_data (dict): a dict holding the model data for the
Right
from_copyright (:class:`~coalaip.models.Copyright`): the id
of the Copyright that this Right should be derived from
user (any, keyword): a user based on the format specified by
the persistence layer
data_format (str, keyword, optional): the data format of the
created Right; must be one of:
- 'jsonld' (default)
- 'json'
- 'ipld'
Right.
See the given :attr:`right_model_cls` for requirements.
If ``allowedBy`` is provided in the dict, the
:attr:`source_right` parameter is ignored.
current_holder (any, keyword): the current holder of the
:attr:`source_right`; a user based on the format
specified by the persistence layer
source_right (:class:`~coalaip.models.Right`, keyword, optional):
an already persisted Right that the new Right is allowed
by.
Optional if ``allowedBy`` is provided in :attr:`right_data`.
right_model_cls (Right, keyword, optional): a subclass of
:class:`~coalaip.models.Right` that should be
instantiated for the newly derived right.
Defaults to :class:~coalaip.models.Right`.
**kwargs: keyword arguments passed through to the
:attr:`right_model_cls`'s ``create`` method (e.g.
:meth:`~coalaip.models.CoalaIpEntity.create`'s
``data_format``)
Returns:
a registered :attr:`right_model_cls` Right model (by default a
:class:`~coalaip.models.Right)
Raises:
:class:`~coalaip.exceptions.EntityDataError`: if the
:attr:`right_data` contains invalid or is missing
required properties.
:class:`~coalaip.exceptions.EntityNotYetPersistedError`: if
the :attr:`source_right` has not been persisted yet
:class:`~coalaip.exceptions.EntityCreationError`: if the
right fails to be created on the persistence layer
"""

raise NotImplementedError('derive_right() has not been implemented yet')
if not right_data.get('allowedBy'):
if source_right is None:
raise ValueError(("'source_right' argument to 'derive_right() "
"must be provided if 'allowedBy' is not "
"given as part of 'right_data'"))
if not isinstance(source_right, Right):
raise TypeError(("'source_right' argument to 'derive_right()' "
'must be a Right (or subclass). Given an '
"instance of '{}'".format(type(source_right))))
elif source_right.persist_id is None:
raise EntityNotYetPersistedError(
("Right given as 'source_right' to 'derive_right()' must "
'be already created on the backing persistence layer.')
)

right_data['allowedBy'] = source_right.persist_id

right = right_model_cls(right_data, plugin=self._plugin)
right.create(current_holder, **kwargs)
return right

def transfer_right(self, right, rights_assignment_data, *, from_user, to_user,
data_format=None):
Expand Down
154 changes: 144 additions & 10 deletions tests/test_coalaip.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,13 @@ def test_register_manifestation_with_work_id_in_data(
mock_manifestation_create_id, mock_copyright_create_id):
from tests.utils import create_entity_id_setter
ignored_work_model = work_model
provided_work_id = 'provided_work_id'

# Create the default manifestation model, but change the
# 'manifestationOfWork' key to differentiate it from work_model
manifestation_data = manifestation_data_factory()
manifestation_data['manifestationOfWork'] = 'provided_work_id'
manifestation_data = manifestation_data_factory(data={
'manifestationOfWork': provided_work_id
})

# Set the persisted ids of the entities
mock_plugin.save.side_effect = create_entity_id_setter(
Expand All @@ -123,7 +125,7 @@ def test_register_manifestation_with_work_id_in_data(
)
assert work is None
assert manifestation_copyright.data['rightsOf'] == manifestation.persist_id
assert manifestation.data['manifestationOfWork'] == manifestation_data['manifestationOfWork']
assert manifestation.data['manifestationOfWork'] == provided_work_id


@mark.parametrize('work_data', [None, {'name': 'mock_work_name'}])
Expand Down Expand Up @@ -194,23 +196,23 @@ def test_register_manifestation_with_existing_work(mock_plugin, mock_coalaip,
new_copyright_create_id,
)

# Throws if given Work is not a Work
# Throws if given existing_work is not a Work
with raises(TypeError):
mock_coalaip.register_manifestation(
manifestation_data,
user=alice_user,
existing_work={},
)

# Throws if given Work has not been persisted yet
# Throws if given existing_work has not been persisted yet
with raises(EntityNotYetPersistedError):
mock_coalaip.register_manifestation(
manifestation_data,
user=alice_user,
existing_work=work_model,
)

# Test the new manifestation is created with the given existing_work (and
# Test the new Manifestation is created with the given existing_work (and
# ignores any given work_data)
mock_plugin.reset_mock() # Reset call counts on the mock from before
new_manifestation_copyright, new_manifestation, old_work = mock_coalaip.register_manifestation(
Expand Down Expand Up @@ -254,7 +256,139 @@ def test_register_manifestation_raises_on_creation_error(mock_plugin,

manifestation_data = manifestation_data_factory()
with raises(EntityCreationError):
manifestation_copyright, manifestation, work = mock_coalaip.register_manifestation(
manifestation_data,
user=alice_user,
)
mock_coalaip.register_manifestation(manifestation_data,
user=alice_user)


@mark.parametrize('data_format', [(''), ('json'), ('jsonld')])
def test_derive_right(mock_plugin, mock_coalaip, right_data_factory, alice_user,
data_format, mock_copyright_create_id,
mock_right_create_id):
mock_plugin.save.return_value = mock_right_create_id

# Create the default right model with 'allowedBy' already set
right_data = right_data_factory()

derive_right_kwargs = {}
if data_format:
derive_right_kwargs['data_format'] = data_format

# Create the Right and test it was persisted
right = mock_coalaip.derive_right(right_data, current_holder=alice_user,
**derive_right_kwargs)
assert right.persist_id == mock_right_create_id

# Test the correct data format was persisted
if data_format == 'json':
right_persisted_data = right.to_json()
elif data_format == 'ipld':
raise NotImplementedError('IPLD is not implemented yet')
else:
right_persisted_data = right.to_jsonld()

# Check we called plugin.save() with the right format
mock_plugin.save.assert_called_once_with(right_persisted_data,
user=alice_user)


def test_derive_right_with_allowed_by_in_data(mock_plugin, mock_coalaip,
right_data_factory, alice_user,
copyright_model,
mock_right_create_id):
ignored_copyright_model = copyright_model
provided_copyright_id = 'provided_copyright_id'
mock_plugin.save.return_value = mock_right_create_id

# Create the default right model, but change the 'allowedBy' key to
# differentiate it from copyright_model
right_data = right_data_factory(data={
'allowedBy': provided_copyright_id
})

# Create the Right and test it was persisted with the correct Copyright
right = mock_coalaip.derive_right(right_data, current_holder=alice_user,
source_right=ignored_copyright_model)
assert right.data['allowedBy'] == provided_copyright_id


def test_derive_right_with_existing_source_right(mock_plugin, mock_coalaip,
right_data_factory,
alice_user, copyright_model,
persisted_jsonld_registration,
mock_right_create_id):
from coalaip.exceptions import EntityNotYetPersistedError
persisted_copyright = persisted_jsonld_registration.copyright
mock_plugin.save.return_value = mock_right_create_id

# Create the default right model, but remove the 'allowedBy' key to use
# the source_right
right_data = right_data_factory()
del right_data['allowedBy']

# Throws if given source_right is not a Right
with raises(TypeError):
mock_coalaip.derive_right(right_data, current_holder=alice_user,
source_right={})

# Throws if given source_right has not been persisted yet
with raises(EntityNotYetPersistedError):
mock_coalaip.derive_right(right_data, current_holder=alice_user,
source_right=copyright_model)

# Test the new Right is created with the given source_right
mock_plugin.reset_mock() # Reset call counts on the mock from before
right = mock_coalaip.derive_right(right_data, current_holder=alice_user,
source_right=persisted_copyright)
assert right.data['allowedBy'] == persisted_copyright.persist_id

# Check we called plugin.save() with the correct Copyright
mock_plugin.save.assert_called_once_with(right.to_jsonld(),
user=alice_user)


def test_derive_right_with_custom_model_cls(mock_plugin, mock_coalaip,
right_data_factory, alice_user,
mock_right_create_id):
from coalaip.models import Right
mock_plugin.save.return_value = mock_right_create_id

custom_right_type = 'CustomRight'

class CustomRight(Right):
def __init__(self, data, *args, **kwargs):
super().__init__(data, entity_type=custom_right_type, *args, **kwargs)

# Create the default right model with 'allowedBy' already set
right_data = right_data_factory()

# Test the new Right is created with the given source_right
custom_right = mock_coalaip.derive_right(
right_data,
current_holder=alice_user,
right_model_cls=CustomRight
)
assert isinstance(custom_right, CustomRight)
assert custom_right.to_json()['type'] == custom_right_type
assert custom_right.persist_id == mock_right_create_id
assert custom_right.data['allowedBy'] == right_data['allowedBy']


def test_derive_right_raises_on_no_allowed_by_or_source_right(
mock_plugin, mock_coalaip, right_data_factory, alice_user):

right_data = right_data_factory()
del right_data['allowedBy']
with raises(ValueError):
mock_coalaip.derive_right(right_data, current_holder=alice_user)


def test_derive_right_raises_on_creation_error(mock_plugin, mock_coalaip,
right_data_factory, alice_user):
from coalaip.exceptions import EntityCreationError

mock_creation_error = 'mock_creation_error'
mock_plugin.save.side_effect = EntityCreationError(mock_creation_error)

right_data = right_data_factory()
with raises(EntityCreationError):
mock_coalaip.derive_right(right_data, current_holder=alice_user)
11 changes: 11 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,17 @@ def test_entities_have_none_status_if_not_persisted(mock_plugin,
mock_plugin.get_status.assert_not_called()


def test_entities_data_format_consistent(mock_plugin):
from coalaip.models import CoalaIpEntity
from tests.utils import assert_key_values_present_in_dict
entity_data = {'test_data': 'test_data', 'extra_data': 'extra_data'}
entity_model = CoalaIpEntity(data=entity_data, entity_type='type',
plugin=mock_plugin)

assert_key_values_present_in_dict(entity_model.to_json(), **entity_data)
assert_key_values_present_in_dict(entity_model.to_jsonld(), **entity_data)


def test_entities_get_status(mock_plugin, base_entity_model, alice_user,
mock_base_entity_create_id, mock_model_status):
# Save the entity
Expand Down

0 comments on commit 0d512fe

Please sign in to comment.