From 7d0f94e80d143428da436ef57c7c9f64c24cc427 Mon Sep 17 00:00:00 2001 From: Brett Sun <qisheng.brett.sun@gmail.com> Date: Thu, 16 Feb 2017 18:31:47 +0100 Subject: [PATCH 1/2] Add GET for Manifestations, Copyrights, Rights, and Works --- tests/conftest.py | 4 +- tests/test_api.py | 76 ++++++++++++++++++++++++++++++++++--- web/server.py | 2 + web/views/manifestations.py | 13 ++++++- web/views/rights.py | 37 +++++++++++------- web/views/works.py | 22 +++++++++++ 6 files changed, 131 insertions(+), 23 deletions(-) create mode 100644 web/views/works.py diff --git a/tests/conftest.py b/tests/conftest.py index 937064f..26d7864 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -42,7 +42,7 @@ def created_manifestation_resp(client, alice): }, } - resp = client.post(url_for('manifestation_views.manifestationapi'), + resp = client.post(url_for('manifestation_views.manifestationlistapi'), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) @@ -66,7 +66,7 @@ def created_derived_right(client, alice, created_manifestation_resp): 'sourceRightId': copyright_id, } - resp = client.post(url_for('right_views.rightapi'), + resp = client.post(url_for('right_views.rightlistapi'), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) diff --git a/tests/test_api.py b/tests/test_api.py index 602fe4e..73b5676 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -43,7 +43,7 @@ def test_create_manifestation(client, alice): '@type': 'Copyright', }, } - resp = client.post(url_for('manifestation_views.manifestationapi'), + resp = client.post(url_for('manifestation_views.manifestationlistapi'), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) resp_dict = resp.json @@ -82,7 +82,7 @@ def test_create_manifestation_missing_single_attribute(client, alice): 'author': 'J. R. R. Tolkien', }, } - resp = client.post(url_for('manifestation_views.manifestationapi'), + resp = client.post(url_for('manifestation_views.manifestationlistapi'), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) # TODO: I really don't know why flask_restful includes the extra '' in the @@ -100,7 +100,7 @@ def test_create_manifestation_missing_argument_in_body(client): 'datePublished': '29-07-1954', }, } - resp = client.post(url_for('manifestation_views.manifestationapi'), + resp = client.post(url_for('manifestation_views.manifestationlistapi'), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) assert resp.status_code == 400 @@ -108,6 +108,39 @@ def test_create_manifestation_missing_argument_in_body(client): 'Missing required parameter in the JSON body' +def test_get_manifestation(client, alice, created_manifestation_resp): + manifestation = created_manifestation_resp['manifestation'] + resp = client.get( + url_for('manifestation_views.manifestationapi', + entity_id=manifestation['@id'])) + + assert resp.status_code == 200 + + resp_manifestation = resp.json + # The Manifestation we get back has its @id set to "" as it could be found + # at that location + resp_manifestation.pop('@id') + manifestation.pop('@id') + + assert resp_manifestation == manifestation + + +def test_get_work(client, alice, created_manifestation_resp): + work = created_manifestation_resp['work'] + work_id = work['@id'].split('../works/')[1] + resp = client.get(url_for('work_views.workapi', entity_id=work_id)) + + assert resp.status_code == 200 + + resp_work = resp.json + # The Work we get back has its @id set to "" as it could be found + # at that location + resp_work.pop('@id') + work.pop('@id') + + assert resp_work == work + + def test_create_right(client, alice, created_manifestation_resp): copyright_id = created_manifestation_resp['copyright']['@id'] copyright_id = copyright_id.split('../rights/')[1] @@ -129,7 +162,7 @@ def test_create_right(client, alice, created_manifestation_resp): }, } - resp = client.post(url_for('right_views.rightapi'), + resp = client.post(url_for('right_views.rightlistapi'), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) resp_dict = resp.json @@ -148,7 +181,7 @@ def test_create_right_missing_single_attribute(client, alice): }, 'sourceRightId': 'mockId', } - resp = client.post(url_for('right_views.rightapi'), + resp = client.post(url_for('right_views.rightlistapi'), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) assert resp.status_code == 400 @@ -163,7 +196,7 @@ def test_create_right_missing_argument_in_body(client, alice): 'license': 'http://www.ascribe.io/terms', }, } - resp = client.post(url_for('right_views.rightapi'), + resp = client.post(url_for('right_views.rightlistapi'), data=json.dumps(payload), headers={'Content-Type': 'application/json'}) assert resp.status_code == 400 @@ -171,6 +204,37 @@ def test_create_right_missing_argument_in_body(client, alice): 'Missing required parameter in the JSON body' +def test_get_right_from_copyright(client, alice, created_manifestation_resp): + copyright = created_manifestation_resp['copyright'] + copyright_id = copyright['@id'].split('../rights/')[1] + resp = client.get(url_for('right_views.rightapi', entity_id=copyright_id)) + + assert resp.status_code == 200 + + resp_copyright = resp.json + # The Copyright we get back has its @id set to "" as it could be found + # at that location + resp_copyright.pop('@id') + copyright.pop('@id') + + assert resp_copyright == copyright + + +def test_get_right_from_right(client, alice, created_derived_right): + resp = client.get(url_for('right_views.rightapi', + entity_id=created_derived_right['@id'])) + + assert resp.status_code == 200 + + resp_right = resp.json + # The Right we get back has its @id set to "" as it could be found + # at that location + resp_right.pop('@id') + created_derived_right.pop('@id') + + assert resp_right == created_derived_right + + def test_transfer_right(client, alice, bob, carly, created_derived_right): payload = { 'rightId': created_derived_right['@id'], diff --git a/web/server.py b/web/server.py index 726083e..38ea8cd 100644 --- a/web/server.py +++ b/web/server.py @@ -15,6 +15,7 @@ from web.views.users import user_views from web.views.manifestations import manifestation_views from web.views.rights import right_views +from web.views.works import work_views class StandaloneApplication(gunicorn.app.base.BaseApplication): @@ -67,6 +68,7 @@ def create_app(settings): app.register_blueprint(user_views, url_prefix='/api/v1') app.register_blueprint(manifestation_views, url_prefix='/api/v1') app.register_blueprint(right_views, url_prefix='/api/v1') + app.register_blueprint(work_views, url_prefix='/api/v1') return app diff --git a/web/views/manifestations.py b/web/views/manifestations.py index 910c72c..7b03ef3 100644 --- a/web/views/manifestations.py +++ b/web/views/manifestations.py @@ -1,7 +1,7 @@ from flask import Blueprint from flask_restful import reqparse, Resource, Api -from coalaip import CoalaIp +from coalaip import CoalaIp, entities from coalaip_bigchaindb.plugin import Plugin from web.models import manifestation_model, user_model, work_model from web.utils import get_bigchaindb_api_url @@ -14,6 +14,13 @@ class ManifestationApi(Resource): + def get(self, entity_id): + manifestation = entities.Manifestation.from_persist_id( + entity_id, plugin=coalaip.plugin, force_load=True) + return manifestation.to_jsonld() + + +class ManifestationListApi(Resource): def post(self): parser = reqparse.RequestParser() parser.add_argument('manifestation', type=manifestation_model, @@ -51,5 +58,7 @@ def post(self): return res -manifestation_api.add_resource(ManifestationApi, '/manifestations', +manifestation_api.add_resource(ManifestationApi, '/manifestations/<entity_id>', + strict_slashes=False) +manifestation_api.add_resource(ManifestationListApi, '/manifestations', strict_slashes=False) diff --git a/web/views/rights.py b/web/views/rights.py index d9e5edb..14e3460 100644 --- a/web/views/rights.py +++ b/web/views/rights.py @@ -13,7 +13,28 @@ right_api = Api(right_views) +def load_right(entity_id): + # 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(entity_id, + plugin=coalaip.plugin, + force_load=True) + except ModelDataError: + right = entities.Copyright.from_persist_id(entity_id, + plugin=coalaip.plugin, + force_load=True) + return right + + class RightApi(Resource): + def get(self, entity_id): + right = load_right(entity_id) + return right.to_jsonld() + + +class RightListApi(Resource): def post(self): parser = reqparse.RequestParser() parser.add_argument('right', type=right_model, required=True, @@ -78,18 +99,7 @@ def post(self): 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) - + right = load_right(right_id) res = coalaip.transfer_right(right=right, rights_assignment_data=rights_assignment, current_holder=current_holder, @@ -100,8 +110,9 @@ def post(self): return res -right_api.add_resource(RightApi, '/rights', strict_slashes=False) +right_api.add_resource(RightApi, '/rights/<entity_id>', strict_slashes=False) right_api.add_resource(RightHistoryApi, '/rights/history/<string:right_id>', strict_slashes=False) +right_api.add_resource(RightListApi, '/rights', strict_slashes=False) right_api.add_resource(RightTransferApi, '/rights/transfer', strict_slashes=False) diff --git a/web/views/works.py b/web/views/works.py new file mode 100644 index 0000000..c2db32c --- /dev/null +++ b/web/views/works.py @@ -0,0 +1,22 @@ +from flask import Blueprint +from flask_restful import Resource, Api + +from coalaip import CoalaIp, entities +from coalaip_bigchaindb.plugin import Plugin +from web.utils import get_bigchaindb_api_url + + +coalaip = CoalaIp(Plugin(get_bigchaindb_api_url())) + +work_views = Blueprint('work_views', __name__) +work_api = Api(work_views) + + +class WorkApi(Resource): + def get(self, entity_id): + work = entities.Work.from_persist_id( + entity_id, plugin=coalaip.plugin, force_load=True) + return work.to_jsonld() + + +work_api.add_resource(WorkApi, '/work/<entity_id>', strict_slashes=False) From 1f97c3099b941273b5cc09c8186378b3b9962ab2 Mon Sep 17 00:00:00 2001 From: Brett Sun <qisheng.brett.sun@gmail.com> Date: Thu, 16 Feb 2017 18:34:45 +0100 Subject: [PATCH 2/2] Add docs for GET requests --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 82c9520..2e50efe 100644 --- a/README.md +++ b/README.md @@ -253,6 +253,10 @@ To check if your POST was successful, try validating by doing the following: or +1. Try to [request for the entity with a GET request](#requesting-submitted-entities) + +or + 1. Open your browser and go to `http://localhost:9984/api/v1` (your locally running BigchainDB instance - if using the default Docker settings, use port `32768` instead). @@ -366,6 +370,17 @@ manifestation](#was-my-post-to-manifestations-successful) and use the returned Right's data instead. +### Requesting Submitted Entities + +You can retrieve any submitted entities via a GET to one of the following +endpoints with the entity's ID (usually the `@id` stripped of any relative URL +artifacts): + +* `/manifestations/<manifestation_id>`: Manifestations +* `/works/<work_id>`: Works +* `/rights/<right_id>`: Copyrights and Rights + + ### Querying the ownership history of a Right The ownership history of a Right is represented as an time-series array of