Added a new backend settings system

This can be used for saving runtime-configurable settings in the database.
This commit is contained in:
Donkie
2024-01-03 18:44:09 +01:00
parent cad6d4a7f7
commit 44da991d47
11 changed files with 573 additions and 3 deletions

View File

@@ -3,7 +3,7 @@
from datetime import datetime
from typing import Optional
from sqlalchemy import ForeignKey, Integer, String
from sqlalchemy import ForeignKey, Integer, String, Text
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
@@ -58,3 +58,11 @@ class Spool(Base):
lot_nr: Mapped[Optional[str]] = mapped_column(String(64))
comment: Mapped[Optional[str]] = mapped_column(String(1024))
archived: Mapped[Optional[bool]] = mapped_column()
class Setting(Base):
__tablename__ = "setting"
key: Mapped[str] = mapped_column(String(64), primary_key=True, index=True)
value: Mapped[str] = mapped_column(Text())
last_updated: Mapped[datetime] = mapped_column()

View File

@@ -0,0 +1,69 @@
"""Helper functions for interacting with vendor database objects."""
from datetime import datetime
from typing import Optional
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from spoolman.api.v1.models import EventType, SettingEvent, SettingKV
from spoolman.database import models
from spoolman.exceptions import ItemNotFoundError
from spoolman.settings import SettingDefinition
from spoolman.ws import websocket_manager
SETTING_MAX_LENGTH = 2**16 - 1
async def update(
*,
db: AsyncSession,
definition: SettingDefinition,
value: str,
) -> None:
"""Set a setting in the database."""
if len(value) > SETTING_MAX_LENGTH:
raise ValueError(f"Setting value is too big, max size is {SETTING_MAX_LENGTH} characters.")
setting = models.Setting(
key=definition.key,
value=value,
last_updated=datetime.utcnow().replace(microsecond=0),
)
await db.merge(setting)
await setting_changed(definition, value, EventType.UPDATED)
async def get(db: AsyncSession, definition: SettingDefinition) -> models.Setting:
"""Get a specific setting from the database."""
setting = await db.get(models.Setting, definition.key)
if setting is None:
raise ItemNotFoundError(f"Setting with key {definition.key} has not been set.")
return setting
async def get_all(db: AsyncSession) -> list[models.Setting]:
"""Get all set settings in the database."""
stmt = select(models.Setting)
rows = await db.execute(stmt)
return list(rows.scalars().all())
async def delete(db: AsyncSession, definition: SettingDefinition) -> None:
"""Delete a setting from the database."""
setting = await get(db, definition)
await db.delete(setting)
await setting_changed(definition, None, EventType.DELETED)
async def setting_changed(definition: SettingDefinition, set_value: Optional[str], typ: EventType) -> None:
"""Notify websocket clients that a setting has changed."""
await websocket_manager.send(
("setting", str(definition.key)),
SettingEvent(
type=typ,
resource="setting",
date=datetime.utcnow(),
payload=SettingKV.from_db(definition, set_value),
),
)