fix: ty compliant & issues from change to mapped columns
This commit is contained in:
parent
55927946c7
commit
58e7ae6c5c
31 changed files with 271 additions and 254 deletions
|
|
@ -14,12 +14,12 @@ class Status(StrEnum):
|
|||
Enumeration of organisation statuses.
|
||||
|
||||
Attributes:
|
||||
PARTIAL(str): Organisation has been created but questionnaire hasn't been submitted.
|
||||
SUBMITTED (str): Questionnaire submitted but not approved.
|
||||
REMEDIATION (str): Questionnaire submitted but requires revisions.
|
||||
APPROVED (str): Questionnaire has been approved by an admin.
|
||||
REJECTED (str): Questionnaire has been rejected by an admin.
|
||||
REMOVED (str): Organisation has been removed.
|
||||
PARTIAL(str): Organisation has been created but questionnaire hasn't been submitted.
|
||||
SUBMITTED (str): Questionnaire submitted but not approved.
|
||||
REMEDIATION (str): Questionnaire submitted but requires revisions.
|
||||
APPROVED (str): Questionnaire has been approved by an admin.
|
||||
REJECTED (str): Questionnaire has been rejected by an admin.
|
||||
REMOVED (str): Organisation has been removed.
|
||||
"""
|
||||
|
||||
PARTIAL = auto()
|
||||
|
|
@ -47,9 +47,9 @@ class ContactType(StrEnum):
|
|||
Enumeration of organisation contact types.
|
||||
|
||||
Attributes:
|
||||
BILLING(str): Billing contact.
|
||||
SECURITY (str): Security contact.
|
||||
OWNER (str): Owner contact.
|
||||
BILLING(str): Billing contact.
|
||||
SECURITY (str): Security contact.
|
||||
OWNER (str): Owner contact.
|
||||
"""
|
||||
|
||||
BILLING = auto()
|
||||
|
|
|
|||
|
|
@ -17,19 +17,17 @@ from src.organisation.models import Organisation as Org
|
|||
from src.organisation.exceptions import OrgNotFoundException
|
||||
|
||||
|
||||
def get_org_model_query(
|
||||
db: db_dependency, org_id: Annotated[int, Query(gt=0)]
|
||||
) -> type[Org]:
|
||||
def get_org_model_query(db: db_dependency, org_id: Annotated[int, Query(gt=0)]) -> Org:
|
||||
org_model = db.get(Org, org_id)
|
||||
if org_model is None:
|
||||
raise OrgNotFoundException(org_id)
|
||||
return org_model
|
||||
|
||||
|
||||
org_model_query_dependency = Annotated[type[Org], Depends(get_org_model_query)]
|
||||
org_model_query_dependency = Annotated[Org, Depends(get_org_model_query)]
|
||||
|
||||
|
||||
def get_org_model_body(db: db_dependency, request_model: OrgIDMixin) -> type[Org]:
|
||||
def get_org_model_body(db: db_dependency, request_model: OrgIDMixin) -> Org:
|
||||
org_id: Optional[int] = getattr(request_model, "organisation_id", None)
|
||||
if org_id is None:
|
||||
raise OrgNotFoundException()
|
||||
|
|
@ -41,4 +39,4 @@ def get_org_model_body(db: db_dependency, request_model: OrgIDMixin) -> type[Org
|
|||
return org_model
|
||||
|
||||
|
||||
org_model_body_dependency = Annotated[type[Org], Depends(get_org_model_body)]
|
||||
org_model_body_dependency = Annotated[Org, Depends(get_org_model_body)]
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ Models:
|
|||
- owner_contact_rel: ORM relationship to Contact with owner_contact FK
|
||||
- OrgUsers: org_id[FK][PK], user_id[FK][PK]
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import ForeignKey
|
||||
|
|
@ -30,15 +31,17 @@ class Organisation(CustomBase):
|
|||
intake_questionnaire: Mapped[dict[str, Any] | None]
|
||||
|
||||
root_user_id: Mapped[int] = mapped_column(ForeignKey("user.id"))
|
||||
billing_contact_id: Mapped[int] = mapped_column(ForeignKey("contact.id"))
|
||||
security_contact_id: Mapped[int] = mapped_column(ForeignKey("contact.id"))
|
||||
owner_contact_id: Mapped[int] = mapped_column(ForeignKey("contact.id"))
|
||||
|
||||
user_rel = relationship(
|
||||
"User", secondary="orgusers", back_populates="organisation_rel"
|
||||
billing_contact_id: Mapped[int] = mapped_column(ForeignKey("contact.id"), nullable=True)
|
||||
security_contact_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("contact.id"), nullable=True
|
||||
)
|
||||
owner_contact_id: Mapped[int] = mapped_column(ForeignKey("contact.id"), nullable=True)
|
||||
|
||||
group_rel = relationship("Group", back_populates="org_rel")
|
||||
user_rel = relationship("User", secondary="orgusers", back_populates="organisation_rel")
|
||||
|
||||
group_rel = relationship(
|
||||
"Group", back_populates="org_rel", cascade="all, delete-orphan"
|
||||
)
|
||||
root_user_rel = relationship("User", foreign_keys="Organisation.root_user_id")
|
||||
|
||||
billing_contact_rel = relationship(
|
||||
|
|
@ -56,8 +59,9 @@ class Organisation(CustomBase):
|
|||
)
|
||||
|
||||
@property
|
||||
def root_user_email(self):
|
||||
return self.root_user_rel.email if self.root_user_rel else None
|
||||
def root_user_email(self) -> str:
|
||||
return self.root_user_rel.email if self.root_user_rel else ""
|
||||
|
||||
|
||||
class OrgUsers(CustomBase):
|
||||
__tablename__ = "orgusers"
|
||||
|
|
@ -65,4 +69,6 @@ class OrgUsers(CustomBase):
|
|||
org_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("organisation.id", ondelete="CASCADE"), primary_key=True
|
||||
)
|
||||
user_id: Mapped[int] = mapped_column(ForeignKey("user.id", ondelete="CASCADE"), primary_key=True)
|
||||
user_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("user.id", ondelete="CASCADE"), primary_key=True
|
||||
)
|
||||
|
|
|
|||
|
|
@ -133,9 +133,7 @@ async def get_org_by_id(
|
|||
response_model=OrgPostOrgResponse,
|
||||
responses={
|
||||
status.HTTP_201_CREATED: {"description": "Successfully created organisation."},
|
||||
status.HTTP_422_UNPROCESSABLE_CONTENT: {
|
||||
"description": "Invalid data in request."
|
||||
},
|
||||
status.HTTP_422_UNPROCESSABLE_CONTENT: {"description": "Invalid data in request."},
|
||||
status.HTTP_401_UNAUTHORIZED: {
|
||||
"description": "User must be logged in with OIDC to create organisation."
|
||||
},
|
||||
|
|
@ -169,6 +167,7 @@ async def create_org(
|
|||
org_model = Org(
|
||||
name=request_model.name,
|
||||
intake_questionnaire=intake_questionnaire.model_dump(mode="json"),
|
||||
root_user_id=user_model.id,
|
||||
)
|
||||
|
||||
org_model.status = "partial"
|
||||
|
|
@ -181,13 +180,10 @@ async def create_org(
|
|||
isinstance(e.orig, UniqueViolation) # Postgres unique violation
|
||||
or "UNIQUE constraint failed" in str(e.orig) # SQLite unique violation
|
||||
):
|
||||
raise ConflictException(
|
||||
message="Organisation with this name already exists"
|
||||
)
|
||||
raise ConflictException(message="Organisation with this name already exists")
|
||||
raise
|
||||
# Adds currently logged-in user to org users list and sets them as root_user
|
||||
org_model.user_rel.append(user_model)
|
||||
org_model.root_user_rel = user_model
|
||||
|
||||
background_tasks.add_task(
|
||||
assign_defaults, db, org_id=org_model.id, user_id=user_model.id
|
||||
|
|
@ -214,9 +210,7 @@ async def create_org(
|
|||
response_model=OrgPatchQuestionnaireResponse,
|
||||
responses={
|
||||
status.HTTP_200_OK: {"description": "Successfully updated questionnaire."},
|
||||
status.HTTP_422_UNPROCESSABLE_CONTENT: {
|
||||
"description": "Invalid data in request."
|
||||
},
|
||||
status.HTTP_422_UNPROCESSABLE_CONTENT: {"description": "Invalid data in request."},
|
||||
status.HTTP_403_FORBIDDEN: {
|
||||
"description": "Not authorised. Must be org root user."
|
||||
},
|
||||
|
|
@ -234,12 +228,22 @@ async def update_questionnaire(
|
|||
"""
|
||||
org_status = StatusEnum(org_model.status)
|
||||
if not org_status.is_pre_submission:
|
||||
raise ForbiddenException(
|
||||
"Questionnaire may only be modified prior to submission."
|
||||
)
|
||||
update_data = request_model.intake_questionnaire.model_dump(exclude_none=True)
|
||||
raise ForbiddenException("Questionnaire may only be modified prior to submission.")
|
||||
update_data: dict = request_model.intake_questionnaire.model_dump(exclude_none=True)
|
||||
questionnaire = org_model.intake_questionnaire
|
||||
questions_model = QuestionnaireQuestionsVersion0(**questionnaire["questions"])
|
||||
if questionnaire is None:
|
||||
questionnaire_questions = QuestionnaireQuestionsVersion0().model_dump()
|
||||
|
||||
questionnaire_metadata = QuestionnaireMetadata(version=0, submission_date=None)
|
||||
|
||||
questionnaire = Questionnaire(
|
||||
metadata=questionnaire_metadata,
|
||||
questions=questionnaire_questions,
|
||||
).model_dump()
|
||||
|
||||
questions_model = QuestionnaireQuestionsVersion0(**questionnaire["questions"])
|
||||
else:
|
||||
questions_model = QuestionnaireQuestionsVersion0(**questionnaire["questions"])
|
||||
for key, value in update_data.items():
|
||||
if hasattr(questions_model, key):
|
||||
setattr(questions_model, key, value)
|
||||
|
|
@ -271,15 +275,9 @@ async def update_questionnaire(
|
|||
status_code=status.HTTP_200_OK,
|
||||
response_model=OrgPatchStatusResponse,
|
||||
responses={
|
||||
status.HTTP_200_OK: {
|
||||
"description": "Successfully updated organisation status."
|
||||
},
|
||||
status.HTTP_422_UNPROCESSABLE_CONTENT: {
|
||||
"description": "Invalid data in request."
|
||||
},
|
||||
status.HTTP_403_FORBIDDEN: {
|
||||
"description": "Not authorised. Must be super admin."
|
||||
},
|
||||
status.HTTP_200_OK: {"description": "Successfully updated organisation status."},
|
||||
status.HTTP_422_UNPROCESSABLE_CONTENT: {"description": "Invalid data in request."},
|
||||
status.HTTP_403_FORBIDDEN: {"description": "Not authorised. Must be super admin."},
|
||||
},
|
||||
)
|
||||
async def update_status(
|
||||
|
|
@ -329,15 +327,11 @@ async def get_users(org_model: org_model_root_claim_query_dependency):
|
|||
status_code=status.HTTP_200_OK,
|
||||
response_model=OrgPostUserResponse,
|
||||
responses={
|
||||
status.HTTP_200_OK: {
|
||||
"description": "Successfully added user to the organisation."
|
||||
},
|
||||
status.HTTP_200_OK: {"description": "Successfully added user to the organisation."},
|
||||
status.HTTP_403_FORBIDDEN: {
|
||||
"description": "Not authorised. Must be org root user."
|
||||
},
|
||||
status.HTTP_422_UNPROCESSABLE_CONTENT: {
|
||||
"description": "Invalid data in request."
|
||||
},
|
||||
status.HTTP_422_UNPROCESSABLE_CONTENT: {"description": "Invalid data in request."},
|
||||
status.HTTP_409_CONFLICT: {
|
||||
"description": "User is already a member of the organisation."
|
||||
},
|
||||
|
|
@ -378,12 +372,8 @@ async def add_user_to_org(
|
|||
summary="Delete organisation from the hub.",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={
|
||||
status.HTTP_204_NO_CONTENT: {
|
||||
"description": "Successfully deleted organisation."
|
||||
},
|
||||
status.HTTP_403_FORBIDDEN: {
|
||||
"description": "Not authorised. Must be super admin."
|
||||
},
|
||||
status.HTTP_204_NO_CONTENT: {"description": "Successfully deleted organisation."},
|
||||
status.HTTP_403_FORBIDDEN: {"description": "Not authorised. Must be super admin."},
|
||||
status.HTTP_422_UNPROCESSABLE_CONTENT: {
|
||||
"description": "Org ID missing or invalid."
|
||||
},
|
||||
|
|
@ -406,9 +396,7 @@ async def delete_organisation_by_id(
|
|||
summary="Delete organisation from the hub as root user before it has been approved.",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={
|
||||
status.HTTP_204_NO_CONTENT: {
|
||||
"description": "Successfully deleted organisation."
|
||||
},
|
||||
status.HTTP_204_NO_CONTENT: {"description": "Successfully deleted organisation."},
|
||||
status.HTTP_422_UNPROCESSABLE_CONTENT: {
|
||||
"description": "Unprocessable content.",
|
||||
"content": {
|
||||
|
|
@ -452,9 +440,7 @@ async def delete_organisation_by_id(
|
|||
"content": {
|
||||
"application/json": {
|
||||
"examples": {
|
||||
"db_id": {
|
||||
"summary": "User not found in db when checking claims."
|
||||
},
|
||||
"db_id": {"summary": "User not found in db when checking claims."},
|
||||
"user_model": {"summary": "User model not found in db."},
|
||||
"org_model": {"summary": "Org model not found in db."},
|
||||
}
|
||||
|
|
@ -472,9 +458,7 @@ async def delete_preapproved_organisation_by_id(
|
|||
"""
|
||||
org_status = StatusEnum(org_model.status)
|
||||
if not org_status.is_pre_approval:
|
||||
raise ForbiddenException(
|
||||
message="Organisation is no longer in pre-approval state."
|
||||
)
|
||||
raise ForbiddenException(message="Organisation is no longer in pre-approval state.")
|
||||
|
||||
db.delete(org_model)
|
||||
db.commit()
|
||||
|
|
@ -487,9 +471,7 @@ async def delete_preapproved_organisation_by_id(
|
|||
response_model=OrgPatchRootResponse,
|
||||
responses={
|
||||
status.HTTP_200_OK: {"description": "Successfully updated root user."},
|
||||
status.HTTP_422_UNPROCESSABLE_CONTENT: {
|
||||
"description": "Invalid data in request."
|
||||
},
|
||||
status.HTTP_422_UNPROCESSABLE_CONTENT: {"description": "Invalid data in request."},
|
||||
status.HTTP_401_UNAUTHORIZED: {
|
||||
"description": "Not authorised. Must be super admin."
|
||||
},
|
||||
|
|
@ -539,9 +521,7 @@ async def get_org_groups(org_model: org_model_root_claim_query_dependency):
|
|||
"""
|
||||
return {
|
||||
"organisation": org_model,
|
||||
"groups": [
|
||||
{"id": group.id, "name": group.name} for group in org_model.group_rel
|
||||
],
|
||||
"groups": [{"id": group.id, "name": group.name} for group in org_model.group_rel],
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -554,9 +534,7 @@ async def get_org_groups(org_model: org_model_root_claim_query_dependency):
|
|||
status.HTTP_403_FORBIDDEN: {
|
||||
"description": "Not authorised. Must be org root user."
|
||||
},
|
||||
status.HTTP_422_UNPROCESSABLE_CONTENT: {
|
||||
"description": "Invalid data in request."
|
||||
},
|
||||
status.HTTP_422_UNPROCESSABLE_CONTENT: {"description": "Invalid data in request."},
|
||||
},
|
||||
)
|
||||
async def remove_user_from_org(
|
||||
|
|
@ -581,9 +559,7 @@ async def remove_user_from_org(
|
|||
response_model=OrgGetContactResponse,
|
||||
responses={
|
||||
status.HTTP_200_OK: {"description": "Successful retrieval of contact."},
|
||||
status.HTTP_422_UNPROCESSABLE_CONTENT: {
|
||||
"description": "Invalid data in request."
|
||||
},
|
||||
status.HTTP_422_UNPROCESSABLE_CONTENT: {"description": "Invalid data in request."},
|
||||
status.HTTP_403_FORBIDDEN: {
|
||||
"description": "Not authorised. Must be org root user."
|
||||
},
|
||||
|
|
@ -626,9 +602,7 @@ async def get_contact(
|
|||
response_model=OrgPatchContactResponse,
|
||||
responses={
|
||||
status.HTTP_200_OK: {"description": "Successfully updated contact."},
|
||||
status.HTTP_422_UNPROCESSABLE_CONTENT: {
|
||||
"description": "Invalid data in request."
|
||||
},
|
||||
status.HTTP_422_UNPROCESSABLE_CONTENT: {"description": "Invalid data in request."},
|
||||
status.HTTP_403_FORBIDDEN: {
|
||||
"description": "Not authorised. Must be org root user."
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ Reusable business logic functions for the organisation module
|
|||
"""
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import cast
|
||||
|
||||
from src.iam.service import assign_default_group
|
||||
from src.organisation.models import Organisation as Org
|
||||
|
|
@ -50,9 +49,6 @@ async def assign_defaults(
|
|||
print("User not found while adding defaults")
|
||||
return
|
||||
|
||||
org_model = cast(Org, org_model)
|
||||
user_model = cast(User, user_model)
|
||||
|
||||
await add_default_org_permissions(db, org_model, default_org_permissions)
|
||||
await assign_default_group(
|
||||
db=db,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue