Added websocket endpoints for filament and vendors
This commit is contained in:
@@ -1,9 +1,10 @@
|
|||||||
"""Filament related endpoints."""
|
"""Filament related endpoints."""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from typing import Annotated, Optional
|
from typing import Annotated, Optional
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, Query
|
from fastapi import APIRouter, Depends, Query, WebSocket, WebSocketDisconnect
|
||||||
from fastapi.encoders import jsonable_encoder
|
from fastapi.encoders import jsonable_encoder
|
||||||
from fastapi.exceptions import RequestValidationError
|
from fastapi.exceptions import RequestValidationError
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
@@ -16,6 +17,7 @@ from spoolman.database import filament
|
|||||||
from spoolman.database.database import get_db_session
|
from spoolman.database.database import get_db_session
|
||||||
from spoolman.database.utils import SortOrder
|
from spoolman.database.utils import SortOrder
|
||||||
from spoolman.exceptions import ItemDeleteError
|
from spoolman.exceptions import ItemDeleteError
|
||||||
|
from spoolman.ws import websocket_manager
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -232,7 +234,10 @@ async def find(
|
|||||||
@router.get(
|
@router.get(
|
||||||
"/{filament_id}",
|
"/{filament_id}",
|
||||||
name="Get filament",
|
name="Get filament",
|
||||||
description="Get a specific filament.",
|
description=(
|
||||||
|
"Get a specific filament. A websocket is served on the same path to listen for changes to the filament. "
|
||||||
|
"The response model is the same for the websocket messages as for this endpoint."
|
||||||
|
),
|
||||||
response_model_exclude_none=True,
|
response_model_exclude_none=True,
|
||||||
responses={404: {"model": Message}},
|
responses={404: {"model": Message}},
|
||||||
)
|
)
|
||||||
@@ -244,6 +249,25 @@ async def get(
|
|||||||
return Filament.from_db(db_item)
|
return Filament.from_db(db_item)
|
||||||
|
|
||||||
|
|
||||||
|
@router.websocket(
|
||||||
|
"/{filament_id}",
|
||||||
|
name="Listen to filament changes",
|
||||||
|
)
|
||||||
|
async def notify(
|
||||||
|
websocket: WebSocket,
|
||||||
|
filament_id: int,
|
||||||
|
) -> None:
|
||||||
|
await websocket.accept()
|
||||||
|
websocket_manager.connect(("filament", str(filament_id)), websocket)
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
if await websocket.receive_text():
|
||||||
|
await websocket.send_json({"status": "healthy"})
|
||||||
|
except WebSocketDisconnect:
|
||||||
|
websocket_manager.disconnect(("filament", str(filament_id)), websocket)
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"",
|
"",
|
||||||
name="Add filament",
|
name="Add filament",
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
"""Vendor related endpoints."""
|
"""Vendor related endpoints."""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
from typing import Annotated, Optional
|
from typing import Annotated, Optional
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, Query
|
from fastapi import APIRouter, Depends, Query, WebSocket, WebSocketDisconnect
|
||||||
from fastapi.encoders import jsonable_encoder
|
from fastapi.encoders import jsonable_encoder
|
||||||
from fastapi.exceptions import RequestValidationError
|
from fastapi.exceptions import RequestValidationError
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
@@ -14,6 +15,7 @@ from spoolman.api.v1.models import Message, Vendor
|
|||||||
from spoolman.database import vendor
|
from spoolman.database import vendor
|
||||||
from spoolman.database.database import get_db_session
|
from spoolman.database.database import get_db_session
|
||||||
from spoolman.database.utils import SortOrder
|
from spoolman.database.utils import SortOrder
|
||||||
|
from spoolman.ws import websocket_manager
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/vendor",
|
prefix="/vendor",
|
||||||
@@ -100,7 +102,10 @@ async def find(
|
|||||||
@router.get(
|
@router.get(
|
||||||
"/{vendor_id}",
|
"/{vendor_id}",
|
||||||
name="Get vendor",
|
name="Get vendor",
|
||||||
description="Get a specific vendor.",
|
description=(
|
||||||
|
"Get a specific vendor. A websocket is served on the same path to listen for changes to the vendor. "
|
||||||
|
"The response model is the same for the websocket messages as for this endpoint."
|
||||||
|
),
|
||||||
response_model_exclude_none=True,
|
response_model_exclude_none=True,
|
||||||
responses={404: {"model": Message}},
|
responses={404: {"model": Message}},
|
||||||
)
|
)
|
||||||
@@ -112,6 +117,25 @@ async def get(
|
|||||||
return Vendor.from_db(db_item)
|
return Vendor.from_db(db_item)
|
||||||
|
|
||||||
|
|
||||||
|
@router.websocket(
|
||||||
|
"/{vendor_id}",
|
||||||
|
name="Listen to vendor changes",
|
||||||
|
)
|
||||||
|
async def notify(
|
||||||
|
websocket: WebSocket,
|
||||||
|
vendor_id: int,
|
||||||
|
) -> None:
|
||||||
|
await websocket.accept()
|
||||||
|
websocket_manager.connect(("vendor", str(vendor_id)), websocket)
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
if await websocket.receive_text():
|
||||||
|
await websocket.send_json({"status": "healthy"})
|
||||||
|
except WebSocketDisconnect:
|
||||||
|
websocket_manager.disconnect(("vendor", str(vendor_id)), websocket)
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"",
|
"",
|
||||||
name="Add vendor",
|
name="Add vendor",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from sqlalchemy.exc import IntegrityError
|
|||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy.orm import contains_eager, joinedload
|
from sqlalchemy.orm import contains_eager, joinedload
|
||||||
|
|
||||||
|
from spoolman.api.v1.models import Filament
|
||||||
from spoolman.database import models, vendor
|
from spoolman.database import models, vendor
|
||||||
from spoolman.database.utils import (
|
from spoolman.database.utils import (
|
||||||
SortOrder,
|
SortOrder,
|
||||||
@@ -19,6 +20,7 @@ from spoolman.database.utils import (
|
|||||||
parse_nested_field,
|
parse_nested_field,
|
||||||
)
|
)
|
||||||
from spoolman.exceptions import ItemDeleteError, ItemNotFoundError
|
from spoolman.exceptions import ItemDeleteError, ItemNotFoundError
|
||||||
|
from spoolman.ws import websocket_manager
|
||||||
|
|
||||||
|
|
||||||
async def create(
|
async def create(
|
||||||
@@ -148,6 +150,7 @@ async def update(
|
|||||||
else:
|
else:
|
||||||
setattr(filament, k, v)
|
setattr(filament, k, v)
|
||||||
await db.commit()
|
await db.commit()
|
||||||
|
await filament_changed(filament)
|
||||||
return filament
|
return filament
|
||||||
|
|
||||||
|
|
||||||
@@ -183,3 +186,8 @@ async def find_article_numbers(
|
|||||||
stmt = select(models.Filament.article_number).distinct()
|
stmt = select(models.Filament.article_number).distinct()
|
||||||
rows = await db.execute(stmt)
|
rows = await db.execute(stmt)
|
||||||
return [row[0] for row in rows.all() if row[0] is not None]
|
return [row[0] for row in rows.all() if row[0] is not None]
|
||||||
|
|
||||||
|
|
||||||
|
async def filament_changed(filament: models.Filament) -> None:
|
||||||
|
"""Notify websocket clients that a filament has changed."""
|
||||||
|
await websocket_manager.send(("filament", str(filament.id)), Filament.from_db(filament).json())
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ from typing import Optional
|
|||||||
from sqlalchemy import func, select
|
from sqlalchemy import func, select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from spoolman.api.v1.models import Vendor
|
||||||
from spoolman.database import models
|
from spoolman.database import models
|
||||||
from spoolman.database.utils import SortOrder, add_where_clause_str
|
from spoolman.database.utils import SortOrder, add_where_clause_str
|
||||||
from spoolman.exceptions import ItemNotFoundError
|
from spoolman.exceptions import ItemNotFoundError
|
||||||
|
from spoolman.ws import websocket_manager
|
||||||
|
|
||||||
|
|
||||||
async def create(
|
async def create(
|
||||||
@@ -87,6 +89,7 @@ async def update(
|
|||||||
for k, v in data.items():
|
for k, v in data.items():
|
||||||
setattr(vendor, k, v)
|
setattr(vendor, k, v)
|
||||||
await db.commit()
|
await db.commit()
|
||||||
|
await vendor_changed(vendor)
|
||||||
return vendor
|
return vendor
|
||||||
|
|
||||||
|
|
||||||
@@ -94,3 +97,8 @@ async def delete(db: AsyncSession, vendor_id: int) -> None:
|
|||||||
"""Delete a vendor object."""
|
"""Delete a vendor object."""
|
||||||
vendor = await get_by_id(db, vendor_id)
|
vendor = await get_by_id(db, vendor_id)
|
||||||
await db.delete(vendor)
|
await db.delete(vendor)
|
||||||
|
|
||||||
|
|
||||||
|
async def vendor_changed(vendor: models.Vendor) -> None:
|
||||||
|
"""Notify websocket clients that a vendor has changed."""
|
||||||
|
await websocket_manager.send(("vendor", str(vendor.id)), Vendor.from_db(vendor).json())
|
||||||
|
|||||||
Reference in New Issue
Block a user