feat: handling for integrity errors

Resolves: #7
This commit is contained in:
Chris Milne 2026-05-27 16:26:34 +01:00
parent fc835dc982
commit 1ed0cfb38c
4 changed files with 47 additions and 9 deletions

View file

@ -13,3 +13,12 @@ class UnprocessableContent(HTTPException):
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
detail=detail,
)
class Conflict(HTTPException):
def __init__(self, message: Optional[str] = None) -> None:
detail = "Conflict" if not message else message
super().__init__(
status_code=status.HTTP_409_CONFLICT,
detail=detail,
)

View file

@ -6,8 +6,10 @@ Endpoints:
- Endpoints: Description
"""
from fastapi import APIRouter, status
from sqlalchemy.exc import IntegrityError
from psycopg import errors
from src.exceptions import Conflict
from src.database import db_dependency
from src.schemas import ResourceName
from src.auth.exceptions import UnauthorizedException
@ -84,7 +86,11 @@ async def create_group(db: db_dependency, org_model: org_model_root_claim_body_d
group_model = Group(name=request_model.name, org_id=org_model.id)
db.add(group_model)
db.flush()
try:
db.flush()
except IntegrityError as e:
if isinstance(e.orig, errors.UniqueViolation):
raise Conflict("Group with this name already exists")
response = GroupResponse(**group_model.__dict__)
db.commit()
return {"group": response}
@ -95,6 +101,9 @@ async def add_group_permission(db: db_dependency, group_model: group_model_body_
if group_model.org_id == org_model.id:
raise UnauthorizedException()
if perm_model in group_model.permission_rel:
raise Conflict("Group already has this permission")
group_model.permission_rel.append(perm_model)
db.flush()
@ -108,6 +117,9 @@ async def add_group_user(db: db_dependency, group_model: group_model_body_depend
if group_model.org_id == org_model.id:
raise UnauthorizedException()
if user_model in group_model.user_rel:
raise Conflict("User already in group")
group_model.user_rel.append(user_model)
db.flush()
response = IAMPutGroupUserResponse(group=GroupResponse(**group_model.__dict__), users=group_model.user_rel)
@ -151,8 +163,11 @@ async def get_permissions(db: db_dependency, org_model: org_model_root_claim_bod
@router.post("/permission")
async def create_new_permission(db: db_dependency, su: super_admin_dependency, request_mode: IAMPostPermissionRequest):
perm_model = Perm(**request_mode.__dict__)
db.add(perm_model)
try:
db.add(perm_model)
except IntegrityError as e:
if isinstance(e.orig, errors.UniqueViolation):
raise Conflict(message="Permission already exists")
db.flush()
response = IAMPostPermissionResponse(permission=PermissionResponse(**perm_model.__dict__))
db.commit()

View file

@ -16,8 +16,10 @@ from typing import Annotated, Optional
from fastapi import APIRouter, status
from fastapi.params import Query
from psycopg.errors import UniqueViolation
from sqlalchemy.exc import IntegrityError
from src.exceptions import UnprocessableContent
from src.exceptions import UnprocessableContent, Conflict
from src.contact.models import Contact
from src.contact.schemas import ContactAddress
from src.contact.exceptions import ContactNotFoundException
@ -66,9 +68,12 @@ async def create_org(db: db_dependency, user_model: user_model_claims_dependency
org_model.status = "partial" # Status is always set to partial at first, see update_questionnaire() doc
db.add(org_model)
db.flush()
try:
db.flush()
except IntegrityError as e:
if isinstance(e.orig, UniqueViolation):
raise Conflict(message="Organisation with this name already exists")
# Adds currently logged-in user to org users list and sets them as root_user
user_model = db.get(User, db_id)
org_model.user_rel.append(user_model)
org_model.root_user_rel = user_model
for contact_type in ["billing_contact_id", "security_contact_id", "owner_contact_id"]:
@ -110,7 +115,7 @@ async def get_users(org_model: org_model_root_claim_query_dependency):
@router.post("/users")
async def add_user_to_org(db: db_dependency, org_model: org_model_root_claim_body_dependency, user_model: user_model_body_dependency, request_model: OrgUserPostRequest):
if user_model in org_model.user_rel:
return
raise Conflict(message="User already a part of this organisation")
org_model.user_rel.append(user_model)
db.commit()

View file

@ -6,7 +6,11 @@ Endpoints:
- Endpoints: Description
"""
from fastapi import APIRouter, status
from psycopg.errors import UniqueViolation
from sqlalchemy.exc import IntegrityError
from auth.exceptions import UnauthorizedException
from exceptions import Conflict
from src.database import db_dependency
from src.auth.service import claims_dependency
from src.auth.dependencies import super_admin_dependency, org_model_root_claim_query_dependency, org_model_root_claim_body_dependency
@ -34,7 +38,12 @@ async def register_service(db: db_dependency, su: super_admin_dependency, servic
service_model = Service(name=service_request.name, api_key=key)
db.add(service_model)
db.flush()
try:
db.flush()
except IntegrityError as e:
if isinstance(e.orig, UniqueViolation):
raise Conflict(message="Service with this name already exists")
db.commit()
response = ServiceWithKeyResponse(**service_model.__dict__)
db.commit()
return {"service": response}