From edaa33c6a3e624ec5ed697eb19a1f6183113a686 Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Wed, 23 Apr 2025 10:48:00 +0530 Subject: [PATCH 01/13] introducing columns --- .../versions/0f205e3779ee_add_api_key_table.py | 9 +++++---- .../versions/543f97951bd0_add_credential_table.py | 4 ++-- .../99f4fc325617_add_organization_project_setup.py | 10 +++++----- .../versions/c43313eca57d_add_document_tables.py | 2 +- .../versions/e2412789c190_initialize_models.py | 12 ++++++------ backend/app/crud/api_key.py | 1 + backend/app/crud/document.py | 3 +++ backend/app/crud/project_user.py | 1 + backend/app/models/api_key.py | 5 +++-- backend/app/models/document.py | 2 +- backend/app/models/project_user.py | 4 ++-- backend/app/tests/crud/documents/test_crud_delete.py | 2 +- backend/app/tests/crud/documents/test_crud_update.py | 2 +- 13 files changed, 32 insertions(+), 25 deletions(-) diff --git a/backend/app/alembic/versions/0f205e3779ee_add_api_key_table.py b/backend/app/alembic/versions/0f205e3779ee_add_api_key_table.py index 6e864d245..b62314076 100644 --- a/backend/app/alembic/versions/0f205e3779ee_add_api_key_table.py +++ b/backend/app/alembic/versions/0f205e3779ee_add_api_key_table.py @@ -21,12 +21,13 @@ def upgrade(): # ### commands auto generated by Alembic - please adjust! ### op.create_table( "apikey", - sa.Column("organization_id", sa.Integer(), nullable=False), - sa.Column("user_id", sa.Uuid(), nullable=False), - sa.Column("key", sqlmodel.sql.sqltypes.AutoString(), nullable=False), sa.Column("id", sa.Integer(), nullable=False), - sa.Column("created_at", sa.DateTime(), nullable=False), + sa.Column("key", sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column("user_id", sa.Uuid(), nullable=False), + sa.Column("organization_id", sa.Integer(), nullable=False), sa.Column("is_deleted", sa.Boolean(), nullable=False), + sa.Column("inserted_at", sa.DateTime(), nullable=False), + sa.Column("updated_at", sa.DateTime(), nullable=False), sa.Column("deleted_at", sa.DateTime(), nullable=True), sa.ForeignKeyConstraint( ["organization_id"], ["organization.id"], ondelete="CASCADE" diff --git a/backend/app/alembic/versions/543f97951bd0_add_credential_table.py b/backend/app/alembic/versions/543f97951bd0_add_credential_table.py index 392b259dd..24927d9b8 100644 --- a/backend/app/alembic/versions/543f97951bd0_add_credential_table.py +++ b/backend/app/alembic/versions/543f97951bd0_add_credential_table.py @@ -20,10 +20,10 @@ def upgrade(): op.create_table( "credential", - sa.Column("organization_id", sa.Integer(), nullable=False), - sa.Column("is_active", sa.Boolean(), nullable=False), sa.Column("id", sa.Integer(), nullable=False), + sa.Column("is_active", sa.Boolean(), nullable=False), sa.Column("credential", sa.JSON(), nullable=True), + sa.Column("organization_id", sa.Integer(), nullable=False), sa.Column("inserted_at", sa.DateTime(), nullable=True), sa.Column("updated_at", sa.DateTime(), nullable=True), sa.Column("deleted_at", sa.DateTime(), nullable=True), diff --git a/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py b/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py index 9122c0f73..61b5e9a2c 100644 --- a/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py +++ b/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py @@ -21,20 +21,20 @@ def upgrade(): # ### commands auto generated by Alembic - please adjust! ### op.create_table( "organization", + sa.Column("id", sa.Integer(), nullable=False), sa.Column("name", sqlmodel.sql.sqltypes.AutoString(length=255), nullable=False), sa.Column("is_active", sa.Boolean(), nullable=False), - sa.Column("id", sa.Integer(), nullable=False), sa.PrimaryKeyConstraint("id"), ) op.create_index(op.f("ix_organization_name"), "organization", ["name"], unique=True) op.create_table( "project", + sa.Column("id", sa.Integer(), nullable=False), sa.Column("name", sqlmodel.sql.sqltypes.AutoString(length=255), nullable=False), sa.Column( "description", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=True ), sa.Column("is_active", sa.Boolean(), nullable=False), - sa.Column("id", sa.Integer(), nullable=False), sa.Column("organization_id", sa.Integer(), nullable=False), sa.ForeignKeyConstraint( ["organization_id"], @@ -48,13 +48,13 @@ def upgrade(): ) op.create_table( "projectuser", + sa.Column("id", sa.Integer(), nullable=False), sa.Column("project_id", sa.Integer(), nullable=False), sa.Column("user_id", sa.Uuid(), nullable=False), sa.Column("is_admin", sa.Boolean(), nullable=False), - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("created_at", sa.DateTime(), nullable=False), - sa.Column("updated_at", sa.DateTime(), nullable=False), sa.Column("is_deleted", sa.Boolean(), nullable=False), + sa.Column("inserted_at", sa.DateTime(), nullable=False), + sa.Column("updated_at", sa.DateTime(), nullable=False), sa.Column("deleted_at", sa.DateTime(), nullable=True), sa.ForeignKeyConstraint(["project_id"], ["project.id"], ondelete="CASCADE"), sa.ForeignKeyConstraint(["user_id"], ["user.id"], ondelete="CASCADE"), diff --git a/backend/app/alembic/versions/c43313eca57d_add_document_tables.py b/backend/app/alembic/versions/c43313eca57d_add_document_tables.py index 6dd66f0e9..c3abd47df 100644 --- a/backend/app/alembic/versions/c43313eca57d_add_document_tables.py +++ b/backend/app/alembic/versions/c43313eca57d_add_document_tables.py @@ -27,7 +27,7 @@ def upgrade(): sa.Column( "object_store_url", sqlmodel.sql.sqltypes.AutoString(), nullable=False ), - sa.Column("created_at", sa.DateTime(), nullable=False), + sa.Column("inserted_at", sa.DateTime(), nullable=False), sa.Column("deleted_at", sa.DateTime(), nullable=True), sa.ForeignKeyConstraint(["owner_id"], ["user.id"], ondelete="CASCADE"), sa.PrimaryKeyConstraint("id"), diff --git a/backend/app/alembic/versions/e2412789c190_initialize_models.py b/backend/app/alembic/versions/e2412789c190_initialize_models.py index 7529ea91f..9fa1784c0 100644 --- a/backend/app/alembic/versions/e2412789c190_initialize_models.py +++ b/backend/app/alembic/versions/e2412789c190_initialize_models.py @@ -20,23 +20,23 @@ def upgrade(): # ### commands auto generated by Alembic - please adjust! ### op.create_table( "user", - sa.Column("email", sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column("is_active", sa.Boolean(), nullable=False), - sa.Column("is_superuser", sa.Boolean(), nullable=False), - sa.Column("full_name", sqlmodel.sql.sqltypes.AutoString(), nullable=True), sa.Column("id", sa.Integer(), nullable=False), + sa.Column("full_name", sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column("email", sqlmodel.sql.sqltypes.AutoString(), nullable=False), sa.Column( "hashed_password", sqlmodel.sql.sqltypes.AutoString(), nullable=False ), + sa.Column("is_active", sa.Boolean(), nullable=False), + sa.Column("is_superuser", sa.Boolean(), nullable=False), sa.PrimaryKeyConstraint("id"), ) op.create_index(op.f("ix_user_email"), "user", ["email"], unique=True) op.create_table( "item", - sa.Column("description", sqlmodel.sql.sqltypes.AutoString(), nullable=True), sa.Column("id", sa.Integer(), nullable=False), - sa.Column("title", sqlmodel.sql.sqltypes.AutoString(), nullable=False), sa.Column("owner_id", sa.Integer(), nullable=False), + sa.Column("title", sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column("description", sqlmodel.sql.sqltypes.AutoString(), nullable=True), sa.ForeignKeyConstraint( ["owner_id"], ["user.id"], diff --git a/backend/app/crud/api_key.py b/backend/app/crud/api_key.py index 309562de1..8179cff39 100644 --- a/backend/app/crud/api_key.py +++ b/backend/app/crud/api_key.py @@ -66,6 +66,7 @@ def delete_api_key(session: Session, api_key_id: int) -> None: api_key.is_deleted = True api_key.deleted_at = datetime.utcnow() + api_key.updated_at = datetime.utcnow() session.add(api_key) session.commit() diff --git a/backend/app/crud/document.py b/backend/app/crud/document.py index 1229a1f1b..ceca4cbba 100644 --- a/backend/app/crud/document.py +++ b/backend/app/crud/document.py @@ -54,6 +54,8 @@ def update(self, document: Document): ) raise PermissionError(error) + document.updated_at = now() + self.session.add(document) self.session.commit() self.session.refresh(document) @@ -63,5 +65,6 @@ def update(self, document: Document): def delete(self, doc_id: UUID): document = self.read_one(doc_id) document.deleted_at = now() + document.updated_at = now() return self.update(document) diff --git a/backend/app/crud/project_user.py b/backend/app/crud/project_user.py index f11224b59..4c4f5d28c 100644 --- a/backend/app/crud/project_user.py +++ b/backend/app/crud/project_user.py @@ -63,6 +63,7 @@ def remove_user_from_project( project_user.is_deleted = True project_user.deleted_at = datetime.utcnow() + project_user.updated_at = datetime.utcnow() # Update the updated_at timestamp session.add(project_user) # Required to mark as dirty for commit session.commit() diff --git a/backend/app/models/api_key.py b/backend/app/models/api_key.py index 7bbc91004..91d4f73b5 100644 --- a/backend/app/models/api_key.py +++ b/backend/app/models/api_key.py @@ -19,12 +19,13 @@ class APIKeyBase(SQLModel): class APIKeyPublic(APIKeyBase): id: int - created_at: datetime + inserted_at: datetime class APIKey(APIKeyBase, table=True): id: int = Field(default=None, primary_key=True) - created_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) + inserted_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) + updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) is_deleted: bool = Field(default=False, nullable=False) deleted_at: Optional[datetime] = Field(default=None, nullable=True) diff --git a/backend/app/models/document.py b/backend/app/models/document.py index 80d3d9ebe..f0f983e6a 100644 --- a/backend/app/models/document.py +++ b/backend/app/models/document.py @@ -19,7 +19,7 @@ class Document(SQLModel, table=True): ) fname: str object_store_url: str - created_at: datetime = Field( + inserted_at: datetime = Field( default_factory=now, ) # updated_at: datetime | None diff --git a/backend/app/models/project_user.py b/backend/app/models/project_user.py index ab828be41..f2d799586 100644 --- a/backend/app/models/project_user.py +++ b/backend/app/models/project_user.py @@ -19,14 +19,14 @@ class ProjectUserBase(SQLModel): class ProjectUserPublic(ProjectUserBase): id: int - created_at: datetime + inserted_at: datetime updated_at: datetime # Database model, database table inferred from class name class ProjectUser(ProjectUserBase, table=True): id: int = Field(default=None, primary_key=True) - created_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) + inserted_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) is_deleted: bool = Field(default=False, nullable=False) deleted_at: Optional[datetime] = Field(default=None, nullable=True) diff --git a/backend/app/tests/crud/documents/test_crud_delete.py b/backend/app/tests/crud/documents/test_crud_delete.py index 18c3a55d6..304dd5647 100644 --- a/backend/app/tests/crud/documents/test_crud_delete.py +++ b/backend/app/tests/crud/documents/test_crud_delete.py @@ -28,7 +28,7 @@ def test_delete_marks_deleted(self, document: Document): assert document.deleted_at is not None def test_delete_follows_insert(self, document: Document): - assert document.created_at <= document.deleted_at + assert document.inserted_at <= document.deleted_at def test_cannot_delete_others_documents(self, db: Session): store = DocumentStore(db) diff --git a/backend/app/tests/crud/documents/test_crud_update.py b/backend/app/tests/crud/documents/test_crud_update.py index 69f0ab621..a14805bcf 100644 --- a/backend/app/tests/crud/documents/test_crud_update.py +++ b/backend/app/tests/crud/documents/test_crud_update.py @@ -30,7 +30,7 @@ def test_sequential_update_is_ordered( crud = DocumentCrud(db, documents.owner_id) (a, b) = (crud.update(y) for (_, y) in zip(range(2), documents)) - assert a.created_at <= b.created_at + assert a.inserted_at <= b.inserted_at def test_insert_does_not_delete( self, From 02dd4741a0836011408a7c47a9bd90be96b3b2f9 Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Wed, 23 Apr 2025 10:50:53 +0530 Subject: [PATCH 02/13] added logic for updating updated_at --- backend/app/models/document.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/app/models/document.py b/backend/app/models/document.py index f0f983e6a..c0c02ee75 100644 --- a/backend/app/models/document.py +++ b/backend/app/models/document.py @@ -22,7 +22,9 @@ class Document(SQLModel, table=True): inserted_at: datetime = Field( default_factory=now, ) - # updated_at: datetime | None + updated_at: datetime = Field( + default_factory=now, + ) deleted_at: datetime | None owner: User = Relationship(back_populates="documents") From 91889975ce475c31a96f8952356be41f983f3e02 Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Wed, 23 Apr 2025 10:55:02 +0530 Subject: [PATCH 03/13] added missing timestamp column from other tables too --- .../versions/99f4fc325617_add_organization_project_setup.py | 6 ++++++ .../alembic/versions/c43313eca57d_add_document_tables.py | 1 + 2 files changed, 7 insertions(+) diff --git a/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py b/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py index 61b5e9a2c..0947438cd 100644 --- a/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py +++ b/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py @@ -24,6 +24,9 @@ def upgrade(): sa.Column("id", sa.Integer(), nullable=False), sa.Column("name", sqlmodel.sql.sqltypes.AutoString(length=255), nullable=False), sa.Column("is_active", sa.Boolean(), nullable=False), + sa.Column("inserted_at", sa.DateTime(), nullable=False), + sa.Column("updated_at", sa.DateTime(), nullable=False), + sa.Column("deleted_at", sa.DateTime(), nullable=True), sa.PrimaryKeyConstraint("id"), ) op.create_index(op.f("ix_organization_name"), "organization", ["name"], unique=True) @@ -36,6 +39,9 @@ def upgrade(): ), sa.Column("is_active", sa.Boolean(), nullable=False), sa.Column("organization_id", sa.Integer(), nullable=False), + sa.Column("inserted_at", sa.DateTime(), nullable=False), + sa.Column("updated_at", sa.DateTime(), nullable=False), + sa.Column("deleted_at", sa.DateTime(), nullable=True), sa.ForeignKeyConstraint( ["organization_id"], ["organization.id"], diff --git a/backend/app/alembic/versions/c43313eca57d_add_document_tables.py b/backend/app/alembic/versions/c43313eca57d_add_document_tables.py index c3abd47df..a6bfb17b9 100644 --- a/backend/app/alembic/versions/c43313eca57d_add_document_tables.py +++ b/backend/app/alembic/versions/c43313eca57d_add_document_tables.py @@ -28,6 +28,7 @@ def upgrade(): "object_store_url", sqlmodel.sql.sqltypes.AutoString(), nullable=False ), sa.Column("inserted_at", sa.DateTime(), nullable=False), + sa.Column("updated_at", sa.DateTime(), nullable=False), sa.Column("deleted_at", sa.DateTime(), nullable=True), sa.ForeignKeyConstraint(["owner_id"], ["user.id"], ondelete="CASCADE"), sa.PrimaryKeyConstraint("id"), From af36ab272aa75962d67c9bae865cb3f864cdf508 Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Wed, 23 Apr 2025 11:18:14 +0530 Subject: [PATCH 04/13] updating models and endpoints --- backend/app/api/routes/organization.py | 21 ++++++++--------- backend/app/crud/organization.py | 32 +++++++++++++++++++++++++- backend/app/crud/project.py | 30 +++++++++++++++++++++++- backend/app/models/organization.py | 7 ++++++ backend/app/models/project.py | 7 ++++++ 5 files changed, 83 insertions(+), 14 deletions(-) diff --git a/backend/app/api/routes/organization.py b/backend/app/api/routes/organization.py index 5c7682e65..6d19826b1 100644 --- a/backend/app/api/routes/organization.py +++ b/backend/app/api/routes/organization.py @@ -15,7 +15,12 @@ SessionDep, get_current_active_superuser, ) -from app.crud.organization import create_organization, get_organization_by_id +from app.crud.organization import ( + create_organization, + get_organization_by_id, + update_organization, + delete_organization, +) from app.utils import APIResponse router = APIRouter(prefix="/organizations", tags=["organizations"]) @@ -76,14 +81,8 @@ def update_organization( if org is None: raise HTTPException(status_code=404, detail="Organization not found") - org_data = org_in.model_dump(exclude_unset=True) - org = org.model_copy(update=org_data) - - session.add(org) - session.commit() - session.flush() - - return APIResponse.success_response(org) + updated_org = update_organization(session=session, organization=org, org_in=org_in) + return APIResponse.success_response(updated_org) # Delete an organization @@ -97,7 +96,5 @@ def delete_organization(session: SessionDep, org_id: int): if org is None: raise HTTPException(status_code=404, detail="Organization not found") - session.delete(org) - session.commit() - + delete_organization(session=session, organization=org) return APIResponse.success_response(None) diff --git a/backend/app/crud/organization.py b/backend/app/crud/organization.py index b899a9e6c..2094ce04d 100644 --- a/backend/app/crud/organization.py +++ b/backend/app/crud/organization.py @@ -1,14 +1,18 @@ from typing import Any, Optional +from datetime import datetime from sqlmodel import Session, select -from app.models import Organization, OrganizationCreate +from app.models import Organization, OrganizationCreate, OrganizationUpdate def create_organization( *, session: Session, org_create: OrganizationCreate ) -> Organization: db_org = Organization.model_validate(org_create) + # Set timestamps + db_org.inserted_at = datetime.utcnow() + db_org.updated_at = datetime.utcnow() session.add(db_org) session.commit() session.refresh(db_org) @@ -39,3 +43,29 @@ def validate_organization(session: Session, org_id: int) -> Organization: raise ValueError("Organization is not active") return organization + + +def update_organization( + *, session: Session, organization: Organization, org_in: OrganizationUpdate +) -> Organization: + org_data = org_in.model_dump(exclude_unset=True) + for key, value in org_data.items(): + setattr(organization, key, value) + # Update the updated_at timestamp + organization.updated_at = datetime.utcnow() + session.add(organization) + session.commit() + session.refresh(organization) + return organization + + +def delete_organization( + *, session: Session, organization: Organization +) -> Organization: + organization.is_deleted = True + organization.deleted_at = datetime.utcnow() + organization.updated_at = datetime.utcnow() + session.add(organization) + session.commit() + session.refresh(organization) + return organization diff --git a/backend/app/crud/project.py b/backend/app/crud/project.py index 204adefff..bb93ac450 100644 --- a/backend/app/crud/project.py +++ b/backend/app/crud/project.py @@ -1,12 +1,16 @@ from typing import List, Optional +from datetime import datetime from sqlmodel import Session, select -from app.models import Project, ProjectCreate +from app.models import Project, ProjectCreate, ProjectUpdate def create_project(*, session: Session, project_create: ProjectCreate) -> Project: db_project = Project.model_validate(project_create) + # Set timestamps + db_project.inserted_at = datetime.utcnow() + db_project.updated_at = datetime.utcnow() session.add(db_project) session.commit() session.refresh(db_project) @@ -21,3 +25,27 @@ def get_project_by_id(*, session: Session, project_id: int) -> Optional[Project] def get_projects_by_organization(*, session: Session, org_id: int) -> List[Project]: statement = select(Project).where(Project.organization_id == org_id) return session.exec(statement).all() + + +def update_project( + *, session: Session, project: Project, project_in: ProjectUpdate +) -> Project: + project_data = project_in.model_dump(exclude_unset=True) + for key, value in project_data.items(): + setattr(project, key, value) + # Update the updated_at timestamp + project.updated_at = datetime.utcnow() + session.add(project) + session.commit() + session.refresh(project) + return project + + +def delete_project(*, session: Session, project: Project) -> Project: + project.is_deleted = True + project.deleted_at = datetime.utcnow() + project.updated_at = datetime.utcnow() + session.add(project) + session.commit() + session.refresh(project) + return project diff --git a/backend/app/models/organization.py b/backend/app/models/organization.py index e646ded9b..e8b003c00 100644 --- a/backend/app/models/organization.py +++ b/backend/app/models/organization.py @@ -1,3 +1,4 @@ +from datetime import datetime from typing import List, TYPE_CHECKING from sqlmodel import Field, Relationship, SQLModel from sqlalchemy.orm import relationship @@ -27,6 +28,10 @@ class OrganizationUpdate(SQLModel): # Database model for Organization class Organization(OrganizationBase, table=True): id: int = Field(default=None, primary_key=True) + inserted_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) + updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) + is_deleted: bool = Field(default=False, nullable=False) + deleted_at: datetime | None = Field(default=None, nullable=True) # Relationship back to Creds api_keys: list["APIKey"] = Relationship(back_populates="organization") @@ -38,6 +43,8 @@ class Organization(OrganizationBase, table=True): # Properties to return via API class OrganizationPublic(OrganizationBase): id: int + inserted_at: datetime + updated_at: datetime class OrganizationsPublic(SQLModel): diff --git a/backend/app/models/project.py b/backend/app/models/project.py index 3c4dfd9aa..5aa55265b 100644 --- a/backend/app/models/project.py +++ b/backend/app/models/project.py @@ -1,3 +1,4 @@ +from datetime import datetime from sqlmodel import Field, Relationship, SQLModel @@ -24,6 +25,10 @@ class ProjectUpdate(SQLModel): class Project(ProjectBase, table=True): id: int = Field(default=None, primary_key=True) organization_id: int = Field(foreign_key="organization.id", index=True) + inserted_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) + updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) + is_deleted: bool = Field(default=False, nullable=False) + deleted_at: datetime | None = Field(default=None, nullable=True) users: list["ProjectUser"] = Relationship( back_populates="project", cascade_delete=True @@ -34,6 +39,8 @@ class Project(ProjectBase, table=True): class ProjectPublic(ProjectBase): id: int organization_id: int + inserted_at: datetime + updated_at: datetime class ProjectsPublic(SQLModel): From ff500df9d33a84a0619a9ea2357bf304d1ead3c5 Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Wed, 23 Apr 2025 11:32:24 +0530 Subject: [PATCH 05/13] added is_deleted columns for consistency --- .../versions/99f4fc325617_add_organization_project_setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py b/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py index 0947438cd..63b2d97ec 100644 --- a/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py +++ b/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py @@ -24,6 +24,7 @@ def upgrade(): sa.Column("id", sa.Integer(), nullable=False), sa.Column("name", sqlmodel.sql.sqltypes.AutoString(length=255), nullable=False), sa.Column("is_active", sa.Boolean(), nullable=False), + sa.Column("is_deleted", sa.Boolean(), nullable=False), sa.Column("inserted_at", sa.DateTime(), nullable=False), sa.Column("updated_at", sa.DateTime(), nullable=False), sa.Column("deleted_at", sa.DateTime(), nullable=True), @@ -38,6 +39,7 @@ def upgrade(): "description", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=True ), sa.Column("is_active", sa.Boolean(), nullable=False), + sa.Column("is_deleted", sa.Boolean(), nullable=False), sa.Column("organization_id", sa.Integer(), nullable=False), sa.Column("inserted_at", sa.DateTime(), nullable=False), sa.Column("updated_at", sa.DateTime(), nullable=False), From 193e130720809d599712ae63b70ca55ce2ba60fb Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Wed, 23 Apr 2025 12:53:37 +0530 Subject: [PATCH 06/13] cleanups --- .../99f4fc325617_add_organization_project_setup.py | 12 +++++++++--- backend/app/api/deps.py | 4 ++-- backend/app/crud/project_user.py | 14 ++++---------- backend/app/models/project.py | 2 -- backend/app/models/project_user.py | 2 -- backend/app/tests/api/routes/test_project_user.py | 9 ++++----- backend/app/tests/crud/test_project_user.py | 13 +++++-------- 7 files changed, 24 insertions(+), 32 deletions(-) diff --git a/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py b/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py index 63b2d97ec..141586e42 100644 --- a/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py +++ b/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py @@ -25,7 +25,9 @@ def upgrade(): sa.Column("name", sqlmodel.sql.sqltypes.AutoString(length=255), nullable=False), sa.Column("is_active", sa.Boolean(), nullable=False), sa.Column("is_deleted", sa.Boolean(), nullable=False), - sa.Column("inserted_at", sa.DateTime(), nullable=False), + sa.Column( + "inserted_at", sa.DateTime(), nullable=False, server_default=sa.func.now() + ), sa.Column("updated_at", sa.DateTime(), nullable=False), sa.Column("deleted_at", sa.DateTime(), nullable=True), sa.PrimaryKeyConstraint("id"), @@ -41,7 +43,9 @@ def upgrade(): sa.Column("is_active", sa.Boolean(), nullable=False), sa.Column("is_deleted", sa.Boolean(), nullable=False), sa.Column("organization_id", sa.Integer(), nullable=False), - sa.Column("inserted_at", sa.DateTime(), nullable=False), + sa.Column( + "inserted_at", sa.DateTime(), nullable=False, server_default=sa.func.now() + ), sa.Column("updated_at", sa.DateTime(), nullable=False), sa.Column("deleted_at", sa.DateTime(), nullable=True), sa.ForeignKeyConstraint( @@ -61,7 +65,9 @@ def upgrade(): sa.Column("user_id", sa.Uuid(), nullable=False), sa.Column("is_admin", sa.Boolean(), nullable=False), sa.Column("is_deleted", sa.Boolean(), nullable=False), - sa.Column("inserted_at", sa.DateTime(), nullable=False), + sa.Column( + "inserted_at", sa.DateTime(), nullable=False, server_default=sa.func.now() + ), sa.Column("updated_at", sa.DateTime(), nullable=False), sa.Column("deleted_at", sa.DateTime(), nullable=True), sa.ForeignKeyConstraint(["project_id"], ["project.id"], ondelete="CASCADE"), diff --git a/backend/app/api/deps.py b/backend/app/api/deps.py index 3bbc85eb4..f14aa660b 100644 --- a/backend/app/api/deps.py +++ b/backend/app/api/deps.py @@ -130,7 +130,8 @@ async def http_exception_handler(request: Request, exc: HTTPException): return JSONResponse( status_code=exc.status_code, content=APIResponse.failure_response(exc.detail).model_dump() - | {"detail": exc.detail}, # TEMPORARY: Keep "detail" for backward compatibility + # TEMPORARY: Keep "detail" for backward compatibility + | {"detail": exc.detail}, ) @@ -194,7 +195,6 @@ def verify_user_project_organization( select(ProjectUser).where( ProjectUser.user_id == current_user.id, ProjectUser.project_id == project_id, - ProjectUser.is_deleted == False, ) ).first() diff --git a/backend/app/crud/project_user.py b/backend/app/crud/project_user.py index 4c4f5d28c..a9226279d 100644 --- a/backend/app/crud/project_user.py +++ b/backend/app/crud/project_user.py @@ -12,7 +12,6 @@ def is_project_admin(session: Session, user_id: str, project_id: int) -> bool: select(ProjectUser).where( ProjectUser.project_id == project_id, ProjectUser.user_id == user_id, - ProjectUser.is_deleted == False, ) ).first() @@ -55,16 +54,12 @@ def remove_user_from_project( select(ProjectUser).where( ProjectUser.project_id == project_id, ProjectUser.user_id == user_id, - ProjectUser.is_deleted == False, # Ignore already deleted users ) ).first() if not project_user: - raise ValueError("User is not a member of this project or already removed.") + raise ValueError("User is not a member of this project.") - project_user.is_deleted = True - project_user.deleted_at = datetime.utcnow() - project_user.updated_at = datetime.utcnow() # Update the updated_at timestamp - session.add(project_user) # Required to mark as dirty for commit + session.delete(project_user) session.commit() @@ -77,13 +72,13 @@ def get_users_by_project( count_statement = ( select(func.count()) .select_from(ProjectUser) - .where(ProjectUser.project_id == project_id, ProjectUser.is_deleted == False) + .where(ProjectUser.project_id == project_id) ) total_count = session.exec(count_statement).one() statement = ( select(ProjectUser) - .where(ProjectUser.project_id == project_id, ProjectUser.is_deleted == False) + .where(ProjectUser.project_id == project_id) .offset(skip) .limit(limit) ) @@ -105,7 +100,6 @@ def is_user_part_of_organization( .where( Project.organization_id == org_id, ProjectUser.user_id == user_id, - ProjectUser.is_deleted == False, ) ).first() diff --git a/backend/app/models/project.py b/backend/app/models/project.py index 5aa55265b..d617fc03c 100644 --- a/backend/app/models/project.py +++ b/backend/app/models/project.py @@ -27,8 +27,6 @@ class Project(ProjectBase, table=True): organization_id: int = Field(foreign_key="organization.id", index=True) inserted_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) - is_deleted: bool = Field(default=False, nullable=False) - deleted_at: datetime | None = Field(default=None, nullable=True) users: list["ProjectUser"] = Relationship( back_populates="project", cascade_delete=True diff --git a/backend/app/models/project_user.py b/backend/app/models/project_user.py index f2d799586..fc84f9323 100644 --- a/backend/app/models/project_user.py +++ b/backend/app/models/project_user.py @@ -28,8 +28,6 @@ class ProjectUser(ProjectUserBase, table=True): id: int = Field(default=None, primary_key=True) inserted_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) - is_deleted: bool = Field(default=False, nullable=False) - deleted_at: Optional[datetime] = Field(default=None, nullable=True) # Relationships project: "Project" = Relationship(back_populates="users") diff --git a/backend/app/tests/api/routes/test_project_user.py b/backend/app/tests/api/routes/test_project_user.py index fab5f9937..c71e17aea 100644 --- a/backend/app/tests/api/routes/test_project_user.py +++ b/backend/app/tests/api/routes/test_project_user.py @@ -33,7 +33,8 @@ def create_organization_and_project(db: Session) -> tuple[Organization, Project] db.refresh(organization) # Ensure project with unique name - project_name = f"Test Project {uuid.uuid4()}" # Ensuring unique project name + # Ensuring unique project name + project_name = f"Test Project {uuid.uuid4()}" project = Project( name=project_name, description="A test project", @@ -136,7 +137,7 @@ def test_remove_user_from_project( "message": "User removed from project successfully." } - # Ensure user is marked as deleted in the database (Fixed) + # Verify user is completely removed from project project_user = db.exec( select(ProjectUser).where( ProjectUser.project_id == project.id, @@ -144,9 +145,7 @@ def test_remove_user_from_project( ) ).first() - assert project_user is not None - assert project_user.is_deleted is True - assert project_user.deleted_at is not None + assert project_user is None # Ensure the record is completely removed def test_normal_user_cannot_add_user( diff --git a/backend/app/tests/crud/test_project_user.py b/backend/app/tests/crud/test_project_user.py index e5eea790c..6ba919721 100644 --- a/backend/app/tests/crud/test_project_user.py +++ b/backend/app/tests/crud/test_project_user.py @@ -20,7 +20,8 @@ def create_organization_and_project(db: Session) -> tuple[Organization, Project] db.refresh(organization) # Ensure project with unique name - project_name = f"Test Project {uuid.uuid4()}" # Ensuring unique project name + # Ensuring unique project name + project_name = f"Test Project {uuid.uuid4()}" project = Project( name=project_name, description="A test project", @@ -95,16 +96,14 @@ def test_remove_user_from_project(db: Session) -> None: # Remove user from project project_user_crud.remove_user_from_project(db, project.id, user.id) - # Retrieve project user with both project_id and user_id + # Verify user is completely removed from project project_user = db.exec( select(ProjectUser).where( ProjectUser.project_id == project.id, ProjectUser.user_id == user.id ) ).first() - assert project_user is not None # Ensure the record still exists (soft delete) - assert project_user.is_deleted is True - assert project_user.deleted_at is not None + assert project_user is None # Ensure the record is completely removed def test_remove_user_from_project_not_member(db: Session) -> None: @@ -113,9 +112,7 @@ def test_remove_user_from_project_not_member(db: Session) -> None: project_id = project.id user_id = uuid.uuid4() - with pytest.raises( - ValueError, match="User is not a member of this project or already removed" - ): + with pytest.raises(ValueError, match="User is not a member of this project"): project_user_crud.remove_user_from_project(db, project_id, user_id) From d50fc138c1c2103f8ec4caf8af7e4b2ce28afd62 Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Wed, 23 Apr 2025 13:09:58 +0530 Subject: [PATCH 07/13] cleanups --- ...fc325617_add_organization_project_setup.py | 2 -- backend/app/api/routes/organization.py | 21 +++++++------ backend/app/crud/organization.py | 30 +------------------ backend/app/crud/project.py | 28 +---------------- backend/app/crud/project_user.py | 13 +++++--- backend/app/models/organization.py | 2 -- 6 files changed, 23 insertions(+), 73 deletions(-) diff --git a/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py b/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py index 141586e42..7d29344d1 100644 --- a/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py +++ b/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py @@ -41,13 +41,11 @@ def upgrade(): "description", sqlmodel.sql.sqltypes.AutoString(length=500), nullable=True ), sa.Column("is_active", sa.Boolean(), nullable=False), - sa.Column("is_deleted", sa.Boolean(), nullable=False), sa.Column("organization_id", sa.Integer(), nullable=False), sa.Column( "inserted_at", sa.DateTime(), nullable=False, server_default=sa.func.now() ), sa.Column("updated_at", sa.DateTime(), nullable=False), - sa.Column("deleted_at", sa.DateTime(), nullable=True), sa.ForeignKeyConstraint( ["organization_id"], ["organization.id"], diff --git a/backend/app/api/routes/organization.py b/backend/app/api/routes/organization.py index 6d19826b1..5c7682e65 100644 --- a/backend/app/api/routes/organization.py +++ b/backend/app/api/routes/organization.py @@ -15,12 +15,7 @@ SessionDep, get_current_active_superuser, ) -from app.crud.organization import ( - create_organization, - get_organization_by_id, - update_organization, - delete_organization, -) +from app.crud.organization import create_organization, get_organization_by_id from app.utils import APIResponse router = APIRouter(prefix="/organizations", tags=["organizations"]) @@ -81,8 +76,14 @@ def update_organization( if org is None: raise HTTPException(status_code=404, detail="Organization not found") - updated_org = update_organization(session=session, organization=org, org_in=org_in) - return APIResponse.success_response(updated_org) + org_data = org_in.model_dump(exclude_unset=True) + org = org.model_copy(update=org_data) + + session.add(org) + session.commit() + session.flush() + + return APIResponse.success_response(org) # Delete an organization @@ -96,5 +97,7 @@ def delete_organization(session: SessionDep, org_id: int): if org is None: raise HTTPException(status_code=404, detail="Organization not found") - delete_organization(session=session, organization=org) + session.delete(org) + session.commit() + return APIResponse.success_response(None) diff --git a/backend/app/crud/organization.py b/backend/app/crud/organization.py index 2094ce04d..bef289da6 100644 --- a/backend/app/crud/organization.py +++ b/backend/app/crud/organization.py @@ -1,16 +1,14 @@ from typing import Any, Optional from datetime import datetime - from sqlmodel import Session, select -from app.models import Organization, OrganizationCreate, OrganizationUpdate +from app.models import Organization, OrganizationCreate def create_organization( *, session: Session, org_create: OrganizationCreate ) -> Organization: db_org = Organization.model_validate(org_create) - # Set timestamps db_org.inserted_at = datetime.utcnow() db_org.updated_at = datetime.utcnow() session.add(db_org) @@ -43,29 +41,3 @@ def validate_organization(session: Session, org_id: int) -> Organization: raise ValueError("Organization is not active") return organization - - -def update_organization( - *, session: Session, organization: Organization, org_in: OrganizationUpdate -) -> Organization: - org_data = org_in.model_dump(exclude_unset=True) - for key, value in org_data.items(): - setattr(organization, key, value) - # Update the updated_at timestamp - organization.updated_at = datetime.utcnow() - session.add(organization) - session.commit() - session.refresh(organization) - return organization - - -def delete_organization( - *, session: Session, organization: Organization -) -> Organization: - organization.is_deleted = True - organization.deleted_at = datetime.utcnow() - organization.updated_at = datetime.utcnow() - session.add(organization) - session.commit() - session.refresh(organization) - return organization diff --git a/backend/app/crud/project.py b/backend/app/crud/project.py index bb93ac450..b62fbaab9 100644 --- a/backend/app/crud/project.py +++ b/backend/app/crud/project.py @@ -1,14 +1,12 @@ from typing import List, Optional from datetime import datetime - from sqlmodel import Session, select -from app.models import Project, ProjectCreate, ProjectUpdate +from app.models import Project, ProjectCreate def create_project(*, session: Session, project_create: ProjectCreate) -> Project: db_project = Project.model_validate(project_create) - # Set timestamps db_project.inserted_at = datetime.utcnow() db_project.updated_at = datetime.utcnow() session.add(db_project) @@ -25,27 +23,3 @@ def get_project_by_id(*, session: Session, project_id: int) -> Optional[Project] def get_projects_by_organization(*, session: Session, org_id: int) -> List[Project]: statement = select(Project).where(Project.organization_id == org_id) return session.exec(statement).all() - - -def update_project( - *, session: Session, project: Project, project_in: ProjectUpdate -) -> Project: - project_data = project_in.model_dump(exclude_unset=True) - for key, value in project_data.items(): - setattr(project, key, value) - # Update the updated_at timestamp - project.updated_at = datetime.utcnow() - session.add(project) - session.commit() - session.refresh(project) - return project - - -def delete_project(*, session: Session, project: Project) -> Project: - project.is_deleted = True - project.deleted_at = datetime.utcnow() - project.updated_at = datetime.utcnow() - session.add(project) - session.commit() - session.refresh(project) - return project diff --git a/backend/app/crud/project_user.py b/backend/app/crud/project_user.py index a9226279d..f11224b59 100644 --- a/backend/app/crud/project_user.py +++ b/backend/app/crud/project_user.py @@ -12,6 +12,7 @@ def is_project_admin(session: Session, user_id: str, project_id: int) -> bool: select(ProjectUser).where( ProjectUser.project_id == project_id, ProjectUser.user_id == user_id, + ProjectUser.is_deleted == False, ) ).first() @@ -54,12 +55,15 @@ def remove_user_from_project( select(ProjectUser).where( ProjectUser.project_id == project_id, ProjectUser.user_id == user_id, + ProjectUser.is_deleted == False, # Ignore already deleted users ) ).first() if not project_user: - raise ValueError("User is not a member of this project.") + raise ValueError("User is not a member of this project or already removed.") - session.delete(project_user) + project_user.is_deleted = True + project_user.deleted_at = datetime.utcnow() + session.add(project_user) # Required to mark as dirty for commit session.commit() @@ -72,13 +76,13 @@ def get_users_by_project( count_statement = ( select(func.count()) .select_from(ProjectUser) - .where(ProjectUser.project_id == project_id) + .where(ProjectUser.project_id == project_id, ProjectUser.is_deleted == False) ) total_count = session.exec(count_statement).one() statement = ( select(ProjectUser) - .where(ProjectUser.project_id == project_id) + .where(ProjectUser.project_id == project_id, ProjectUser.is_deleted == False) .offset(skip) .limit(limit) ) @@ -100,6 +104,7 @@ def is_user_part_of_organization( .where( Project.organization_id == org_id, ProjectUser.user_id == user_id, + ProjectUser.is_deleted == False, ) ).first() diff --git a/backend/app/models/organization.py b/backend/app/models/organization.py index e8b003c00..1a880da62 100644 --- a/backend/app/models/organization.py +++ b/backend/app/models/organization.py @@ -30,8 +30,6 @@ class Organization(OrganizationBase, table=True): id: int = Field(default=None, primary_key=True) inserted_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) - is_deleted: bool = Field(default=False, nullable=False) - deleted_at: datetime | None = Field(default=None, nullable=True) # Relationship back to Creds api_keys: list["APIKey"] = Relationship(back_populates="organization") From f5f3b338f8d53e6348378f7686de1cb90f0c23bf Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Wed, 23 Apr 2025 13:26:15 +0530 Subject: [PATCH 08/13] reverting few unnecesary changes in testcases --- backend/app/tests/api/routes/test_project_user.py | 9 +++++---- backend/app/tests/crud/test_project_user.py | 13 ++++++++----- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/backend/app/tests/api/routes/test_project_user.py b/backend/app/tests/api/routes/test_project_user.py index c71e17aea..fab5f9937 100644 --- a/backend/app/tests/api/routes/test_project_user.py +++ b/backend/app/tests/api/routes/test_project_user.py @@ -33,8 +33,7 @@ def create_organization_and_project(db: Session) -> tuple[Organization, Project] db.refresh(organization) # Ensure project with unique name - # Ensuring unique project name - project_name = f"Test Project {uuid.uuid4()}" + project_name = f"Test Project {uuid.uuid4()}" # Ensuring unique project name project = Project( name=project_name, description="A test project", @@ -137,7 +136,7 @@ def test_remove_user_from_project( "message": "User removed from project successfully." } - # Verify user is completely removed from project + # Ensure user is marked as deleted in the database (Fixed) project_user = db.exec( select(ProjectUser).where( ProjectUser.project_id == project.id, @@ -145,7 +144,9 @@ def test_remove_user_from_project( ) ).first() - assert project_user is None # Ensure the record is completely removed + assert project_user is not None + assert project_user.is_deleted is True + assert project_user.deleted_at is not None def test_normal_user_cannot_add_user( diff --git a/backend/app/tests/crud/test_project_user.py b/backend/app/tests/crud/test_project_user.py index 6ba919721..e5eea790c 100644 --- a/backend/app/tests/crud/test_project_user.py +++ b/backend/app/tests/crud/test_project_user.py @@ -20,8 +20,7 @@ def create_organization_and_project(db: Session) -> tuple[Organization, Project] db.refresh(organization) # Ensure project with unique name - # Ensuring unique project name - project_name = f"Test Project {uuid.uuid4()}" + project_name = f"Test Project {uuid.uuid4()}" # Ensuring unique project name project = Project( name=project_name, description="A test project", @@ -96,14 +95,16 @@ def test_remove_user_from_project(db: Session) -> None: # Remove user from project project_user_crud.remove_user_from_project(db, project.id, user.id) - # Verify user is completely removed from project + # Retrieve project user with both project_id and user_id project_user = db.exec( select(ProjectUser).where( ProjectUser.project_id == project.id, ProjectUser.user_id == user.id ) ).first() - assert project_user is None # Ensure the record is completely removed + assert project_user is not None # Ensure the record still exists (soft delete) + assert project_user.is_deleted is True + assert project_user.deleted_at is not None def test_remove_user_from_project_not_member(db: Session) -> None: @@ -112,7 +113,9 @@ def test_remove_user_from_project_not_member(db: Session) -> None: project_id = project.id user_id = uuid.uuid4() - with pytest.raises(ValueError, match="User is not a member of this project"): + with pytest.raises( + ValueError, match="User is not a member of this project or already removed" + ): project_user_crud.remove_user_from_project(db, project_id, user_id) From b8c169a3a3ccc818c3614d5a4b1b81c5a5410dba Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Wed, 23 Apr 2025 13:38:02 +0530 Subject: [PATCH 09/13] removed is_deleted from org --- .../versions/99f4fc325617_add_organization_project_setup.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py b/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py index 7d29344d1..163fea1cd 100644 --- a/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py +++ b/backend/app/alembic/versions/99f4fc325617_add_organization_project_setup.py @@ -24,12 +24,10 @@ def upgrade(): sa.Column("id", sa.Integer(), nullable=False), sa.Column("name", sqlmodel.sql.sqltypes.AutoString(length=255), nullable=False), sa.Column("is_active", sa.Boolean(), nullable=False), - sa.Column("is_deleted", sa.Boolean(), nullable=False), sa.Column( "inserted_at", sa.DateTime(), nullable=False, server_default=sa.func.now() ), sa.Column("updated_at", sa.DateTime(), nullable=False), - sa.Column("deleted_at", sa.DateTime(), nullable=True), sa.PrimaryKeyConstraint("id"), ) op.create_index(op.f("ix_organization_name"), "organization", ["name"], unique=True) From c6fbbf088c6d87713761fdb8fb30f90cb05b71eb Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Wed, 23 Apr 2025 13:51:49 +0530 Subject: [PATCH 10/13] fixing testcases --- backend/app/models/project_user.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/app/models/project_user.py b/backend/app/models/project_user.py index fc84f9323..d386ac8e0 100644 --- a/backend/app/models/project_user.py +++ b/backend/app/models/project_user.py @@ -28,7 +28,8 @@ class ProjectUser(ProjectUserBase, table=True): id: int = Field(default=None, primary_key=True) inserted_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) - + is_deleted: bool = Field(default=False, nullable=False) + deleted_at: Optional[datetime] = Field(default=None, nullable=True) # Relationships project: "Project" = Relationship(back_populates="users") user: "User" = Relationship(back_populates="projects") From 6b2ce8af6254a629273d97890a38cf308ad524da Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Mon, 5 May 2025 10:22:32 +0530 Subject: [PATCH 11/13] moving to now --- backend/app/models/api_key.py | 4 ++-- backend/app/models/credentials.py | 4 ++-- backend/app/models/project.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/app/models/api_key.py b/backend/app/models/api_key.py index 91d4f73b5..956ac8fd5 100644 --- a/backend/app/models/api_key.py +++ b/backend/app/models/api_key.py @@ -24,8 +24,8 @@ class APIKeyPublic(APIKeyBase): class APIKey(APIKeyBase, table=True): id: int = Field(default=None, primary_key=True) - inserted_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) - updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) + inserted_at: datetime = Field(default_factory=now, nullable=False) + updated_at: datetime = Field(default_factory=now, nullable=False) is_deleted: bool = Field(default=False, nullable=False) deleted_at: Optional[datetime] = Field(default=None, nullable=True) diff --git a/backend/app/models/credentials.py b/backend/app/models/credentials.py index 3d8ebb5df..fdac79788 100644 --- a/backend/app/models/credentials.py +++ b/backend/app/models/credentials.py @@ -24,11 +24,11 @@ class Credential(CredsBase, table=True): id: int = Field(default=None, primary_key=True) credential: Dict[str, Any] = Field(default=None, sa_column=sa.Column(sa.JSON)) inserted_at: datetime = Field( - default_factory=datetime.utcnow, + default_factory=now, sa_column=sa.Column(sa.DateTime, default=datetime.utcnow), ) updated_at: datetime = Field( - default_factory=datetime.utcnow, + default_factory=now, sa_column=sa.Column(sa.DateTime, onupdate=datetime.utcnow), ) deleted_at: Optional[datetime] = Field( diff --git a/backend/app/models/project.py b/backend/app/models/project.py index 448e1b0df..125cf13f8 100644 --- a/backend/app/models/project.py +++ b/backend/app/models/project.py @@ -26,8 +26,8 @@ class ProjectUpdate(SQLModel): class Project(ProjectBase, table=True): id: int = Field(default=None, primary_key=True) organization_id: int = Field(foreign_key="organization.id", index=True) - inserted_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) - updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) + inserted_at: datetime = Field(default_factory=now, nullable=False) + updated_at: datetime = Field(default_factory=now, nullable=False) users: list["ProjectUser"] = Relationship( back_populates="project", cascade_delete=True From 6ea4368562cd4cfd5574343972317f3f2f1ecffc Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Mon, 5 May 2025 10:31:42 +0530 Subject: [PATCH 12/13] loading now --- backend/app/models/api_key.py | 3 ++- backend/app/models/credentials.py | 1 + backend/app/models/organization.py | 5 +++-- backend/app/models/project.py | 1 + backend/app/models/project_user.py | 5 +++-- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/backend/app/models/api_key.py b/backend/app/models/api_key.py index 956ac8fd5..fd7050b3c 100644 --- a/backend/app/models/api_key.py +++ b/backend/app/models/api_key.py @@ -4,6 +4,7 @@ from typing import Optional, List from sqlmodel import SQLModel, Field, Relationship +from app.core.util import now class APIKeyBase(SQLModel): organization_id: int = Field( @@ -19,7 +20,7 @@ class APIKeyBase(SQLModel): class APIKeyPublic(APIKeyBase): id: int - inserted_at: datetime + inserted_at: datetime= Field(default_factory=now, nullable=False) class APIKey(APIKeyBase, table=True): diff --git a/backend/app/models/credentials.py b/backend/app/models/credentials.py index fdac79788..92d47db0e 100644 --- a/backend/app/models/credentials.py +++ b/backend/app/models/credentials.py @@ -3,6 +3,7 @@ from sqlmodel import Field, Relationship, SQLModel from datetime import datetime +from app.core.util import now class CredsBase(SQLModel): organization_id: int = Field(foreign_key="organization.id") diff --git a/backend/app/models/organization.py b/backend/app/models/organization.py index 4a08d1f6d..41862f0ab 100644 --- a/backend/app/models/organization.py +++ b/backend/app/models/organization.py @@ -3,6 +3,7 @@ from sqlmodel import Field, Relationship, SQLModel from sqlalchemy.orm import relationship +from app.core.util import now if TYPE_CHECKING: from .credentials import Credential @@ -30,8 +31,8 @@ class OrganizationUpdate(SQLModel): # Database model for Organization class Organization(OrganizationBase, table=True): id: int = Field(default=None, primary_key=True) - inserted_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) - updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) + inserted_at: datetime = Field(default_factory=now, nullable=False) + updated_at: datetime = Field(default_factory=now, nullable=False) # Relationship back to Creds api_keys: list["APIKey"] = Relationship( diff --git a/backend/app/models/project.py b/backend/app/models/project.py index 125cf13f8..c627c9bc1 100644 --- a/backend/app/models/project.py +++ b/backend/app/models/project.py @@ -2,6 +2,7 @@ from typing import Optional from sqlmodel import Field, Relationship, SQLModel +from app.core.util import now # Shared properties for a Project class ProjectBase(SQLModel): diff --git a/backend/app/models/project_user.py b/backend/app/models/project_user.py index d386ac8e0..d4581f40d 100644 --- a/backend/app/models/project_user.py +++ b/backend/app/models/project_user.py @@ -3,6 +3,7 @@ from typing import Optional, List from sqlmodel import SQLModel, Field, Relationship +from app.core.util import now # Shared properties class ProjectUserBase(SQLModel): @@ -26,8 +27,8 @@ class ProjectUserPublic(ProjectUserBase): # Database model, database table inferred from class name class ProjectUser(ProjectUserBase, table=True): id: int = Field(default=None, primary_key=True) - inserted_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) - updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) + inserted_at: datetime = Field(default_factory=now, nullable=False) + updated_at: datetime = Field(default_factory=now, nullable=False) is_deleted: bool = Field(default=False, nullable=False) deleted_at: Optional[datetime] = Field(default=None, nullable=True) # Relationships From 988e68200bd17335d7a3e53d9400b413a74e19f3 Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Mon, 5 May 2025 10:44:39 +0530 Subject: [PATCH 13/13] formatting --- backend/app/models/api_key.py | 3 ++- backend/app/models/credentials.py | 1 + backend/app/models/project.py | 1 + backend/app/models/project_user.py | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/app/models/api_key.py b/backend/app/models/api_key.py index fd7050b3c..357836dac 100644 --- a/backend/app/models/api_key.py +++ b/backend/app/models/api_key.py @@ -6,6 +6,7 @@ from app.core.util import now + class APIKeyBase(SQLModel): organization_id: int = Field( foreign_key="organization.id", nullable=False, ondelete="CASCADE" @@ -20,7 +21,7 @@ class APIKeyBase(SQLModel): class APIKeyPublic(APIKeyBase): id: int - inserted_at: datetime= Field(default_factory=now, nullable=False) + inserted_at: datetime = Field(default_factory=now, nullable=False) class APIKey(APIKeyBase, table=True): diff --git a/backend/app/models/credentials.py b/backend/app/models/credentials.py index 92d47db0e..d006b572c 100644 --- a/backend/app/models/credentials.py +++ b/backend/app/models/credentials.py @@ -5,6 +5,7 @@ from app.core.util import now + class CredsBase(SQLModel): organization_id: int = Field(foreign_key="organization.id") is_active: bool = True diff --git a/backend/app/models/project.py b/backend/app/models/project.py index c627c9bc1..19a5cc1aa 100644 --- a/backend/app/models/project.py +++ b/backend/app/models/project.py @@ -4,6 +4,7 @@ from app.core.util import now + # Shared properties for a Project class ProjectBase(SQLModel): name: str = Field(index=True, max_length=255) diff --git a/backend/app/models/project_user.py b/backend/app/models/project_user.py index d4581f40d..cbc18efd5 100644 --- a/backend/app/models/project_user.py +++ b/backend/app/models/project_user.py @@ -5,6 +5,7 @@ from app.core.util import now + # Shared properties class ProjectUserBase(SQLModel): project_id: int = Field(