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

Implement Rights registration from existing source Rights #26

Merged
merged 4 commits into from
Sep 16, 2016
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
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