Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ownership history querying for Rights #26

Merged
merged 3 commits into from
Feb 17, 2017
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
40 changes: 37 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,9 +322,10 @@ transfer transaction recorded on BigchainDB links to that contract (via the
RightsAssignment) and provides a record of the parties, the date of the
transaction, and the contract that applies.

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.
You may only transfer a Right (or Copyright) 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
Expand Down Expand Up @@ -363,3 +364,36 @@ 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.


### Querying the ownership history of a Right

The ownership history of a Right is represented as an time-series array of
ownership events (sorted from the initial creation of the entity), each in the
form of:

```json
{
"user": {
"publicKey": "<public key of the owner>",
"privateKey": null
},
"eventId": "<transaction id of the transaction detailing the ownership event>"
}
```

Note that, as in the recipient (`to`) of a Rights transfer, the returned user
models only contain their public information.

```
GET /api/v1/rights/<ID of an existing right>
RETURNS:
[{
"user": {
"publicKey": "<public key of the owner>",
"privateKey": null
},
"eventId": "<transaction id of the transaction detailing the ownership event>"
}, ...]
```
52 changes: 52 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,55 @@ def created_derived_right(client, alice, created_manifestation_resp):
# Sleep for a bit to let the transaction become valid
sleep(3)
return resp.json['right']


@pytest.fixture
def transferred_derived_right(client, alice, bob, created_derived_right):
import json
from time import sleep

payload = {
'rightId': created_derived_right['@id'],
'rightsAssignment': {
'action': 'loan',
},
'currentHolder': alice,
'to': {
'publicKey': bob['publicKey'],
'privateKey': None,
}
}

client.post(url_for('right_views.righttransferapi'),
data=json.dumps(payload),
headers={'Content-Type': 'application/json'})

# Sleep for a bit to let the transaction become valid
sleep(3)
return created_derived_right


@pytest.fixture
def retransferred_derived_right(client, bob, carly, transferred_derived_right):
import json
from time import sleep

payload = {
'rightId': transferred_derived_right['@id'],
'rightsAssignment': {
'action': 'loan',
},
'currentHolder': bob,
'to': {
'publicKey': carly['publicKey'],
'privateKey': None,
}
}

client.post(url_for('right_views.righttransferapi'),
data=json.dumps(payload),
headers={'Content-Type': 'application/json'})

# Sleep for a bit to let the transaction become valid
sleep(3)
return transferred_derived_right
21 changes: 16 additions & 5 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,6 @@ def test_create_right_missing_argument_in_body(client, alice):


def test_transfer_right(client, alice, bob, carly, created_derived_right):
from time import sleep

payload = {
'rightId': created_derived_right['@id'],
'rightsAssignment': {
Expand Down Expand Up @@ -201,10 +199,10 @@ def test_transfer_right(client, alice, bob, carly, created_derived_right):
assert resp.status_code == 200
assert resp.json == expected

# Test re-transfer, after waiting for the first transfer to become valid
sleep(3)

def test_retransferred_right(client, bob, carly, transferred_derived_right):
retransfer_payload = {
'rightId': created_derived_right['@id'],
'rightId': transferred_derived_right['@id'],
'rightsAssignment': {
'action': 'reloan',
},
Expand All @@ -229,3 +227,16 @@ def test_transfer_right(client, alice, bob, carly, created_derived_right):
headers={'Content-Type': 'application/json'})
assert resp.status_code == 200
assert resp.json == retransfer_expected


def test_right_history(client, alice, bob, carly, retransferred_derived_right):
right_id = retransferred_derived_right['@id']
resp = client.get(
url_for('right_views.righthistoryapi', right_id=right_id))
assert resp.status_code == 200
assert resp.json[0]['user']['publicKey'] == alice['publicKey']
assert resp.json[1]['user']['publicKey'] == bob['publicKey']
assert resp.json[2]['user']['publicKey'] == carly['publicKey']

# First transaction should be the CREATE transaction
assert resp.json[0]['eventId'] == right_id
17 changes: 17 additions & 0 deletions web/views/rights.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,21 @@ def post(self):
return res


class RightHistoryApi(Resource):
def get(self, right_id):
# Don't worry about whether the entity corresponding to `right_id` is a
# Right or a Copyright since we won't be loading it
right = entities.Right.from_persist_id(right_id, plugin=coalaip.plugin,
force_load=False)
return [{
'user': {
'publicKey': event['user']['public_key'],
'privateKey': event['user']['private_key'],
},
'eventId': event['event_id'],
} for event in right.history]


class RightTransferApi(Resource):
def post(self):
parser = reqparse.RequestParser()
Expand Down Expand Up @@ -86,5 +101,7 @@ def post(self):


right_api.add_resource(RightApi, '/rights', strict_slashes=False)
right_api.add_resource(RightHistoryApi, '/rights/history/<string:right_id>',
strict_slashes=False)
right_api.add_resource(RightTransferApi, '/rights/transfer',
strict_slashes=False)