Skip to content

Commit

Permalink
31 delete (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
hitz authored and caseylitton committed Jan 8, 2018
1 parent 34fc45f commit ba8e6e4
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 3 deletions.
30 changes: 27 additions & 3 deletions src/snovault/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
collections,
scoped_session,
sessionmaker,
backref,
)
from sqlalchemy.orm.exc import (
FlushError,
Expand Down Expand Up @@ -167,6 +168,25 @@ def update(self, model, properties=None, sheets=None, unique_keys=None, links=No
msg = 'Keys conflict: %r' % conflicts
raise HTTPConflict(msg)

def delete_by_uuid(self, rid):
# WARNING USE WITH CARE PERMANENTLY DELETES RESOURCES
session = self.DBSession()
sp = session.begin_nested()
model = self.get_by_uuid(rid)
try:
for current_propsheet in model.data.values():
# delete the propsheet history
for propsheet in current_propsheet.history:
session.delete(propsheet)
# now delete the currentPropsheet
session.delete(current_propsheet)
# now delete the resource, keys and links(via cascade)
session.delete(model)
sp.commit()
except Exception as e:
sp.rollback()
raise e

def _update_properties(self, model, properties, sheets=None):
if properties is not None:
model.propsheets[''] = properties
Expand Down Expand Up @@ -364,7 +384,7 @@ class Key(Base):
nullable=False, index=True)

# Be explicit about dependencies to the ORM layer
resource = orm.relationship('Resource', backref='unique_keys')
resource = orm.relationship('Resource', backref=backref('unique_keys', cascade='all, delete-orphan'))


class Link(Base):
Expand All @@ -379,9 +399,9 @@ class Link(Base):
index=True) # Single column index for reverse lookup

source = orm.relationship(
'Resource', foreign_keys=[source_rid], backref='rels')
'Resource', foreign_keys=[source_rid], backref=backref('rels', cascade='all, delete-orphan'))
target = orm.relationship(
'Resource', foreign_keys=[target_rid], backref='revs')
'Resource', foreign_keys=[target_rid], backref=backref('revs', cascade='all, delete-orphan'))


class PropertySheet(Base):
Expand Down Expand Up @@ -433,6 +453,9 @@ class CurrentPropertySheet(Base):
viewonly=True,
)
resource = orm.relationship('Resource')
__mapper_args__ = {
'confirm_deleted_rows': False,
}


class Resource(Base):
Expand Down Expand Up @@ -528,6 +551,7 @@ class TransactionRecord(Base):
import cryptacular.bcrypt
crypt = cryptacular.bcrypt.BCRYPTPasswordManager()


def hash_password(password):
return crypt.encode(password)

Expand Down
79 changes: 79 additions & 0 deletions src/snovault/tests/test_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,85 @@ def test_current_propsheet_update(session):
assert current.sid


def test_delete_simple(session, storage):
from snovault.storage import (
Resource,
Key,
PropertySheet,
CurrentPropertySheet,
TransactionRecord,
)
name = 'testdata'
props1 = {'foo': 'bar'}
resource = Resource('test_item', {name: props1})
session.add(resource)
session.flush()
resource = session.query(Resource).one()
check = storage.get_by_uuid(str(resource.rid))
assert check[name] == props1
# add a key
testname = 'foo'
key = Key(rid=resource.rid, name=testname, value=props1[testname])
session.add(key)
session.flush()
assert session.query(Key).count() == 1

propsheet = session.query(PropertySheet).one()
assert propsheet.sid
assert propsheet.rid == resource.rid
current = session.query(CurrentPropertySheet).one()
assert current.sid == propsheet.sid
assert current.rid == resource.rid

storage.delete_by_uuid(str(resource.rid))
check_post = storage.get_by_uuid(str(resource.rid))
assert not check_post
assert session.query(Key).count() == 0
assert session.query(PropertySheet).count() == 0
assert session.query(CurrentPropertySheet).count() == 0


def test_delete_compound(session, storage):
from snovault.storage import (
CurrentPropertySheet,
Resource,
PropertySheet,
Key,
)
name = 'testdata'
props1 = {'foo': 'bar'}
resource = Resource('test_item', {name: props1})
session.add(resource)
session.flush()
resource = session.query(Resource).one()
check = storage.get_by_uuid(str(resource.rid))
assert check[name] == props1
# add a key
testname = 'foo'
key = Key(rid=resource.rid, name=testname, value=props1[testname])
session.add(key)
session.flush()
assert session.query(Key).count() == 1

props2 = {'foo': 'baz'}
resource[name] = props2
session.flush()
resource = session.query(Resource).one()
session.flush()
assert resource[name] == props2
assert session.query(PropertySheet).count() == 2
assert [propsheet.properties for propsheet in resource.data[name].history] == [props1, props2]
current = session.query(CurrentPropertySheet).one()
assert current.sid

storage.delete_by_uuid(str(resource.rid))
check_post = storage.get_by_uuid(str(resource.rid))
assert not check_post
assert session.query(Key).count() == 0
assert session.query(PropertySheet).count() == 0
assert session.query(CurrentPropertySheet).count() == 0


def test_keys(session):
from sqlalchemy.orm.exc import FlushError
from snovault.storage import (
Expand Down
67 changes: 67 additions & 0 deletions src/snowflakes/commands/delete.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""\
Migrate dataset type
"""
import logging
import transaction
from pyramid.paster import get_app
from pyramid.threadlocal import manager
from pyramid.testing import DummyRequest
from snovault.interfaces import (
STORAGE,
DBSESSION
)
from snovault.storage import (
CurrentPropertySheet,
)

EPILOG = __doc__

logger = logging.getLogger(__name__)


test_uuids = ['1b18dab4-50be-4a1d-9b95-5e9fd840c8fb']


def run(app, dry_run=False):

storage = app.registry[STORAGE].write
to_delete = test_uuids
# to_delete = storage.__iter__('talen')
for rid in to_delete:
storage.delete_by_uuid(str(rid))


def main():
import argparse
parser = argparse.ArgumentParser(
description="Migrate dataset type", epilog=EPILOG,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument('--app-name', default="app", help="Pyramid app name in configfile")
parser.add_argument('--abort', action='store_true', help="Rollback transaction")
parser.add_argument('--dry-run', action='store_true', help="Don't actually do anything")
parser.add_argument('config_uri', help="path to configfile")
args = parser.parse_args()

logging.basicConfig()
app = get_app(args.config_uri, args.app_name)
# Loading app will have configured from config file. Reconfigure here:
logging.getLogger('snowflakes').setLevel(logging.DEBUG)

raised = False
try:
run(app, args.dry_run)
except:
raised = True
raise
finally:
if raised or args.abort:
transaction.abort()
logger.info('Rolled back.')
else:
transaction.commit()


if __name__ == '__main__':
main()

0 comments on commit ba8e6e4

Please sign in to comment.