Add 'DatastoreRecord.delete' method w/ tests

Remove the unused '_fields' instance var on DatastoreRecord
pull/2099/head
tuxxy 2020-06-25 03:53:06 +02:00
parent 662fddbeb7
commit 17920cd4ca
2 changed files with 58 additions and 27 deletions

View File

@ -63,7 +63,6 @@ class DatastoreRecord:
record_id: Union[int, str],
writeable: bool = False) -> None:
self._record_id = record_id
self._fields = [field[1:] for field in type(self).__dict__ if type(type(self).__dict__[field]) == RecordField]
self.__db_tx = db_tx
self.__writeable = writeable
@ -100,6 +99,11 @@ class DatastoreRecord:
# A datastore record is only mutated iff writeable is True.
elif self.__writeable is True:
record_field = self.__get_record_field(attr)
# We delete records by setting the record to `None`.
if value is None:
return self.__delete_record(attr)
if not type(value) == record_field.field_type:
raise TypeError(f'Given record is type {type(value)}; expected {record_field.field_type}')
field_value = msgpack.packb(record_field.encode(value))
@ -147,7 +151,16 @@ class DatastoreRecord:
"""
key = self.__storagekey.format(record_field=record_field, record_id=self._record_id).encode()
if not self.__db_tx.put(key, value, overwrite=True):
raise DBWriteError("Couldn't write the record to the database.")
raise DBWriteError(f"Couldn't write the record (key: {key}) to the database.")
def __delete_record(self, record_field: str) -> None:
"""
Deletes the record from the datastore.
"""
key = self.__storagekey.format(record_field=record_field, record_id=self._record_id).encode()
if not self.__db_tx.delete(key) and self.__db_tx.get(key) is not None:
# We do this check to ensure that the key was actually deleted.
raise DBWriteError(f"Couldn't delete the record (key: {key}) from the database.")
def __get_record_field(self, attr: str) -> 'RecordField':
"""
@ -186,3 +199,14 @@ class DatastoreRecord:
useful when comparing two records of different type.
"""
return hash(self._record_id)
def delete(self):
"""
Deletes the entire record.
This works by iterating over class variables, identifying the record fields,
and then deleting them.
"""
for class_var in type(self).__dict__:
if type(type(self).__dict__[class_var]) == RecordField:
setattr(self, class_var[1:], None)

View File

@ -25,21 +25,20 @@ from nucypher.datastore import datastore, keypairs
from nucypher.datastore.base import DatastoreRecord, RecordField
from nucypher.datastore.models import PolicyArrangement, Workorder
class TestRecord(DatastoreRecord):
_test = RecordField(bytes)
_test_date = RecordField(datetime,
encode=lambda val: datetime.isoformat(val).encode(),
decode=lambda val: datetime.fromisoformat(val.decode()))
def test_datastore():
class TestRecord(DatastoreRecord):
_test = RecordField(bytes)
_test_date = RecordField(datetime,
encode=lambda val: datetime.isoformat(val).encode(),
decode=lambda val: datetime.fromisoformat(val.decode()))
def test_datastore_describe():
temp_path = tempfile.mkdtemp()
storage = datastore.Datastore(temp_path)
assert storage.LMDB_MAP_SIZE == 1_000_000_000_000
assert storage.db_path == temp_path
assert storage._Datastore__db_env.path() == temp_path
#
# Tests for `Datastore.describe`
#
@ -153,11 +152,10 @@ def test_datastore():
with storage.describe(TestRecord, 'new_id') as new_test_record:
assert new_test_record.test == b'now it exists :)'
def test_datastore_query_by():
temp_path = tempfile.mkdtemp()
storage = datastore.Datastore(temp_path)
#
# Tests for `Datastore.query`
#
# Make two test record classes
class FooRecord(DatastoreRecord):
_foo = RecordField(bytes)
@ -254,18 +252,11 @@ def test_datastore():
assert len(records) == 'this never gets executed'
def test_datastore_record_read():
class TestRecord(DatastoreRecord):
_test = RecordField(bytes)
_test_date = RecordField(datetime,
encode=lambda val: datetime.isoformat(val).encode(),
decode=lambda val: datetime.fromisoformat(val.decode()))
db_env = lmdb.open(tempfile.mkdtemp())
with db_env.begin() as db_tx:
# Check the default attrs.
test_rec = TestRecord(db_tx, 'testing', writeable=False)
assert test_rec._record_id == 'testing'
assert test_rec._fields == ['test', 'test_date']
assert test_rec._DatastoreRecord__db_tx == db_tx
assert test_rec._DatastoreRecord__writeable == False
assert test_rec._DatastoreRecord__storagekey == 'TestRecord:{record_field}:{record_id}'
@ -284,12 +275,6 @@ def test_datastore_record_read():
def test_datastore_record_write():
class TestRecord(DatastoreRecord):
_test = RecordField(bytes)
_test_date = RecordField(datetime,
encode=lambda val: datetime.isoformat(val).encode(),
decode=lambda val: datetime.fromisoformat(val.decode()))
# Test writing
db_env = lmdb.open(tempfile.mkdtemp())
with db_env.begin(write=True) as db_tx:
@ -306,6 +291,11 @@ def test_datastore_record_write():
with pytest.raises(TypeError):
test_rec.test = 1234
# Test deleting a field
test_rec.test = None
with pytest.raises(AttributeError):
should_error = test_rec.test
# Test writing a valid field and getting it.
test_rec.test = b'good write'
assert test_rec.test == b'good write'
@ -343,6 +333,14 @@ def test_datastore_policy_arrangement_model():
assert policy_arrangement.arrangement_id == bytes.fromhex(arrangement_id_hex)
assert policy_arrangement.expiration == expiration
assert policy_arrangement.alice_verifying_key == alice_verifying_key
# Now let's `delete` it
with storage.describe(PolicyArrangement, arrangement_id_hex, writeable=True) as policy_arrangement:
policy_arrangement.delete()
# Should be deleted now.
with pytest.raises(AttributeError):
should_error = policy_arrangement.arrangement_id
def test_datastore_workorder_model():
@ -354,16 +352,25 @@ def test_datastore_workorder_model():
bob_verifying_key = bob_keypair.pubkey
bob_signature = bob_keypair.sign(b'test')
# Test create
with storage.describe(Workorder, arrangement_id_hex, writeable=True) as work_order:
work_order.arrangement_id = bytes.fromhex(arrangement_id_hex)
work_order.bob_verifying_key = bob_verifying_key
work_order.bob_signature = bob_signature
with storage.describe(Workorder, arrangement_id_hex) as work_order:
assert work_order.arrangement_id == bytes.fromhex(arrangement_id_hex)
assert work_order.bob_verifying_key == bob_verifying_key
assert work_order.bob_signature == bob_signature
# Test delete
with storage.describe(Workorder, arrangement_id_hex, writeable=True) as work_order:
work_order.delete()
# Should be deleted now.
with pytest.raises(AttributeError):
should_error = work_order.arrangement_id
def test_key_tuple():
partial_key = datastore.DatastoreKey.from_bytestring(b'TestRecord:test_field')