Merge pull request #1939 from bigchaindb/feat/integrate-metadata-api

integrate metadata api
This commit is contained in:
vrde 2017-12-20 16:01:19 +01:00 committed by GitHub
commit ab4328fa1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 168 additions and 3 deletions

View File

@ -32,6 +32,24 @@ def get_transaction(conn, transaction_id):
pass
@register_query(LocalMongoDBConnection)
def store_metadata(conn, metadata):
try:
return conn.run(
conn.collection('metadata')
.insert_many(metadata, ordered=False))
except DuplicateKeyError:
pass
@register_query(LocalMongoDBConnection)
def get_metadata(conn, transaction_ids):
return conn.run(
conn.collection('metadata')
.find({'id': {'$in': transaction_ids}},
projection={'_id': False}))
@register_query(LocalMongoDBConnection)
def store_asset(conn, asset):
try:

View File

@ -27,7 +27,7 @@ def create_database(conn, dbname):
@register_schema(LocalMongoDBConnection)
def create_tables(conn, dbname):
for table_name in ['transactions', 'assets', 'blocks']:
for table_name in ['transactions', 'assets', 'blocks', 'metadata']:
logger.info('Create `%s` table.', table_name)
# create the table
# TODO: read and write concerns can be declared here
@ -39,6 +39,7 @@ def create_indexes(conn, dbname):
create_transactions_secondary_index(conn, dbname)
create_assets_secondary_index(conn, dbname)
create_blocks_secondary_index(conn, dbname)
create_metadata_secondary_index(conn, dbname)
@register_schema(LocalMongoDBConnection)
@ -86,3 +87,15 @@ def create_assets_secondary_index(conn, dbname):
def create_blocks_secondary_index(conn, dbname):
conn.conn[dbname]['blocks']\
.create_index([('height', DESCENDING)], name='height')
def create_metadata_secondary_index(conn, dbname):
logger.info('Create `assets` secondary index.')
# the id is the txid of the transaction where metadata was defined
conn.conn[dbname]['metadata'].create_index('id',
name='transaction_id',
unique=True)
# full text search index
conn.conn[dbname]['metadata'].create_index([('$**', TEXT)], name='text')

View File

