From d6ae5e6871c4a604deb5eab787e6551d7012517d Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Mon, 19 May 2025 13:56:16 +0200 Subject: [PATCH 01/11] migration --- api/specs/web-server/_projects_comments.py | 116 ------- api/specs/web-server/openapi.py | 1 - ...14f_preparation_for_osparc_io_migration.py | 298 ++++++++++++++++++ .../models/file_meta_data.py | 15 +- .../models/folders_v2.py | 2 + .../models/payments_autorecharge.py | 8 +- .../models/payments_methods.py | 14 + .../models/payments_transactions.py | 20 ++ .../models/projects_comments.py | 53 ---- ..._service_runs__osparc_io_history_202508.py | 228 ++++++++++++++ .../models/tokens.py | 14 +- .../api/v0/openapi.yaml | 268 ---------------- .../projects/_comments_repository.py | 98 ------ .../projects/_comments_service.py | 96 ------ .../projects/_controller/comments_rest.py | 228 -------------- .../projects/_projects_repository_legacy.py | 54 ---- .../projects/plugin.py | 2 - 17 files changed, 594 insertions(+), 921 deletions(-) delete mode 100644 api/specs/web-server/_projects_comments.py create mode 100644 packages/postgres-database/src/simcore_postgres_database/migration/versions/e98c45ff314f_preparation_for_osparc_io_migration.py delete mode 100644 packages/postgres-database/src/simcore_postgres_database/models/projects_comments.py create mode 100644 packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_service_runs__osparc_io_history_202508.py delete mode 100644 services/web/server/src/simcore_service_webserver/projects/_comments_repository.py delete mode 100644 services/web/server/src/simcore_service_webserver/projects/_comments_service.py delete mode 100644 services/web/server/src/simcore_service_webserver/projects/_controller/comments_rest.py diff --git a/api/specs/web-server/_projects_comments.py b/api/specs/web-server/_projects_comments.py deleted file mode 100644 index 04ad1f1fa43b..000000000000 --- a/api/specs/web-server/_projects_comments.py +++ /dev/null @@ -1,116 +0,0 @@ -"""Helper script to automatically generate OAS - -This OAS are the source of truth -""" - -# pylint: disable=redefined-outer-name -# pylint: disable=unused-argument -# pylint: disable=unused-variable -# pylint: disable=too-many-arguments - -from typing import Literal - -from _common import assert_handler_signature_against_model -from fastapi import APIRouter, status -from models_library.generics import Envelope -from models_library.projects import ProjectID -from models_library.projects_comments import CommentID, ProjectsCommentsAPI -from pydantic import NonNegativeInt -from simcore_service_webserver._meta import API_VTAG -from simcore_service_webserver.projects._controller.comments_rest import ( - _ProjectCommentsBodyParams, - _ProjectCommentsPathParams, - _ProjectCommentsWithCommentPathParams, -) - -router = APIRouter( - prefix=f"/{API_VTAG}", - tags=[ - "projects", - "comments", - ], -) - - -# -# API entrypoints -# - - -@router.post( - "/projects/{project_uuid}/comments", - response_model=Envelope[dict[Literal["comment_id"], CommentID]], - description="Create a new comment for a specific project. The request body should contain the comment contents and user information.", - status_code=status.HTTP_201_CREATED, - deprecated=True, -) -async def create_project_comment( - project_uuid: ProjectID, body: _ProjectCommentsBodyParams -): ... - - -assert_handler_signature_against_model( - create_project_comment, _ProjectCommentsPathParams -) - - -@router.get( - "/projects/{project_uuid}/comments", - response_model=Envelope[list[ProjectsCommentsAPI]], - description="Retrieve all comments for a specific project.", - deprecated=True, -) -async def list_project_comments( - project_uuid: ProjectID, limit: int = 20, offset: NonNegativeInt = 0 -): ... - - -assert_handler_signature_against_model( - list_project_comments, _ProjectCommentsPathParams -) - - -@router.put( - "/projects/{project_uuid}/comments/{comment_id}", - response_model=Envelope[ProjectsCommentsAPI], - description="Update the contents of a specific comment for a project. The request body should contain the updated comment contents.", - deprecated=True, -) -async def update_project_comment( - project_uuid: ProjectID, - comment_id: CommentID, - body: _ProjectCommentsBodyParams, -): ... - - -assert_handler_signature_against_model( - update_project_comment, _ProjectCommentsWithCommentPathParams -) - - -@router.delete( - "/projects/{project_uuid}/comments/{comment_id}", - description="Delete a specific comment associated with a project.", - status_code=status.HTTP_204_NO_CONTENT, - deprecated=True, -) -async def delete_project_comment(project_uuid: ProjectID, comment_id: CommentID): ... - - -assert_handler_signature_against_model( - delete_project_comment, _ProjectCommentsWithCommentPathParams -) - - -@router.get( - "/projects/{project_uuid}/comments/{comment_id}", - response_model=Envelope[ProjectsCommentsAPI], - description="Retrieve a specific comment by its ID within a project.", - deprecated=True, -) -async def get_project_comment(project_uuid: ProjectID, comment_id: CommentID): ... - - -assert_handler_signature_against_model( - get_project_comment, _ProjectCommentsWithCommentPathParams -) diff --git a/api/specs/web-server/openapi.py b/api/specs/web-server/openapi.py index fa0c86abcc74..fae410644d14 100644 --- a/api/specs/web-server/openapi.py +++ b/api/specs/web-server/openapi.py @@ -44,7 +44,6 @@ "_nih_sparc_redirections", "_projects", "_projects_access_rights", - "_projects_comments", "_projects_conversations", "_projects_folders", "_projects_metadata", diff --git a/packages/postgres-database/src/simcore_postgres_database/migration/versions/e98c45ff314f_preparation_for_osparc_io_migration.py b/packages/postgres-database/src/simcore_postgres_database/migration/versions/e98c45ff314f_preparation_for_osparc_io_migration.py new file mode 100644 index 000000000000..70b06ca060b0 --- /dev/null +++ b/packages/postgres-database/src/simcore_postgres_database/migration/versions/e98c45ff314f_preparation_for_osparc_io_migration.py @@ -0,0 +1,298 @@ +"""preparation for osparc.io migration + +Revision ID: e98c45ff314f +Revises: b39f2dc87ccd +Create Date: 2025-05-19 11:55:04.513120+00:00 + +""" + +import sqlalchemy as sa +from alembic import op +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = "e98c45ff314f" +down_revision = "b39f2dc87ccd" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "resource_tracker_service_runs__osparc_io_history_202508", + sa.Column("product_name", sa.String(), nullable=False), + sa.Column("service_run_id", sa.String(), nullable=False), + sa.Column("wallet_id", sa.BigInteger(), nullable=True), + sa.Column("wallet_name", sa.String(), nullable=True), + sa.Column("pricing_plan_id", sa.BigInteger(), nullable=True), + sa.Column("pricing_unit_id", sa.BigInteger(), nullable=True), + sa.Column("pricing_unit_cost_id", sa.BigInteger(), nullable=True), + sa.Column("pricing_unit_cost", sa.Numeric(scale=2), nullable=True), + sa.Column("simcore_user_agent", sa.String(), nullable=True), + sa.Column("user_id", sa.BigInteger(), nullable=False), + sa.Column("user_email", sa.String(), nullable=True), + sa.Column("project_id", sa.String(), nullable=False), + sa.Column("project_name", sa.String(), nullable=False), + sa.Column("node_id", sa.String(), nullable=False), + sa.Column("node_name", sa.String(), nullable=False), + sa.Column("parent_project_id", sa.String(), nullable=False), + sa.Column("root_parent_project_id", sa.String(), nullable=False), + sa.Column("root_parent_project_name", sa.String(), nullable=False), + sa.Column("parent_node_id", sa.String(), nullable=False), + sa.Column("root_parent_node_id", sa.String(), nullable=False), + sa.Column("service_key", sa.String(), nullable=False), + sa.Column("service_version", sa.String(), nullable=False), + sa.Column( + "service_type", + sa.Enum( + "COMPUTATIONAL_SERVICE", + "DYNAMIC_SERVICE", + name="resourcetrackerservicetypeosparciohistory", + ), + nullable=False, + ), + sa.Column( + "service_resources", postgresql.JSONB(astext_type=sa.Text()), nullable=False + ), + sa.Column( + "service_additional_metadata", + postgresql.JSONB(astext_type=sa.Text()), + nullable=False, + ), + sa.Column("started_at", sa.DateTime(timezone=True), nullable=False), + sa.Column("stopped_at", sa.DateTime(timezone=True), nullable=True), + sa.Column( + "service_run_status", + sa.Enum( + "RUNNING", + "SUCCESS", + "ERROR", + name="resourcetrackerservicerunstatusosparciohistory", + ), + nullable=False, + ), + sa.Column( + "modified", + sa.DateTime(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ), + sa.Column("last_heartbeat_at", sa.DateTime(timezone=True), nullable=False), + sa.Column("service_run_status_msg", sa.String(), nullable=True), + sa.Column("missed_heartbeat_counter", sa.SmallInteger(), nullable=False), + sa.PrimaryKeyConstraint("product_name", "service_run_id"), + ) + op.create_index( + op.f("ix_resource_tracker_service_runs__osparc_io_history_202508_wallet_id"), + "resource_tracker_service_runs__osparc_io_history_202508", + ["wallet_id"], + unique=False, + ) + op.create_index( + op.f("ix_resource_tracker_service_runs__osparc_io_history_202508_user_id"), + "resource_tracker_service_runs__osparc_io_history_202508", + ["user_id"], + unique=False, + ) + op.create_index( + op.f("ix_resource_tracker_service_runs__osparc_io_history_202508_started_at"), + "resource_tracker_service_runs__osparc_io_history_202508", + ["started_at"], + unique=False, + ) + op.drop_index("ix_projects_comments_project_uuid", table_name="projects_comments") + op.drop_table("projects_comments") + op.drop_column("file_meta_data", "node_id") + op.drop_constraint("fk_new_folders_to_folders_id", "folders_v2", type_="foreignkey") + op.drop_constraint("fk_new_folders_to_groups_gid", "folders_v2", type_="foreignkey") + op.create_foreign_key( + "fk_new_folders_to_folders_id", + "folders_v2", + "folders_v2", + ["parent_folder_id"], + ["folder_id"], + onupdate="CASCADE", + ) + op.create_foreign_key( + "fk_new_folders_to_groups_gid", + "folders_v2", + "groups", + ["created_by_gid"], + ["gid"], + onupdate="CASCADE", + ondelete="SET NULL", + ) + op.create_foreign_key( + "fk_payments_autorecharge_id_wallets", + "payments_autorecharge", + "wallets", + ["wallet_id"], + ["wallet_id"], + onupdate="CASCADE", + ondelete="CASCADE", + ) + op.create_foreign_key( + "fk_payments_methods_to_user_id", + "payments_methods", + "users", + ["user_id"], + ["id"], + onupdate="CASCADE", + ondelete="CASCADE", + ) + op.create_foreign_key( + "fk_payments_methods_to_wallet_id", + "payments_methods", + "wallets", + ["wallet_id"], + ["wallet_id"], + onupdate="CASCADE", + ondelete="CASCADE", + ) + op.create_foreign_key( + "fk_payments_transactions_to_wallet_id", + "payments_transactions", + "wallets", + ["wallet_id"], + ["wallet_id"], + onupdate="CASCADE", + ondelete="CASCADE", + ) + op.create_foreign_key( + "fk_payments_transactions_to_user_id", + "payments_transactions", + "users", + ["user_id"], + ["id"], + onupdate="CASCADE", + ondelete="CASCADE", + ) + op.create_foreign_key( + "fk_payments_transactions_to_products_name", + "payments_transactions", + "products", + ["product_name"], + ["name"], + onupdate="CASCADE", + ondelete="CASCADE", + ) + op.drop_constraint("tokens_user_id_fkey", "tokens", type_="foreignkey") + op.create_foreign_key( + None, + "tokens", + "users", + ["user_id"], + ["id"], + onupdate="CASCADE", + ondelete="CASCADE", + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(None, "tokens", type_="foreignkey") + op.create_foreign_key("tokens_user_id_fkey", "tokens", "users", ["user_id"], ["id"]) + op.drop_constraint( + "fk_payments_transactions_to_products_name", + "payments_transactions", + type_="foreignkey", + ) + op.drop_constraint( + "fk_payments_transactions_to_user_id", + "payments_transactions", + type_="foreignkey", + ) + op.drop_constraint( + "fk_payments_transactions_to_wallet_id", + "payments_transactions", + type_="foreignkey", + ) + op.drop_constraint( + "fk_payments_methods_to_wallet_id", "payments_methods", type_="foreignkey" + ) + op.drop_constraint( + "fk_payments_methods_to_user_id", "payments_methods", type_="foreignkey" + ) + op.drop_constraint( + "fk_payments_autorecharge_id_wallets", + "payments_autorecharge", + type_="foreignkey", + ) + op.drop_constraint("fk_new_folders_to_groups_gid", "folders_v2", type_="foreignkey") + op.drop_constraint("fk_new_folders_to_folders_id", "folders_v2", type_="foreignkey") + op.create_foreign_key( + "fk_new_folders_to_groups_gid", + "folders_v2", + "groups", + ["created_by_gid"], + ["gid"], + ondelete="SET NULL", + ) + op.create_foreign_key( + "fk_new_folders_to_folders_id", + "folders_v2", + "folders_v2", + ["parent_folder_id"], + ["folder_id"], + ) + op.add_column( + "file_meta_data", + sa.Column("node_id", sa.VARCHAR(), autoincrement=False, nullable=True), + ) + op.create_table( + "projects_comments", + sa.Column("comment_id", sa.BIGINT(), autoincrement=True, nullable=False), + sa.Column("project_uuid", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column("user_id", sa.BIGINT(), autoincrement=False, nullable=True), + sa.Column("contents", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column( + "created", + postgresql.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + autoincrement=False, + nullable=False, + ), + sa.Column( + "modified", + postgresql.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + autoincrement=False, + nullable=False, + ), + sa.ForeignKeyConstraint( + ["project_uuid"], + ["projects.uuid"], + name="fk_projects_comments_project_uuid", + onupdate="CASCADE", + ondelete="CASCADE", + ), + sa.ForeignKeyConstraint( + ["user_id"], + ["users.id"], + name="fk_projects_comments_user_id", + ondelete="SET NULL", + ), + sa.PrimaryKeyConstraint("comment_id", name="projects_comments_pkey"), + ) + op.create_index( + "ix_projects_comments_project_uuid", + "projects_comments", + ["project_uuid"], + unique=False, + ) + op.drop_index( + op.f("ix_resource_tracker_service_runs__osparc_io_history_202508_started_at"), + table_name="resource_tracker_service_runs__osparc_io_history_202508", + ) + op.drop_index( + op.f("ix_resource_tracker_service_runs__osparc_io_history_202508_user_id"), + table_name="resource_tracker_service_runs__osparc_io_history_202508", + ) + op.drop_index( + op.f("ix_resource_tracker_service_runs__osparc_io_history_202508_wallet_id"), + table_name="resource_tracker_service_runs__osparc_io_history_202508", + ) + op.drop_table("resource_tracker_service_runs__osparc_io_history_202508") + # ### end Alembic commands ### diff --git a/packages/postgres-database/src/simcore_postgres_database/models/file_meta_data.py b/packages/postgres-database/src/simcore_postgres_database/models/file_meta_data.py index 9ece039863f2..21d6ce899beb 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/file_meta_data.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/file_meta_data.py @@ -1,4 +1,5 @@ import sqlalchemy as sa +from simcore_postgres_database.models._common import RefActions from .base import metadata @@ -10,7 +11,18 @@ sa.Column("bucket_name", sa.String()), sa.Column("object_name", sa.String()), sa.Column("project_id", sa.String(), index=True), - sa.Column("node_id", sa.String()), + sa.Column( + "user_id", + sa.BigInteger(), + sa.ForeignKey( + "users.id", + name="fk_file_meta_data_user_id_users", + onupdate=RefActions.CASCADE, + ondelete=RefActions.CASCADE, + ), + nullable=False, + doc="The user id with which the run entry is associated", + ), sa.Column("user_id", sa.String(), index=True), sa.Column("file_id", sa.String(), primary_key=True), sa.Column("created_at", sa.String()), @@ -56,4 +68,5 @@ doc="SHA256 checksum of the file content", index=True, ), + ### ) diff --git a/packages/postgres-database/src/simcore_postgres_database/models/folders_v2.py b/packages/postgres-database/src/simcore_postgres_database/models/folders_v2.py index eebfd2079f80..5d06503f78ef 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/folders_v2.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/folders_v2.py @@ -33,6 +33,7 @@ sa.BigInteger, sa.ForeignKey( "folders_v2.folder_id", + onupdate=RefActions.CASCADE, name="fk_new_folders_to_folders_id", ), nullable=True, @@ -77,6 +78,7 @@ "groups.gid", name="fk_new_folders_to_groups_gid", ondelete=RefActions.SET_NULL, + onupdate=RefActions.CASCADE, ), nullable=True, ), diff --git a/packages/postgres-database/src/simcore_postgres_database/models/payments_autorecharge.py b/packages/postgres-database/src/simcore_postgres_database/models/payments_autorecharge.py index df30251c50ce..b96ffdb2e11c 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/payments_autorecharge.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/payments_autorecharge.py @@ -9,6 +9,7 @@ ) from .base import metadata from .payments_methods import payments_methods +from .wallets import wallets # # NOTE: @@ -29,7 +30,12 @@ sa.Column( "wallet_id", sa.BigInteger, - # NOTE: cannot use foreign-key because it would require a link to wallets table + sa.ForeignKey( + wallets.c.wallet_id, + name="fk_payments_autorecharge_id_wallets", + onupdate=RefActions.CASCADE, + ondelete=RefActions.CASCADE, + ), nullable=False, doc="Wallet associated to the auto-recharge", unique=True, diff --git a/packages/postgres-database/src/simcore_postgres_database/models/payments_methods.py b/packages/postgres-database/src/simcore_postgres_database/models/payments_methods.py index 3aabc3f992c3..9d9d84ee7410 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/payments_methods.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/payments_methods.py @@ -3,11 +3,13 @@ import sqlalchemy as sa from ._common import ( + RefActions, column_created_datetime, column_modified_datetime, register_modified_datetime_auto_update_trigger, ) from .base import metadata +from .wallets import wallets @enum.unique @@ -41,6 +43,12 @@ class InitPromptAckFlowState(str, enum.Enum): sa.Column( "user_id", sa.BigInteger, + sa.ForeignKey( + "users.id", + onupdate=RefActions.CASCADE, + ondelete=RefActions.CASCADE, + name="fk_payments_methods_to_user_id", + ), nullable=False, doc="Unique identifier of the user", index=True, @@ -48,6 +56,12 @@ class InitPromptAckFlowState(str, enum.Enum): sa.Column( "wallet_id", sa.BigInteger, + sa.ForeignKey( + wallets.c.wallet_id, + name="fk_payments_methods_to_wallet_id", + onupdate=RefActions.CASCADE, + ondelete=RefActions.CASCADE, + ), nullable=False, doc="Unique identifier to the wallet owned by the user", index=True, diff --git a/packages/postgres-database/src/simcore_postgres_database/models/payments_transactions.py b/packages/postgres-database/src/simcore_postgres_database/models/payments_transactions.py index 21916b0615b6..f44613690b93 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/payments_transactions.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/payments_transactions.py @@ -4,11 +4,13 @@ from ._common import ( NUMERIC_KWARGS, + RefActions, column_created_datetime, column_modified_datetime, register_modified_datetime_auto_update_trigger, ) from .base import metadata +from .wallets import wallets @unique @@ -60,12 +62,24 @@ def is_acknowledged(self) -> bool: sa.Column( "product_name", sa.String, + sa.ForeignKey( + "products.name", + onupdate=RefActions.CASCADE, + ondelete=RefActions.CASCADE, + name="fk_payments_transactions_to_products_name", + ), nullable=False, doc="Product name from which the transaction took place", ), sa.Column( "user_id", sa.BigInteger, + sa.ForeignKey( + "users.id", + onupdate=RefActions.CASCADE, + ondelete=RefActions.CASCADE, + name="fk_payments_transactions_to_user_id", + ), nullable=False, doc="User unique identifier", index=True, @@ -79,6 +93,12 @@ def is_acknowledged(self) -> bool: sa.Column( "wallet_id", sa.BigInteger, + sa.ForeignKey( + wallets.c.wallet_id, + name="fk_payments_transactions_to_wallet_id", + onupdate=RefActions.CASCADE, + ondelete=RefActions.CASCADE, + ), nullable=False, doc="Wallet identifier owned by the user", index=True, diff --git a/packages/postgres-database/src/simcore_postgres_database/models/projects_comments.py b/packages/postgres-database/src/simcore_postgres_database/models/projects_comments.py deleted file mode 100644 index 919b143bff3e..000000000000 --- a/packages/postgres-database/src/simcore_postgres_database/models/projects_comments.py +++ /dev/null @@ -1,53 +0,0 @@ -import sqlalchemy as sa - -from ._common import RefActions, column_created_datetime, column_modified_datetime -from .base import metadata -from .projects import projects -from .users import users - -projects_comments = sa.Table( - "projects_comments", - metadata, - sa.Column( - "comment_id", - sa.BigInteger, - nullable=False, - autoincrement=True, - primary_key=True, - doc="Primary key, identifies the comment", - ), - sa.Column( - "project_uuid", - sa.String, - sa.ForeignKey( - projects.c.uuid, - name="fk_projects_comments_project_uuid", - ondelete=RefActions.CASCADE, - onupdate=RefActions.CASCADE, - ), - index=True, - nullable=False, - doc="project reference for this table", - ), - # NOTE: if the user gets deleted, it sets to null which should be interpreted as "unknown" user - sa.Column( - "user_id", - sa.BigInteger, - sa.ForeignKey( - users.c.id, - name="fk_projects_comments_user_id", - ondelete=RefActions.SET_NULL, - ), - doc="user who created the comment", - nullable=True, - ), - sa.Column( - "contents", - sa.String, - nullable=False, - doc="Content of the comment", - ), - column_created_datetime(timezone=True), - column_modified_datetime(timezone=True), - sa.PrimaryKeyConstraint("comment_id", name="projects_comments_pkey"), -) diff --git a/packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_service_runs__osparc_io_history_202508.py b/packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_service_runs__osparc_io_history_202508.py new file mode 100644 index 000000000000..d0e5720efce0 --- /dev/null +++ b/packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_service_runs__osparc_io_history_202508.py @@ -0,0 +1,228 @@ +"""resource_tracker_service_runs table""" + +import enum + +import sqlalchemy as sa +from sqlalchemy.dialects.postgresql import JSONB + +from ._common import NUMERIC_KWARGS, column_modified_datetime +from .base import metadata + + +class ResourceTrackerServiceTypeOsparcIoHistory(str, enum.Enum): + COMPUTATIONAL_SERVICE = "COMPUTATIONAL_SERVICE" + DYNAMIC_SERVICE = "DYNAMIC_SERVICE" + + +class ResourceTrackerServiceRunStatusOsparcIoHistory(str, enum.Enum): + RUNNING = "RUNNING" + SUCCESS = "SUCCESS" + ERROR = "ERROR" + + +resource_tracker_service_runs__osparc_io_history_202508 = sa.Table( + "resource_tracker_service_runs__osparc_io_history_202508", + metadata, + # Primary keys + sa.Column( + "product_name", sa.String, nullable=False, doc="Product name", primary_key=True + ), + sa.Column( + "service_run_id", + sa.String, + nullable=False, + doc="Refers to the unique service_run_id provided by the director-v2/dynamic-sidecars.", + primary_key=True, + ), + # Wallet fields + sa.Column( + "wallet_id", + sa.BigInteger, + nullable=True, + doc="We want to store the wallet id for tracking/billing purposes and be sure it stays there even when the wallet is deleted (that's also reason why we do not introduce foreign key)", + index=True, + ), + sa.Column( + "wallet_name", + sa.String, + nullable=True, + doc="We want to store the wallet name for tracking/billing purposes and be sure it stays there even when the wallet is deleted (that's also reason why we do not introduce foreign key)", + ), + # Pricing fields + sa.Column( + "pricing_plan_id", + sa.BigInteger, + nullable=True, + doc="Pricing plan id for billing purposes", + ), + sa.Column( + "pricing_unit_id", + sa.BigInteger, + nullable=True, + doc="Pricing unit id for billing purposes", + ), + sa.Column( + "pricing_unit_cost_id", + sa.BigInteger, + nullable=True, + doc="Pricing unit cost id for billing purposes", + ), + sa.Column( + "pricing_unit_cost", + sa.Numeric(**NUMERIC_KWARGS), # type: ignore + nullable=True, + doc="Pricing unit cost used for billing purposes", + ), + # User agent field + sa.Column( + "simcore_user_agent", + sa.String, + nullable=True, + doc="Information about whether it is Puppeteer or not", + ), + # User fields + sa.Column( + "user_id", + sa.BigInteger, + nullable=False, + doc="We want to store the user id for tracking/billing purposes and be sure it stays there even when the user is deleted (that's also reason why we do not introduce foreign key)", + index=True, + ), + sa.Column( + "user_email", + sa.String, + nullable=True, + doc="we want to store the email for tracking/billing purposes and be sure it stays there even when the user is deleted (that's also reason why we do not introduce foreign key)", + ), + # Project fields + sa.Column( + "project_id", # UUID + sa.String, + nullable=False, + doc="We want to store the project id for tracking/billing purposes and be sure it stays there even when the project is deleted (that's also reason why we do not introduce foreign key)", + ), + sa.Column( + "project_name", + sa.String, + nullable=False, + doc="we want to store the project name for tracking/billing purposes and be sure it stays there even when the project is deleted (that's also reason why we do not introduce foreign key)", + ), + # Node fields + sa.Column( + "node_id", # UUID + sa.String, + nullable=False, + doc="We want to store the node id for tracking/billing purposes and be sure it stays there even when the node is deleted (that's also reason why we do not introduce foreign key)", + ), + sa.Column( + "node_name", + sa.String, + nullable=False, + doc="we want to store the node/service name/label for tracking/billing purposes and be sure it stays there even when the node is deleted.", + ), + # Project/Node parent fields + sa.Column( + "parent_project_id", # UUID + sa.String, + nullable=False, + doc="If a user starts computational jobs via a dynamic service, a new project is created in the backend. This newly created project is considered a child project, and the project from which it was created is the parent project. We want to store the parent project ID for tracking and billing purposes, and ensure it remains even when the node is deleted. This is also the reason why we do not introduce a foreign key.", + ), + sa.Column( + "root_parent_project_id", # UUID + sa.String, + nullable=False, + doc="Similar to the parent project concept, we are flexible enough to allow multiple nested computational jobs, which create multiple nested projects. For this reason, we keep the parent project ID, so we know from which project the user started their computation.", + ), + sa.Column( + "root_parent_project_name", + sa.String, + nullable=False, + doc="We want to store the root parent project name for tracking/billing purposes.", + ), + sa.Column( + "parent_node_id", # UUID + sa.String, + nullable=False, + doc="Since each project can have multiple nodes, similar to the parent project concept, we also store the parent node..", + ), + sa.Column( + "root_parent_node_id", # UUID + sa.String, + nullable=False, + doc="Since each project can have multiple nodes, similar to the root parent project concept, we also store the root parent node.", + ), + # Service fields + sa.Column( + "service_key", + sa.String, + nullable=False, + doc="Service Key", + ), + sa.Column( + "service_version", + sa.String, + nullable=False, + doc="Service Version", + ), + sa.Column( + "service_type", + sa.Enum(ResourceTrackerServiceTypeOsparcIoHistory), + nullable=False, + doc="Service type, ex. COMPUTATIONAL, DYNAMIC", + ), + sa.Column( + "service_resources", + JSONB, + nullable=False, + default="'{}'::jsonb", + doc="Service aresources, ex. cpu, gpu, memory, ...", + ), + sa.Column( + "service_additional_metadata", + JSONB, + nullable=False, + default="'{}'::jsonb", + doc="Service additional metadata.", + ), + # Run timestamps + sa.Column( + "started_at", + sa.DateTime(timezone=True), + nullable=False, + doc="Timestamp when the service was started", + index=True, + ), + sa.Column( + "stopped_at", + sa.DateTime(timezone=True), + nullable=True, + doc="Timestamp when the service was stopped", + ), + # Run status + sa.Column( + "service_run_status", # Partial index was defined bellow + sa.Enum(ResourceTrackerServiceRunStatusOsparcIoHistory), + nullable=False, + ), + column_modified_datetime(timezone=True), + # Last Heartbeat + sa.Column( + "last_heartbeat_at", + sa.DateTime(timezone=True), + nullable=False, + doc="Timestamp when was the last heartbeat", + ), + sa.Column( + "service_run_status_msg", + sa.String, + nullable=True, + doc="Custom message/comment, for example to help understand root cause of the error during investigation", + ), + sa.Column( + "missed_heartbeat_counter", + sa.SmallInteger, + nullable=False, + default=0, + doc="How many heartbeat checks have been missed", + ), +) diff --git a/packages/postgres-database/src/simcore_postgres_database/models/tokens.py b/packages/postgres-database/src/simcore_postgres_database/models/tokens.py index 990de23c7247..e5ee2ce04192 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/tokens.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/tokens.py @@ -1,6 +1,7 @@ -""" User Tokens table -""" +"""User Tokens table""" + import sqlalchemy as sa +from simcore_postgres_database.models._common import RefActions from .base import metadata from .users import users @@ -10,7 +11,14 @@ "tokens", metadata, sa.Column("token_id", sa.BigInteger, nullable=False, primary_key=True), - sa.Column("user_id", sa.BigInteger, sa.ForeignKey(users.c.id), nullable=False), + sa.Column( + "user_id", + sa.BigInteger, + sa.ForeignKey( + users.c.id, onupdate=RefActions.CASCADE, ondelete=RefActions.CASCADE + ), + nullable=False, + ), sa.Column("token_service", sa.String, nullable=False), sa.Column("token_data", sa.JSON, nullable=False), ) diff --git a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml index bcda438663d3..823d889bfe64 100644 --- a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml +++ b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml @@ -4712,172 +4712,6 @@ paths: application/json: schema: $ref: '#/components/schemas/Envelope_list_ProjectGroupGet__' - /v0/projects/{project_uuid}/comments: - post: - tags: - - projects - - comments - summary: Create Project Comment - description: Create a new comment for a specific project. The request body should - contain the comment contents and user information. - operationId: create_project_comment - deprecated: true - parameters: - - name: project_uuid - in: path - required: true - schema: - type: string - format: uuid - title: Project Uuid - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/_ProjectCommentsBodyParams' - responses: - '201': - description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/Envelope_dict_Literal__comment_id____Annotated_int__Gt___' - get: - tags: - - projects - - comments - summary: List Project Comments - description: Retrieve all comments for a specific project. - operationId: list_project_comments - deprecated: true - parameters: - - name: project_uuid - in: path - required: true - schema: - type: string - format: uuid - title: Project Uuid - - name: limit - in: query - required: false - schema: - type: integer - default: 20 - title: Limit - - name: offset - in: query - required: false - schema: - type: integer - minimum: 0 - default: 0 - title: Offset - responses: - '200': - description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/Envelope_list_ProjectsCommentsAPI__' - /v0/projects/{project_uuid}/comments/{comment_id}: - put: - tags: - - projects - - comments - summary: Update Project Comment - description: Update the contents of a specific comment for a project. The request - body should contain the updated comment contents. - operationId: update_project_comment - deprecated: true - parameters: - - name: project_uuid - in: path - required: true - schema: - type: string - format: uuid - title: Project Uuid - - name: comment_id - in: path - required: true - schema: - type: integer - exclusiveMinimum: true - title: Comment Id - minimum: 0 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/_ProjectCommentsBodyParams' - responses: - '200': - description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/Envelope_ProjectsCommentsAPI_' - delete: - tags: - - projects - - comments - summary: Delete Project Comment - description: Delete a specific comment associated with a project. - operationId: delete_project_comment - deprecated: true - parameters: - - name: project_uuid - in: path - required: true - schema: - type: string - format: uuid - title: Project Uuid - - name: comment_id - in: path - required: true - schema: - type: integer - exclusiveMinimum: true - title: Comment Id - minimum: 0 - responses: - '204': - description: Successful Response - get: - tags: - - projects - - comments - summary: Get Project Comment - description: Retrieve a specific comment by its ID within a project. - operationId: get_project_comment - deprecated: true - parameters: - - name: project_uuid - in: path - required: true - schema: - type: string - format: uuid - title: Project Uuid - - name: comment_id - in: path - required: true - schema: - type: integer - exclusiveMinimum: true - title: Comment Id - minimum: 0 - responses: - '200': - description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/Envelope_ProjectsCommentsAPI_' /v0/projects/{project_id}/conversations: post: tags: @@ -10300,19 +10134,6 @@ components: title: Error type: object title: Envelope[ProjectState] - Envelope_ProjectsCommentsAPI_: - properties: - data: - anyOf: - - $ref: '#/components/schemas/ProjectsCommentsAPI' - - type: 'null' - error: - anyOf: - - {} - - type: 'null' - title: Error - type: object - title: Envelope[ProjectsCommentsAPI] Envelope_RegisterPhoneNextPage_: properties: data: @@ -10607,26 +10428,6 @@ components: title: Error type: object title: Envelope[dict[Annotated[str, StringConstraints], ImageResources]] - Envelope_dict_Literal__comment_id____Annotated_int__Gt___: - properties: - data: - anyOf: - - additionalProperties: - type: integer - exclusiveMinimum: true - minimum: 0 - propertyNames: - const: comment_id - type: object - - type: 'null' - title: Data - error: - anyOf: - - {} - - type: 'null' - title: Error - type: object - title: Envelope[dict[Literal['comment_id'], Annotated[int, Gt]]] Envelope_dict_UUID__Activity__: properties: data: @@ -10904,22 +10705,6 @@ components: title: Error type: object title: Envelope[list[ProjectMetadataPortGet]] - Envelope_list_ProjectsCommentsAPI__: - properties: - data: - anyOf: - - items: - $ref: '#/components/schemas/ProjectsCommentsAPI' - type: array - - type: 'null' - title: Data - error: - anyOf: - - {} - - type: 'null' - title: Error - type: object - title: Envelope[list[ProjectsCommentsAPI]] Envelope_list_ResourceHit__: properties: data: @@ -14992,49 +14777,6 @@ components: - template - user title: ProjectTypeAPI - ProjectsCommentsAPI: - properties: - comment_id: - type: integer - exclusiveMinimum: true - title: Comment Id - description: Primary key, identifies the comment - minimum: 0 - project_uuid: - type: string - format: uuid - title: Project Uuid - description: project reference for this table - user_id: - type: integer - exclusiveMinimum: true - title: User Id - description: user reference for this table - minimum: 0 - contents: - type: string - title: Contents - description: Contents of the comment - created: - type: string - format: date-time - title: Created - description: Timestamp on creation - modified: - type: string - format: date-time - title: Modified - description: Timestamp with last update - additionalProperties: false - type: object - required: - - comment_id - - project_uuid - - user_id - - contents - - created - - modified - title: ProjectsCommentsAPI ProjectsGroupsBodyParams: properties: read: @@ -17264,16 +17006,6 @@ components: title: Expiration 2Fa type: object title: _PageParams - _ProjectCommentsBodyParams: - properties: - contents: - type: string - title: Contents - additionalProperties: false - type: object - required: - - contents - title: _ProjectCommentsBodyParams _ProjectConversationMessagesCreateBodyParams: properties: content: diff --git a/services/web/server/src/simcore_service_webserver/projects/_comments_repository.py b/services/web/server/src/simcore_service_webserver/projects/_comments_repository.py deleted file mode 100644 index 1a871f12ed38..000000000000 --- a/services/web/server/src/simcore_service_webserver/projects/_comments_repository.py +++ /dev/null @@ -1,98 +0,0 @@ -import logging - -from aiopg.sa.result import ResultProxy -from models_library.projects import ProjectID -from models_library.projects_comments import CommentID, ProjectsCommentsDB -from models_library.users import UserID -from pydantic import TypeAdapter -from pydantic.types import PositiveInt -from simcore_postgres_database.models.projects_comments import projects_comments -from sqlalchemy import func, literal_column -from sqlalchemy.sql import select - -_logger = logging.getLogger(__name__) - - -async def create_project_comment( - conn, project_uuid: ProjectID, user_id: UserID, contents: str -) -> CommentID: - project_comment_id: ResultProxy = await conn.execute( - projects_comments.insert() - .values( - project_uuid=project_uuid, - user_id=user_id, - contents=contents, - modified=func.now(), - ) - .returning(projects_comments.c.comment_id) - ) - result: tuple[PositiveInt] = await project_comment_id.first() - return TypeAdapter(CommentID).validate_python(result[0]) - - -async def list_project_comments( - conn, - project_uuid: ProjectID, - offset: PositiveInt, - limit: int, -) -> list[ProjectsCommentsDB]: - result = [] - project_comment_result: ResultProxy = await conn.execute( - projects_comments.select() - .where(projects_comments.c.project_uuid == f"{project_uuid}") - .order_by(projects_comments.c.created.asc()) - .offset(offset) - .limit(limit) - ) - result = [ - ProjectsCommentsDB.model_validate(row) - for row in await project_comment_result.fetchall() - ] - return result - - -async def total_project_comments( - conn, - project_uuid: ProjectID, -) -> PositiveInt: - project_comment_result: ResultProxy = await conn.execute( - select(func.count()) - .select_from(projects_comments) - .where(projects_comments.c.project_uuid == f"{project_uuid}") - ) - result: tuple[PositiveInt] = await project_comment_result.first() - return result[0] - - -async def update_project_comment( - conn, - comment_id: CommentID, - project_uuid: ProjectID, - contents: str, -) -> ProjectsCommentsDB: - project_comment_result = await conn.execute( - projects_comments.update() - .values( - project_uuid=project_uuid, - contents=contents, - modified=func.now(), - ) - .where(projects_comments.c.comment_id == comment_id) - .returning(literal_column("*")) - ) - result = await project_comment_result.first() - return ProjectsCommentsDB.model_validate(result) - - -async def delete_project_comment(conn, comment_id: CommentID) -> None: - await conn.execute( - projects_comments.delete().where(projects_comments.c.comment_id == comment_id) - ) - - -async def get_project_comment(conn, comment_id: CommentID) -> ProjectsCommentsDB: - project_comment_result = await conn.execute( - projects_comments.select().where(projects_comments.c.comment_id == comment_id) - ) - result = await project_comment_result.first() - return ProjectsCommentsDB.model_validate(result) diff --git a/services/web/server/src/simcore_service_webserver/projects/_comments_service.py b/services/web/server/src/simcore_service_webserver/projects/_comments_service.py deleted file mode 100644 index 7999d1e591ca..000000000000 --- a/services/web/server/src/simcore_service_webserver/projects/_comments_service.py +++ /dev/null @@ -1,96 +0,0 @@ -import logging - -from aiohttp import web -from models_library.projects import ProjectID -from models_library.projects_comments import ( - CommentID, - ProjectsCommentsAPI, - ProjectsCommentsDB, -) -from models_library.users import UserID -from pydantic import PositiveInt - -from ._projects_repository_legacy import APP_PROJECT_DBAPI, ProjectDBAPI - -log = logging.getLogger(__name__) - - -# -# PROJECT COMMENTS ------------------------------------------------------------------- -# - - -async def create_project_comment( - request: web.Request, project_uuid: ProjectID, user_id: UserID, contents: str -) -> CommentID: - db: ProjectDBAPI = request.app[APP_PROJECT_DBAPI] - - comment_id: CommentID = await db.create_project_comment( - project_uuid, user_id, contents - ) - return comment_id - - -async def list_project_comments( - request: web.Request, - project_uuid: ProjectID, - offset: PositiveInt, - limit: int, -) -> list[ProjectsCommentsAPI]: - db: ProjectDBAPI = request.app[APP_PROJECT_DBAPI] - - projects_comments_db_model: list[ProjectsCommentsDB] = ( - await db.list_project_comments(project_uuid, offset, limit) - ) - projects_comments_api_model = [ - ProjectsCommentsAPI(**comment.model_dump()) - for comment in projects_comments_db_model - ] - return projects_comments_api_model - - -async def total_project_comments( - request: web.Request, - project_uuid: ProjectID, -) -> PositiveInt: - db: ProjectDBAPI = request.app[APP_PROJECT_DBAPI] - - project_comments_total: PositiveInt = await db.total_project_comments(project_uuid) - return project_comments_total - - -async def update_project_comment( - request: web.Request, - comment_id: CommentID, - project_uuid: ProjectID, - contents: str, -) -> ProjectsCommentsAPI: - db: ProjectDBAPI = request.app[APP_PROJECT_DBAPI] - - projects_comments_db_model: ProjectsCommentsDB = await db.update_project_comment( - comment_id, project_uuid, contents - ) - projects_comments_api_model = ProjectsCommentsAPI( - **projects_comments_db_model.model_dump() - ) - return projects_comments_api_model - - -async def delete_project_comment(request: web.Request, comment_id: CommentID) -> None: - db: ProjectDBAPI = request.app[APP_PROJECT_DBAPI] - - await db.delete_project_comment(comment_id) - - -async def get_project_comment( - request: web.Request, comment_id: CommentID -) -> ProjectsCommentsAPI: - db: ProjectDBAPI = request.app[APP_PROJECT_DBAPI] - - projects_comments_db_model: ProjectsCommentsDB = await db.get_project_comment( - comment_id - ) - projects_comments_api_model = ProjectsCommentsAPI( - **projects_comments_db_model.model_dump() - ) - return projects_comments_api_model diff --git a/services/web/server/src/simcore_service_webserver/projects/_controller/comments_rest.py b/services/web/server/src/simcore_service_webserver/projects/_controller/comments_rest.py deleted file mode 100644 index 183cf1fa3b66..000000000000 --- a/services/web/server/src/simcore_service_webserver/projects/_controller/comments_rest.py +++ /dev/null @@ -1,228 +0,0 @@ -import logging -from typing import Any - -from aiohttp import web -from models_library.projects import ProjectID -from models_library.projects_comments import CommentID -from models_library.rest_pagination import ( - DEFAULT_NUMBER_OF_ITEMS_PER_PAGE, - MAXIMUM_NUMBER_OF_ITEMS_PER_PAGE, - Page, -) -from models_library.rest_pagination_utils import paginate_data -from pydantic import BaseModel, ConfigDict, Field, NonNegativeInt -from servicelib.aiohttp import status -from servicelib.aiohttp.requests_validation import ( - parse_request_body_as, - parse_request_path_parameters_as, - parse_request_query_parameters_as, -) -from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON -from servicelib.rest_constants import RESPONSE_MODEL_POLICY - -from ..._meta import API_VTAG as VTAG -from ...login.decorators import login_required -from ...security.decorators import permission_required -from ...utils_aiohttp import envelope_json_response -from .. import _comments_service, _projects_service -from ._rest_exceptions import handle_plugin_requests_exceptions -from ._rest_schemas import RequestContext - -_logger = logging.getLogger(__name__) - -# -# projects/*/comments COLLECTION ------------------------- -# - -routes = web.RouteTableDef() - - -class _ProjectCommentsPathParams(BaseModel): - project_uuid: ProjectID - model_config = ConfigDict(extra="forbid") - - -class _ProjectCommentsWithCommentPathParams(BaseModel): - project_uuid: ProjectID - comment_id: CommentID - model_config = ConfigDict(extra="forbid") - - -class _ProjectCommentsBodyParams(BaseModel): - contents: str - model_config = ConfigDict(extra="forbid") - - -@routes.post( - f"/{VTAG}/projects/{{project_uuid}}/comments", name="create_project_comment" -) -@login_required -@permission_required("project.read") -@handle_plugin_requests_exceptions -async def create_project_comment(request: web.Request): - req_ctx = RequestContext.model_validate(request) - path_params = parse_request_path_parameters_as(_ProjectCommentsPathParams, request) - body_params = await parse_request_body_as(_ProjectCommentsBodyParams, request) - - # ensure the project exists - await _projects_service.get_project_for_user( - request.app, - project_uuid=f"{path_params.project_uuid}", - user_id=req_ctx.user_id, - include_state=False, - ) - - comment_id = await _comments_service.create_project_comment( - request=request, - project_uuid=path_params.project_uuid, - user_id=req_ctx.user_id, - contents=body_params.contents, - ) - - return envelope_json_response({"comment_id": comment_id}, web.HTTPCreated) - - -class _ListProjectCommentsQueryParams(BaseModel): - limit: int = Field( - default=DEFAULT_NUMBER_OF_ITEMS_PER_PAGE, - description="maximum number of items to return (pagination)", - ge=1, - lt=MAXIMUM_NUMBER_OF_ITEMS_PER_PAGE, - ) - offset: NonNegativeInt = Field( - default=0, description="index to the first item to return (pagination)" - ) - model_config = ConfigDict(extra="forbid") - - -@routes.get(f"/{VTAG}/projects/{{project_uuid}}/comments", name="list_project_comments") -@login_required -@permission_required("project.read") -@handle_plugin_requests_exceptions -async def list_project_comments(request: web.Request): - req_ctx = RequestContext.model_validate(request) - path_params = parse_request_path_parameters_as(_ProjectCommentsPathParams, request) - query_params: _ListProjectCommentsQueryParams = parse_request_query_parameters_as( - _ListProjectCommentsQueryParams, request - ) - - # ensure the project exists - await _projects_service.get_project_for_user( - request.app, - project_uuid=f"{path_params.project_uuid}", - user_id=req_ctx.user_id, - include_state=False, - ) - - total_project_comments = await _comments_service.total_project_comments( - request=request, - project_uuid=path_params.project_uuid, - ) - - project_comments = await _comments_service.list_project_comments( - request=request, - project_uuid=path_params.project_uuid, - offset=query_params.offset, - limit=query_params.limit, - ) - - page = Page[dict[str, Any]].model_validate( - paginate_data( - chunk=project_comments, - request_url=request.url, - total=total_project_comments, - limit=query_params.limit, - offset=query_params.offset, - ) - ) - return web.Response( - text=page.model_dump_json(**RESPONSE_MODEL_POLICY), - content_type=MIMETYPE_APPLICATION_JSON, - ) - - -@routes.put( - f"/{VTAG}/projects/{{project_uuid}}/comments/{{comment_id}}", - name="update_project_comment", -) -@login_required -@permission_required("project.read") -@handle_plugin_requests_exceptions -async def update_project_comment(request: web.Request): - req_ctx = RequestContext.model_validate(request) - path_params = parse_request_path_parameters_as( - _ProjectCommentsWithCommentPathParams, request - ) - body_params = await parse_request_body_as(_ProjectCommentsBodyParams, request) - - # ensure the project exists - await _projects_service.get_project_for_user( - request.app, - project_uuid=f"{path_params.project_uuid}", - user_id=req_ctx.user_id, - include_state=False, - ) - - updated_comment = await _comments_service.update_project_comment( - request=request, - comment_id=path_params.comment_id, - project_uuid=path_params.project_uuid, - contents=body_params.contents, - ) - return envelope_json_response(updated_comment) - - -@routes.delete( - f"/{VTAG}/projects/{{project_uuid}}/comments/{{comment_id}}", - name="delete_project_comment", -) -@login_required -@permission_required("project.read") -@handle_plugin_requests_exceptions -async def delete_project_comment(request: web.Request): - req_ctx = RequestContext.model_validate(request) - path_params = parse_request_path_parameters_as( - _ProjectCommentsWithCommentPathParams, request - ) - - # ensure the project exists - await _projects_service.get_project_for_user( - request.app, - project_uuid=f"{path_params.project_uuid}", - user_id=req_ctx.user_id, - include_state=False, - ) - - await _comments_service.delete_project_comment( - request=request, - comment_id=path_params.comment_id, - ) - return web.json_response(status=status.HTTP_204_NO_CONTENT) - - -@routes.get( - f"/{VTAG}/projects/{{project_uuid}}/comments/{{comment_id}}", - name="get_project_comment", -) -@login_required -@permission_required("project.read") -@handle_plugin_requests_exceptions -async def get_project_comment(request: web.Request): - req_ctx = RequestContext.model_validate(request) - path_params = parse_request_path_parameters_as( - _ProjectCommentsWithCommentPathParams, request - ) - - # ensure the project exists - await _projects_service.get_project_for_user( - request.app, - project_uuid=f"{path_params.project_uuid}", - user_id=req_ctx.user_id, - include_state=False, - ) - - comment = await _comments_service.get_project_comment( - request=request, - comment_id=path_params.comment_id, - ) - return envelope_json_response(comment) diff --git a/services/web/server/src/simcore_service_webserver/projects/_projects_repository_legacy.py b/services/web/server/src/simcore_service_webserver/projects/_projects_repository_legacy.py index f6df80d866a0..1b12ed99f032 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_projects_repository_legacy.py +++ b/services/web/server/src/simcore_service_webserver/projects/_projects_repository_legacy.py @@ -20,7 +20,6 @@ from models_library.groups import GroupID from models_library.products import ProductName from models_library.projects import ProjectID, ProjectIDStr -from models_library.projects_comments import CommentID, ProjectsCommentsDB from models_library.projects_nodes import Node from models_library.projects_nodes_io import NodeID, NodeIDStr from models_library.resource_tracker import ( @@ -72,14 +71,6 @@ from tenacity.retry import retry_if_exception_type from ..utils import now_str -from ._comments_repository import ( - create_project_comment, - delete_project_comment, - get_project_comment, - list_project_comments, - total_project_comments, - update_project_comment, -) from ._projects_repository import PROJECT_DB_COLS from ._projects_repository_legacy_utils import ( ANY_USER_ID_SENTINEL, @@ -1337,51 +1328,6 @@ async def remove_tag( project["tags"].remove(tag_id) return convert_to_schema_names(project, user_email) - # - # Project Comments - # - - async def create_project_comment( - self, project_uuid: ProjectID, user_id: UserID, contents: str - ) -> CommentID: - async with self.engine.acquire() as conn: - return await create_project_comment(conn, project_uuid, user_id, contents) - - async def list_project_comments( - self, - project_uuid: ProjectID, - offset: PositiveInt, - limit: int, - ) -> list[ProjectsCommentsDB]: - async with self.engine.acquire() as conn: - return await list_project_comments(conn, project_uuid, offset, limit) - - async def total_project_comments( - self, - project_uuid: ProjectID, - ) -> PositiveInt: - async with self.engine.acquire() as conn: - return await total_project_comments(conn, project_uuid) - - async def update_project_comment( - self, - comment_id: CommentID, - project_uuid: ProjectID, - contents: str, - ) -> ProjectsCommentsDB: - async with self.engine.acquire() as conn: - return await update_project_comment( - conn, comment_id, project_uuid, contents - ) - - async def delete_project_comment(self, comment_id: CommentID) -> None: - async with self.engine.acquire() as conn: - return await delete_project_comment(conn, comment_id) - - async def get_project_comment(self, comment_id: CommentID) -> ProjectsCommentsDB: - async with self.engine.acquire() as conn: - return await get_project_comment(conn, comment_id) - # # Project Wallet # diff --git a/services/web/server/src/simcore_service_webserver/projects/plugin.py b/services/web/server/src/simcore_service_webserver/projects/plugin.py index 5028739d881b..51a47294ffb5 100644 --- a/services/web/server/src/simcore_service_webserver/projects/plugin.py +++ b/services/web/server/src/simcore_service_webserver/projects/plugin.py @@ -13,7 +13,6 @@ from ..rabbitmq import setup_rabbitmq from ._controller import ( access_rights_rest, - comments_rest, conversations_rest, folders_rest, metadata_rest, @@ -62,7 +61,6 @@ def setup_projects(app: web.Application) -> bool: # setup REST-controllers app.router.add_routes(projects_states_rest.routes) app.router.add_routes(projects_rest.routes) - app.router.add_routes(comments_rest.routes) app.router.add_routes(conversations_rest.routes) app.router.add_routes(access_rights_rest.routes) app.router.add_routes(metadata_rest.routes) From 1e702126788367f18395dd9df8dcd44916e4565d Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Mon, 19 May 2025 14:27:29 +0200 Subject: [PATCH 02/11] fix --- ...f4_preparation_for_osparc_io_migration.py} | 96 +++++++++++++------ .../models/resource_tracker_service_runs.py | 25 ++++- ..._service_runs__osparc_io_history_202508.py | 4 +- 3 files changed, 89 insertions(+), 36 deletions(-) rename packages/postgres-database/src/simcore_postgres_database/migration/versions/{e98c45ff314f_preparation_for_osparc_io_migration.py => 43d7e61eedf4_preparation_for_osparc_io_migration.py} (81%) diff --git a/packages/postgres-database/src/simcore_postgres_database/migration/versions/e98c45ff314f_preparation_for_osparc_io_migration.py b/packages/postgres-database/src/simcore_postgres_database/migration/versions/43d7e61eedf4_preparation_for_osparc_io_migration.py similarity index 81% rename from packages/postgres-database/src/simcore_postgres_database/migration/versions/e98c45ff314f_preparation_for_osparc_io_migration.py rename to packages/postgres-database/src/simcore_postgres_database/migration/versions/43d7e61eedf4_preparation_for_osparc_io_migration.py index 70b06ca060b0..736b99a6cb17 100644 --- a/packages/postgres-database/src/simcore_postgres_database/migration/versions/e98c45ff314f_preparation_for_osparc_io_migration.py +++ b/packages/postgres-database/src/simcore_postgres_database/migration/versions/43d7e61eedf4_preparation_for_osparc_io_migration.py @@ -1,8 +1,8 @@ """preparation for osparc.io migration -Revision ID: e98c45ff314f +Revision ID: 43d7e61eedf4 Revises: b39f2dc87ccd -Create Date: 2025-05-19 11:55:04.513120+00:00 +Create Date: 2025-05-19 12:27:17.367971+00:00 """ @@ -11,7 +11,7 @@ from sqlalchemy.dialects import postgresql # revision identifiers, used by Alembic. -revision = "e98c45ff314f" +revision = "43d7e61eedf4" down_revision = "b39f2dc87ccd" branch_labels = None depends_on = None @@ -20,7 +20,7 @@ def upgrade(): # ### commands auto generated by Alembic - please adjust! ### op.create_table( - "resource_tracker_service_runs__osparc_io_history_202508", + "zzz_resource_tracker_service_runs__osparc_io_archive_202508", sa.Column("product_name", sa.String(), nullable=False), sa.Column("service_run_id", sa.String(), nullable=False), sa.Column("wallet_id", sa.BigInteger(), nullable=True), @@ -84,21 +84,25 @@ def upgrade(): sa.PrimaryKeyConstraint("product_name", "service_run_id"), ) op.create_index( - op.f("ix_resource_tracker_service_runs__osparc_io_history_202508_wallet_id"), - "resource_tracker_service_runs__osparc_io_history_202508", - ["wallet_id"], + op.f( + "ix_zzz_resource_tracker_service_runs__osparc_io_archive_202508_started_at" + ), + "zzz_resource_tracker_service_runs__osparc_io_archive_202508", + ["started_at"], unique=False, ) op.create_index( - op.f("ix_resource_tracker_service_runs__osparc_io_history_202508_user_id"), - "resource_tracker_service_runs__osparc_io_history_202508", - ["user_id"], + op.f( + "ix_zzz_resource_tracker_service_runs__osparc_io_archive_202508_wallet_id" + ), + "zzz_resource_tracker_service_runs__osparc_io_archive_202508", + ["wallet_id"], unique=False, ) op.create_index( - op.f("ix_resource_tracker_service_runs__osparc_io_history_202508_started_at"), - "resource_tracker_service_runs__osparc_io_history_202508", - ["started_at"], + op.f("ix_zzz_resource_tracker_service_runs__osparc_io_archive_202508_user_id"), + "zzz_resource_tracker_service_runs__osparc_io_archive_202508", + ["user_id"], unique=False, ) op.drop_index("ix_projects_comments_project_uuid", table_name="projects_comments") @@ -133,20 +137,20 @@ def upgrade(): ondelete="CASCADE", ) op.create_foreign_key( - "fk_payments_methods_to_user_id", + "fk_payments_methods_to_wallet_id", "payments_methods", - "users", - ["user_id"], - ["id"], + "wallets", + ["wallet_id"], + ["wallet_id"], onupdate="CASCADE", ondelete="CASCADE", ) op.create_foreign_key( - "fk_payments_methods_to_wallet_id", + "fk_payments_methods_to_user_id", "payments_methods", - "wallets", - ["wallet_id"], - ["wallet_id"], + "users", + ["user_id"], + ["id"], onupdate="CASCADE", ondelete="CASCADE", ) @@ -177,6 +181,24 @@ def upgrade(): onupdate="CASCADE", ondelete="CASCADE", ) + op.create_foreign_key( + "fk_service_runs_to_user_id", + "resource_tracker_service_runs", + "users", + ["user_id"], + ["id"], + onupdate="CASCADE", + ondelete="CASCADE", + ) + op.create_foreign_key( + "fk_service_runs_to_product_name", + "resource_tracker_service_runs", + "products", + ["product_name"], + ["name"], + onupdate="CASCADE", + ondelete="CASCADE", + ) op.drop_constraint("tokens_user_id_fkey", "tokens", type_="foreignkey") op.create_foreign_key( None, @@ -194,6 +216,16 @@ def downgrade(): # ### commands auto generated by Alembic - please adjust! ### op.drop_constraint(None, "tokens", type_="foreignkey") op.create_foreign_key("tokens_user_id_fkey", "tokens", "users", ["user_id"], ["id"]) + op.drop_constraint( + "fk_service_runs_to_product_name", + "resource_tracker_service_runs", + type_="foreignkey", + ) + op.drop_constraint( + "fk_service_runs_to_user_id", + "resource_tracker_service_runs", + type_="foreignkey", + ) op.drop_constraint( "fk_payments_transactions_to_products_name", "payments_transactions", @@ -210,10 +242,10 @@ def downgrade(): type_="foreignkey", ) op.drop_constraint( - "fk_payments_methods_to_wallet_id", "payments_methods", type_="foreignkey" + "fk_payments_methods_to_user_id", "payments_methods", type_="foreignkey" ) op.drop_constraint( - "fk_payments_methods_to_user_id", "payments_methods", type_="foreignkey" + "fk_payments_methods_to_wallet_id", "payments_methods", type_="foreignkey" ) op.drop_constraint( "fk_payments_autorecharge_id_wallets", @@ -283,16 +315,20 @@ def downgrade(): unique=False, ) op.drop_index( - op.f("ix_resource_tracker_service_runs__osparc_io_history_202508_started_at"), - table_name="resource_tracker_service_runs__osparc_io_history_202508", + op.f("ix_zzz_resource_tracker_service_runs__osparc_io_archive_202508_user_id"), + table_name="zzz_resource_tracker_service_runs__osparc_io_archive_202508", ) op.drop_index( - op.f("ix_resource_tracker_service_runs__osparc_io_history_202508_user_id"), - table_name="resource_tracker_service_runs__osparc_io_history_202508", + op.f( + "ix_zzz_resource_tracker_service_runs__osparc_io_archive_202508_wallet_id" + ), + table_name="zzz_resource_tracker_service_runs__osparc_io_archive_202508", ) op.drop_index( - op.f("ix_resource_tracker_service_runs__osparc_io_history_202508_wallet_id"), - table_name="resource_tracker_service_runs__osparc_io_history_202508", + op.f( + "ix_zzz_resource_tracker_service_runs__osparc_io_archive_202508_started_at" + ), + table_name="zzz_resource_tracker_service_runs__osparc_io_archive_202508", ) - op.drop_table("resource_tracker_service_runs__osparc_io_history_202508") + op.drop_table("zzz_resource_tracker_service_runs__osparc_io_archive_202508") # ### end Alembic commands ### diff --git a/packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_service_runs.py b/packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_service_runs.py index 33eddcb9fc77..715a35e22012 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_service_runs.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_service_runs.py @@ -1,12 +1,13 @@ -""" resource_tracker_service_runs table -""" +"""resource_tracker_service_runs table""" + import enum import sqlalchemy as sa from sqlalchemy.dialects.postgresql import JSONB -from ._common import NUMERIC_KWARGS, column_modified_datetime +from ._common import NUMERIC_KWARGS, RefActions, column_modified_datetime from .base import metadata +from .users import users class ResourceTrackerServiceType(str, enum.Enum): @@ -25,7 +26,17 @@ class ResourceTrackerServiceRunStatus(str, enum.Enum): metadata, # Primary keys sa.Column( - "product_name", sa.String, nullable=False, doc="Product name", primary_key=True + "product_name", + sa.String, + sa.ForeignKey( + "products.name", + onupdate=RefActions.CASCADE, + ondelete=RefActions.CASCADE, + name="fk_service_runs_to_product_name", + ), + nullable=False, + doc="Product name", + primary_key=True, ), sa.Column( "service_run_id", @@ -84,6 +95,12 @@ class ResourceTrackerServiceRunStatus(str, enum.Enum): sa.Column( "user_id", sa.BigInteger, + sa.ForeignKey( + users.c.id, + onupdate=RefActions.CASCADE, + ondelete=RefActions.CASCADE, + name="fk_service_runs_to_user_id", + ), nullable=False, doc="We want to store the user id for tracking/billing purposes and be sure it stays there even when the user is deleted (that's also reason why we do not introduce foreign key)", index=True, diff --git a/packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_service_runs__osparc_io_history_202508.py b/packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_service_runs__osparc_io_history_202508.py index d0e5720efce0..a11538339044 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_service_runs__osparc_io_history_202508.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_service_runs__osparc_io_history_202508.py @@ -20,8 +20,8 @@ class ResourceTrackerServiceRunStatusOsparcIoHistory(str, enum.Enum): ERROR = "ERROR" -resource_tracker_service_runs__osparc_io_history_202508 = sa.Table( - "resource_tracker_service_runs__osparc_io_history_202508", +zzz_resource_tracker_service_runs__osparc_io_archive_202508 = sa.Table( + "zzz_resource_tracker_service_runs__osparc_io_archive_202508", metadata, # Primary keys sa.Column( From a2cb4d589bcf6565eb8447cb78582e7ef464b4ee Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Mon, 19 May 2025 14:29:56 +0200 Subject: [PATCH 03/11] fix --- ...zz_resource_tracker_service_runs__osparc_io_archive_202508.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/postgres-database/src/simcore_postgres_database/models/{resource_tracker_service_runs__osparc_io_history_202508.py => zzz_resource_tracker_service_runs__osparc_io_archive_202508.py} (100%) diff --git a/packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_service_runs__osparc_io_history_202508.py b/packages/postgres-database/src/simcore_postgres_database/models/zzz_resource_tracker_service_runs__osparc_io_archive_202508.py similarity index 100% rename from packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_service_runs__osparc_io_history_202508.py rename to packages/postgres-database/src/simcore_postgres_database/models/zzz_resource_tracker_service_runs__osparc_io_archive_202508.py From 0eb5710f020c8f16ba2c783602cf77a1e6972117 Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Mon, 19 May 2025 15:09:31 +0200 Subject: [PATCH 04/11] fix --- ...99_preparation_for_osparc_io_migration.py} | 74 ++++++++----------- .../models/resource_tracker_service_runs.py | 7 -- 2 files changed, 30 insertions(+), 51 deletions(-) rename packages/postgres-database/src/simcore_postgres_database/migration/versions/{43d7e61eedf4_preparation_for_osparc_io_migration.py => 1a791d837399_preparation_for_osparc_io_migration.py} (95%) diff --git a/packages/postgres-database/src/simcore_postgres_database/migration/versions/43d7e61eedf4_preparation_for_osparc_io_migration.py b/packages/postgres-database/src/simcore_postgres_database/migration/versions/1a791d837399_preparation_for_osparc_io_migration.py similarity index 95% rename from packages/postgres-database/src/simcore_postgres_database/migration/versions/43d7e61eedf4_preparation_for_osparc_io_migration.py rename to packages/postgres-database/src/simcore_postgres_database/migration/versions/1a791d837399_preparation_for_osparc_io_migration.py index 736b99a6cb17..65f8ce6141de 100644 --- a/packages/postgres-database/src/simcore_postgres_database/migration/versions/43d7e61eedf4_preparation_for_osparc_io_migration.py +++ b/packages/postgres-database/src/simcore_postgres_database/migration/versions/1a791d837399_preparation_for_osparc_io_migration.py @@ -1,8 +1,8 @@ """preparation for osparc.io migration -Revision ID: 43d7e61eedf4 +Revision ID: 1a791d837399 Revises: b39f2dc87ccd -Create Date: 2025-05-19 12:27:17.367971+00:00 +Create Date: 2025-05-19 13:09:24.279828+00:00 """ @@ -11,7 +11,7 @@ from sqlalchemy.dialects import postgresql # revision identifiers, used by Alembic. -revision = "43d7e61eedf4" +revision = "1a791d837399" down_revision = "b39f2dc87ccd" branch_labels = None depends_on = None @@ -108,16 +108,8 @@ def upgrade(): op.drop_index("ix_projects_comments_project_uuid", table_name="projects_comments") op.drop_table("projects_comments") op.drop_column("file_meta_data", "node_id") - op.drop_constraint("fk_new_folders_to_folders_id", "folders_v2", type_="foreignkey") op.drop_constraint("fk_new_folders_to_groups_gid", "folders_v2", type_="foreignkey") - op.create_foreign_key( - "fk_new_folders_to_folders_id", - "folders_v2", - "folders_v2", - ["parent_folder_id"], - ["folder_id"], - onupdate="CASCADE", - ) + op.drop_constraint("fk_new_folders_to_folders_id", "folders_v2", type_="foreignkey") op.create_foreign_key( "fk_new_folders_to_groups_gid", "folders_v2", @@ -127,6 +119,14 @@ def upgrade(): onupdate="CASCADE", ondelete="SET NULL", ) + op.create_foreign_key( + "fk_new_folders_to_folders_id", + "folders_v2", + "folders_v2", + ["parent_folder_id"], + ["folder_id"], + onupdate="CASCADE", + ) op.create_foreign_key( "fk_payments_autorecharge_id_wallets", "payments_autorecharge", @@ -155,11 +155,11 @@ def upgrade(): ondelete="CASCADE", ) op.create_foreign_key( - "fk_payments_transactions_to_wallet_id", + "fk_payments_transactions_to_products_name", "payments_transactions", - "wallets", - ["wallet_id"], - ["wallet_id"], + "products", + ["product_name"], + ["name"], onupdate="CASCADE", ondelete="CASCADE", ) @@ -173,20 +173,11 @@ def upgrade(): ondelete="CASCADE", ) op.create_foreign_key( - "fk_payments_transactions_to_products_name", + "fk_payments_transactions_to_wallet_id", "payments_transactions", - "products", - ["product_name"], - ["name"], - onupdate="CASCADE", - ondelete="CASCADE", - ) - op.create_foreign_key( - "fk_service_runs_to_user_id", - "resource_tracker_service_runs", - "users", - ["user_id"], - ["id"], + "wallets", + ["wallet_id"], + ["wallet_id"], onupdate="CASCADE", ondelete="CASCADE", ) @@ -222,12 +213,7 @@ def downgrade(): type_="foreignkey", ) op.drop_constraint( - "fk_service_runs_to_user_id", - "resource_tracker_service_runs", - type_="foreignkey", - ) - op.drop_constraint( - "fk_payments_transactions_to_products_name", + "fk_payments_transactions_to_wallet_id", "payments_transactions", type_="foreignkey", ) @@ -237,7 +223,7 @@ def downgrade(): type_="foreignkey", ) op.drop_constraint( - "fk_payments_transactions_to_wallet_id", + "fk_payments_transactions_to_products_name", "payments_transactions", type_="foreignkey", ) @@ -252,8 +238,15 @@ def downgrade(): "payments_autorecharge", type_="foreignkey", ) - op.drop_constraint("fk_new_folders_to_groups_gid", "folders_v2", type_="foreignkey") op.drop_constraint("fk_new_folders_to_folders_id", "folders_v2", type_="foreignkey") + op.drop_constraint("fk_new_folders_to_groups_gid", "folders_v2", type_="foreignkey") + op.create_foreign_key( + "fk_new_folders_to_folders_id", + "folders_v2", + "folders_v2", + ["parent_folder_id"], + ["folder_id"], + ) op.create_foreign_key( "fk_new_folders_to_groups_gid", "folders_v2", @@ -262,13 +255,6 @@ def downgrade(): ["gid"], ondelete="SET NULL", ) - op.create_foreign_key( - "fk_new_folders_to_folders_id", - "folders_v2", - "folders_v2", - ["parent_folder_id"], - ["folder_id"], - ) op.add_column( "file_meta_data", sa.Column("node_id", sa.VARCHAR(), autoincrement=False, nullable=True), diff --git a/packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_service_runs.py b/packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_service_runs.py index 715a35e22012..0854fa4dd036 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_service_runs.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_service_runs.py @@ -7,7 +7,6 @@ from ._common import NUMERIC_KWARGS, RefActions, column_modified_datetime from .base import metadata -from .users import users class ResourceTrackerServiceType(str, enum.Enum): @@ -95,12 +94,6 @@ class ResourceTrackerServiceRunStatus(str, enum.Enum): sa.Column( "user_id", sa.BigInteger, - sa.ForeignKey( - users.c.id, - onupdate=RefActions.CASCADE, - ondelete=RefActions.CASCADE, - name="fk_service_runs_to_user_id", - ), nullable=False, doc="We want to store the user id for tracking/billing purposes and be sure it stays there even when the user is deleted (that's also reason why we do not introduce foreign key)", index=True, From 7b4ed2af1396ae3271f7d0ed9ed47146b5919a4d Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Mon, 19 May 2025 16:30:18 +0200 Subject: [PATCH 05/11] fix --- ...40_preparation_for_osparc_io_migration.py} | 68 +++++++++++++++---- .../models/api_keys.py | 4 +- .../models/confirmations.py | 10 +-- 3 files changed, 62 insertions(+), 20 deletions(-) rename packages/postgres-database/src/simcore_postgres_database/migration/versions/{1a791d837399_preparation_for_osparc_io_migration.py => f4c938f80040_preparation_for_osparc_io_migration.py} (90%) diff --git a/packages/postgres-database/src/simcore_postgres_database/migration/versions/1a791d837399_preparation_for_osparc_io_migration.py b/packages/postgres-database/src/simcore_postgres_database/migration/versions/f4c938f80040_preparation_for_osparc_io_migration.py similarity index 90% rename from packages/postgres-database/src/simcore_postgres_database/migration/versions/1a791d837399_preparation_for_osparc_io_migration.py rename to packages/postgres-database/src/simcore_postgres_database/migration/versions/f4c938f80040_preparation_for_osparc_io_migration.py index 65f8ce6141de..5acc9befa186 100644 --- a/packages/postgres-database/src/simcore_postgres_database/migration/versions/1a791d837399_preparation_for_osparc_io_migration.py +++ b/packages/postgres-database/src/simcore_postgres_database/migration/versions/f4c938f80040_preparation_for_osparc_io_migration.py @@ -1,8 +1,8 @@ """preparation for osparc.io migration -Revision ID: 1a791d837399 +Revision ID: f4c938f80040 Revises: b39f2dc87ccd -Create Date: 2025-05-19 13:09:24.279828+00:00 +Create Date: 2025-05-19 14:30:09.366828+00:00 """ @@ -11,7 +11,7 @@ from sqlalchemy.dialects import postgresql # revision identifiers, used by Alembic. -revision = "1a791d837399" +revision = "f4c938f80040" down_revision = "b39f2dc87ccd" branch_labels = None depends_on = None @@ -107,6 +107,26 @@ def upgrade(): ) op.drop_index("ix_projects_comments_project_uuid", table_name="projects_comments") op.drop_table("projects_comments") + op.drop_constraint("api_keys_user_id_fkey", "api_keys", type_="foreignkey") + op.create_foreign_key( + None, + "api_keys", + "users", + ["user_id"], + ["id"], + onupdate="CASCADE", + ondelete="CASCADE", + ) + op.drop_constraint("user_confirmation_fkey", "confirmations", type_="foreignkey") + op.create_foreign_key( + "user_confirmation_fkey", + "confirmations", + "users", + ["user_id"], + ["id"], + onupdate="CASCADE", + ondelete="CASCADE", + ) op.drop_column("file_meta_data", "node_id") op.drop_constraint("fk_new_folders_to_groups_gid", "folders_v2", type_="foreignkey") op.drop_constraint("fk_new_folders_to_folders_id", "folders_v2", type_="foreignkey") @@ -154,6 +174,15 @@ def upgrade(): onupdate="CASCADE", ondelete="CASCADE", ) + op.create_foreign_key( + "fk_payments_transactions_to_wallet_id", + "payments_transactions", + "wallets", + ["wallet_id"], + ["wallet_id"], + onupdate="CASCADE", + ondelete="CASCADE", + ) op.create_foreign_key( "fk_payments_transactions_to_products_name", "payments_transactions", @@ -172,15 +201,6 @@ def upgrade(): onupdate="CASCADE", ondelete="CASCADE", ) - op.create_foreign_key( - "fk_payments_transactions_to_wallet_id", - "payments_transactions", - "wallets", - ["wallet_id"], - ["wallet_id"], - onupdate="CASCADE", - ondelete="CASCADE", - ) op.create_foreign_key( "fk_service_runs_to_product_name", "resource_tracker_service_runs", @@ -213,17 +233,17 @@ def downgrade(): type_="foreignkey", ) op.drop_constraint( - "fk_payments_transactions_to_wallet_id", + "fk_payments_transactions_to_user_id", "payments_transactions", type_="foreignkey", ) op.drop_constraint( - "fk_payments_transactions_to_user_id", + "fk_payments_transactions_to_products_name", "payments_transactions", type_="foreignkey", ) op.drop_constraint( - "fk_payments_transactions_to_products_name", + "fk_payments_transactions_to_wallet_id", "payments_transactions", type_="foreignkey", ) @@ -259,6 +279,24 @@ def downgrade(): "file_meta_data", sa.Column("node_id", sa.VARCHAR(), autoincrement=False, nullable=True), ) + op.drop_constraint("user_confirmation_fkey", "confirmations", type_="foreignkey") + op.create_foreign_key( + "user_confirmation_fkey", + "confirmations", + "users", + ["user_id"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint(None, "api_keys", type_="foreignkey") + op.create_foreign_key( + "api_keys_user_id_fkey", + "api_keys", + "users", + ["user_id"], + ["id"], + ondelete="CASCADE", + ) op.create_table( "projects_comments", sa.Column("comment_id", sa.BIGINT(), autoincrement=True, nullable=False), diff --git a/packages/postgres-database/src/simcore_postgres_database/models/api_keys.py b/packages/postgres-database/src/simcore_postgres_database/models/api_keys.py index 2c3f12eca3ab..8db4d2303fec 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/api_keys.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/api_keys.py @@ -37,7 +37,9 @@ sa.Column( "user_id", sa.BigInteger(), - sa.ForeignKey(users.c.id, ondelete=RefActions.CASCADE), + sa.ForeignKey( + users.c.id, ondelete=RefActions.CASCADE, onupdate=RefActions.CASCADE + ), nullable=False, doc="Identified user", ), diff --git a/packages/postgres-database/src/simcore_postgres_database/models/confirmations.py b/packages/postgres-database/src/simcore_postgres_database/models/confirmations.py index 6fd56e8c8e01..411d7c35c05a 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/confirmations.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/confirmations.py @@ -1,10 +1,11 @@ -""" User's confirmations table +"""User's confirmations table - - Keeps a list of tokens to identify an action (registration, invitation, reset, etc) authorized - by link to a a user in the framework - - These tokens have an expiration date defined by configuration +- Keeps a list of tokens to identify an action (registration, invitation, reset, etc) authorized +by link to a a user in the framework +- These tokens have an expiration date defined by configuration """ + import enum import sqlalchemy as sa @@ -61,6 +62,7 @@ class ConfirmationAction(enum.Enum): ["user_id"], [users.c.id], name="user_confirmation_fkey", + onupdate=RefActions.CASCADE, ondelete=RefActions.CASCADE, ), ) From 0b2eddaf79cd556c426cd02c23bbb1756143cd8d Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Mon, 19 May 2025 16:55:33 +0200 Subject: [PATCH 06/11] fix --- ...8e_preparation_for_osparc_io_migration.py} | 80 +++++++++---------- .../models/api_keys.py | 5 +- .../models/tokens.py | 5 +- 3 files changed, 48 insertions(+), 42 deletions(-) rename packages/postgres-database/src/simcore_postgres_database/migration/versions/{f4c938f80040_preparation_for_osparc_io_migration.py => 47dc5c0a138e_preparation_for_osparc_io_migration.py} (97%) diff --git a/packages/postgres-database/src/simcore_postgres_database/migration/versions/f4c938f80040_preparation_for_osparc_io_migration.py b/packages/postgres-database/src/simcore_postgres_database/migration/versions/47dc5c0a138e_preparation_for_osparc_io_migration.py similarity index 97% rename from packages/postgres-database/src/simcore_postgres_database/migration/versions/f4c938f80040_preparation_for_osparc_io_migration.py rename to packages/postgres-database/src/simcore_postgres_database/migration/versions/47dc5c0a138e_preparation_for_osparc_io_migration.py index 5acc9befa186..2bab7dc936d3 100644 --- a/packages/postgres-database/src/simcore_postgres_database/migration/versions/f4c938f80040_preparation_for_osparc_io_migration.py +++ b/packages/postgres-database/src/simcore_postgres_database/migration/versions/47dc5c0a138e_preparation_for_osparc_io_migration.py @@ -1,8 +1,8 @@ """preparation for osparc.io migration -Revision ID: f4c938f80040 +Revision ID: 47dc5c0a138e Revises: b39f2dc87ccd -Create Date: 2025-05-19 14:30:09.366828+00:00 +Create Date: 2025-05-19 14:55:15.889813+00:00 """ @@ -11,7 +11,7 @@ from sqlalchemy.dialects import postgresql # revision identifiers, used by Alembic. -revision = "f4c938f80040" +revision = "47dc5c0a138e" down_revision = "b39f2dc87ccd" branch_labels = None depends_on = None @@ -109,7 +109,7 @@ def upgrade(): op.drop_table("projects_comments") op.drop_constraint("api_keys_user_id_fkey", "api_keys", type_="foreignkey") op.create_foreign_key( - None, + "fk_api_keys_to_user_id", "api_keys", "users", ["user_id"], @@ -128,17 +128,8 @@ def upgrade(): ondelete="CASCADE", ) op.drop_column("file_meta_data", "node_id") - op.drop_constraint("fk_new_folders_to_groups_gid", "folders_v2", type_="foreignkey") op.drop_constraint("fk_new_folders_to_folders_id", "folders_v2", type_="foreignkey") - op.create_foreign_key( - "fk_new_folders_to_groups_gid", - "folders_v2", - "groups", - ["created_by_gid"], - ["gid"], - onupdate="CASCADE", - ondelete="SET NULL", - ) + op.drop_constraint("fk_new_folders_to_groups_gid", "folders_v2", type_="foreignkey") op.create_foreign_key( "fk_new_folders_to_folders_id", "folders_v2", @@ -148,17 +139,17 @@ def upgrade(): onupdate="CASCADE", ) op.create_foreign_key( - "fk_payments_autorecharge_id_wallets", - "payments_autorecharge", - "wallets", - ["wallet_id"], - ["wallet_id"], + "fk_new_folders_to_groups_gid", + "folders_v2", + "groups", + ["created_by_gid"], + ["gid"], onupdate="CASCADE", - ondelete="CASCADE", + ondelete="SET NULL", ) op.create_foreign_key( - "fk_payments_methods_to_wallet_id", - "payments_methods", + "fk_payments_autorecharge_id_wallets", + "payments_autorecharge", "wallets", ["wallet_id"], ["wallet_id"], @@ -175,8 +166,8 @@ def upgrade(): ondelete="CASCADE", ) op.create_foreign_key( - "fk_payments_transactions_to_wallet_id", - "payments_transactions", + "fk_payments_methods_to_wallet_id", + "payments_methods", "wallets", ["wallet_id"], ["wallet_id"], @@ -201,6 +192,15 @@ def upgrade(): onupdate="CASCADE", ondelete="CASCADE", ) + op.create_foreign_key( + "fk_payments_transactions_to_wallet_id", + "payments_transactions", + "wallets", + ["wallet_id"], + ["wallet_id"], + onupdate="CASCADE", + ondelete="CASCADE", + ) op.create_foreign_key( "fk_service_runs_to_product_name", "resource_tracker_service_runs", @@ -212,7 +212,7 @@ def upgrade(): ) op.drop_constraint("tokens_user_id_fkey", "tokens", type_="foreignkey") op.create_foreign_key( - None, + "fk_tokens_to_user_id", "tokens", "users", ["user_id"], @@ -225,7 +225,7 @@ def upgrade(): def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint(None, "tokens", type_="foreignkey") + op.drop_constraint("fk_tokens_to_user_id", "tokens", type_="foreignkey") op.create_foreign_key("tokens_user_id_fkey", "tokens", "users", ["user_id"], ["id"]) op.drop_constraint( "fk_service_runs_to_product_name", @@ -233,40 +233,33 @@ def downgrade(): type_="foreignkey", ) op.drop_constraint( - "fk_payments_transactions_to_user_id", + "fk_payments_transactions_to_wallet_id", "payments_transactions", type_="foreignkey", ) op.drop_constraint( - "fk_payments_transactions_to_products_name", + "fk_payments_transactions_to_user_id", "payments_transactions", type_="foreignkey", ) op.drop_constraint( - "fk_payments_transactions_to_wallet_id", + "fk_payments_transactions_to_products_name", "payments_transactions", type_="foreignkey", ) op.drop_constraint( - "fk_payments_methods_to_user_id", "payments_methods", type_="foreignkey" + "fk_payments_methods_to_wallet_id", "payments_methods", type_="foreignkey" ) op.drop_constraint( - "fk_payments_methods_to_wallet_id", "payments_methods", type_="foreignkey" + "fk_payments_methods_to_user_id", "payments_methods", type_="foreignkey" ) op.drop_constraint( "fk_payments_autorecharge_id_wallets", "payments_autorecharge", type_="foreignkey", ) - op.drop_constraint("fk_new_folders_to_folders_id", "folders_v2", type_="foreignkey") op.drop_constraint("fk_new_folders_to_groups_gid", "folders_v2", type_="foreignkey") - op.create_foreign_key( - "fk_new_folders_to_folders_id", - "folders_v2", - "folders_v2", - ["parent_folder_id"], - ["folder_id"], - ) + op.drop_constraint("fk_new_folders_to_folders_id", "folders_v2", type_="foreignkey") op.create_foreign_key( "fk_new_folders_to_groups_gid", "folders_v2", @@ -275,6 +268,13 @@ def downgrade(): ["gid"], ondelete="SET NULL", ) + op.create_foreign_key( + "fk_new_folders_to_folders_id", + "folders_v2", + "folders_v2", + ["parent_folder_id"], + ["folder_id"], + ) op.add_column( "file_meta_data", sa.Column("node_id", sa.VARCHAR(), autoincrement=False, nullable=True), @@ -288,7 +288,7 @@ def downgrade(): ["id"], ondelete="CASCADE", ) - op.drop_constraint(None, "api_keys", type_="foreignkey") + op.drop_constraint("fk_api_keys_to_user_id", "api_keys", type_="foreignkey") op.create_foreign_key( "api_keys_user_id_fkey", "api_keys", diff --git a/packages/postgres-database/src/simcore_postgres_database/models/api_keys.py b/packages/postgres-database/src/simcore_postgres_database/models/api_keys.py index 8db4d2303fec..9d500bcb55e5 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/api_keys.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/api_keys.py @@ -38,7 +38,10 @@ "user_id", sa.BigInteger(), sa.ForeignKey( - users.c.id, ondelete=RefActions.CASCADE, onupdate=RefActions.CASCADE + users.c.id, + ondelete=RefActions.CASCADE, + onupdate=RefActions.CASCADE, + name="fk_api_keys_to_user_id", ), nullable=False, doc="Identified user", diff --git a/packages/postgres-database/src/simcore_postgres_database/models/tokens.py b/packages/postgres-database/src/simcore_postgres_database/models/tokens.py index e5ee2ce04192..5a504b6a81b4 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/tokens.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/tokens.py @@ -15,7 +15,10 @@ "user_id", sa.BigInteger, sa.ForeignKey( - users.c.id, onupdate=RefActions.CASCADE, ondelete=RefActions.CASCADE + users.c.id, + onupdate=RefActions.CASCADE, + ondelete=RefActions.CASCADE, + name="fk_tokens_to_user_id", ), nullable=False, ), From d696f97e41797541225e8f24409e34a401ade2f0 Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Mon, 19 May 2025 17:02:36 +0200 Subject: [PATCH 07/11] fix --- .../02/test_projects_comments_handlers.py | 228 ------------------ 1 file changed, 228 deletions(-) delete mode 100644 services/web/server/tests/unit/with_dbs/02/test_projects_comments_handlers.py diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_comments_handlers.py b/services/web/server/tests/unit/with_dbs/02/test_projects_comments_handlers.py deleted file mode 100644 index 9a187a1d0816..000000000000 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_comments_handlers.py +++ /dev/null @@ -1,228 +0,0 @@ -# pylint: disable=protected-access -# pylint: disable=redefined-outer-name -# pylint: disable=too-many-arguments -# pylint: disable=unused-argument -# pylint: disable=unused-variable -# pylint: disable=too-many-statements - - -from http import HTTPStatus - -import pytest -import sqlalchemy as sa -from aiohttp.test_utils import TestClient -from pytest_simcore.helpers.assert_checks import assert_status -from pytest_simcore.helpers.webserver_login import LoggedUser, UserInfoDict -from servicelib.aiohttp import status -from simcore_service_webserver._meta import api_version_prefix -from simcore_service_webserver.db.models import UserRole -from simcore_service_webserver.projects._groups_repository import ( - update_or_insert_project_group, -) -from simcore_service_webserver.projects.models import ProjectDict - -API_PREFIX = "/" + api_version_prefix - - -@pytest.mark.parametrize( - "user_role,expected", - [ - (UserRole.ANONYMOUS, status.HTTP_401_UNAUTHORIZED), - (UserRole.GUEST, status.HTTP_200_OK), - (UserRole.USER, status.HTTP_200_OK), - (UserRole.TESTER, status.HTTP_200_OK), - ], -) -async def test_project_comments_user_role_access( - client: TestClient, - logged_user: UserInfoDict, - user_project: ProjectDict, - user_role: UserRole, - expected: HTTPStatus, -): - assert client.app - base_url = client.app.router["list_project_comments"].url_for( - project_uuid=user_project["uuid"] - ) - resp = await client.get(f"{base_url}") - assert resp.status == 401 if user_role == UserRole.ANONYMOUS else 200 - - -@pytest.mark.acceptance_test( - "https://github.com/ITISFoundation/osparc-issues/issues/993" -) -@pytest.mark.parametrize( - "user_role,expected", - [ - (UserRole.USER, status.HTTP_200_OK), - ], -) -async def test_project_comments_full_workflow( - client: TestClient, - logged_user: UserInfoDict, - user_project: ProjectDict, - expected: HTTPStatus, - postgres_db: sa.engine.Engine, -): - base_url = client.app.router["list_project_comments"].url_for( - project_uuid=user_project["uuid"] - ) - resp = await client.get(f"{base_url}") - data, _, meta, links = await assert_status( - resp, - expected, - include_meta=True, - include_links=True, - ) - assert data == [] - assert meta["total"] == 0 - assert links - - # Now we will add first comment - body = {"contents": "My first comment"} - resp = await client.post(f"{base_url}", json=body) - data, _ = await assert_status( - resp, - status.HTTP_201_CREATED, - ) - first_comment_id = data["comment_id"] - - # Now we will add second comment - resp = await client.post(f"{base_url}", json={"contents": "My second comment"}) - data, _ = await assert_status( - resp, - status.HTTP_201_CREATED, - ) - second_comment_id = data["comment_id"] - - # Now we will list all comments for the project - resp = await client.get(f"{base_url}") - data, _, meta, links = await assert_status( - resp, - expected, - include_meta=True, - include_links=True, - ) - assert len(data) == 2 - assert meta["total"] == 2 - assert links - - # Now we will update the second comment - updated_comment = "Updated second comment" - resp = await client.put( - f"{base_url}/{second_comment_id}", - json={"contents": updated_comment}, - ) - data, _ = await assert_status( - resp, - expected, - ) - - # Now we will get the second comment - resp = await client.get(f"{base_url}/{second_comment_id}") - data, _ = await assert_status( - resp, - expected, - ) - assert data["contents"] == updated_comment - - # Now we will delete the second comment - resp = await client.delete(f"{base_url}/{second_comment_id}") - data, _ = await assert_status( - resp, - status.HTTP_204_NO_CONTENT, - ) - - # Now we will list all comments for the project - resp = await client.get(f"{base_url}") - data, _, meta, links = await assert_status( - resp, - expected, - include_meta=True, - include_links=True, - ) - assert meta["total"] == 1 - assert links - assert len(data) == 1 - - # Now we will log as a different user - async with LoggedUser(client) as new_logged_user: - # As this user does not have access to the project, they should get 403 - resp = await client.get(f"{base_url}") - _, errors = await assert_status( - resp, - status.HTTP_403_FORBIDDEN, - ) - assert errors - - resp = await client.get(f"{base_url}/{first_comment_id}") - _, errors = await assert_status( - resp, - status.HTTP_403_FORBIDDEN, - ) - assert errors - - # Now we will share the project with the new user - await update_or_insert_project_group( - client.app, - project_id=user_project["uuid"], - group_id=new_logged_user["primary_gid"], - read=True, - write=True, - delete=True, - ) - - # Now the user should have access to the project now - # New user will add comment - resp = await client.post( - f"{base_url}", - json={"contents": "My first comment as a new user"}, - ) - data, _ = await assert_status( - resp, - status.HTTP_201_CREATED, - ) - new_user_comment_id = data["comment_id"] - - # New user will modify the comment - updated_comment = "Updated My first comment as a new user" - resp = await client.put( - f"{base_url}/{new_user_comment_id}", - json={"contents": updated_comment}, - ) - data, _ = await assert_status( - resp, - expected, - ) - assert data["contents"] == updated_comment - - # New user will list all comments - resp = await client.get(f"{base_url}") - data, _, meta, links = await assert_status( - resp, - expected, - include_meta=True, - include_links=True, - ) - assert meta["total"] == 2 - assert links - assert len(data) == 2 - - # New user will modify comment of the previous user - updated_comment = "Updated comment of previous user" - resp = await client.put( - f"{base_url}/{first_comment_id}", - json={"contents": updated_comment}, - ) - data, _ = await assert_status( - resp, - expected, - ) - assert data["contents"] == updated_comment - - # New user will delete comment of the previous user - resp = await client.delete(f"{base_url}/{first_comment_id}") - data, _ = await assert_status( - resp, - status.HTTP_204_NO_CONTENT, - ) From 8c366e0827305b84c81414504a4b9532dc58258c Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Wed, 18 Jun 2025 13:42:39 +0200 Subject: [PATCH 08/11] rename down revision --- .../47dc5c0a138e_preparation_for_osparc_io_migration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/postgres-database/src/simcore_postgres_database/migration/versions/47dc5c0a138e_preparation_for_osparc_io_migration.py b/packages/postgres-database/src/simcore_postgres_database/migration/versions/47dc5c0a138e_preparation_for_osparc_io_migration.py index 2bab7dc936d3..667338338908 100644 --- a/packages/postgres-database/src/simcore_postgres_database/migration/versions/47dc5c0a138e_preparation_for_osparc_io_migration.py +++ b/packages/postgres-database/src/simcore_postgres_database/migration/versions/47dc5c0a138e_preparation_for_osparc_io_migration.py @@ -1,7 +1,7 @@ """preparation for osparc.io migration Revision ID: 47dc5c0a138e -Revises: b39f2dc87ccd +Revises: 4f6fd2586491 Create Date: 2025-05-19 14:55:15.889813+00:00 """ @@ -12,7 +12,7 @@ # revision identifiers, used by Alembic. revision = "47dc5c0a138e" -down_revision = "b39f2dc87ccd" +down_revision = "4f6fd2586491" branch_labels = None depends_on = None From a3e14b7c21733fdfc308494d53c26cdc2555600b Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Thu, 19 Jun 2025 10:21:31 +0200 Subject: [PATCH 09/11] fix migration --- ...38e_preparation_for_osparc_io_migration.py | 27 ++++++++++++++++--- .../models/file_meta_data.py | 4 +-- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/packages/postgres-database/src/simcore_postgres_database/migration/versions/47dc5c0a138e_preparation_for_osparc_io_migration.py b/packages/postgres-database/src/simcore_postgres_database/migration/versions/47dc5c0a138e_preparation_for_osparc_io_migration.py index 667338338908..7730132cf22c 100644 --- a/packages/postgres-database/src/simcore_postgres_database/migration/versions/47dc5c0a138e_preparation_for_osparc_io_migration.py +++ b/packages/postgres-database/src/simcore_postgres_database/migration/versions/47dc5c0a138e_preparation_for_osparc_io_migration.py @@ -127,7 +127,22 @@ def upgrade(): onupdate="CASCADE", ondelete="CASCADE", ) - op.drop_column("file_meta_data", "node_id") + op.alter_column( + "file_meta_data", + "user_id", + existing_type=sa.VARCHAR(), + type_=sa.BigInteger(), + nullable=False, + ) + op.create_foreign_key( + "fk_file_meta_data_user_id_users", + "file_meta_data", + "users", + ["user_id"], + ["id"], + onupdate="CASCADE", + ondelete="CASCADE", + ) op.drop_constraint("fk_new_folders_to_folders_id", "folders_v2", type_="foreignkey") op.drop_constraint("fk_new_folders_to_groups_gid", "folders_v2", type_="foreignkey") op.create_foreign_key( @@ -275,9 +290,15 @@ def downgrade(): ["parent_folder_id"], ["folder_id"], ) - op.add_column( + op.drop_constraint( + "fk_file_meta_data_user_id_users", "file_meta_data", type_="foreignkey" + ) + op.alter_column( "file_meta_data", - sa.Column("node_id", sa.VARCHAR(), autoincrement=False, nullable=True), + "user_id", + existing_type=sa.BigInteger(), + type_=sa.VARCHAR(), + nullable=True, ) op.drop_constraint("user_confirmation_fkey", "confirmations", type_="foreignkey") op.create_foreign_key( diff --git a/packages/postgres-database/src/simcore_postgres_database/models/file_meta_data.py b/packages/postgres-database/src/simcore_postgres_database/models/file_meta_data.py index 21d6ce899beb..3ede314b1f74 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/file_meta_data.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/file_meta_data.py @@ -11,6 +11,7 @@ sa.Column("bucket_name", sa.String()), sa.Column("object_name", sa.String()), sa.Column("project_id", sa.String(), index=True), + sa.Column("node_id", sa.String()), sa.Column( "user_id", sa.BigInteger(), @@ -21,9 +22,9 @@ ondelete=RefActions.CASCADE, ), nullable=False, + index=True, doc="The user id with which the run entry is associated", ), - sa.Column("user_id", sa.String(), index=True), sa.Column("file_id", sa.String(), primary_key=True), sa.Column("created_at", sa.String()), sa.Column("last_modified", sa.String()), @@ -68,5 +69,4 @@ doc="SHA256 checksum of the file content", index=True, ), - ### ) From efefade6f7ccc7eab72b672050b28e4203539ba4 Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Tue, 26 Aug 2025 17:31:42 +0200 Subject: [PATCH 10/11] merge master --- .../47dc5c0a138e_preparation_for_osparc_io_migration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/postgres-database/src/simcore_postgres_database/migration/versions/47dc5c0a138e_preparation_for_osparc_io_migration.py b/packages/postgres-database/src/simcore_postgres_database/migration/versions/47dc5c0a138e_preparation_for_osparc_io_migration.py index 7730132cf22c..69aa677c72a4 100644 --- a/packages/postgres-database/src/simcore_postgres_database/migration/versions/47dc5c0a138e_preparation_for_osparc_io_migration.py +++ b/packages/postgres-database/src/simcore_postgres_database/migration/versions/47dc5c0a138e_preparation_for_osparc_io_migration.py @@ -1,7 +1,7 @@ """preparation for osparc.io migration Revision ID: 47dc5c0a138e -Revises: 4f6fd2586491 +Revises: b566f1b29012 Create Date: 2025-05-19 14:55:15.889813+00:00 """ @@ -12,7 +12,7 @@ # revision identifiers, used by Alembic. revision = "47dc5c0a138e" -down_revision = "4f6fd2586491" +down_revision = "b566f1b29012" branch_labels = None depends_on = None From 5c79fa64aadeceef5359f4c45eef0a4aef665c6f Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Tue, 26 Aug 2025 17:43:07 +0200 Subject: [PATCH 11/11] merge master --- ...38e_preparation_for_osparc_io_migration.py | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/packages/postgres-database/src/simcore_postgres_database/migration/versions/47dc5c0a138e_preparation_for_osparc_io_migration.py b/packages/postgres-database/src/simcore_postgres_database/migration/versions/47dc5c0a138e_preparation_for_osparc_io_migration.py index 69aa677c72a4..862788810e43 100644 --- a/packages/postgres-database/src/simcore_postgres_database/migration/versions/47dc5c0a138e_preparation_for_osparc_io_migration.py +++ b/packages/postgres-database/src/simcore_postgres_database/migration/versions/47dc5c0a138e_preparation_for_osparc_io_migration.py @@ -127,13 +127,20 @@ def upgrade(): onupdate="CASCADE", ondelete="CASCADE", ) - op.alter_column( - "file_meta_data", - "user_id", - existing_type=sa.VARCHAR(), - type_=sa.BigInteger(), - nullable=False, - ) + + # op.alter_column( + # "file_meta_data", + # "user_id", + # existing_type=sa.VARCHAR(), + # type_=sa.BigInteger(), + # nullable=False, + # postgresql_using="user_id::bigint", + # ) + op.execute( + "ALTER TABLE file_meta_data ALTER COLUMN user_id TYPE BIGINT USING user_id::bigint" + ) + op.execute("ALTER TABLE file_meta_data ALTER COLUMN user_id SET NOT NULL") + op.create_foreign_key( "fk_file_meta_data_user_id_users", "file_meta_data", @@ -293,13 +300,19 @@ def downgrade(): op.drop_constraint( "fk_file_meta_data_user_id_users", "file_meta_data", type_="foreignkey" ) - op.alter_column( - "file_meta_data", - "user_id", - existing_type=sa.BigInteger(), - type_=sa.VARCHAR(), - nullable=True, + # op.alter_column( + # "file_meta_data", + # "user_id", + # existing_type=sa.BigInteger(), + # type_=sa.VARCHAR(), + # nullable=True, + # ) + + op.execute( + "ALTER TABLE file_meta_data ALTER COLUMN user_id TYPE VARCHAR USING user_id::varchar" ) + op.execute("ALTER TABLE file_meta_data ALTER COLUMN user_id DROP NOT NULL") + op.drop_constraint("user_confirmation_fkey", "confirmations", type_="foreignkey") op.create_foreign_key( "user_confirmation_fkey",