Unify Snapshot's POST view with Devicehub's; bugfixes

This commit is contained in:
Xavier Bustamante Talavera 2019-02-18 12:43:50 +01:00
parent 32e696c57c
commit 07e8be829e
12 changed files with 2647 additions and 61 deletions

View File

@ -4,7 +4,7 @@ from teal.resource import Converters, Resource
from ereuse_devicehub.resources.device.sync import Sync
from ereuse_devicehub.resources.event import schemas
from ereuse_devicehub.resources.event.views import EventView, SnapshotView
from ereuse_devicehub.resources.event.views import EventView
class EventDef(Resource):
@ -90,13 +90,14 @@ class InstallDef(EventDef):
class SnapshotDef(EventDef):
VIEW = SnapshotView
VIEW = None
SCHEMA = schemas.Snapshot
def __init__(self, app, import_name=__name__.split('.')[0], static_folder=None,
static_url_path=None,
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
url_prefix = '/{}'.format(EventDef.resource)
super().__init__(app, import_name, static_folder, static_url_path, template_folder,
url_prefix, subdomain, url_defaults, root_path, cli_commands)
self.sync = Sync()

View File

@ -12,14 +12,21 @@ from ereuse_devicehub.resources.device.models import Component, Computer
from ereuse_devicehub.resources.enums import SnapshotSoftware
from ereuse_devicehub.resources.event.models import Event, Snapshot, WorkbenchRate
SUPPORTED_WORKBENCH = StrictVersion('11.0')
class EventView(View):
def post(self):
"""Posts an event."""
json = request.get_json(validate=False)
if 'type' not in json:
if not json or 'type' not in json:
raise ValidationError('Resource needs a type.')
e = app.resources[json['type']].schema.load(json)
# todo there should be a way to better get subclassess resource
# defs
resource_def = app.resources[json['type']]
e = resource_def.schema.load(json)
if json['type'] == Snapshot.t:
return self.snapshot(e, resource_def)
Model = db.Model._decl_class_registry.data[json['type']]()
event = Model(**e)
db.session.add(event)
@ -34,25 +41,20 @@ class EventView(View):
event = Event.query.filter_by(id=id).one()
return self.schema.jsonify(event)
SUPPORTED_WORKBENCH = StrictVersion('11.0')
class SnapshotView(View):
def post(self):
def snapshot(self, snapshot_json: dict, resource_def):
"""
Performs a Snapshot.
See `Snapshot` section in docs for more info.
"""
s = request.get_json()
# Note that if we set the device / components into the snapshot
# model object, when we flush them to the db we will flush
# snapshot, and we want to wait to flush snapshot at the end
device = s.pop('device') # type: Computer
components = s.pop('components') \
if s['software'] == SnapshotSoftware.Workbench else None # type: List[Component]
snapshot = Snapshot(**s)
device = snapshot_json.pop('device') # type: Computer
components = None
if snapshot_json['software'] == SnapshotSoftware.Workbench:
components = snapshot_json.pop('components') # type: List[Component]
snapshot = Snapshot(**snapshot_json)
# Remove new events from devices so they don't interfere with sync
events_device = set(e for e in device.events_one)
@ -62,10 +64,9 @@ class SnapshotView(View):
for component in components:
component.events_one.clear()
# noinspection PyArgumentList
assert not device.events_one
assert all(not c.events_one for c in components) if components else True
db_device, remove_events = self.resource_def.sync.run(device, components)
db_device, remove_events = resource_def.sync.run(device, components)
snapshot.device = db_device
snapshot.events |= remove_events | events_device # Set events to snapshot
# commit will change the order of the components by what

View File

@ -24,6 +24,7 @@ class User(Thing):
backref=db.backref('users', lazy=True, collection_class=set),
secondary=lambda: UserInventory.__table__,
collection_class=set)
# todo set restriction that user has, at least, one active db
def __init__(self, email, password=None, inventories=None) -> None:
@ -41,6 +42,10 @@ class User(Thing):
def __repr__(self) -> str:
return '<User {0.email}>'.format(self)
@property
def type(self) -> str:
return self.__class__.__name__
@property
def individual(self):
"""The individual associated for this database, or None."""

2546
file.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -24,7 +24,7 @@ requests[security]==2.19.1
requests-mock==1.5.2
SQLAlchemy==1.2.17
SQLAlchemy-Utils==0.33.11
teal==0.2.0a36
teal==0.2.0a37
webargs==4.0.0
Werkzeug==0.14.1
sqlalchemy-citext==1.3.post0

View File

