Skip to content

Commit 64242a2

Browse files
feat(api/asset): Add new endpoint to update or delete asset thing association
1 parent 3210684 commit 64242a2

2 files changed

Lines changed: 64 additions & 2 deletions

File tree

api/asset.py

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
from fastapi import APIRouter, Depends, Form, UploadFile, File
2121
from fastapi_pagination.ext.sqlalchemy import paginate
22-
from sqlalchemy import select
22+
from sqlalchemy import delete, select
2323
from sqlalchemy.exc import ProgrammingError
2424
from starlette.concurrency import run_in_threadpool
2525
from starlette.status import (
@@ -38,7 +38,13 @@
3838
)
3939
from db import Thing
4040
from db.asset import Asset, AssetThingAssociation
41-
from schemas.asset import AssetResponse, CreateAsset, UpdateAsset
41+
from schemas.asset import (
42+
AssetAssociationResponse,
43+
AssetAssociationUpdate,
44+
AssetResponse,
45+
CreateAsset,
46+
UpdateAsset,
47+
)
4248
from services.audit_helper import audit_add
4349
from services.crud_helper import model_patcher, model_deleter
4450
from services.edit_notification_helper import (
@@ -481,6 +487,53 @@ async def get_asset(
481487

482488

483489
# PATCH ======================================================================
490+
@router.patch("/{asset_id}/association")
491+
async def update_asset_thing_association(
492+
asset_id: int,
493+
association_data: AssetAssociationUpdate,
494+
session: session_dependency,
495+
user: editor_dependency,
496+
) -> AssetAssociationResponse:
497+
"""
498+
Move an asset to another Thing or remove its Thing association.
499+
500+
Passing a `thing_id` replaces any existing Thing links for the asset with
501+
that one Thing. Passing `thing_id: null` leaves the Asset record in place
502+
and removes all Thing links.
503+
"""
504+
simple_get_by_id(session, Asset, asset_id)
505+
506+
thing_id = association_data.thing_id
507+
thing = None
508+
if thing_id is not None:
509+
thing = session.get(Thing, thing_id)
510+
if thing is None:
511+
raise PydanticStyleException(
512+
status_code=HTTP_409_CONFLICT,
513+
detail=[
514+
{
515+
"loc": ["body", "thing_id"],
516+
"msg": f"Thing with ID {thing_id} not found.",
517+
"type": "value_error",
518+
"input": {"thing_id": thing_id},
519+
}
520+
],
521+
)
522+
523+
association_delete = delete(AssetThingAssociation).where(
524+
AssetThingAssociation.asset_id == asset_id
525+
)
526+
session.execute(association_delete)
527+
528+
if thing is not None:
529+
assoc = AssetThingAssociation(asset_id=asset_id, thing_id=thing.id)
530+
audit_add(user, assoc)
531+
session.add(assoc)
532+
533+
session.commit()
534+
return AssetAssociationResponse(asset_id=asset_id, thing_id=thing_id)
535+
536+
484537
@router.patch("/{asset_id}")
485538
async def update_asset(
486539
asset_id: int,

schemas/asset.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,15 @@ class AssetResponse(BaseResponseModel, BaseAsset):
3838
signed_url: str | None = None
3939

4040

41+
class AssetAssociationUpdate(BaseModel):
42+
thing_id: int | None = None
43+
44+
45+
class AssetAssociationResponse(BaseModel):
46+
asset_id: int
47+
thing_id: int | None = None
48+
49+
4150
# -------- UPDATE ----------
4251
class UpdateAsset(BaseUpdateModel):
4352
name: str | None = None

0 commit comments

Comments
 (0)