feat(backend): add single-record draft read endpoints
add backend read endpoints for single draft records support direct fetch of individual draft entities improve draft inspection and targeted editor workflows
This commit is contained in:
@@ -5,6 +5,7 @@ from app.repositories.audit import create_audit_event
|
|||||||
from app.repositories.scheme_groups import (
|
from app.repositories.scheme_groups import (
|
||||||
create_scheme_version_group,
|
create_scheme_version_group,
|
||||||
delete_scheme_version_group_by_record_id,
|
delete_scheme_version_group_by_record_id,
|
||||||
|
get_scheme_version_group_by_record_id,
|
||||||
list_scheme_version_groups,
|
list_scheme_version_groups,
|
||||||
update_scheme_version_group_by_record_id,
|
update_scheme_version_group_by_record_id,
|
||||||
)
|
)
|
||||||
@@ -12,12 +13,14 @@ from app.repositories.scheme_seats import (
|
|||||||
bulk_update_scheme_version_seats_by_record_id,
|
bulk_update_scheme_version_seats_by_record_id,
|
||||||
cascade_update_seat_group_reference,
|
cascade_update_seat_group_reference,
|
||||||
cascade_update_seat_sector_reference,
|
cascade_update_seat_sector_reference,
|
||||||
|
get_scheme_version_seat_by_record_id,
|
||||||
list_scheme_version_seats,
|
list_scheme_version_seats,
|
||||||
update_scheme_version_seat_by_record_id,
|
update_scheme_version_seat_by_record_id,
|
||||||
)
|
)
|
||||||
from app.repositories.scheme_sectors import (
|
from app.repositories.scheme_sectors import (
|
||||||
create_scheme_version_sector,
|
create_scheme_version_sector,
|
||||||
delete_scheme_version_sector_by_record_id,
|
delete_scheme_version_sector_by_record_id,
|
||||||
|
get_scheme_version_sector_by_record_id,
|
||||||
list_scheme_version_sectors,
|
list_scheme_version_sectors,
|
||||||
update_scheme_version_sector_by_record_id,
|
update_scheme_version_sector_by_record_id,
|
||||||
)
|
)
|
||||||
@@ -154,6 +157,85 @@ async def get_draft_validation(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get(f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/draft/seats/records/{{seat_record_id}}", response_model=DraftSeatItem)
|
||||||
|
async def get_draft_seat_by_record_id(
|
||||||
|
scheme_id: str,
|
||||||
|
seat_record_id: str,
|
||||||
|
role: str = Depends(require_api_key),
|
||||||
|
):
|
||||||
|
_scheme, version = await get_current_draft_context(scheme_id)
|
||||||
|
row = await get_scheme_version_seat_by_record_id(
|
||||||
|
scheme_version_id=version.scheme_version_id,
|
||||||
|
seat_record_id=seat_record_id,
|
||||||
|
)
|
||||||
|
return DraftSeatItem(
|
||||||
|
seat_record_id=row.seat_record_id,
|
||||||
|
scheme_id=row.scheme_id,
|
||||||
|
scheme_version_id=row.scheme_version_id,
|
||||||
|
element_id=row.element_id,
|
||||||
|
seat_id=row.seat_id,
|
||||||
|
sector_id=row.sector_id,
|
||||||
|
group_id=row.group_id,
|
||||||
|
row_label=row.row_label,
|
||||||
|
seat_number=row.seat_number,
|
||||||
|
tag=row.tag,
|
||||||
|
classes_raw=row.classes_raw,
|
||||||
|
x=row.x,
|
||||||
|
y=row.y,
|
||||||
|
cx=row.cx,
|
||||||
|
cy=row.cy,
|
||||||
|
width=row.width,
|
||||||
|
height=row.height,
|
||||||
|
created_at=row.created_at.isoformat(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get(f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/draft/sectors/records/{{sector_record_id}}", response_model=DraftSectorItem)
|
||||||
|
async def get_draft_sector_by_record_id(
|
||||||
|
scheme_id: str,
|
||||||
|
sector_record_id: str,
|
||||||
|
role: str = Depends(require_api_key),
|
||||||
|
):
|
||||||
|
_scheme, version = await get_current_draft_context(scheme_id)
|
||||||
|
row = await get_scheme_version_sector_by_record_id(
|
||||||
|
scheme_version_id=version.scheme_version_id,
|
||||||
|
sector_record_id=sector_record_id,
|
||||||
|
)
|
||||||
|
return DraftSectorItem(
|
||||||
|
sector_record_id=row.sector_record_id,
|
||||||
|
scheme_id=row.scheme_id,
|
||||||
|
scheme_version_id=row.scheme_version_id,
|
||||||
|
element_id=row.element_id,
|
||||||
|
sector_id=row.sector_id,
|
||||||
|
name=row.name,
|
||||||
|
classes_raw=row.classes_raw,
|
||||||
|
created_at=row.created_at.isoformat(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get(f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/draft/groups/records/{{group_record_id}}", response_model=DraftGroupItem)
|
||||||
|
async def get_draft_group_by_record_id(
|
||||||
|
scheme_id: str,
|
||||||
|
group_record_id: str,
|
||||||
|
role: str = Depends(require_api_key),
|
||||||
|
):
|
||||||
|
_scheme, version = await get_current_draft_context(scheme_id)
|
||||||
|
row = await get_scheme_version_group_by_record_id(
|
||||||
|
scheme_version_id=version.scheme_version_id,
|
||||||
|
group_record_id=group_record_id,
|
||||||
|
)
|
||||||
|
return DraftGroupItem(
|
||||||
|
group_record_id=row.group_record_id,
|
||||||
|
scheme_id=row.scheme_id,
|
||||||
|
scheme_version_id=row.scheme_version_id,
|
||||||
|
element_id=row.element_id,
|
||||||
|
group_id=row.group_id,
|
||||||
|
name=row.name,
|
||||||
|
classes_raw=row.classes_raw,
|
||||||
|
created_at=row.created_at.isoformat(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get(f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/draft/compare-preview", response_model=StructureDiffResponse)
|
@router.get(f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/draft/compare-preview", response_model=StructureDiffResponse)
|
||||||
async def get_draft_compare_preview(
|
async def get_draft_compare_preview(
|
||||||
scheme_id: str,
|
scheme_id: str,
|
||||||
|
|||||||
@@ -166,3 +166,26 @@ async def delete_scheme_version_group_by_record_id(
|
|||||||
|
|
||||||
await session.delete(group)
|
await session.delete(group)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
async def get_scheme_version_group_by_record_id(
|
||||||
|
*,
|
||||||
|
scheme_version_id: str,
|
||||||
|
group_record_id: str,
|
||||||
|
) -> SchemeGroupRecord:
|
||||||
|
async with AsyncSessionLocal() as session:
|
||||||
|
result = await session.execute(
|
||||||
|
select(SchemeGroupRecord).where(
|
||||||
|
SchemeGroupRecord.scheme_version_id == scheme_version_id,
|
||||||
|
SchemeGroupRecord.group_record_id == group_record_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
row = result.scalar_one_or_none()
|
||||||
|
|
||||||
|
if row is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail="Group record not found in current draft version",
|
||||||
|
)
|
||||||
|
|
||||||
|
return row
|
||||||
|
|||||||
@@ -114,6 +114,29 @@ async def get_scheme_version_seat_by_seat_id(
|
|||||||
return row
|
return row
|
||||||
|
|
||||||
|
|
||||||
|
async def get_scheme_version_seat_by_record_id(
|
||||||
|
*,
|
||||||
|
scheme_version_id: str,
|
||||||
|
seat_record_id: str,
|
||||||
|
) -> SchemeSeatRecord:
|
||||||
|
async with AsyncSessionLocal() as session:
|
||||||
|
result = await session.execute(
|
||||||
|
select(SchemeSeatRecord).where(
|
||||||
|
SchemeSeatRecord.scheme_version_id == scheme_version_id,
|
||||||
|
SchemeSeatRecord.seat_record_id == seat_record_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
row = result.scalar_one_or_none()
|
||||||
|
|
||||||
|
if row is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail="Seat record not found in current draft version",
|
||||||
|
)
|
||||||
|
|
||||||
|
return row
|
||||||
|
|
||||||
|
|
||||||
async def update_scheme_version_seat_by_record_id(
|
async def update_scheme_version_seat_by_record_id(
|
||||||
*,
|
*,
|
||||||
scheme_version_id: str,
|
scheme_version_id: str,
|
||||||
|
|||||||
@@ -166,3 +166,26 @@ async def delete_scheme_version_sector_by_record_id(
|
|||||||
|
|
||||||
await session.delete(sector)
|
await session.delete(sector)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
async def get_scheme_version_sector_by_record_id(
|
||||||
|
*,
|
||||||
|
scheme_version_id: str,
|
||||||
|
sector_record_id: str,
|
||||||
|
) -> SchemeSectorRecord:
|
||||||
|
async with AsyncSessionLocal() as session:
|
||||||
|
result = await session.execute(
|
||||||
|
select(SchemeSectorRecord).where(
|
||||||
|
SchemeSectorRecord.scheme_version_id == scheme_version_id,
|
||||||
|
SchemeSectorRecord.sector_record_id == sector_record_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
row = result.scalar_one_or_none()
|
||||||
|
|
||||||
|
if row is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail="Sector record not found in current draft version",
|
||||||
|
)
|
||||||
|
|
||||||
|
return row
|
||||||
|
|||||||
@@ -57,6 +57,10 @@
|
|||||||
|
|
||||||
## app/api/routes/editor.py
|
## app/api/routes/editor.py
|
||||||
- GET /api/v1/schemes/{scheme_id}/draft/structure
|
- GET /api/v1/schemes/{scheme_id}/draft/structure
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/draft/validation
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/draft/seats/records/{seat_record_id}
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/draft/sectors/records/{sector_record_id}
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/draft/groups/records/{group_record_id}
|
||||||
- GET /api/v1/schemes/{scheme_id}/draft/compare-preview
|
- GET /api/v1/schemes/{scheme_id}/draft/compare-preview
|
||||||
- POST /api/v1/schemes/{scheme_id}/draft/sectors
|
- POST /api/v1/schemes/{scheme_id}/draft/sectors
|
||||||
- POST /api/v1/schemes/{scheme_id}/draft/groups
|
- POST /api/v1/schemes/{scheme_id}/draft/groups
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ Validate:
|
|||||||
|
|
||||||
## 6. Draft publish preview
|
## 6. Draft publish preview
|
||||||
|
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/draft/validation -> 200
|
||||||
- GET /api/v1/schemes/{scheme_id}/publish/validation -> 200
|
- GET /api/v1/schemes/{scheme_id}/publish/validation -> 200
|
||||||
- POST /api/v1/schemes/{scheme_id}/draft/pricing/snapshot -> 200 when scheme is in draft
|
- POST /api/v1/schemes/{scheme_id}/draft/pricing/snapshot -> 200 when scheme is in draft
|
||||||
- GET /api/v1/schemes/{scheme_id}/draft/publish-preview?refresh=true -> 200
|
- GET /api/v1/schemes/{scheme_id}/draft/publish-preview?refresh=true -> 200
|
||||||
@@ -140,3 +141,16 @@ Run this checklist after:
|
|||||||
- display pipeline changes
|
- display pipeline changes
|
||||||
- route reorganization
|
- route reorganization
|
||||||
- startup/import/config changes
|
- startup/import/config changes
|
||||||
|
|
||||||
|
|
||||||
|
## 3.1. Draft editor read model
|
||||||
|
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/draft/structure -> 200 when current version is draft
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/draft/seats/records/{seat_record_id} -> 200 for known seat record
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/draft/sectors/records/{sector_record_id} -> 200 for known sector record
|
||||||
|
- GET /api/v1/schemes/{scheme_id}/draft/groups/records/{group_record_id} -> 200 for known group record
|
||||||
|
|
||||||
|
Validate:
|
||||||
|
- returned record belongs to current draft scheme_version_id
|
||||||
|
- single-entity endpoints match items visible in draft structure
|
||||||
|
- missing draft record returns 404, not 500
|
||||||
|
|||||||
Reference in New Issue
Block a user