From 550478541e940a006581cff731b0c50a0bd7ef40 Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Fri, 10 Feb 2017 11:31:56 +0100 Subject: [PATCH 01/13] Restore docker-compose BDB_NODE settings to containerized settings --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index b2bac07..e0a96b2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,8 +14,8 @@ services: - .env environment: API_HOST: api - BDB_NODE_HOST: "${BDB_NODE_HOST}" - BDB_NODE_PORT: "${BDB_NODE_PORT}" + BDB_NODE_HOST: bigchaindb + BDB_NODE_PORT: 9984 ports: - "${API_PORT}:3000" command: python web/server.py From 3092d6d307aefc24a7cd77fd86743b8327c554db Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Fri, 10 Feb 2017 11:44:14 +0100 Subject: [PATCH 02/13] Upgrade BigchainDB Server to 0.9 --- .travis.yml | 2 +- compose/bigchaindb/Dockerfile | 2 +- setup.py | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index ce48ab9..f8232c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ install: before_script: - rethinkdb --daemon - - bigchaindb -y configure + - bigchaindb -y configure rethinkdb # Start BigchainDB in the background and ignore any output - bigchaindb start >/dev/null 2>&1 & diff --git a/compose/bigchaindb/Dockerfile b/compose/bigchaindb/Dockerfile index 3b06fcb..57db999 100644 --- a/compose/bigchaindb/Dockerfile +++ b/compose/bigchaindb/Dockerfile @@ -5,4 +5,4 @@ WORKDIR /usr/src/app RUN pip install --upgrade pip RUN pip install bigchaindb -RUN bigchaindb -y configure +RUN bigchaindb -y configure rethinkdb diff --git a/setup.py b/setup.py index 22603a5..5432245 100644 --- a/setup.py +++ b/setup.py @@ -6,8 +6,7 @@ install_requires = [ 'coalaip>=0.0.1.dev3', 'coalaip-bigchaindb>=0.0.1.dev3', - 'bigchaindb==0.8.0', - 'bigchaindb-driver==0.1.2', + 'bigchaindb~=0.9.0', 'flask>=0.11.1', 'flask-restful>=0.3.5', 'gunicorn>=19.6.0', From 75b262167d5eae4a01a56725c4cd72a300248653 Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Fri, 10 Feb 2017 11:51:41 +0100 Subject: [PATCH 03/13] Install pip dependencies in API docker-container via requirements file --- compose/api/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compose/api/Dockerfile b/compose/api/Dockerfile index fe5c7a9..b9d0354 100644 --- a/compose/api/Dockerfile +++ b/compose/api/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.5 -RUN apt-get update && apt-get install -y vim +RUN apt-get update RUN mkdir -p /usr/src/app WORKDIR /usr/src/app @@ -9,4 +9,4 @@ RUN pip install --upgrade pip COPY . /usr/src/app/ -RUN pip install --no-cache-dir --process-dependency-links -e . +RUN pip install --no-cache-dir --process-dependency-links -r requirements_dev.txt From 8adaae92bd6df5ab9ef9cbcb1d4d7381940c459c Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Fri, 10 Feb 2017 11:56:05 +0100 Subject: [PATCH 04/13] Install coalaip and coalaip-bigchaindb dependencies via dependency links for now --- dependency_links.txt | 4 ++++ requirements_dev.txt | 2 ++ setup.py | 4 ++-- 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 dependency_links.txt diff --git a/dependency_links.txt b/dependency_links.txt new file mode 100644 index 0000000..66079bc --- /dev/null +++ b/dependency_links.txt @@ -0,0 +1,4 @@ +--process-dependency-links + +git+https://github.com/bigchaindb/pycoalaip.git#egg=coalaip +git+https://github.com/bigchaindb/pycoalaip-bigchaindb.git@10-support-transfers#egg=coalaip-bigchaindb diff --git a/requirements_dev.txt b/requirements_dev.txt index 56763f7..52d7543 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -2,4 +2,6 @@ pip==8.1.2 bumpversion==0.5.3 wheel==0.29.0 +-r dependency_links.txt + -e .[dev] diff --git a/setup.py b/setup.py index 5432245..417202b 100644 --- a/setup.py +++ b/setup.py @@ -4,8 +4,8 @@ from setuptools import setup, find_packages install_requires = [ - 'coalaip>=0.0.1.dev3', - 'coalaip-bigchaindb>=0.0.1.dev3', + 'coalaip==0.0.1.dev3', + 'coalaip-bigchaindb==0.0.1.dev3', 'bigchaindb~=0.9.0', 'flask>=0.11.1', 'flask-restful>=0.3.5', From b2856e365e262d436e4fb26cc9cdc4233fdd260f Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Fri, 10 Feb 2017 11:57:43 +0100 Subject: [PATCH 05/13] Upgrade pip to 9.0.1 --- requirements_dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_dev.txt b/requirements_dev.txt index 52d7543..2204987 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,4 +1,4 @@ -pip==8.1.2 +pip==9.0.1 bumpversion==0.5.3 wheel==0.29.0 From 0b4ac49be8d379c6b198820a06f21224ac5f0dde Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Fri, 10 Feb 2017 11:58:23 +0100 Subject: [PATCH 06/13] Update user model to use public/private key instead of verifying/signing key because of upstream changes --- README.md | 18 +++++++++--------- tests/test_api.py | 4 ++-- web/models.py | 2 +- web/views/manifestations.py | 4 ++-- web/views/rights.py | 4 ++-- web/views/users.py | 6 +++--- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 0e2441b..cfc44f8 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ The API server can be configured with a number of environment variables [see ### Create Users This call will not store any data on the running instance of BigchainDB. -It simply generates a verifying and signing key-pair that can be used in a +It simply generates a public/private key-pair that can be used in a POST-manifestation call. ``` @@ -151,8 +151,8 @@ PAYLOAD: None RETURNS: { - "verifyingKey": "", - "signingKey": "", + "publicKey": "", + "privateKey": "", } ``` @@ -160,8 +160,8 @@ RETURNS: ### Register a Manifestation In order to register the manifestation on BigchainDB as transactions on a -specific copyright holder's name, the copyright holder's `verifyingKey` and -`signingKey` must be provided here. +specific copyright holder's name, the copyright holder's `publicKey` and +`privateKey` must be provided here. Note that the attributes shown for `manifestation` and `work` can be much more diverse; for this, see the COALA IP models definition (not yet publicly @@ -179,8 +179,8 @@ PAYLOAD: "url": "" }, "copyrightHolder": { - "verifyingKey": "", - "signingKey": "" + "publicKey": "", + "privateKey": "" }, "work": { "name": "The Lord of the Rings Triology", @@ -278,8 +278,8 @@ PAYLOAD: "license": "" }, "currentHolder": { - "verifyingKey": "", - "signingKey": "" + "publicKey": "", + "privateKey": "" }, "sourceRightId": "" } diff --git a/tests/test_api.py b/tests/test_api.py index e71c448..c07ff9a 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -6,8 +6,8 @@ def test_create_user(client): resp = client.post(url_for('user_views.userapi')) assert resp.status_code == 200 - assert resp.json['verifyingKey'] - assert resp.json['signingKey'] + assert resp.json['publicKey'] + assert resp.json['privateKey'] def test_create_manifestation(client, user): diff --git a/web/models.py b/web/models.py index 7b288da..397bf40 100644 --- a/web/models.py +++ b/web/models.py @@ -1,7 +1,7 @@ from web.utils import parse_model -user_model = parse_model(['verifyingKey', 'signingKey']) +user_model = parse_model(['publicKey', 'privateKey']) manifestation_model = parse_model(['name', 'datePublished', 'url']) work_model = parse_model(['name', 'author']) right_model = parse_model(['license']) diff --git a/web/views/manifestations.py b/web/views/manifestations.py index bf44d07..910c72c 100644 --- a/web/views/manifestations.py +++ b/web/views/manifestations.py @@ -29,8 +29,8 @@ def post(self): copyright_holder = args['copyrightHolder'] copyright_holder = { - 'verifying_key': copyright_holder.pop('verifyingKey'), - 'signing_key': copyright_holder.pop('signingKey') + 'public_key': copyright_holder.pop('publicKey'), + 'private_key': copyright_holder.pop('privateKey') } copyright_, manifestation, work = coalaip.register_manifestation( diff --git a/web/views/rights.py b/web/views/rights.py index b3a24b0..65dbb89 100644 --- a/web/views/rights.py +++ b/web/views/rights.py @@ -29,8 +29,8 @@ def post(self): right['allowedBy'] = source_right_id current_holder = args['currentHolder'] - current_holder['verifying_key'] = current_holder.pop('verifyingKey') - current_holder['signing_key'] = current_holder.pop('signingKey') + current_holder['public_key'] = current_holder.pop('publicKey') + current_holder['private_key'] = current_holder.pop('privateKey') right = coalaip.derive_right(right_data=right, current_holder=current_holder) diff --git a/web/views/users.py b/web/views/users.py index 4dc4766..25f06df 100644 --- a/web/views/users.py +++ b/web/views/users.py @@ -17,13 +17,13 @@ def post(self): """API endpoint to create a new keypair for a user Return: - A dict containing the verifying_key and signing_key. + A dict containing the publicKey and privateKey. """ # TODO FOR COALA IP: Return CamelCase key names user = coalaip.generate_user() # TODO: We might want to have a generic function for this at one point. - user['verifyingKey'] = user.pop('verifying_key') - user['signingKey'] = user.pop('signing_key') + user['publicKey'] = user.pop('public_key') + user['privateKey'] = user.pop('private_key') return user From 226e8b812d3c47f8e2a2d8181d8f6e6f5495c41c Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Fri, 10 Feb 2017 12:12:13 +0100 Subject: [PATCH 07/13] Remove settings and configuration for BDB_NODE_API_PATH as the driver does this for us now --- .env_template | 1 - web/utils.py | 13 +++---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/.env_template b/.env_template index 8b79ff5..9ba8800 100644 --- a/.env_template +++ b/.env_template @@ -8,7 +8,6 @@ API_PORT=3000 # Settings for the BigchainDB node that the API server should connect to BDB_NODE_HOST= BDB_NODE_PORT= -BDB_NODE_API_PATH= ##### Docker settings ##### # Externally exposed port for accessing BigchainDB's API diff --git a/web/utils.py b/web/utils.py index d71a41e..2a661a5 100644 --- a/web/utils.py +++ b/web/utils.py @@ -5,7 +5,6 @@ BigchainDBConfiguration = namedtuple('BigchainDBConfiguration', [ 'hostname', 'port', - 'api_path' ]) @@ -19,20 +18,14 @@ if not BDB_PORT: BDB_PORT = '9984' -BDB_API_PATH = os.environ.get('BDB_NODE_API_PATH', None) -if not BDB_API_PATH: - BDB_API_PATH = 'api/v1' - def get_bigchaindb_configuration(): - return BigchainDBConfiguration(BDB_HOST, BDB_PORT, BDB_API_PATH) + return BigchainDBConfiguration(BDB_HOST, BDB_PORT) def get_bigchaindb_api_url(): - hostname, port, api_path = get_bigchaindb_configuration() - return 'http://{hostname}:{port}/{api_path}'.format(hostname=hostname, - port=port, - api_path=api_path) + hostname, port = get_bigchaindb_configuration() + return 'http://{hostname}:{port}'.format(hostname=hostname, port=port) def parse_model(required_fields): From dd8d9fef254e7b20db9b1786013567fcc2d901dd Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Tue, 4 Oct 2016 17:58:12 +0200 Subject: [PATCH 08/13] Use alice and bob for users --- tests/conftest.py | 7 ++++++- tests/test_api.py | 22 +++++++++++----------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index ea554f7..59085d1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,5 +11,10 @@ def app(): @pytest.fixture -def user(client): +def alice(client): + return client.post(url_for('user_views.userapi')).json + + +@pytest.fixture +def bob(client): return client.post(url_for('user_views.userapi')).json diff --git a/tests/test_api.py b/tests/test_api.py index c07ff9a..b104b1f 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -10,14 +10,14 @@ def test_create_user(client): assert resp.json['privateKey'] -def test_create_manifestation(client, user): +def test_create_manifestation(client, alice): payload = { 'manifestation': { 'name': 'The Fellowship of the Ring', 'datePublished': '29-07-1954', 'url': 'http://localhost/lordoftherings.txt', }, - 'copyrightHolder': user, + 'copyrightHolder': alice, 'work': { 'name': 'The Lord of the Rings Triology', 'author': 'J. R. R. Tolkien', @@ -71,13 +71,13 @@ def test_create_manifestation(client, user): assert resp.status_code == 200 -def test_create_manifestation_missing_single_attribute(client, user): +def test_create_manifestation_missing_single_attribute(client, alice): payload = { 'manifestation': { 'name': 'The Fellowship of the Ring', 'url': 'http://localhost/lordoftherings.txt', }, - 'copyrightHolder': user, + 'copyrightHolder': alice, 'work': { 'name': 'The Lord of the Rings Triology', 'author': 'J. R. R. Tolkien', @@ -109,9 +109,9 @@ def test_create_manifestation_missing_argument_in_body(client): 'Missing required parameter in the JSON body' -def test_create_right(client, user): +def test_create_right(client, alice): payload = { - 'currentHolder': user, + 'currentHolder': alice, 'right': { 'license': 'http://www.ascribe.io/terms', }, @@ -124,7 +124,7 @@ def test_create_right(client, user): '@type': 'Right', 'allowedBy': payload['sourceRightId'], 'license': 'http://www.ascribe.io/terms', - } + }, } resp = client.post(url_for('right_views.rightapi'), @@ -138,9 +138,9 @@ def test_create_right(client, user): assert resp.status_code == 200 -def test_create_right_missing_single_attribute(client, user): +def test_create_right_missing_single_attribute(client, alice): payload = { - 'currentHolder': user, + 'currentHolder': alice, 'right': { 'notALicense': 'this is not a license', }, @@ -154,9 +154,9 @@ def test_create_right_missing_single_attribute(client, user): "'`license` must be provided'" -def test_create_right_missing_argument_in_body(client, user): +def test_create_right_missing_argument_in_body(client, alice): payload = { - 'currentHolder': user, + 'currentHolder': alice, 'right': { 'license': 'http://www.ascribe.io/terms', }, From dae599a8954bcdd337dd367494eb626f9aadaa8b Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Wed, 5 Oct 2016 13:37:00 +0200 Subject: [PATCH 09/13] Implement Rights transfers --- tests/conftest.py | 27 +++++++++++++++++++++ tests/test_api.py | 58 +++++++++++++++++++++++++++++++++++++++++++++ web/views/rights.py | 47 +++++++++++++++++++++++++++++++++++- 3 files changed, 131 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 59085d1..85e4809 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -18,3 +18,30 @@ def alice(client): @pytest.fixture def bob(client): return client.post(url_for('user_views.userapi')).json + + +@pytest.fixture +def carly(client): + return client.post(url_for('user_views.userapi')).json + + +@pytest.fixture +def created_derived_right_with_mock_source(client, alice): + import json + from time import sleep + + payload = { + 'currentHolder': alice, + 'right': { + 'license': 'http://www.ascribe.io/terms', + }, + 'sourceRightId': 'mockId', + } + + resp = client.post(url_for('right_views.rightapi'), + data=json.dumps(payload), + headers={'Content-Type': 'application/json'}) + + # Sleep for a bit to let the transaction become valid + sleep(3) + return resp.json['right'] diff --git a/tests/test_api.py b/tests/test_api.py index b104b1f..876fc47 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -167,3 +167,61 @@ def test_create_right_missing_argument_in_body(client, alice): assert resp.status_code == 400 assert resp.json['message']['sourceRightId'] == \ 'Missing required parameter in the JSON body' + + +def test_transfer_right(client, alice, bob, carly, + created_derived_right_with_mock_source): + from time import sleep + + payload = { + 'rightId': created_derived_right_with_mock_source['@id'], + 'rightsAssignment': { + 'action': 'loan', + }, + 'currentHolder': alice, + 'to': bob, + } + + expected = { + 'rightsAssignment': { + '@context': ['', 'http://schema.org/'], + '@type': 'RightsTransferAction', + '@id': '', + 'action': 'loan', + }, + } + + resp = client.post(url_for('right_views.righttransferapi'), + data=json.dumps(payload), + headers={'Content-Type': 'application/json'}) + assert resp.status_code == 200 + assert resp.json == expected + + # Test re-transfer, after waiting for the first transfer to become valid + sleep(3) + retransfer_payload = { + 'rightId': created_derived_right_with_mock_source['@id'], + 'rightsAssignment': { + 'action': 'reloan', + }, + 'currentHolder': bob, + 'to': { + 'publicKey': carly['publicKey'], + 'privateKey': None, + } + } + + retransfer_expected = { + 'rightsAssignment': { + '@context': ['', 'http://schema.org/'], + '@type': 'RightsTransferAction', + '@id': '', + 'action': 'reloan', + }, + } + + resp = client.post(url_for('right_views.righttransferapi'), + data=json.dumps(retransfer_payload), + headers={'Content-Type': 'application/json'}) + assert resp.status_code == 200 + assert resp.json == retransfer_expected diff --git a/web/views/rights.py b/web/views/rights.py index 65dbb89..861625e 100644 --- a/web/views/rights.py +++ b/web/views/rights.py @@ -1,7 +1,7 @@ from flask import Blueprint from flask_restful import reqparse, Resource, Api -from coalaip import CoalaIp +from coalaip import CoalaIp, ModelDataError, entities from coalaip_bigchaindb.plugin import Plugin from web.models import right_model, user_model from web.utils import get_bigchaindb_api_url @@ -42,4 +42,49 @@ def post(self): return res +class RightTransferApi(Resource): + def post(self): + parser = reqparse.RequestParser() + parser.add_argument('rightId', type=str, required=True, + location='json') + parser.add_argument('currentHolder', type=user_model, required=True, + location='json') + parser.add_argument('to', type=user_model, required=True, + location='json') + parser.add_argument('rightsAssignment', type=dict, location='json') + args = parser.parse_args() + + right_id = args['rightId'] + current_holder = args['currentHolder'] + to = args['to'] + rights_assignment = args['rightsAssignment'] + + for user in [current_holder, to]: + user['public_key'] = user.pop('publicKey') + user['private_key'] = user.pop('privateKey') + + # We can't be sure of the type of Right that's given by using just the + # id, so let's assume it's a normal Right first before trying to make a + # Copyright + try: + right = entities.Right.from_persist_id(right_id, + plugin=coalaip.plugin, + force_load=True) + except ModelDataError: + right = entities.Copyright.from_persist_id(right_id, + plugin=coalaip.plugin, + force_load=True) + + res = coalaip.transfer_right(right=right, + rights_assignment_data=rights_assignment, + current_holder=current_holder, + to=to) + + res = {'rightsAssignment': res.to_jsonld()} + + return res + + right_api.add_resource(RightApi, '/rights', strict_slashes=False) +right_api.add_resource(RightTransferApi, '/rights/transfer', + strict_slashes=False) From 32c27b833d0e0a67604d1dfd6c6565be1fd9af73 Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Wed, 5 Oct 2016 13:48:47 +0200 Subject: [PATCH 10/13] Add documentation for rights transfers --- README.md | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cfc44f8..22d185e 100644 --- a/README.md +++ b/README.md @@ -281,7 +281,7 @@ PAYLOAD: "publicKey": "", "privateKey": "" }, - "sourceRightId": "" + "sourceRightId": "" } RETURNS: @@ -289,7 +289,7 @@ RETURNS: "right": { "@id": "", "@type": "Right", - "allowedBy": , + "allowedBy": "", "license": "", } } @@ -298,3 +298,44 @@ RETURNS: To check if your POST was successful, follow the steps in [registering a manifestation](#was-my-post-to-manifestations-successful) and use the returned Right's data instead. + + +### Transfer a Right + +You may only transfer a Right that you are currently holding. RightsAssignment +entities are automatically created for each transfer and may include additional, +arbitrary attributes if a `rightsAssignment` dict is given in the payload. + +``` +POST /api/v1/rights/transfer +HEADERS {"Content-Type": "application/json"} + +PAYLOAD: +{ + "rightId": "", + "rightsAssignment": { + ... + }, + "currentHolder": { + "publicKey": "", + "privateKey": "" + }, + "to": { + "publicKey": "", + "privateKey": "" + } +} + +RETURNS: +{ + "rightsAssignment": { + "@id": "", + "@type": "RightsTransferAction", + ... (provided `rightsAssignment`) + } +} +``` + +To check if your POST was successful, follow the steps in [registering a +manifestation](#was-my-post-to-manifestations-successful) and use the returned +Right's data instead. From 5be8368b40e5f797d11e54c5211246f8eb354f3b Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Fri, 10 Feb 2017 11:20:14 +0100 Subject: [PATCH 11/13] Avoid passing secret details of recipient in transfer --- README.md | 6 +++++- tests/test_api.py | 5 ++++- web/models.py | 1 + web/utils.py | 2 +- web/views/rights.py | 4 ++-- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 22d185e..f06cd7b 100644 --- a/README.md +++ b/README.md @@ -322,7 +322,7 @@ PAYLOAD: }, "to": { "publicKey": "", - "privateKey": "" + "privateKey": null } } @@ -336,6 +336,10 @@ RETURNS: } ``` +Note that the `to` field in the payload may avoid specifying the new holder's +private details (i.e. `signingKey`), but should still provide the keys needed to +conform to the [user model](#create-users). + To check if your POST was successful, follow the steps in [registering a manifestation](#was-my-post-to-manifestations-successful) and use the returned Right's data instead. diff --git a/tests/test_api.py b/tests/test_api.py index 876fc47..ae774c8 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -179,7 +179,10 @@ def test_transfer_right(client, alice, bob, carly, 'action': 'loan', }, 'currentHolder': alice, - 'to': bob, + 'to': { + 'publicKey': bob['publicKey'], + 'privateKey': None, + } } expected = { diff --git a/web/models.py b/web/models.py index 397bf40..2217443 100644 --- a/web/models.py +++ b/web/models.py @@ -2,6 +2,7 @@ user_model = parse_model(['publicKey', 'privateKey']) +public_user_model = parse_model(['publicKey']) manifestation_model = parse_model(['name', 'datePublished', 'url']) work_model = parse_model(['name', 'author']) right_model = parse_model(['license']) diff --git a/web/utils.py b/web/utils.py index 2a661a5..8ea95f3 100644 --- a/web/utils.py +++ b/web/utils.py @@ -35,7 +35,7 @@ def _parse_model(inputs): value = inputs[field] except KeyError: raise KeyError('`{}` must be provided'.format(field)) - if bool(value) is not True: + if not value: raise ValueError("`{}`'s value must be defined".format(field)) return inputs return _parse_model diff --git a/web/views/rights.py b/web/views/rights.py index 861625e..e94ba88 100644 --- a/web/views/rights.py +++ b/web/views/rights.py @@ -3,7 +3,7 @@ from coalaip import CoalaIp, ModelDataError, entities from coalaip_bigchaindb.plugin import Plugin -from web.models import right_model, user_model +from web.models import right_model, public_user_model, user_model from web.utils import get_bigchaindb_api_url @@ -49,7 +49,7 @@ def post(self): location='json') parser.add_argument('currentHolder', type=user_model, required=True, location='json') - parser.add_argument('to', type=user_model, required=True, + parser.add_argument('to', type=public_user_model, required=True, location='json') parser.add_argument('rightsAssignment', type=dict, location='json') args = parser.parse_args() From 9c18d2ac58023492c87fb0954ffba2e6cc5195d4 Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Tue, 14 Feb 2017 17:19:34 +0100 Subject: [PATCH 12/13] Upgrade as per changes in COALA IP spec --- README.md | 7 +++---- tests/test_api.py | 5 ++--- web/views/rights.py | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index f06cd7b..79a1cee 100644 --- a/README.md +++ b/README.md @@ -193,7 +193,7 @@ RETURNS: { "work": { "@id": "", - "@type": "CreativeWork", + "@type": "AbstractWork", "name": "The Lord of the Rings Trilogy", "author": "J. R. R. Tolkien" }, @@ -203,8 +203,7 @@ RETURNS: "name": "The Fellowship of the Ring", "manifestationOfWork": "", "datePublished": "29-07-1954", - "url": "", - "isManifestation": true + "url": "" }, "copyright": { "@id": "", @@ -289,7 +288,7 @@ RETURNS: "right": { "@id": "", "@type": "Right", - "allowedBy": "", + "source": "", "license": "", } } diff --git a/tests/test_api.py b/tests/test_api.py index ae774c8..b12913b 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -27,7 +27,7 @@ def test_create_manifestation(client, alice): expected = { 'work': { '@context': ['', 'http://schema.org/'], - '@type': 'CreativeWork', + '@type': 'AbstractWork', 'name': 'The Lord of the Rings Triology', 'author': 'J. R. R. Tolkien', }, @@ -37,7 +37,6 @@ def test_create_manifestation(client, alice): 'name': 'The Fellowship of the Ring', 'datePublished': '29-07-1954', 'url': 'http://localhost/lordoftherings.txt', - 'isManifestation': True, }, 'copyright': { '@context': ['', 'http://schema.org/'], @@ -122,7 +121,7 @@ def test_create_right(client, alice): 'right': { '@context': ['', 'http://schema.org/'], '@type': 'Right', - 'allowedBy': payload['sourceRightId'], + 'source': payload['sourceRightId'], 'license': 'http://www.ascribe.io/terms', }, } diff --git a/web/views/rights.py b/web/views/rights.py index e94ba88..d8bfabd 100644 --- a/web/views/rights.py +++ b/web/views/rights.py @@ -26,7 +26,7 @@ def post(self): source_right_id = args['sourceRightId'] right = args['right'] - right['allowedBy'] = source_right_id + right['source'] = source_right_id current_holder = args['currentHolder'] current_holder['public_key'] = current_holder.pop('publicKey') From 25e0e3f3a8accfe329cf64247ec96b7c579c0d4e Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Thu, 16 Feb 2017 17:37:26 +0100 Subject: [PATCH 13/13] Fix tests to comply with pycoalaip's stricter `derived_right()` sanity checking --- README.md | 4 ++-- dependency_links.txt | 4 ++-- tests/conftest.py | 32 ++++++++++++++++++++++++++++++-- tests/test_api.py | 13 ++++++++----- 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 79a1cee..99e15c1 100644 --- a/README.md +++ b/README.md @@ -264,8 +264,8 @@ part of the new Right's chain of provenance. Note that the attributes for the `right` may be much more diverse; see the COALA IP models definition (not yet publicly available yet). -Also see transferring a Right on how to transfer a registered Right to new -holders. +Also see [transferring a Right](#transfer-a-right) on how to transfer a +registered Right to new holders. ``` POST /api/v1/rights/ diff --git a/dependency_links.txt b/dependency_links.txt index 66079bc..348e848 100644 --- a/dependency_links.txt +++ b/dependency_links.txt @@ -1,4 +1,4 @@ --process-dependency-links -git+https://github.com/bigchaindb/pycoalaip.git#egg=coalaip -git+https://github.com/bigchaindb/pycoalaip-bigchaindb.git@10-support-transfers#egg=coalaip-bigchaindb +git+https://github.com/bigchaindb/pycoalaip.git@check-rights-holder-consistent#egg=coalaip +git+https://github.com/bigchaindb/pycoalaip-bigchaindb.git@add-is-same-user#egg=coalaip-bigchaindb diff --git a/tests/conftest.py b/tests/conftest.py index 85e4809..b25309a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -26,16 +26,44 @@ def carly(client): @pytest.fixture -def created_derived_right_with_mock_source(client, alice): +def created_manifestation_resp(client, alice): + import json + from time import sleep + payload = { + 'manifestation': { + 'name': 'The Fellowship of the Ring', + 'datePublished': '29-07-1954', + 'url': 'http://localhost/lordoftherings.txt', + }, + 'copyrightHolder': alice, + 'work': { + 'name': 'The Lord of the Rings Triology', + 'author': 'J. R. R. Tolkien', + }, + } + + resp = client.post(url_for('manifestation_views.manifestationapi'), + data=json.dumps(payload), + headers={'Content-Type': 'application/json'}) + + # Sleep for a bit to let the transaction become valid + sleep(3) + return resp.json + + +@pytest.fixture +def created_derived_right(client, alice, created_manifestation_resp): import json from time import sleep + copyright_id = created_manifestation_resp['copyright']['@id'] + copyright_id = copyright_id.split('../rights/')[1] payload = { 'currentHolder': alice, 'right': { 'license': 'http://www.ascribe.io/terms', }, - 'sourceRightId': 'mockId', + 'sourceRightId': copyright_id, } resp = client.post(url_for('right_views.rightapi'), diff --git a/tests/test_api.py b/tests/test_api.py index b12913b..42cf964 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -108,13 +108,16 @@ def test_create_manifestation_missing_argument_in_body(client): 'Missing required parameter in the JSON body' -def test_create_right(client, alice): +def test_create_right(client, alice, created_manifestation_resp): + copyright_id = created_manifestation_resp['copyright']['@id'] + copyright_id = copyright_id.split('../rights/')[1] + payload = { 'currentHolder': alice, 'right': { 'license': 'http://www.ascribe.io/terms', }, - 'sourceRightId': 'mockId', + 'sourceRightId': copyright_id, } expected = { @@ -169,11 +172,11 @@ def test_create_right_missing_argument_in_body(client, alice): def test_transfer_right(client, alice, bob, carly, - created_derived_right_with_mock_source): + created_derived_right): from time import sleep payload = { - 'rightId': created_derived_right_with_mock_source['@id'], + 'rightId': created_derived_right['@id'], 'rightsAssignment': { 'action': 'loan', }, @@ -202,7 +205,7 @@ def test_transfer_right(client, alice, bob, carly, # Test re-transfer, after waiting for the first transfer to become valid sleep(3) retransfer_payload = { - 'rightId': created_derived_right_with_mock_source['@id'], + 'rightId': created_derived_right['@id'], 'rightsAssignment': { 'action': 'reloan', },