feat: contact model restructure

Blank contacts are now generated on org creation and assigned to each contact type. These contacts are linked to the org, only accessible to the org, and removed when the org is removed.

With this all contact endpoints have been removed. Contact manipulation is done via the org only.
This commit is contained in:
Chris Milne 2026-05-25 15:15:50 +01:00
parent 707482adc2
commit 2b6d923ae1
7 changed files with 146 additions and 165 deletions

View file

@ -19,6 +19,7 @@ from fastapi.params import Path, Query
from sqlalchemy.sql import exists
from src.contact.schemas import ContactAddress
from src.database import db_dependency
from src.contact.models import Contact
from src.iam.models import Group
@ -42,9 +43,9 @@ async def get_org_by_id(db: db_dependency, org_model: org_model_dependency, org_
response = {
"name": org_model.name,
"status": org_model.status,
"owner_contact": (db.query(Contact).filter(Contact.id == org_model.owner_contact_id).first()),
"billing_contact": (db.query(Contact).filter(Contact.id == org_model.billing_contact_id).first()),
"security_contact": (db.query(Contact).filter(Contact.id == org_model.security_contact_id).first()),
"owner_contact": org_model.owner_contact_rel.email,
"billing_contact": org_model.billing_contact_rel.email,
"security_contact": org_model.security_contact_rel.email,
"root_user": org_model.root_user_email
}
@ -54,11 +55,17 @@ async def get_org_by_id(db: db_dependency, org_model: org_model_dependency, org_
@router.post("/")
async def create_org(db: db_dependency, org_request: OrgOrgPostRequest):
# TODO: Root user from current user
org_model = Org(**org_request.model_dump())
org_model = Org(name=org_request.name, intake_questionnaire=org_request.intake_questionnaire)
org_model.status = "partial" # Status is always set to partial at first, see update_questionnaire() doc
db.add(org_model)
db.flush()
for contact_type in ["billing_contact_id", "security_contact_id", "owner_contact_id"]:
contact_model = Contact(org_id=org_model.id)
db.add(contact_model)
db.flush()
org_model.__setattr__(contact_type, contact_model.id)
db.commit()
@ -95,26 +102,6 @@ async def update_status(db: db_dependency, status_request: OrgStatusPatchRequest
db.commit()
@router.patch("/{org_id}/contact")
async def update_contact(db: db_dependency, contact_request: OrgContactPatchRequest, org_id: Annotated[int, Path(gt=0)]):
org_model = db.query(Org).filter(Org.id == org_id).first()
if org_model is None:
raise HTTPException(status_code=404, detail="Organisation not found")
match contact_request.contact_type:
case "billing":
org_model.billing_contact_id = contact_request.contact_id
case "security":
org_model.security_contact_id = contact_request.contact_id
case "owner":
org_model.owner_contact_id = contact_request.contact_id
case _:
raise HTTPException(status_code=422, detail="Invalid contact type")
db.add(org_model)
db.commit()
@router.get("/{org_id}/users", response_model=list[OrgUserGetResponse])
async def get_users(db: db_dependency, org_id: Annotated[int, Path(gt=0)]):
org_exists = db.query(exists().where(Org.id == org_id)).scalar()
@ -147,29 +134,6 @@ async def delete_organisation_by_id(db: db_dependency, org_id: Annotated[int, Pa
db.commit()
@router.get("/{org_id}/contact/{contact_type}", response_model=OrgContactGetResponse)
async def get_contact(db: db_dependency, contact_type: ContactType, org_id: Annotated[int, Path(gt=0)]):
org_model = db.query(Org).filter(Org.id == org_id).first()
if org_model is None:
raise HTTPException(status_code=404, detail="Organisation not found")
match contact_type:
case "billing":
contact_id = org_model.billing_contact_id
case "security":
contact_id = org_model.security_contact_id
case "owner":
contact_id = org_model.owner_contact_id
case _:
raise HTTPException(status_code=422, detail="Invalid contact type")
contact_model = (db.query(Contact).filter(Contact.id == contact_id).first())
if contact_model is None:
raise HTTPException(status_code=404, detail="Contact not found")
return contact_model
@router.patch("/{org_id}/root_user")
async def update_root_user(db: db_dependency, org_model: org_model_dependency, org_id: Annotated[int, Path(gt=0)], root_user: Annotated[int, Query(gt=0)]):
# TODO: Request model, ditch query
@ -197,3 +161,56 @@ async def remove_user_from_org(db: db_dependency, org_model: org_model_dependenc
db.delete(orguser_model)
db.commit()
pass
@router.get("/{org_id}/contact", response_model=OrgContactGetResponse)
async def get_contact(db: db_dependency, org_model: org_model_dependency, contact_type: Annotated[ContactType, Query()], org_id: Annotated[int, Path(gt=0)]):
match contact_type:
case "billing":
contact_id = org_model.billing_contact_id
case "security":
contact_id = org_model.security_contact_id
case "owner":
contact_id = org_model.owner_contact_id
case _:
raise HTTPException(status_code=422, detail="Invalid contact type")
contact_model = (db.query(Contact).filter(Contact.id == contact_id).first())
if contact_model is None:
raise HTTPException(status_code=404, detail="Contact not found")
address = ContactAddress.model_validate(contact_model)
response = OrgContactGetResponse.model_construct(
**contact_model.__dict__,
address=address
)
return response
@router.patch("/{org_id}/contact")
async def update_contact(db: db_dependency, org_model: org_model_dependency, contact_type: Annotated[ContactType, Query()], contact_request: OrgContactPatchRequest, org_id: Annotated[int, Path(gt=0)]):
match contact_type:
case "billing":
contact_id = org_model.billing_contact_id
case "security":
contact_id = org_model.security_contact_id
case "owner":
contact_id = org_model.owner_contact_id
case _:
raise HTTPException(status_code=422, detail="Invalid contact type")
contact_model = (db.query(Contact).filter(Contact.id == contact_id).first())
if contact_model is None:
raise HTTPException(status_code=404, detail="Contact not found")
update_data = contact_request.model_dump(exclude_none=True)
for key, value in update_data.items():
if hasattr(contact_model, key):
setattr(contact_model, key, value)
else:
raise HTTPException(status_code=422, detail="Invalid keys in update request")
db.add(org_model)
db.commit()