@ -29,7 +29,7 @@ setup(
long_description=long_description,
long_description_content_type='text/markdown',
install_requires=[
'teal>=0.2.0a36', # teal always first
'teal>=0.2.0a37', # teal always first
'click',
'click-spinner',
'ereuse-utils[naming, test, session, cli]>=0.4b21',

View File

@ -5,16 +5,16 @@ device:
type: Desktop
chassis: Tower
components:
- manufacturer: p1c1m
- manufacturer: p1c1m
serialNumber: p1c1s
type: Motherboard
- manufacturer: p1c2m
- manufacturer: p1c2m
serialNumber: p1c2s
model: p1c2
speed: 1.23
cores: 2
type: Processor
- manufacturer: p1c3m
- manufacturer: p1c3m
serialNumber: p1c3s
type: GraphicCard
memory: 1.5
@ -22,3 +22,4 @@ elapsed: 25
software: Workbench
uuid: 76860eca-c3fd-41f6-a801-6af7bd8cf832
version: '11.0'
type: Snapshot

View File

@ -5,10 +5,10 @@ device:
type: Desktop
chassis: Microtower
components:
- manufacturer: p2c1m
- manufacturer: p2c1m
serialNumber: p2c1s
type: Motherboard
- manufacturer: p1c2m
- manufacturer: p1c2m
serialNumber: p1c2s
model: p1c2
speed: 1.23
@ -18,3 +18,4 @@ elapsed: 25
software: Workbench
uuid: f2e02261-87a1-4a50-b9b7-92c0e476e5f2
version: '11.0'
type: Snapshot

View File

@ -5,13 +5,13 @@ device:
type: Desktop
chassis: Microtower
components:
- manufacturer: p1c2m
- manufacturer: p1c2m
serialNumber: p1c2s
model: p1c2
type: Processor
cores: 2
speed: 1.23
- manufacturer: p1c3m
- manufacturer: p1c3m
serialNumber: p1c3s
type: GraphicCard
memory: 1.5
@ -19,3 +19,4 @@ elapsed: 30
software: Workbench
uuid: 3be271b6-5ef4-47d8-8237-5e1133eebfc6
version: '11.0'
type: Snapshot

View File

@ -5,12 +5,12 @@ device:
type: Desktop
chassis: Tower
components:
- manufacturer: p1c4m
- manufacturer: p1c4m
serialNumber: p1c4s
type: NetworkAdapter
speed: 1000
wireless: False
- manufacturer: p1c3m
- manufacturer: p1c3m
serialNumber: p1c3s
type: GraphicCard
memory: 1.5
@ -18,3 +18,4 @@ elapsed: 25
software: Workbench
uuid: fd007eb4-48e3-454a-8763-169491904c6e
version: '11.0'
type: Snapshot

View File

@ -21,7 +21,6 @@ def test_api_docs(client: Client):
'/users/',
'/devices/',
'/tags/',
'/snapshots/',
'/users/login/',
'/events/',
'/lots/',

30
tests/test_db.py Normal file
View File

@ -0,0 +1,30 @@
import datetime
from uuid import UUID
from teal.db import UniqueViolation
def test_unique_violation():
class IntegrityErrorMock:
def __init__(self) -> None:
self.params = {
'uuid': UUID('f5efd26e-8754-46bc-87bf-fbccc39d60d9'),
'version': '11.0',
'software': 'Workbench', 'elapsed': datetime.timedelta(0, 4),
'expected_events': None,
'id': UUID('dbdef3d8-2cac-48cb-adb8-419bc3e59687')
}
def __str__(self):
return """(psycopg2.IntegrityError) duplicate key value violates unique constraint "snapshot_uuid_key"
DETAIL: Key (uuid)=(f5efd26e-8754-46bc-87bf-fbccc39d60d9) already exists.
[SQL: 'INSERT INTO snapshot (uuid, version, software, elapsed, expected_events, id)
VALUES (%(uuid)s, %(version)s, %(software)s, %(elapsed)s, CAST(%(expected_events)s
AS snapshotexpectedevents[]), %(id)s)'] [parameters: {'uuid': UUID('f5efd26e-8754-46bc-87bf-fbccc39d60d9'),
'version': '11.0', 'software': 'Workbench', 'elapsed': datetime.timedelta(0, 4), 'expected_events': None,
'id': UUID('dbdef3d8-2cac-48cb-adb8-419bc3e59687')}] (Background on this error at: http://sqlalche.me/e/gkpj)"""
u = UniqueViolation(IntegrityErrorMock())
assert u.constraint == 'snapshot_uuid_key'
assert u.field_name == 'uuid'
assert u.field_value == UUID('f5efd26e-8754-46bc-87bf-fbccc39d60d9')