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