@ -33,6 +33,20 @@ def store_asset(connection, asset):
raise NotImplementedError
@singledispatch
def store_metadata(connection, metadata):
"""Write metadata to the metadata table.
Args:
metadata (dict): transaction metadata.
Returns:
The result of the operation.
"""
raise NotImplementedError
@singledispatch
def store_transaction(connection, signed_transaction):
"""Same as write_transaction."""
@ -331,11 +345,11 @@ def get_assets(connection, asset_ids):
@singledispatch
def get_metadata(connection, txn_ids):
def get_metadata(connection, transaction_ids):
"""Get a list of metadata from the metadata table.
Args:
txn_ids (list): a list of ids for the metadata to be retrieved from
transaction_ids (list): a list of ids for the metadata to be retrieved from
the database.
Returns:

View File

@ -51,11 +51,18 @@ class BigchainDB(Bigchain):
if asset['data']:
backend.query.store_asset(self.connection, asset)
metadata = transaction.pop('metadata')
transaction_metadata = {'id': transaction['id'],
'metadata': metadata}
backend.query.store_metadata(self.connection, [transaction_metadata])
return backend.query.store_transaction(self.connection, transaction)
def get_transaction(self, transaction_id, include_status=False):
transaction = backend.query.get_transaction(self.connection, transaction_id)
asset = backend.query.get_asset(self.connection, transaction_id)
metadata = backend.query.get_metadata(self.connection, [transaction_id])
if transaction:
if asset:
@ -63,6 +70,13 @@ class BigchainDB(Bigchain):
else:
transaction['asset'] = {'data': None}
if 'metadata' not in transaction:
metadata = metadata[0] if metadata else None
if metadata:
metadata = metadata.get('metadata')
transaction.update({'metadata': metadata})
transaction = Transaction.from_dict(transaction)
if include_status:

View File

@ -77,6 +77,49 @@ def test_text_search():
test_text_search('assets')
def test_write_metadata():
from bigchaindb.backend import connect, query
conn = connect()
metadata = [
{'id': 1, 'data': '1'},
{'id': 2, 'data': '2'},
{'id': 3, 'data': '3'}
]
# write the assets
query.store_metadata(conn, deepcopy(metadata))
# check that 3 assets were written to the database
cursor = conn.db.metadata.find({}, projection={'_id': False})\
.sort('id', pymongo.ASCENDING)
assert cursor.count() == 3
assert list(cursor) == metadata
def test_get_metadata():
from bigchaindb.backend import connect, query
conn = connect()
metadata = [
{'id': 1, 'metadata': None},
{'id': 2, 'metadata': {'key': 'value'}},
{'id': 3, 'metadata': '3'},
]
conn.db.metadata.insert_many(deepcopy(metadata), ordered=False)
for meta in metadata:
assert query.get_metadata(conn, [meta['id']])
def test_text_metadata():
from ..mongodb.test_queries import test_text_search
test_text_search('metadata')
def test_get_owned_ids(signed_create_tx, user_pk):
from bigchaindb.backend import connect, query
conn = connect()

View File

@ -3,6 +3,7 @@ import pytest
METADATA_ENDPOINT = '/api/v1/metadata/'
@pytest.mark.tendermint
def test_get_metadata_with_empty_text_search(client):
res = client.get(METADATA_ENDPOINT + '?search=')
assert res.json == {'status': 400,
@ -10,6 +11,7 @@ def test_get_metadata_with_empty_text_search(client):
assert res.status_code == 400
@pytest.mark.tendermint
def test_get_metadata_with_missing_text_search(client):
res = client.get(METADATA_ENDPOINT)
assert res.status_code == 400
@ -85,3 +87,64 @@ def test_get_metadata_limit(client, b):
res = client.get(METADATA_ENDPOINT + '?search=meta&limit=1')
assert res.status_code == 200
assert len(res.json) == 1
@pytest.mark.bdb
@pytest.mark.tendermint
def test_get_metadata_tendermint(client, tb):
from bigchaindb.models import Transaction
b = tb
# test returns empty list when no assets are found
res = client.get(METADATA_ENDPOINT + '?search=abc')
assert res.json == []
assert res.status_code == 200
# create asset
asset = {'msg': 'abc'}
metadata = {'key': 'my_meta'}
tx = Transaction.create([b.me], [([b.me], 1)], metadata=metadata,
asset=asset).sign([b.me_private])
b.store_transaction(tx)
# test that metadata is returned
res = client.get(METADATA_ENDPOINT + '?search=my_meta')
assert res.status_code == 200
assert len(res.json) == 1
assert res.json[0] == {
'metadata': {'key': 'my_meta'},
'id': tx.id
}
@pytest.mark.bdb
@pytest.mark.tendermint
def test_get_metadata_limit_tendermint(client, tb):
from bigchaindb.models import Transaction
b = tb
# create two assets
asset1 = {'msg': 'abc 1'}
meta1 = {'key': 'meta 1'}
tx1 = Transaction.create([b.me], [([b.me], 1)], metadata=meta1,
asset=asset1).sign([b.me_private])
b.store_transaction(tx1)
asset2 = {'msg': 'abc 2'}
meta2 = {'key': 'meta 2'}
tx2 = Transaction.create([b.me], [([b.me], 1)], metadata=meta2,
asset=asset2).sign([b.me_private])
b.store_transaction(tx2)
# test that both assets are returned without limit
res = client.get(METADATA_ENDPOINT + '?search=meta')
assert res.status_code == 200
assert len(res.json) == 2
# test that only one asset is returned when using limit=1
res = client.get(METADATA_ENDPOINT + '?search=meta&limit=1')
assert res.status_code == 200
assert len(res.json) == 1