Unify Snapshot's POST view with Devicehub's; bugfixes
This commit is contained in:
parent
32e696c57c
commit
07e8be829e
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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
|
||||
|
|
2
setup.py
2
setup.py
|
@ -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',
|
||||
|
|
|
@ -5,20 +5,21 @@ device:
|
|||
type: Desktop
|
||||
chassis: Tower
|
||||
components:
|
||||
- manufacturer: p1c1m
|
||||
serialNumber: p1c1s
|
||||
type: Motherboard
|
||||
- manufacturer: p1c2m
|
||||
serialNumber: p1c2s
|
||||
model: p1c2
|
||||
speed: 1.23
|
||||
cores: 2
|
||||
type: Processor
|
||||
- manufacturer: p1c3m
|
||||
serialNumber: p1c3s
|
||||
type: GraphicCard
|
||||
memory: 1.5
|
||||
- manufacturer: p1c1m
|
||||
serialNumber: p1c1s
|
||||
type: Motherboard
|
||||
- manufacturer: p1c2m
|
||||
serialNumber: p1c2s
|
||||
model: p1c2
|
||||
speed: 1.23
|
||||
cores: 2
|
||||
type: Processor
|
||||
- manufacturer: p1c3m
|
||||
serialNumber: p1c3s
|
||||
type: GraphicCard
|
||||
memory: 1.5
|
||||
elapsed: 25
|
||||
software: Workbench
|
||||
uuid: 76860eca-c3fd-41f6-a801-6af7bd8cf832
|
||||
version: '11.0'
|
||||
type: Snapshot
|
||||
|
|
|
@ -5,16 +5,17 @@ device:
|
|||
type: Desktop
|
||||
chassis: Microtower
|
||||
components:
|
||||
- manufacturer: p2c1m
|
||||
serialNumber: p2c1s
|
||||
type: Motherboard
|
||||
- manufacturer: p1c2m
|
||||
serialNumber: p1c2s
|
||||
model: p1c2
|
||||
speed: 1.23
|
||||
cores: 2
|
||||
type: Processor
|
||||
- manufacturer: p2c1m
|
||||
serialNumber: p2c1s
|
||||
type: Motherboard
|
||||
- manufacturer: p1c2m
|
||||
serialNumber: p1c2s
|
||||
model: p1c2
|
||||
speed: 1.23
|
||||
cores: 2
|
||||
type: Processor
|
||||
elapsed: 25
|
||||
software: Workbench
|
||||
uuid: f2e02261-87a1-4a50-b9b7-92c0e476e5f2
|
||||
version: '11.0'
|
||||
type: Snapshot
|
||||
|
|
|
@ -5,17 +5,18 @@ device:
|
|||
type: Desktop
|
||||
chassis: Microtower
|
||||
components:
|
||||
- manufacturer: p1c2m
|
||||
serialNumber: p1c2s
|
||||
model: p1c2
|
||||
type: Processor
|
||||
cores: 2
|
||||
speed: 1.23
|
||||
- manufacturer: p1c3m
|
||||
serialNumber: p1c3s
|
||||
type: GraphicCard
|
||||
memory: 1.5
|
||||
- manufacturer: p1c2m
|
||||
serialNumber: p1c2s
|
||||
model: p1c2
|
||||
type: Processor
|
||||
cores: 2
|
||||
speed: 1.23
|
||||
- manufacturer: p1c3m
|
||||
serialNumber: p1c3s
|
||||
type: GraphicCard
|
||||
memory: 1.5
|
||||
elapsed: 30
|
||||
software: Workbench
|
||||
uuid: 3be271b6-5ef4-47d8-8237-5e1133eebfc6
|
||||
version: '11.0'
|
||||
type: Snapshot
|
||||
|
|
|
@ -5,16 +5,17 @@ device:
|
|||
type: Desktop
|
||||
chassis: Tower
|
||||
components:
|
||||
- manufacturer: p1c4m
|
||||
serialNumber: p1c4s
|
||||
type: NetworkAdapter
|
||||
speed: 1000
|
||||
wireless: False
|
||||
- manufacturer: p1c3m
|
||||
serialNumber: p1c3s
|
||||
type: GraphicCard
|
||||
memory: 1.5
|
||||
- manufacturer: p1c4m
|
||||
serialNumber: p1c4s
|
||||
type: NetworkAdapter
|
||||
speed: 1000
|
||||
wireless: False
|
||||
- manufacturer: p1c3m
|
||||
serialNumber: p1c3s
|
||||
type: GraphicCard
|
||||
memory: 1.5
|
||||
elapsed: 25
|
||||
software: Workbench
|
||||
uuid: fd007eb4-48e3-454a-8763-169491904c6e
|
||||
version: '11.0'
|
||||
type: Snapshot
|
||||
|
|
|
@ -21,7 +21,6 @@ def test_api_docs(client: Client):
|
|||
'/users/',
|
||||
'/devices/',
|
||||
'/tags/',
|
||||
'/snapshots/',
|
||||
'/users/login/',
|
||||
'/events/',
|
||||
'/lots/',
|
||||
|
|
|
@ -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')
|
Reference in New Issue