Revert so commit now happens before notice, but add a catch-all to websocket notifications

This commit is contained in:
Donkie
2024-08-16 15:13:09 +02:00
parent 8edf6d2e20
commit 32c737fa98
3 changed files with 55 additions and 41 deletions

View File

@@ -76,8 +76,8 @@ async def create(
extra=[models.FilamentField(key=k, value=v) for k, v in (extra or {}).items()], extra=[models.FilamentField(key=k, value=v) for k, v in (extra or {}).items()],
) )
db.add(filament) db.add(filament)
await filament_changed(filament, EventType.ADDED)
await db.commit() await db.commit()
await filament_changed(filament, EventType.ADDED)
return filament return filament
@@ -172,8 +172,8 @@ async def update(
filament.multi_color_direction = v.value if v is not None else None filament.multi_color_direction = v.value if v is not None else None
else: else:
setattr(filament, k, v) setattr(filament, k, v)
await filament_changed(filament, EventType.UPDATED)
await db.commit() await db.commit()
await filament_changed(filament, EventType.UPDATED)
return filament return filament
@@ -182,8 +182,8 @@ async def delete(db: AsyncSession, filament_id: int) -> None:
filament = await get_by_id(db, filament_id) filament = await get_by_id(db, filament_id)
await db.delete(filament) await db.delete(filament)
try: try:
await filament_changed(filament, EventType.DELETED)
await db.commit() # Flush immediately so any errors are propagated in this request. await db.commit() # Flush immediately so any errors are propagated in this request.
await filament_changed(filament, EventType.DELETED)
except IntegrityError as exc: except IntegrityError as exc:
await db.rollback() await db.rollback()
raise ItemDeleteError("Failed to delete filament.") from exc raise ItemDeleteError("Failed to delete filament.") from exc
@@ -255,12 +255,16 @@ async def find_by_color(
async def filament_changed(filament: models.Filament, typ: EventType) -> None: async def filament_changed(filament: models.Filament, typ: EventType) -> None:
"""Notify websocket clients that a filament has changed.""" """Notify websocket clients that a filament has changed."""
await websocket_manager.send( try:
("filament", str(filament.id)), await websocket_manager.send(
FilamentEvent( ("filament", str(filament.id)),
type=typ, FilamentEvent(
resource="filament", type=typ,
date=datetime.utcnow(), resource="filament",
payload=Filament.from_db(filament), date=datetime.utcnow(),
), payload=Filament.from_db(filament),
) ),
)
except Exception:
# Important to have a catch-all here since we don't want to stop the call if this fails.
logger.exception("Failed to send websocket message")

View File

@@ -1,5 +1,6 @@
"""Helper functions for interacting with spool database objects.""" """Helper functions for interacting with spool database objects."""
import logging
from collections.abc import Sequence from collections.abc import Sequence
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import Optional, Union from typing import Optional, Union
@@ -25,6 +26,8 @@ from spoolman.exceptions import ItemCreateError, ItemNotFoundError, SpoolMeasure
from spoolman.math import weight_from_length from spoolman.math import weight_from_length
from spoolman.ws import websocket_manager from spoolman.ws import websocket_manager
logger = logging.getLogger(__name__)
def utc_timezone_naive(dt: datetime) -> datetime: def utc_timezone_naive(dt: datetime) -> datetime:
"""Convert a datetime object to UTC and remove timezone info.""" """Convert a datetime object to UTC and remove timezone info."""
@@ -92,8 +95,8 @@ async def create(
extra=[models.SpoolField(key=k, value=v) for k, v in (extra or {}).items()], extra=[models.SpoolField(key=k, value=v) for k, v in (extra or {}).items()],
) )
db.add(spool) db.add(spool)
await spool_changed(spool, EventType.ADDED)
await db.commit() await db.commit()
await spool_changed(spool, EventType.ADDED)
return spool return spool
@@ -228,8 +231,8 @@ async def update(
spool.extra = [models.SpoolField(key=k, value=v) for k, v in v.items()] spool.extra = [models.SpoolField(key=k, value=v) for k, v in v.items()]
else: else:
setattr(spool, k, v) setattr(spool, k, v)
await spool_changed(spool, EventType.UPDATED)
await db.commit() await db.commit()
await spool_changed(spool, EventType.UPDATED)
return spool return spool
@@ -291,8 +294,8 @@ async def use_weight(db: AsyncSession, spool_id: int, weight: float) -> models.S
spool.first_used = datetime.utcnow().replace(microsecond=0) spool.first_used = datetime.utcnow().replace(microsecond=0)
spool.last_used = datetime.utcnow().replace(microsecond=0) spool.last_used = datetime.utcnow().replace(microsecond=0)
await spool_changed(spool, EventType.UPDATED)
await db.commit() await db.commit()
await spool_changed(spool, EventType.UPDATED)
return spool return spool
@@ -337,12 +340,8 @@ async def use_length(db: AsyncSession, spool_id: int, length: float) -> models.S
spool.first_used = datetime.utcnow().replace(microsecond=0) spool.first_used = datetime.utcnow().replace(microsecond=0)
spool.last_used = datetime.utcnow().replace(microsecond=0) spool.last_used = datetime.utcnow().replace(microsecond=0)
await spool_changed(spool, EventType.UPDATED)
# Commit should be the last action, everything after that must never fail
# Otherwise you can end up in a non-atomic thing where the http use request fails
# but the data still has been committed.
await db.commit() await db.commit()
await spool_changed(spool, EventType.UPDATED)
return spool return spool
@@ -436,15 +435,19 @@ async def find_lot_numbers(
async def spool_changed(spool: models.Spool, typ: EventType) -> None: async def spool_changed(spool: models.Spool, typ: EventType) -> None:
"""Notify websocket clients that a spool has changed.""" """Notify websocket clients that a spool has changed."""
await websocket_manager.send( try:
("spool", str(spool.id)), await websocket_manager.send(
SpoolEvent( ("spool", str(spool.id)),
type=typ, SpoolEvent(
resource="spool", type=typ,
date=datetime.utcnow(), resource="spool",
payload=Spool.from_db(spool), date=datetime.utcnow(),
), payload=Spool.from_db(spool),
) ),
)
except Exception:
# Important to have a catch-all here since we don't want to stop the call if this fails.
logger.exception("Failed to send websocket message")
async def reset_initial_weight(db: AsyncSession, spool_id: int, weight: float) -> models.Spool: async def reset_initial_weight(db: AsyncSession, spool_id: int, weight: float) -> models.Spool:
@@ -453,6 +456,6 @@ async def reset_initial_weight(db: AsyncSession, spool_id: int, weight: float) -
spool.initial_weight = weight spool.initial_weight = weight
spool.used_weight = 0 spool.used_weight = 0
await spool_changed(spool, EventType.UPDATED)
await db.commit() await db.commit()
await spool_changed(spool, EventType.UPDATED)
return spool return spool

View File

@@ -1,5 +1,6 @@
"""Helper functions for interacting with vendor database objects.""" """Helper functions for interacting with vendor database objects."""
import logging
from datetime import datetime from datetime import datetime
from typing import Optional from typing import Optional
@@ -13,6 +14,8 @@ from spoolman.database.utils import SortOrder, add_where_clause_str, add_where_c
from spoolman.exceptions import ItemNotFoundError from spoolman.exceptions import ItemNotFoundError
from spoolman.ws import websocket_manager from spoolman.ws import websocket_manager
logger = logging.getLogger(__name__)
async def create( async def create(
*, *,
@@ -33,8 +36,8 @@ async def create(
extra=[models.VendorField(key=k, value=v) for k, v in (extra or {}).items()], extra=[models.VendorField(key=k, value=v) for k, v in (extra or {}).items()],
) )
db.add(vendor) db.add(vendor)
await vendor_changed(vendor, EventType.ADDED)
await db.commit() await db.commit()
await vendor_changed(vendor, EventType.ADDED)
return vendor return vendor
@@ -101,8 +104,8 @@ async def update(
vendor.extra = [models.VendorField(key=k, value=v) for k, v in v.items()] vendor.extra = [models.VendorField(key=k, value=v) for k, v in v.items()]
else: else:
setattr(vendor, k, v) setattr(vendor, k, v)
await vendor_changed(vendor, EventType.UPDATED)
await db.commit() await db.commit()
await vendor_changed(vendor, EventType.UPDATED)
return vendor return vendor
@@ -122,12 +125,16 @@ async def clear_extra_field(db: AsyncSession, key: str) -> None:
async def vendor_changed(vendor: models.Vendor, typ: EventType) -> None: async def vendor_changed(vendor: models.Vendor, typ: EventType) -> None:
"""Notify websocket clients that a vendor has changed.""" """Notify websocket clients that a vendor has changed."""
await websocket_manager.send( try:
("vendor", str(vendor.id)), await websocket_manager.send(
VendorEvent( ("vendor", str(vendor.id)),
type=typ, VendorEvent(
resource="vendor", type=typ,
date=datetime.utcnow(), resource="vendor",
payload=Vendor.from_db(vendor), date=datetime.utcnow(),
), payload=Vendor.from_db(vendor),
) ),
)
except Exception:
# Important to have a catch-all here since we don't want to stop the call if this fails.
logger.exception("Failed to send websocket message")