investigations.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. from uuid import UUID
  2. from fastapi import APIRouter, HTTPException, Depends, status
  3. from typing import List
  4. from sqlalchemy.orm import Session
  5. from flowsint_core.core.types import Role
  6. from flowsint_core.core.postgre_db import get_db
  7. from flowsint_core.core.models import Profile
  8. from flowsint_core.core.services import (
  9. create_investigation_service,
  10. NotFoundError,
  11. PermissionDeniedError,
  12. ConflictError,
  13. DatabaseError,
  14. )
  15. from app.api.deps import get_current_user
  16. from app.api.schemas.investigation import (
  17. InvestigationRead,
  18. InvestigationCreate,
  19. InvestigationUpdate,
  20. CollaboratorAdd,
  21. CollaboratorUpdate,
  22. CollaboratorRead,
  23. )
  24. from app.api.schemas.sketch import SketchRead
  25. router = APIRouter()
  26. def _inject_current_user_role(service, investigation, user_id) -> InvestigationRead:
  27. """Build InvestigationRead with the current user's role attached."""
  28. result = InvestigationRead.model_validate(investigation)
  29. role_entry = service.get_user_role_for_investigation(user_id, investigation.id)
  30. if role_entry and role_entry.roles:
  31. result.current_user_role = role_entry.roles[0].value
  32. return result
  33. @router.get("", response_model=List[InvestigationRead])
  34. def get_investigations(
  35. db: Session = Depends(get_db),
  36. current_user: Profile = Depends(get_current_user),
  37. ):
  38. """Get all investigations accessible to the user based on their roles."""
  39. service = create_investigation_service(db)
  40. allowed_roles = [Role.OWNER, Role.ADMIN, Role.EDITOR, Role.VIEWER]
  41. investigations = service.get_accessible_investigations(
  42. user_id=current_user.id, allowed_roles=allowed_roles
  43. )
  44. return [
  45. _inject_current_user_role(service, inv, current_user.id)
  46. for inv in investigations
  47. ]
  48. @router.post(
  49. "/create", response_model=InvestigationRead, status_code=status.HTTP_201_CREATED
  50. )
  51. def create_investigation(
  52. payload: InvestigationCreate,
  53. db: Session = Depends(get_db),
  54. current_user: Profile = Depends(get_current_user),
  55. ):
  56. service = create_investigation_service(db)
  57. investigation = service.create(
  58. name=payload.name,
  59. description=payload.description,
  60. owner_id=current_user.id,
  61. )
  62. return _inject_current_user_role(service, investigation, current_user.id)
  63. @router.get("/{investigation_id}", response_model=InvestigationRead)
  64. def get_investigation_by_id(
  65. investigation_id: UUID,
  66. db: Session = Depends(get_db),
  67. current_user: Profile = Depends(get_current_user),
  68. ):
  69. service = create_investigation_service(db)
  70. try:
  71. investigation = service.get_by_id(investigation_id, current_user.id)
  72. return _inject_current_user_role(service, investigation, current_user.id)
  73. except NotFoundError:
  74. raise HTTPException(status_code=404, detail="Investigation not found")
  75. except PermissionDeniedError:
  76. raise HTTPException(status_code=403, detail="Forbidden")
  77. @router.get("/{investigation_id}/sketches", response_model=List[SketchRead])
  78. def get_sketches_by_investigation(
  79. investigation_id: UUID,
  80. db: Session = Depends(get_db),
  81. current_user: Profile = Depends(get_current_user),
  82. ):
  83. service = create_investigation_service(db)
  84. try:
  85. return service.get_sketches(investigation_id, current_user.id)
  86. except NotFoundError:
  87. raise HTTPException(
  88. status_code=404, detail="No sketches found for this investigation"
  89. )
  90. except PermissionDeniedError:
  91. raise HTTPException(status_code=403, detail="Forbidden")
  92. @router.put("/{investigation_id}", response_model=InvestigationRead)
  93. def update_investigation(
  94. investigation_id: UUID,
  95. payload: InvestigationUpdate,
  96. db: Session = Depends(get_db),
  97. current_user: Profile = Depends(get_current_user),
  98. ):
  99. service = create_investigation_service(db)
  100. try:
  101. investigation = service.update(
  102. investigation_id=investigation_id,
  103. user_id=current_user.id,
  104. name=payload.name,
  105. description=payload.description,
  106. status=payload.status,
  107. )
  108. return _inject_current_user_role(service, investigation, current_user.id)
  109. except NotFoundError:
  110. raise HTTPException(status_code=404, detail="Investigation not found")
  111. except PermissionDeniedError:
  112. raise HTTPException(status_code=403, detail="Forbidden")
  113. @router.delete("/{investigation_id}", status_code=status.HTTP_204_NO_CONTENT)
  114. def delete_investigation(
  115. investigation_id: UUID,
  116. db: Session = Depends(get_db),
  117. current_user: Profile = Depends(get_current_user),
  118. ):
  119. service = create_investigation_service(db)
  120. try:
  121. service.delete(investigation_id, current_user.id)
  122. return None
  123. except NotFoundError:
  124. raise HTTPException(status_code=404, detail="Investigation not found")
  125. except PermissionDeniedError:
  126. raise HTTPException(status_code=403, detail="Forbidden")
  127. except DatabaseError:
  128. raise HTTPException(status_code=500, detail="Failed to clean up graph data")
  129. # ── Collaborator endpoints ───────────────────────────────────────────
  130. @router.get(
  131. "/{investigation_id}/collaborators", response_model=List[CollaboratorRead]
  132. )
  133. def get_collaborators(
  134. investigation_id: UUID,
  135. db: Session = Depends(get_db),
  136. current_user: Profile = Depends(get_current_user),
  137. ):
  138. service = create_investigation_service(db)
  139. try:
  140. entries = service.get_collaborators(investigation_id, current_user.id)
  141. return [
  142. CollaboratorRead(
  143. id=e.id,
  144. user_id=e.user_id,
  145. roles=[r.value for r in e.roles],
  146. user=e.user,
  147. )
  148. for e in entries
  149. ]
  150. except PermissionDeniedError:
  151. raise HTTPException(status_code=403, detail="Forbidden")
  152. @router.post(
  153. "/{investigation_id}/collaborators",
  154. response_model=CollaboratorRead,
  155. status_code=status.HTTP_201_CREATED,
  156. )
  157. def add_collaborator(
  158. investigation_id: UUID,
  159. payload: CollaboratorAdd,
  160. db: Session = Depends(get_db),
  161. current_user: Profile = Depends(get_current_user),
  162. ):
  163. service = create_investigation_service(db)
  164. try:
  165. role = Role(payload.role.lower())
  166. except ValueError:
  167. raise HTTPException(status_code=400, detail="Invalid role")
  168. try:
  169. entry = service.add_collaborator(
  170. investigation_id=investigation_id,
  171. user_id=current_user.id,
  172. target_email=payload.email,
  173. role=role,
  174. )
  175. return CollaboratorRead(
  176. id=entry.id,
  177. user_id=entry.user_id,
  178. roles=[r.value for r in entry.roles],
  179. user=entry.user,
  180. )
  181. except NotFoundError as e:
  182. raise HTTPException(status_code=404, detail=str(e.message))
  183. except PermissionDeniedError:
  184. raise HTTPException(status_code=403, detail="Forbidden")
  185. except ConflictError:
  186. raise HTTPException(status_code=409, detail="User is already a collaborator")
  187. @router.put(
  188. "/{investigation_id}/collaborators/{user_id}",
  189. response_model=CollaboratorRead,
  190. )
  191. def update_collaborator_role(
  192. investigation_id: UUID,
  193. user_id: UUID,
  194. payload: CollaboratorUpdate,
  195. db: Session = Depends(get_db),
  196. current_user: Profile = Depends(get_current_user),
  197. ):
  198. service = create_investigation_service(db)
  199. try:
  200. role = Role(payload.role.lower())
  201. except ValueError:
  202. raise HTTPException(status_code=400, detail="Invalid role")
  203. try:
  204. entry = service.update_collaborator_role(
  205. investigation_id=investigation_id,
  206. user_id=current_user.id,
  207. target_user_id=user_id,
  208. role=role,
  209. )
  210. return CollaboratorRead(
  211. id=entry.id,
  212. user_id=entry.user_id,
  213. roles=[r.value for r in entry.roles],
  214. user=entry.user,
  215. )
  216. except NotFoundError:
  217. raise HTTPException(status_code=404, detail="Collaborator not found")
  218. except PermissionDeniedError:
  219. raise HTTPException(status_code=403, detail="Forbidden")
  220. @router.delete(
  221. "/{investigation_id}/collaborators/{user_id}",
  222. status_code=status.HTTP_204_NO_CONTENT,
  223. )
  224. def remove_collaborator(
  225. investigation_id: UUID,
  226. user_id: UUID,
  227. db: Session = Depends(get_db),
  228. current_user: Profile = Depends(get_current_user),
  229. ):
  230. service = create_investigation_service(db)
  231. try:
  232. service.remove_collaborator(
  233. investigation_id=investigation_id,
  234. user_id=current_user.id,
  235. target_user_id=user_id,
  236. )
  237. return None
  238. except NotFoundError:
  239. raise HTTPException(status_code=404, detail="Collaborator not found")
  240. except PermissionDeniedError:
  241. raise HTTPException(status_code=403, detail="Forbidden")