From 956c3b772010dc8fb156cbc8261514f1a2b6171a Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 23 Oct 2024 13:50:19 +0200 Subject: [PATCH 001/165] upgrade reqs --- services/web/server/requirements/_base.txt | 199 ++++++++++++++++++++- services/web/server/requirements/_test.txt | 8 +- 2 files changed, 199 insertions(+), 8 deletions(-) diff --git a/services/web/server/requirements/_base.txt b/services/web/server/requirements/_base.txt index 2eab661ddef..ec59ee8bad3 100644 --- a/services/web/server/requirements/_base.txt +++ b/services/web/server/requirements/_base.txt @@ -26,17 +26,30 @@ aiofiles==0.8.0 # -r requirements/_base.in aiohttp==3.8.5 # via + # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../requirements/constraints.txt # -c requirements/../../../../requirements/constraints.txt @@ -71,6 +84,8 @@ alembic==1.8.1 # via # -r requirements/../../../../packages/postgres-database/requirements/_base.in # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/_base.in +annotated-types==0.7.0 + # via pydantic anyio==4.3.0 # via # fast-depends @@ -105,17 +120,30 @@ captcha==0.5.0 # via -r requirements/_base.in certifi==2023.7.22 # via + # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../requirements/constraints.txt # -c requirements/../../../../requirements/constraints.txt @@ -130,17 +158,30 @@ click==8.1.3 # via typer cryptography==41.0.7 # via + # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../requirements/constraints.txt # -c requirements/../../../../requirements/constraints.txt @@ -154,7 +195,7 @@ deprecated==1.2.14 # opentelemetry-semantic-conventions dnspython==2.2.1 # via email-validator -email-validator==1.2.1 +email-validator==2.2.0 # via pydantic et-xmlfile==1.1.0 # via openpyxl @@ -199,17 +240,30 @@ jinja-app-loader==1.0.2 # via -r requirements/_base.in jinja2==3.1.2 # via + # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../requirements/constraints.txt # -c requirements/../../../../requirements/constraints.txt @@ -232,17 +286,30 @@ lazy-object-proxy==1.7.1 # via openapi-core mako==1.2.2 # via + # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../requirements/constraints.txt # -c requirements/../../../../requirements/constraints.txt @@ -341,17 +408,30 @@ opentelemetry-util-http==0.47b0 # opentelemetry-instrumentation-requests orjson==3.10.0 # via + # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../requirements/constraints.txt # -c requirements/../../../../requirements/constraints.txt @@ -393,39 +473,84 @@ pycountry==23.12.11 # via -r requirements/_base.in pycparser==2.21 # via cffi -pydantic==1.10.17 +pydantic==2.9.2 # via + # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt - # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/_base.in # -c requirements/../../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../requirements/constraints.txt # -c requirements/../../../../requirements/constraints.txt # -c requirements/./constraints.txt + # -r requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/models-library/requirements/_base.in + # -r requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/postgres-database/requirements/_base.in + # -r requirements/../../../../packages/service-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/_base.in # -r requirements/../../../../packages/service-library/requirements/_base.in + # -r requirements/../../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/settings-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/_base.in # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/_base.in # -r requirements/../../../../packages/simcore-sdk/requirements/_base.in # -r requirements/_base.in # fast-depends + # pydantic-extra-types + # pydantic-settings +pydantic-core==2.23.4 + # via pydantic +pydantic-extra-types==2.9.0 + # via + # -r requirements/../../../../packages/models-library/requirements/_base.in + # -r requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in +pydantic-settings==2.6.0 + # via + # -r requirements/../../../../packages/models-library/requirements/_base.in + # -r requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/_base.in + # -r requirements/../../../../packages/settings-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/_base.in pygments==2.15.1 # via rich pyinstrument==4.6.1 @@ -440,6 +565,8 @@ python-dateutil==2.8.2 # via # arrow # faker +python-dotenv==1.0.1 + # via pydantic-settings python-engineio==4.3.4 # via python-socketio python-magic==0.4.25 @@ -450,17 +577,30 @@ pytz==2022.1 # via twilio pyyaml==6.0.1 # via + # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../requirements/constraints.txt # -c requirements/../../../../requirements/constraints.txt @@ -470,17 +610,30 @@ pyyaml==6.0.1 # openapi-spec-validator redis==5.0.4 # via + # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../requirements/constraints.txt # -c requirements/../../../../requirements/constraints.txt @@ -521,17 +674,30 @@ sniffio==1.3.1 # via anyio sqlalchemy==1.4.47 # via + # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../requirements/constraints.txt # -c requirements/../../../../requirements/constraints.txt @@ -575,37 +741,64 @@ typing-extensions==4.12.0 # opentelemetry-sdk # pint # pydantic + # pydantic-core # typer ujson==5.5.0 # via + # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../requirements/constraints.txt # -c requirements/../../../../requirements/constraints.txt # aiohttp-swagger urllib3==1.26.11 # via + # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../requirements/constraints.txt # -c requirements/../../../../requirements/constraints.txt diff --git a/services/web/server/requirements/_test.txt b/services/web/server/requirements/_test.txt index 8b547336038..fab89d6b0f2 100644 --- a/services/web/server/requirements/_test.txt +++ b/services/web/server/requirements/_test.txt @@ -65,10 +65,6 @@ frozenlist==1.4.1 # -c requirements/_base.txt # aiohttp # aiosignal -greenlet==2.0.2 - # via - # -c requirements/_base.txt - # sqlalchemy hypothesis==6.91.0 # via -r requirements/_test.in icdiff==2.0.7 @@ -173,7 +169,9 @@ python-dateutil==2.8.2 # -c requirements/_base.txt # faker python-dotenv==1.0.1 - # via -r requirements/_test.in + # via + # -c requirements/_base.txt + # -r requirements/_test.in pyyaml==6.0.1 # via # -c requirements/../../../../requirements/constraints.txt From 45119e5f976049335224dce7eb2bab7923f7a972 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 23 Oct 2024 15:31:17 +0200 Subject: [PATCH 002/165] run bump-pydantic --- .../announcements/_models.py | 15 ++- .../application_settings.py | 124 +++++++++++------- .../simcore_service_webserver/catalog/_api.py | 6 +- .../catalog/_handlers.py | 16 ++- .../diagnostics/settings.py | 19 ++- .../director_v2/_handlers.py | 15 ++- .../director_v2/_models.py | 11 +- .../folders/_folders_handlers.py | 33 +++-- .../groups/_classifiers.py | 32 +++-- .../groups/_handlers.py | 50 ++++--- .../login/_auth_handlers.py | 2 +- .../login/_models.py | 11 +- .../login/_registration.py | 8 +- .../login/handlers_registration.py | 25 ++-- .../login/settings.py | 6 +- .../meta_modeling/_handlers.py | 4 +- .../meta_modeling/_results.py | 14 +- .../payments/_autorecharge_db.py | 6 +- .../payments/_methods_db.py | 18 ++- .../payments/_onetime_db.py | 14 +- .../payments/settings.py | 19 ++- .../products/_model.py | 64 ++++----- .../projects/_comments_handlers.py | 18 +-- .../projects/_common_models.py | 7 +- .../projects/_crud_handlers_models.py | 44 +++---- .../projects/_folders_db.py | 6 +- .../projects/_folders_handlers.py | 12 +- .../projects/_groups_handlers.py | 18 +-- .../projects/_nodes_api.py | 10 +- .../projects/_ports_api.py | 7 +- .../_projects_nodes_pricing_unit_handlers.py | 6 +- .../projects/_wallets_handlers.py | 6 +- .../projects/_workspaces_handlers.py | 12 +- .../projects/models.py | 36 ++--- .../_pricing_plans_admin_handlers.py | 10 +- .../resource_usage/_pricing_plans_handlers.py | 6 +- .../resource_usage/_service_runs_handlers.py | 25 ++-- .../scicrunch/models.py | 11 +- .../session/settings.py | 10 +- .../socketio/models.py | 12 +- .../statics/settings.py | 8 +- .../storage/schemas.py | 29 ++-- .../studies_dispatcher/_redirects_handlers.py | 21 ++- .../studies_dispatcher/_rest_handlers.py | 17 ++- .../studies_dispatcher/settings.py | 16 +-- .../users/_notifications.py | 12 +- .../users/_schemas.py | 19 +-- .../users/schemas.py | 26 ++-- .../utils_aiohttp.py | 5 +- .../version_control/_handlers.py | 4 +- .../version_control/models.py | 6 +- .../wallets/_groups_handlers.py | 10 +- .../workspaces/_groups_db.py | 6 +- .../workspaces/_groups_handlers.py | 10 +- .../workspaces/_workspaces_handlers.py | 26 ++-- .../isolated/test_studies_dispatcher_core.py | 16 +-- .../unit/isolated/test_utils_rate_limiting.py | 5 +- .../version_control/test_version_control.py | 8 +- 58 files changed, 497 insertions(+), 515 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/announcements/_models.py b/services/web/server/src/simcore_service_webserver/announcements/_models.py index 4edb7c8d20a..febeeadb175 100644 --- a/services/web/server/src/simcore_service_webserver/announcements/_models.py +++ b/services/web/server/src/simcore_service_webserver/announcements/_models.py @@ -1,8 +1,8 @@ from datetime import datetime -from typing import Any, ClassVar, Literal +from typing import Literal import arrow -from pydantic import BaseModel, validator +from pydantic import BaseModel, ConfigDict, ValidationInfo, field_validator # NOTE: this model is used for BOTH @@ -18,10 +18,10 @@ class Announcement(BaseModel): link: str widgets: list[Literal["login", "ribbon", "user-menu"]] - @validator("end") + @field_validator("end") @classmethod - def check_start_before_end(cls, v, values): - if start := values.get("start"): + def check_start_before_end(cls, v, info: ValidationInfo): + if start := info.data.get("start"): end = v if end <= start: msg = f"end={end!r} is not before start={start!r}" @@ -31,8 +31,8 @@ def check_start_before_end(cls, v, values): def expired(self) -> bool: return self.end <= arrow.utcnow().datetime - class Config: - schema_extra: ClassVar[dict[str, Any]] = { + model_config = ConfigDict( + json_schema_extra={ "examples": [ { "id": "Student_Competition_2023", @@ -56,3 +56,4 @@ class Config: }, ] } + ) diff --git a/services/web/server/src/simcore_service_webserver/application_settings.py b/services/web/server/src/simcore_service_webserver/application_settings.py index eefcc4869fc..c01e5bd96e5 100644 --- a/services/web/server/src/simcore_service_webserver/application_settings.py +++ b/services/web/server/src/simcore_service_webserver/application_settings.py @@ -3,6 +3,7 @@ from typing import Any, Final from aiohttp import web +from common_library.pydantic_fields_extension import is_nullable from models_library.basic_types import ( BootModeEnum, BuildTargetEnum, @@ -11,8 +12,15 @@ VersionTag, ) from models_library.utils.change_case import snake_to_camel -from pydantic import AnyHttpUrl, parse_obj_as, root_validator, validator -from pydantic.fields import Field, ModelField +from pydantic import ( + AliasChoices, + AnyHttpUrl, + TypeAdapter, + ValidationInfo, + field_validator, + model_validator, +) +from pydantic.fields import Field from pydantic.types import PositiveInt from settings_library.base import BaseCustomSettings from settings_library.email import SMTPSettings @@ -53,7 +61,7 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings): # CODE STATICS --------------------------------------------------------- API_VERSION: str = API_VERSION APP_NAME: str = APP_NAME - API_VTAG: VersionTag = parse_obj_as(VersionTag, API_VTAG) + API_VTAG: VersionTag = TypeAdapter(VersionTag).validate_python(API_VTAG) # IMAGE BUILDTIME ------------------------------------------------------ # @Makefile @@ -83,13 +91,13 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings): SIMCORE_VCS_RELEASE_TAG: str | None = Field( default=None, description="Name of the tag that marks this release, or None if undefined", - example="ResistanceIsFutile10", + examples=["ResistanceIsFutile10"], ) SIMCORE_VCS_RELEASE_URL: AnyHttpUrl | None = Field( default=None, description="URL to release notes", - example="https://github.com/ITISFoundation/osparc-simcore/releases/tag/staging_ResistanceIsFutile10", + examples=["https://github.com/ITISFoundation/osparc-simcore/releases/tag/staging_ResistanceIsFutile10"], ) SWARM_STACK_NAME: str | None = Field( @@ -105,13 +113,14 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings): ) WEBSERVER_LOGLEVEL: LogLevel = Field( default=LogLevel.WARNING.value, - env=["WEBSERVER_LOGLEVEL", "LOG_LEVEL", "LOGLEVEL"], + validation_alias=AliasChoices("WEBSERVER_LOGLEVEL", "LOG_LEVEL", "LOGLEVEL"), # NOTE: suffix '_LOGLEVEL' is used overall ) - WEBSERVER_LOG_FORMAT_LOCAL_DEV_ENABLED: bool = Field( default=False, - env=["WEBSERVER_LOG_FORMAT_LOCAL_DEV_ENABLED", "LOG_FORMAT_LOCAL_DEV_ENABLED"], + validation_alias=AliasChoices( + "WEBSERVER_LOG_FORMAT_LOCAL_DEV_ENABLED", "LOG_FORMAT_LOCAL_DEV_ENABLED" + ), description="Enables local development log format. WARNING: make sure it is disabled if you want to have structured logs!", ) # TODO: find a better name!? @@ -120,100 +129,117 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings): description="host name to serve within the container." "NOTE that this different from WEBSERVER_HOST env which is the host seen outside the container", ) - WEBSERVER_HOST: str | None = Field(None, env=["WEBSERVER_HOST", "HOST", "HOSTNAME"]) - WEBSERVER_PORT: PortInt = parse_obj_as(PortInt, DEFAULT_AIOHTTP_PORT) + WEBSERVER_HOST: str | None = Field( + None, validation_alias=AliasChoices("WEBSERVER_HOST", "HOST", "HOSTNAME") + ) + WEBSERVER_PORT: PortInt = TypeAdapter(PortInt).validate_python(DEFAULT_AIOHTTP_PORT) WEBSERVER_FRONTEND: FrontEndAppSettings | None = Field( - auto_default_from_env=True, description="front-end static settings" + json_schema_extra={"auto_default_from_env": True}, + description="front-end static settings", ) # PLUGINS ---------------- WEBSERVER_ACTIVITY: PrometheusSettings | None = Field( - auto_default_from_env=True, + json_schema_extra={"auto_default_from_env": True}, description="activity plugin", ) WEBSERVER_CATALOG: CatalogSettings | None = Field( - auto_default_from_env=True, description="catalog service client's plugin" + json_schema_extra={"auto_default_from_env": True}, + description="catalog service client's plugin", ) # TODO: Shall be required WEBSERVER_DB: PostgresSettings | None = Field( - auto_default_from_env=True, description="database plugin" + json_schema_extra={"auto_default_from_env": True}, description="database plugin" ) WEBSERVER_DIAGNOSTICS: DiagnosticsSettings | None = Field( - auto_default_from_env=True, description="diagnostics plugin" + json_schema_extra={"auto_default_from_env": True}, + description="diagnostics plugin", ) WEBSERVER_DIRECTOR_V2: DirectorV2Settings | None = Field( - auto_default_from_env=True, description="director-v2 service client's plugin" + json_schema_extra={"auto_default_from_env": True}, + description="director-v2 service client's plugin", ) WEBSERVER_EMAIL: SMTPSettings | None = Field( - auto_default_from_env=True, description="email plugin" + json_schema_extra={"auto_default_from_env": True}, description="email plugin" ) WEBSERVER_EXPORTER: ExporterSettings | None = Field( - auto_default_from_env=True, description="exporter plugin" + json_schema_extra={"auto_default_from_env": True}, description="exporter plugin" ) WEBSERVER_GARBAGE_COLLECTOR: GarbageCollectorSettings | None = Field( - auto_default_from_env=True, description="garbage collector plugin" + json_schema_extra={"auto_default_from_env": True}, + description="garbage collector plugin", ) WEBSERVER_INVITATIONS: InvitationsSettings | None = Field( - auto_default_from_env=True, description="invitations plugin" + json_schema_extra={"auto_default_from_env": True}, + description="invitations plugin", ) WEBSERVER_LOGIN: LoginSettings | None = Field( - auto_default_from_env=True, description="login plugin" + json_schema_extra={"auto_default_from_env": True}, description="login plugin" ) WEBSERVER_PAYMENTS: PaymentsSettings | None = Field( - auto_default_from_env=True, description="payments plugin settings" + json_schema_extra={"auto_default_from_env": True}, + description="payments plugin settings", ) WEBSERVER_DYNAMIC_SCHEDULER: DynamicSchedulerSettings | None = Field( - auto_default_from_env=True, description="dynamic-scheduler plugin settings" + description="dynamic-scheduler plugin settings", + json_schema_extra={"auto_default_from_env": True}, ) - WEBSERVER_REDIS: RedisSettings | None = Field(auto_default_from_env=True) + WEBSERVER_REDIS: RedisSettings | None = Field( + json_schema_extra={"auto_default_from_env": True} + ) WEBSERVER_REST: RestSettings | None = Field( - auto_default_from_env=True, description="rest api plugin" + description="rest api plugin", json_schema_extra={"auto_default_from_env": True} ) WEBSERVER_RESOURCE_MANAGER: ResourceManagerSettings = Field( - auto_default_from_env=True, description="resource_manager plugin" + description="resource_manager plugin", + json_schema_extra={"auto_default_from_env": True}, ) WEBSERVER_RESOURCE_USAGE_TRACKER: ResourceUsageTrackerSettings | None = Field( - auto_default_from_env=True, description="resource usage tracker service client's plugin", + json_schema_extra={"auto_default_from_env": True}, ) WEBSERVER_SCICRUNCH: SciCrunchSettings | None = Field( - auto_default_from_env=True, description="scicrunch plugin" + description="scicrunch plugin", + json_schema_extra={"auto_default_from_env": True}, ) WEBSERVER_SESSION: SessionSettings = Field( - auto_default_from_env=True, description="session plugin" + description="session plugin", json_schema_extra={"auto_default_from_env": True} ) WEBSERVER_STATICWEB: StaticWebserverModuleSettings | None = Field( - auto_default_from_env=True, description="static-webserver service plugin" + description="static-webserver service plugin", + json_schema_extra={"auto_default_from_env": True}, ) WEBSERVER_STORAGE: StorageSettings | None = Field( - auto_default_from_env=True, description="storage service client's plugin" + description="storage service client's plugin", + json_schema_extra={"auto_default_from_env": True}, ) WEBSERVER_STUDIES_DISPATCHER: StudiesDispatcherSettings | None = Field( - auto_default_from_env=True, description="studies dispatcher plugin" + description="studies dispatcher plugin", + json_schema_extra={"auto_default_from_env": True}, ) WEBSERVER_TRACING: TracingSettings | None = Field( - auto_default_from_env=True, description="tracing plugin" + description="tracing plugin", json_schema_extra={"auto_default_from_env": True} ) WEBSERVER_PROJECTS: ProjectsSettings | None = Field( - auto_default_from_env=True, description="projects plugin" + description="projects plugin", json_schema_extra={"auto_default_from_env": True} ) WEBSERVER_RABBITMQ: RabbitSettings | None = Field( - auto_default_from_env=True, description="rabbitmq plugin" + description="rabbitmq plugin", json_schema_extra={"auto_default_from_env": True} ) WEBSERVER_USERS: UsersSettings | None = Field( - auto_default_from_env=True, description="users plugin" + description="users plugin", json_schema_extra={"auto_default_from_env": True} ) # These plugins only require (for the moment) an entry to toggle between enabled/disabled @@ -242,7 +268,7 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings): "Currently this is a system plugin and cannot be disabled", ) - @root_validator() + @model_validator(mode="after") @classmethod def build_vcs_release_url_if_unset(cls, values): release_url = values.get("SIMCORE_VCS_RELEASE_URL") @@ -260,39 +286,43 @@ def build_vcs_release_url_if_unset(cls, values): return values - @validator( + @field_validator( # List of plugins under-development (keep up-to-date) # TODO: consider mark as dev-feature in field extras of Config attr. # Then they can be automtically advertised "WEBSERVER_META_MODELING", "WEBSERVER_VERSION_CONTROL", - pre=True, - always=True, + mode="before", ) @classmethod - def enable_only_if_dev_features_allowed(cls, v, values, field: ModelField): + def enable_only_if_dev_features_allowed(cls, v, info: ValidationInfo): """Ensures that plugins 'under development' get programatically disabled if WEBSERVER_DEV_FEATURES_ENABLED=False """ - if values["WEBSERVER_DEV_FEATURES_ENABLED"]: + if info.data["WEBSERVER_DEV_FEATURES_ENABLED"]: return v if v: _logger.warning( - "%s still under development and will be disabled.", field.name + "%s still under development and will be disabled.", info.field_name ) - return None if field.allow_none else False + + return ( + None + if info.field_name and is_nullable(cls.model_fields[info.field_name]) + else False + ) @cached_property def log_level(self) -> int: level: int = getattr(logging, self.WEBSERVER_LOGLEVEL.upper()) return level - @validator("WEBSERVER_LOGLEVEL", pre=True) + @field_validator("WEBSERVER_LOGLEVEL") @classmethod - def valid_log_level(cls, value: str) -> str: + def valid_log_level(cls, value): return cls.validate_log_level(value) - @validator("SC_HEALTHCHECK_TIMEOUT", pre=True) + @field_validator("SC_HEALTHCHECK_TIMEOUT", mode="before") @classmethod def get_healthcheck_timeout_in_seconds(cls, v): # Ex. HEALTHCHECK --interval=5m --timeout=3s diff --git a/services/web/server/src/simcore_service_webserver/catalog/_api.py b/services/web/server/src/simcore_service_webserver/catalog/_api.py index 9bbbae4e43c..b89c79b6dbf 100644 --- a/services/web/server/src/simcore_service_webserver/catalog/_api.py +++ b/services/web/server/src/simcore_service_webserver/catalog/_api.py @@ -23,7 +23,7 @@ from models_library.users import UserID from models_library.utils.fastapi_encoders import jsonable_encoder from pint import UnitRegistry -from pydantic import BaseModel +from pydantic import BaseModel, ConfigDict from servicelib.aiohttp.requests_validation import handle_validation_as_http_error from servicelib.rabbitmq.rpc_interfaces.catalog import services as catalog_rpc from servicelib.rest_constants import RESPONSE_MODEL_POLICY @@ -42,9 +42,7 @@ class CatalogRequestContext(BaseModel): user_id: UserID product_name: str unit_registry: UnitRegistry - - class Config: - arbitrary_types_allowed = True + model_config = ConfigDict(arbitrary_types_allowed=True) @classmethod def create(cls, request: Request) -> "CatalogRequestContext": diff --git a/services/web/server/src/simcore_service_webserver/catalog/_handlers.py b/services/web/server/src/simcore_service_webserver/catalog/_handlers.py index 02e21f37e29..d5aad19f958 100644 --- a/services/web/server/src/simcore_service_webserver/catalog/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/catalog/_handlers.py @@ -26,7 +26,7 @@ ServiceResourcesDict, ServiceResourcesDictHelpers, ) -from pydantic import BaseModel, Extra, Field, parse_obj_as, validator +from pydantic import BaseModel, ConfigDict, Field, TypeAdapter, field_validator from servicelib.aiohttp.requests_validation import ( parse_request_body_as, parse_request_path_parameters_as, @@ -54,12 +54,12 @@ class ServicePathParams(BaseModel): service_key: ServiceKey service_version: ServiceVersion + model_config = ConfigDict( + populate_by_name=True, + extra="forbid", + ) - class Config: - allow_population_by_field_name = True - extra = Extra.forbid - - @validator("service_key", pre=True) + @field_validator("service_key", mode="before") @classmethod def ensure_unquoted(cls, v): # NOTE: this is needed as in pytest mode, the aiohttp server does not seem to unquote automatically @@ -387,4 +387,6 @@ async def get_service_pricing_plan(request: Request): service_version=f"{path_params.service_version}", ) - return envelope_json_response(parse_obj_as(PricingPlanGet, pricing_plan)) + return envelope_json_response( + TypeAdapter(PricingPlanGet).validate_python(pricing_plan) + ) diff --git a/services/web/server/src/simcore_service_webserver/diagnostics/settings.py b/services/web/server/src/simcore_service_webserver/diagnostics/settings.py index 5c82496ad34..b9557f6d231 100644 --- a/services/web/server/src/simcore_service_webserver/diagnostics/settings.py +++ b/services/web/server/src/simcore_service_webserver/diagnostics/settings.py @@ -1,5 +1,12 @@ from aiohttp.web import Application -from pydantic import Field, NonNegativeFloat, PositiveFloat, validator +from pydantic import ( + AliasChoices, + Field, + NonNegativeFloat, + PositiveFloat, + ValidationInfo, + field_validator, +) from servicelib.aiohttp.application_keys import APP_SETTINGS_KEY from settings_library.base import BaseCustomSettings @@ -11,7 +18,9 @@ class DiagnosticsSettings(BaseCustomSettings): "Any task blocked more than slow_duration_secs is logged as WARNING" "Aims to identify possible blocking calls" ), - env=["DIAGNOSTICS_SLOW_DURATION_SECS", "AIODEBUG_SLOW_DURATION_SECS"], + validation_alias=AliasChoices( + "DIAGNOSTICS_SLOW_DURATION_SECS", "AIODEBUG_SLOW_DURATION_SECS" + ), ) DIAGNOSTICS_HEALTHCHECK_ENABLED: bool = Field( @@ -32,13 +41,13 @@ class DiagnosticsSettings(BaseCustomSettings): DIAGNOSTICS_START_SENSING_DELAY: NonNegativeFloat = 60.0 - @validator("DIAGNOSTICS_MAX_TASK_DELAY", pre=True) + @field_validator("DIAGNOSTICS_MAX_TASK_DELAY", mode="before") @classmethod - def _validate_max_task_delay(cls, v, values): + def _validate_max_task_delay(cls, v, info: ValidationInfo): # Sets an upper threshold for blocking functions, i.e. # settings.DIAGNOSTICS_SLOW_DURATION_SECS < settings.DIAGNOSTICS_MAX_TASK_DELAY # - slow_duration_secs = float(values["DIAGNOSTICS_SLOW_DURATION_SECS"]) + slow_duration_secs = float(info.data["DIAGNOSTICS_SLOW_DURATION_SECS"]) return max( 10 * slow_duration_secs, float(v), diff --git a/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py b/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py index f794fa6f148..b84eb68dfd9 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py @@ -8,7 +8,7 @@ from models_library.projects import ProjectID from models_library.users import UserID from models_library.utils.json_serialization import json_dumps -from pydantic import BaseModel, Field, ValidationError, parse_obj_as +from pydantic import BaseModel, Field, TypeAdapter, ValidationError from pydantic.types import NonNegativeInt from servicelib.aiohttp import status from servicelib.aiohttp.rest_responses import create_http_error, exception_to_response @@ -64,7 +64,7 @@ class _ComputationStarted(BaseModel): async def start_computation(request: web.Request) -> web.Response: # pylint: disable=too-many-statements try: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) computations = ComputationsApi(request.app) run_policy = get_project_run_policy(request.app) @@ -78,7 +78,9 @@ async def start_computation(request: web.Request) -> web.Response: if request.can_read_body: body = await request.json() - assert parse_obj_as(ComputationStart, body) is not None # nosec + assert ( + TypeAdapter(ComputationStart).validate_python(body) is not None + ) # nosec subgraph = body.get("subgraph", []) force_restart = bool(body.get("force_restart", force_restart)) @@ -158,7 +160,9 @@ async def start_computation(request: web.Request) -> web.Response: if project_vc_commits: data["ref_ids"] = project_vc_commits - assert parse_obj_as(_ComputationStarted, data) is not None # nosec + assert ( + TypeAdapter(_ComputationStarted).validate_python(data) is not None + ) # nosec return envelope_json_response(data, status_cls=web.HTTPCreated) @@ -234,8 +238,7 @@ async def get_computation(request: web.Request) -> web.Response: request, project_id ) _logger.debug("Project %s will get %d variants", project_id, len(project_ids)) - list_computation_tasks = parse_obj_as( - list[ComputationTaskGet], + list_computation_tasks = TypeAdapter(list[ComputationTaskGet]).validate_python( await asyncio.gather( *[ computations.get(project_id=pid, user_id=user_id) diff --git a/services/web/server/src/simcore_service_webserver/director_v2/_models.py b/services/web/server/src/simcore_service_webserver/director_v2/_models.py index 70dd53ff5fd..d25a4aa23e6 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/_models.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/_models.py @@ -1,5 +1,3 @@ -from typing import Any, ClassVar - from models_library.clusters import ( CLUSTER_ADMIN_RIGHTS, CLUSTER_MANAGER_RIGHTS, @@ -10,7 +8,7 @@ ExternalClusterAuthentication, ) from models_library.users import GroupID -from pydantic import AnyHttpUrl, BaseModel, Field, validator +from pydantic import AnyHttpUrl, BaseModel, ConfigDict, Field, field_validator from pydantic.networks import AnyUrl, HttpUrl from simcore_postgres_database.models.clusters import ClusterType @@ -33,7 +31,7 @@ class ClusterCreate(BaseCluster): alias="accessRights", default_factory=dict ) - @validator("thumbnail", always=True, pre=True) + @field_validator("thumbnail", mode="before") @classmethod def set_default_thumbnail_if_empty(cls, v, values): if v is None and ( @@ -42,8 +40,8 @@ def set_default_thumbnail_if_empty(cls, v, values): return _DEFAULT_THUMBNAILS[f"{cluster_type}"] return v - class Config(BaseCluster.Config): - schema_extra: ClassVar[dict[str, Any]] = { + model_config = ConfigDict( + json_schema_extra={ "examples": [ { "name": "My awesome cluster", @@ -74,6 +72,7 @@ class Config(BaseCluster.Config): }, ] } + ) class ClusterPatch(BaseCluster): diff --git a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py index f331c98da4a..d965678037b 100644 --- a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py +++ b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py @@ -16,7 +16,7 @@ from models_library.users import UserID from models_library.utils.common_validators import null_or_none_str_to_none_validator from models_library.workspaces import WorkspaceID -from pydantic import Extra, Field, Json, parse_obj_as, validator +from pydantic import ConfigDict, Field, Json, TypeAdapter, field_validator from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( RequestParams, @@ -94,7 +94,7 @@ class FolderListWithJsonStrQueryParams(PageQueryParameters): order_by: Json[OrderBy] = Field( default=OrderBy(field=IDStr("modified"), direction=OrderDirection.DESC), description="Order by field (modified_at|name|description) and direction (asc|desc). The default sorting order is ascending.", - example='{"field": "name", "direction": "desc"}', + examples=['{"field": "name", "direction": "desc"}'], alias="order_by", ) folder_id: FolderID | None = Field( @@ -106,7 +106,7 @@ class FolderListWithJsonStrQueryParams(PageQueryParameters): description="List folders in specific workspace. By default, list in the user private workspace", ) - @validator("order_by", check_fields=False) + @field_validator("order_by", check_fields=False) @classmethod def validate_order_by_field(cls, v): if v.field not in { @@ -120,16 +120,15 @@ def validate_order_by_field(cls, v): v.field = "modified" return v - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") # validators - _null_or_none_str_to_none_validator = validator( - "folder_id", allow_reuse=True, pre=True - )(null_or_none_str_to_none_validator) + _null_or_none_str_to_none_validator = field_validator("folder_id", mode="before")( + null_or_none_str_to_none_validator + ) - _null_or_none_str_to_none_validator2 = validator( - "workspace_id", allow_reuse=True, pre=True + _null_or_none_str_to_none_validator2 = field_validator( + "workspace_id", mode="before" )(null_or_none_str_to_none_validator) @@ -138,7 +137,7 @@ class Config: @permission_required("folder.create") @handle_folders_exceptions async def create_folder(request: web.Request): - req_ctx = FoldersRequestContext.parse_obj(request) + req_ctx = FoldersRequestContext.model_validate(request) body_params = await parse_request_body_as(CreateFolderBodyParams, request) folder = await _folders_api.create_folder( @@ -158,7 +157,7 @@ async def create_folder(request: web.Request): @permission_required("folder.read") @handle_folders_exceptions async def list_folders(request: web.Request): - req_ctx = FoldersRequestContext.parse_obj(request) + req_ctx = FoldersRequestContext.model_validate(request) query_params: FolderListWithJsonStrQueryParams = parse_request_query_parameters_as( FolderListWithJsonStrQueryParams, request ) @@ -171,10 +170,10 @@ async def list_folders(request: web.Request): workspace_id=query_params.workspace_id, offset=query_params.offset, limit=query_params.limit, - order_by=parse_obj_as(OrderBy, query_params.order_by), + order_by=TypeAdapter(OrderBy).validate_python(query_params.order_by), ) - page = Page[FolderGet].parse_obj( + page = Page[FolderGet].model_validate( paginate_data( chunk=folders.items, request_url=request.url, @@ -194,7 +193,7 @@ async def list_folders(request: web.Request): @permission_required("folder.read") @handle_folders_exceptions async def get_folder(request: web.Request): - req_ctx = FoldersRequestContext.parse_obj(request) + req_ctx = FoldersRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(FoldersPathParams, request) folder: FolderGet = await _folders_api.get_folder( @@ -215,7 +214,7 @@ async def get_folder(request: web.Request): @permission_required("folder.update") @handle_folders_exceptions async def replace_folder(request: web.Request): - req_ctx = FoldersRequestContext.parse_obj(request) + req_ctx = FoldersRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(FoldersPathParams, request) body_params = await parse_request_body_as(PutFolderBodyParams, request) @@ -238,7 +237,7 @@ async def replace_folder(request: web.Request): @permission_required("folder.delete") @handle_folders_exceptions async def delete_folder_group(request: web.Request): - req_ctx = FoldersRequestContext.parse_obj(request) + req_ctx = FoldersRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(FoldersPathParams, request) await _folders_api.delete_folder( diff --git a/services/web/server/src/simcore_service_webserver/groups/_classifiers.py b/services/web/server/src/simcore_service_webserver/groups/_classifiers.py index 5ac89e0ee94..68b9e2a5fdb 100644 --- a/services/web/server/src/simcore_service_webserver/groups/_classifiers.py +++ b/services/web/server/src/simcore_service_webserver/groups/_classifiers.py @@ -9,20 +9,19 @@ """ import logging -import re -from typing import Any, Final, Literal +from typing import Annotated, Any, Final, Literal, TypeAlias import sqlalchemy as sa from aiohttp import web from aiopg.sa.result import RowProxy from pydantic import ( BaseModel, - ConstrainedStr, Field, HttpUrl, + StringConstraints, + TypeAdapter, ValidationError, - parse_obj_as, - validator, + field_validator, ) from simcore_postgres_database.models.classifiers import group_classifiers @@ -37,8 +36,11 @@ # DOMAIN MODELS --- -class TreePath(ConstrainedStr): - regex = re.compile(r"[\w:]+") # Examples 'a::b::c +TreePath: TypeAlias = Annotated[ + # Examples 'a::b::c + str, + StringConstraints(pattern=r"[\w:]+"), +] class ClassifierItem(BaseModel): @@ -50,10 +52,10 @@ class ClassifierItem(BaseModel): url: HttpUrl | None = Field( None, description="Link to more information", - example="https://scicrunch.org/resources/Any/search?q=osparc&l=osparc", + examples=["https://scicrunch.org/resources/Any/search?q=osparc&l=osparc"], ) - @validator("short_description", pre=True) + @field_validator("short_description", mode="before") @classmethod def truncate_to_short(cls, v): if v and len(v) >= MAX_SIZE_SHORT_MSG: @@ -91,7 +93,9 @@ async def get_classifiers_from_bundle(self, gid: int) -> dict[str, Any]: if bundle: try: # truncate bundle to what is needed and drop the rest - return Classifiers(**bundle).dict(exclude_unset=True, exclude_none=True) + return Classifiers(**bundle).model_dump( + exclude_unset=True, exclude_none=True + ) except ValidationError as err: _logger.error( "DB corrupt data in 'groups_classifiers' table. " @@ -136,7 +140,9 @@ async def build_rrids_tree_view( url=scicrunch.get_resolver_web_url(resource.rrid), ) - node = parse_obj_as(TreePath, validated_item.display_name.replace(":", " ")) + node = TypeAdapter(TreePath).validate_python( + validated_item.display_name.replace(":", " ") + ) flat_tree_view[node] = validated_item except ValidationError as err: @@ -144,4 +150,6 @@ async def build_rrids_tree_view( "Cannot convert RRID into a classifier item. Skipping. Details: %s", err ) - return Classifiers.construct(classifiers=flat_tree_view).dict(exclude_unset=True) + return Classifiers.model_construct(classifiers=flat_tree_view).model_dump( + exclude_unset=True + ) diff --git a/services/web/server/src/simcore_service_webserver/groups/_handlers.py b/services/web/server/src/simcore_service_webserver/groups/_handlers.py index b6284bed8bf..efa3bd906e6 100644 --- a/services/web/server/src/simcore_service_webserver/groups/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/groups/_handlers.py @@ -12,7 +12,7 @@ from models_library.emails import LowerCaseEmailStr from models_library.users import GroupID, UserID from models_library.utils.json_serialization import json_dumps -from pydantic import BaseModel, Extra, Field, parse_obj_as +from pydantic import BaseModel, ConfigDict, Field, TypeAdapter from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_path_parameters_as, @@ -83,7 +83,7 @@ async def list_groups(request: web.Request): """ product: Product = get_current_product(request) - req_ctx = _GroupsRequestContext.parse_obj(request) + req_ctx = _GroupsRequestContext.model_validate(request) primary_group, user_groups, all_group = await api.list_user_groups_with_read_access( request.app, req_ctx.user_id @@ -104,15 +104,13 @@ async def list_groups(request: web.Request): product_gid=product.group_id, ) - assert parse_obj_as(AllUsersGroups, result) is not None # nosec + assert AllUsersGroups.model_validate(result) is not None # nosec return result class _GroupPathParams(BaseModel): gid: GroupID - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") @routes.get(f"/{API_VTAG}/groups/{{gid}}", name="get_group") @@ -121,11 +119,11 @@ class Config: @_handle_groups_exceptions async def get_group(request: web.Request): """Get one group details""" - req_ctx = _GroupsRequestContext.parse_obj(request) + req_ctx = _GroupsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_GroupPathParams, request) group = await api.get_user_group(request.app, req_ctx.user_id, path_params.gid) - assert parse_obj_as(UsersGroup, group) is not None # nosec + assert UsersGroup.model_validate(group) is not None # nosec return group @@ -135,11 +133,11 @@ async def get_group(request: web.Request): @_handle_groups_exceptions async def create_group(request: web.Request): """Creates organization groups""" - req_ctx = _GroupsRequestContext.parse_obj(request) + req_ctx = _GroupsRequestContext.model_validate(request) new_group = await request.json() created_group = await api.create_user_group(request.app, req_ctx.user_id, new_group) - assert parse_obj_as(UsersGroup, created_group) is not None # nosec + assert UsersGroup.model_validate(created_group) is not None # nosec raise web.HTTPCreated( text=json_dumps({"data": created_group}), content_type=MIMETYPE_APPLICATION_JSON ) @@ -150,14 +148,14 @@ async def create_group(request: web.Request): @permission_required("groups.*") @_handle_groups_exceptions async def update_group(request: web.Request): - req_ctx = _GroupsRequestContext.parse_obj(request) + req_ctx = _GroupsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_GroupPathParams, request) new_group_values = await request.json() updated_group = await api.update_user_group( request.app, req_ctx.user_id, path_params.gid, new_group_values ) - assert parse_obj_as(UsersGroup, updated_group) is not None # nosec + assert UsersGroup.model_validate(updated_group) is not None # nosec return envelope_json_response(updated_group) @@ -166,7 +164,7 @@ async def update_group(request: web.Request): @permission_required("groups.*") @_handle_groups_exceptions async def delete_group(request: web.Request): - req_ctx = _GroupsRequestContext.parse_obj(request) + req_ctx = _GroupsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_GroupPathParams, request) await api.delete_user_group(request.app, req_ctx.user_id, path_params.gid) @@ -178,13 +176,15 @@ async def delete_group(request: web.Request): @permission_required("groups.*") @_handle_groups_exceptions async def get_group_users(request: web.Request): - req_ctx = _GroupsRequestContext.parse_obj(request) + req_ctx = _GroupsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_GroupPathParams, request) group_user = await api.list_users_in_group( request.app, req_ctx.user_id, path_params.gid ) - assert parse_obj_as(list[GroupUserGet], group_user) is not None # nosec + assert ( + TypeAdapter(list[GroupUserGet]).validate_python(group_user) is not None + ) # nosec return envelope_json_response(group_user) @@ -204,7 +204,7 @@ async def add_group_user(request: web.Request): new_user_id = new_user_in_group["uid"] if "uid" in new_user_in_group else None new_user_email = ( - parse_obj_as(LowerCaseEmailStr, new_user_in_group["email"]) + TypeAdapter(LowerCaseEmailStr).validate_python(new_user_in_group["email"]) if "email" in new_user_in_group else None ) @@ -222,9 +222,7 @@ async def add_group_user(request: web.Request): class _GroupUserPathParams(BaseModel): gid: GroupID uid: UserID - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") @routes.get(f"/{API_VTAG}/groups/{{gid}}/users/{{uid}}", name="get_group_user") @@ -235,12 +233,12 @@ async def get_group_user(request: web.Request): """ Gets specific user in group """ - req_ctx = _GroupsRequestContext.parse_obj(request) + req_ctx = _GroupsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_GroupUserPathParams, request) user = await api.get_user_in_group( request.app, req_ctx.user_id, path_params.gid, path_params.uid ) - assert parse_obj_as(GroupUserGet, user) is not None # nosec + assert GroupUserGet.model_validate(user) is not None # nosec return envelope_json_response(user) @@ -252,7 +250,7 @@ async def update_group_user(request: web.Request): """ Modify specific user in group """ - req_ctx = _GroupsRequestContext.parse_obj(request) + req_ctx = _GroupsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_GroupUserPathParams, request) new_values_for_user_in_group = await request.json() user = await api.update_user_in_group( @@ -262,7 +260,7 @@ async def update_group_user(request: web.Request): path_params.uid, new_values_for_user_in_group, ) - assert parse_obj_as(GroupUserGet, user) is not None # nosec + assert GroupUserGet.model_validate(user) is not None # nosec return envelope_json_response(user) @@ -271,7 +269,7 @@ async def update_group_user(request: web.Request): @permission_required("groups.*") @_handle_groups_exceptions async def delete_group_user(request: web.Request): - req_ctx = _GroupsRequestContext.parse_obj(request) + req_ctx = _GroupsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_GroupUserPathParams, request) await api.delete_user_in_group( request.app, req_ctx.user_id, path_params.gid, path_params.uid @@ -352,7 +350,7 @@ async def get_scicrunch_resource(request: web.Request): scicrunch = SciCrunch.get_instance(request.app) resource = await scicrunch.get_resource_fields(rrid) - return envelope_json_response(resource.dict()) + return envelope_json_response(resource.model_dump()) @routes.post( @@ -392,4 +390,4 @@ async def search_scicrunch_resources(request: web.Request): scicrunch = SciCrunch.get_instance(request.app) hits: list[ResourceHit] = await scicrunch.search_resource(guess_name) - return envelope_json_response([hit.dict() for hit in hits]) + return envelope_json_response([hit.model_dump() for hit in hits]) diff --git a/services/web/server/src/simcore_service_webserver/login/_auth_handlers.py b/services/web/server/src/simcore_service_webserver/login/_auth_handlers.py index ca1e1a3a18d..95223a55d68 100644 --- a/services/web/server/src/simcore_service_webserver/login/_auth_handlers.py +++ b/services/web/server/src/simcore_service_webserver/login/_auth_handlers.py @@ -275,7 +275,7 @@ async def login_2fa(request: web.Request): class LogoutBody(InputSchema): client_session_id: str | None = Field( - None, example="5ac57685-c40f-448f-8711-70be1936fd63" + None, examples=["5ac57685-c40f-448f-8711-70be1936fd63"] ) diff --git a/services/web/server/src/simcore_service_webserver/login/_models.py b/services/web/server/src/simcore_service_webserver/login/_models.py index 2ac7b94f11a..36e73d8922b 100644 --- a/services/web/server/src/simcore_service_webserver/login/_models.py +++ b/services/web/server/src/simcore_service_webserver/login/_models.py @@ -1,15 +1,16 @@ from typing import Any, Callable -from pydantic import BaseModel, Extra, SecretStr +from pydantic import BaseModel, ConfigDict, SecretStr from ._constants import MSG_PASSWORD_MISMATCH class InputSchema(BaseModel): - class Config: - allow_population_by_field_name = False - extra = Extra.forbid - allow_mutations = False + model_config = ConfigDict( + populate_by_name=False, + extra="forbid", + frozen=True, + ) def create_password_match_validator( diff --git a/services/web/server/src/simcore_service_webserver/login/_registration.py b/services/web/server/src/simcore_service_webserver/login/_registration.py index 282256d1b16..bf9e09fcd30 100644 --- a/services/web/server/src/simcore_service_webserver/login/_registration.py +++ b/services/web/server/src/simcore_service_webserver/login/_registration.py @@ -19,9 +19,9 @@ Field, Json, PositiveInt, + TypeAdapter, ValidationError, - parse_obj_as, - validator, + field_validator, ) from servicelib.logging_errors import create_troubleshotting_log_kwargs from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON @@ -78,7 +78,7 @@ class _InvitationValidator(BaseModel): action: ConfirmationAction data: Json[InvitationData] # pylint: disable=unsubscriptable-object - @validator("action", pre=True) + @field_validator("action", mode="before") @classmethod def ensure_enum(cls, v): if isinstance(v, ConfirmationAction): @@ -256,7 +256,7 @@ async def extract_email_from_invitation( """Returns associated email""" with _invitations_request_context(invitation_code=invitation_code) as url: content = await extract_invitation(app, invitation_url=f"{url}") - return parse_obj_as(LowerCaseEmailStr, content.guest) + return TypeAdapter(LowerCaseEmailStr).validate_python(content.guest) async def check_and_consume_invitation( diff --git a/services/web/server/src/simcore_service_webserver/login/handlers_registration.py b/services/web/server/src/simcore_service_webserver/login/handlers_registration.py index 9ae8b1af582..de7966ccb08 100644 --- a/services/web/server/src/simcore_service_webserver/login/handlers_registration.py +++ b/services/web/server/src/simcore_service_webserver/login/handlers_registration.py @@ -1,12 +1,19 @@ import logging -from datetime import datetime, timedelta -from typing import Any, ClassVar, Literal +from datetime import UTC, datetime, timedelta +from typing import Literal from aiohttp import web from aiohttp.web import RouteTableDef from models_library.emails import LowerCaseEmailStr from models_library.error_codes import create_error_code -from pydantic import BaseModel, Field, PositiveInt, SecretStr, validator +from pydantic import ( + BaseModel, + ConfigDict, + Field, + PositiveInt, + SecretStr, + field_validator, +) from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import parse_request_body_as from servicelib.logging_errors import create_troubleshotting_log_kwargs @@ -115,12 +122,9 @@ class RegisterBody(InputSchema): confirm: SecretStr | None = Field(None, description="Password confirmation") invitation: str | None = Field(None, description="Invitation code") - _password_confirm_match = validator("confirm", allow_reuse=True)( - check_confirm_password_match - ) - - class Config: - schema_extra: ClassVar[dict[str, Any]] = { + _password_confirm_match = field_validator("confirm")(check_confirm_password_match) + model_config = ConfigDict( + json_schema_extra={ "examples": [ { "email": "foo@mymail.com", @@ -130,6 +134,7 @@ class Config: } ] } + ) @routes.post(f"/{API_VTAG}/auth/register", name="auth_register") @@ -204,7 +209,7 @@ async def register(request: web.Request): app=request.app, ) if invitation.trial_account_days: - expires_at = datetime.utcnow() + timedelta(invitation.trial_account_days) + expires_at = datetime.now(UTC) + timedelta(invitation.trial_account_days) # get authorized user or create new user = await _auth_api.get_user_by_email(request.app, email=registration.email) diff --git a/services/web/server/src/simcore_service_webserver/login/settings.py b/services/web/server/src/simcore_service_webserver/login/settings.py index c32ce319c7f..307e5424cff 100644 --- a/services/web/server/src/simcore_service_webserver/login/settings.py +++ b/services/web/server/src/simcore_service_webserver/login/settings.py @@ -2,7 +2,7 @@ from typing import Final, Literal from aiohttp import web -from pydantic import BaseModel, validator +from pydantic import BaseModel, field_validator from pydantic.fields import Field from pydantic.types import PositiveFloat, PositiveInt, SecretStr from settings_library.base import BaseCustomSettings @@ -54,7 +54,7 @@ class LoginSettings(BaseCustomSettings): description="Minimum length of password", ) - @validator("LOGIN_2FA_REQUIRED") + @field_validator("LOGIN_2FA_REQUIRED") @classmethod def login_2fa_needs_email_registration(cls, v, values): # NOTE: this constraint ensures that a phone is registered in current workflow @@ -63,7 +63,7 @@ def login_2fa_needs_email_registration(cls, v, values): raise ValueError(msg) return v - @validator("LOGIN_2FA_REQUIRED") + @field_validator("LOGIN_2FA_REQUIRED") @classmethod def login_2fa_needs_sms_service(cls, v, values): if v and values.get("LOGIN_TWILIO") is None: diff --git a/services/web/server/src/simcore_service_webserver/meta_modeling/_handlers.py b/services/web/server/src/simcore_service_webserver/meta_modeling/_handlers.py index 35244dc5363..7b2007cdb94 100644 --- a/services/web/server/src/simcore_service_webserver/meta_modeling/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/meta_modeling/_handlers.py @@ -9,7 +9,7 @@ from models_library.projects import ProjectID from models_library.rest_pagination import Page, PageQueryParameters from models_library.rest_pagination_utils import paginate_data -from pydantic import BaseModel, ValidationError, validator +from pydantic import BaseModel, ValidationError, field_validator from pydantic.fields import Field from pydantic.networks import HttpUrl from servicelib.rest_constants import RESPONSE_MODEL_POLICY @@ -33,7 +33,7 @@ class ParametersModel(PageQueryParameters): project_uuid: ProjectID ref_id: CommitID - @validator("ref_id", pre=True) + @field_validator("ref_id", mode="before") @classmethod def tags_as_refid_not_implemented(cls, v): try: diff --git a/services/web/server/src/simcore_service_webserver/meta_modeling/_results.py b/services/web/server/src/simcore_service_webserver/meta_modeling/_results.py index 68829e3489a..e4c695687e8 100644 --- a/services/web/server/src/simcore_service_webserver/meta_modeling/_results.py +++ b/services/web/server/src/simcore_service_webserver/meta_modeling/_results.py @@ -7,18 +7,16 @@ import logging -from typing import Any +from typing import Annotated, Any from models_library.projects_nodes import OutputsDict from models_library.projects_nodes_io import NodeIDStr -from pydantic import BaseModel, ConstrainedInt, Field +from pydantic import BaseModel, ConfigDict, Field _logger = logging.getLogger(__name__) -class ProgressInt(ConstrainedInt): - ge = 0 - le = 100 +ProgressInt = Annotated[int, Field(ge=0, le=100)] class ExtractedResults(BaseModel): @@ -31,9 +29,8 @@ class ExtractedResults(BaseModel): values: dict[NodeIDStr, OutputsDict] = Field( ..., description="Captured outputs per node" ) - - class Config: - schema_extra = { + model_config = ConfigDict( + json_schema_extra={ "example": { # sample with 2 computational services, 2 data sources (iterator+parameter) and 2 observers (probes) "progress": { @@ -57,6 +54,7 @@ class Config: }, } } + ) def extract_project_results(workbench: dict[str, Any]) -> ExtractedResults: diff --git a/services/web/server/src/simcore_service_webserver/payments/_autorecharge_db.py b/services/web/server/src/simcore_service_webserver/payments/_autorecharge_db.py index 8aec3e45359..f276b31c5ab 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_autorecharge_db.py +++ b/services/web/server/src/simcore_service_webserver/payments/_autorecharge_db.py @@ -6,7 +6,7 @@ from models_library.basic_types import NonNegativeDecimal from models_library.users import UserID from models_library.wallets import WalletID -from pydantic import BaseModel, PositiveInt +from pydantic import BaseModel, ConfigDict, PositiveInt from simcore_postgres_database.utils_payments_autorecharge import AutoRechargeStmts from ..db.plugin import get_database_engine @@ -24,9 +24,7 @@ class PaymentsAutorechargeDB(BaseModel): primary_payment_method_id: PaymentMethodID top_up_amount_in_usd: NonNegativeDecimal monthly_limit_in_usd: NonNegativeDecimal | None - - class Config: - orm_mode = True + model_config = ConfigDict(from_attributes=True) async def get_wallet_autorecharge( diff --git a/services/web/server/src/simcore_service_webserver/payments/_methods_db.py b/services/web/server/src/simcore_service_webserver/payments/_methods_db.py index b5838eb171c..1d47dc19dd0 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_methods_db.py +++ b/services/web/server/src/simcore_service_webserver/payments/_methods_db.py @@ -8,7 +8,7 @@ from models_library.api_schemas_webserver.wallets import PaymentMethodID from models_library.users import UserID from models_library.wallets import WalletID -from pydantic import BaseModel, parse_obj_as +from pydantic import BaseModel, ConfigDict, TypeAdapter from simcore_postgres_database.models.payments_methods import ( InitPromptAckFlowState, payments_methods, @@ -26,6 +26,9 @@ _logger = logging.getLogger(__name__) +PaymentMethodIDTypeAdapter: TypeAdapter[PaymentMethodID] = TypeAdapter(PaymentMethodID) + + class PaymentsMethodsDB(BaseModel): payment_method_id: PaymentMethodID user_id: UserID @@ -35,9 +38,7 @@ class PaymentsMethodsDB(BaseModel): completed_at: datetime.datetime | None state: InitPromptAckFlowState state_message: str | None - - class Config: - orm_mode = True + model_config = ConfigDict(from_attributes=True) async def insert_init_payment_method( @@ -81,7 +82,7 @@ async def list_successful_payment_methods( .order_by(payments_methods.c.created.desc()) ) # newest first rows = await result.fetchall() or [] - return parse_obj_as(list[PaymentsMethodsDB], rows) + return TypeAdapter(list[PaymentsMethodsDB]).validate_python(rows) async def get_successful_payment_method( @@ -117,7 +118,10 @@ async def get_pending_payment_methods_ids( .order_by(payments_methods.c.initiated_at.asc()) # oldest first ) rows = await result.fetchall() or [] - return [parse_obj_as(PaymentMethodID, row.payment_method_id) for row in rows] + return [ + PaymentMethodIDTypeAdapter.validate_python(row.payment_method_id) + for row in rows + ] async def udpate_payment_method( @@ -168,7 +172,7 @@ async def udpate_payment_method( row = await result.first() assert row, "execute above should have caught this" # nosec - return PaymentsMethodsDB.from_orm(row) + return PaymentsMethodsDB.model_validate(row) async def delete_payment_method( diff --git a/services/web/server/src/simcore_service_webserver/payments/_onetime_db.py b/services/web/server/src/simcore_service_webserver/payments/_onetime_db.py index 9f94d46b707..4cd7e30d328 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_onetime_db.py +++ b/services/web/server/src/simcore_service_webserver/payments/_onetime_db.py @@ -9,7 +9,7 @@ from models_library.products import ProductName from models_library.users import UserID from models_library.wallets import WalletID -from pydantic import BaseModel, HttpUrl, PositiveInt, parse_obj_as +from pydantic import BaseModel, ConfigDict, HttpUrl, PositiveInt, TypeAdapter from simcore_postgres_database.models.payments_transactions import ( PaymentTransactionState, payments_transactions, @@ -27,6 +27,8 @@ _logger = logging.getLogger(__name__) +PaymentIDTypeAdapter: TypeAdapter[PaymentID] = TypeAdapter(PaymentID) + # # NOTE: this will be moved to the payments service # NOTE: with https://sqlmodel.tiangolo.com/ we would only define this once! @@ -44,9 +46,7 @@ class PaymentsTransactionsDB(BaseModel): completed_at: datetime.datetime | None state: PaymentTransactionState state_message: str | None - - class Config: - orm_mode = True + model_config = ConfigDict(from_attributes=True) async def list_user_payment_transactions( @@ -64,7 +64,7 @@ async def list_user_payment_transactions( total_number_of_items, rows = await get_user_payments_transactions( conn, user_id=user_id, offset=offset, limit=limit ) - page = parse_obj_as(list[PaymentsTransactionsDB], rows) + page = TypeAdapter(list[PaymentsTransactionsDB]).validate_python(rows) return total_number_of_items, page @@ -76,7 +76,7 @@ async def get_pending_payment_transactions_ids(app: web.Application) -> list[Pay .order_by(payments_transactions.c.initiated_at.asc()) # oldest first ) rows = await result.fetchall() or [] - return [parse_obj_as(PaymentID, row.payment_id) for row in rows] + return [PaymentIDTypeAdapter.validate_python(row.payment_id) for row in rows] async def complete_payment_transaction( @@ -113,4 +113,4 @@ async def complete_payment_transaction( raise PaymentCompletedError(payment_id=row.payment_id) assert row # nosec - return PaymentsTransactionsDB.from_orm(row) + return PaymentsTransactionsDB.model_validate(row) diff --git a/services/web/server/src/simcore_service_webserver/payments/settings.py b/services/web/server/src/simcore_service_webserver/payments/settings.py index 846e2b1e9f9..db40f5007a9 100644 --- a/services/web/server/src/simcore_service_webserver/payments/settings.py +++ b/services/web/server/src/simcore_service_webserver/payments/settings.py @@ -3,7 +3,14 @@ from aiohttp import web from models_library.basic_types import NonNegativeDecimal -from pydantic import Field, HttpUrl, PositiveInt, SecretStr, parse_obj_as, validator +from pydantic import ( + Field, + HttpUrl, + PositiveInt, + SecretStr, + TypeAdapter, + field_validator, +) from settings_library.base import BaseCustomSettings from settings_library.basic_types import PortInt, VersionTag from settings_library.utils_service import ( @@ -18,7 +25,7 @@ class PaymentsSettings(BaseCustomSettings, MixinServiceSettings): PAYMENTS_HOST: str = "payments" PAYMENTS_PORT: PortInt = DEFAULT_FASTAPI_PORT - PAYMENTS_VTAG: VersionTag = parse_obj_as(VersionTag, "v1") + PAYMENTS_VTAG: VersionTag = TypeAdapter(VersionTag).validate_python("v1") PAYMENTS_USERNAME: str = Field( ..., @@ -42,7 +49,9 @@ class PaymentsSettings(BaseCustomSettings, MixinServiceSettings): ) PAYMENTS_FAKE_GATEWAY_URL: HttpUrl = Field( - default=parse_obj_as(HttpUrl, "https://fake-payment-gateway.com"), + default=TypeAdapter(HttpUrl).validate_python( + "https://fake-payment-gateway.com" + ), description="FAKE Base url to the payment gateway", ) @@ -82,7 +91,7 @@ def base_url(self) -> str: ) return base_url_without_vtag - @validator("PAYMENTS_FAKE_COMPLETION") + @field_validator("PAYMENTS_FAKE_COMPLETION") @classmethod def _payments_cannot_be_faken_in_production(cls, v): if v is True and "production" in os.environ.get("SWARM_STACK_NAME", ""): @@ -90,7 +99,7 @@ def _payments_cannot_be_faken_in_production(cls, v): raise ValueError(msg) return v - @validator("PAYMENTS_AUTORECHARGE_DEFAULT_MONTHLY_LIMIT") + @field_validator("PAYMENTS_AUTORECHARGE_DEFAULT_MONTHLY_LIMIT") @classmethod def _monthly_limit_greater_than_top_up(cls, v, values): top_up = values["PAYMENTS_AUTORECHARGE_DEFAULT_TOP_UP_AMOUNT"] diff --git a/services/web/server/src/simcore_service_webserver/products/_model.py b/services/web/server/src/simcore_service_webserver/products/_model.py index 82c4a3b64aa..a71ca2a201d 100644 --- a/services/web/server/src/simcore_service_webserver/products/_model.py +++ b/services/web/server/src/simcore_service_webserver/products/_model.py @@ -1,9 +1,9 @@ import logging +import re import string from typing import ( # noqa: UP035 # pydantic does not validate with re.Pattern + Annotated, Any, - ClassVar, - Pattern, ) from models_library.basic_regex import ( @@ -14,7 +14,14 @@ from models_library.emails import LowerCaseEmailStr from models_library.products import ProductName from models_library.utils.change_case import snake_to_camel -from pydantic import BaseModel, Extra, Field, PositiveInt, validator +from pydantic import ( + BaseModel, + BeforeValidator, + ConfigDict, + Field, + PositiveInt, + field_validator, +) from simcore_postgres_database.models.products import ( EmailFeedback, Forum, @@ -40,19 +47,20 @@ class Product(BaseModel): SEE descriptions in packages/postgres-database/src/simcore_postgres_database/models/products.py """ - name: ProductName = Field(regex=PUBLIC_VARIABLE_NAME_RE) + name: ProductName = Field(pattern=PUBLIC_VARIABLE_NAME_RE, validate_default=True) display_name: str = Field(..., description="Long display name") short_name: str | None = Field( None, - regex=TWILIO_ALPHANUMERIC_SENDER_ID_RE, + pattern=re.compile(TWILIO_ALPHANUMERIC_SENDER_ID_RE), min_length=2, max_length=11, description="Short display name for SMS", ) - host_regex: Pattern = Field(..., description="Host regex") - # NOTE: typing.Pattern is supported but not re.Pattern (SEE https://github.com/pydantic/pydantic/pull/4366) + host_regex: Annotated[re.Pattern, BeforeValidator(str.strip)] = Field( + ..., description="Host regex" + ) support_email: LowerCaseEmailStr = Field( ..., @@ -82,7 +90,7 @@ class Product(BaseModel): ) registration_email_template: str | None = Field( - None, x_template_name="registration_email" + None, json_schema_extra={"x_template_name": "registration_email"} ) max_open_studies_per_user: PositiveInt | None = Field( @@ -109,7 +117,7 @@ class Product(BaseModel): description="Price of the credits in this product given in credit/USD. None for free product.", ) - @validator("*", pre=True) + @field_validator("*", mode="before") @classmethod def _parse_empty_string_as_null(cls, v): """Safe measure: database entries are sometimes left blank instead of null""" @@ -117,7 +125,7 @@ def _parse_empty_string_as_null(cls, v): return None return v - @validator("name", pre=True, always=True) + @field_validator("name", mode="before") @classmethod def _validate_name(cls, v): if v not in FRONTEND_APPS_AVAILABLE: @@ -125,27 +133,18 @@ def _validate_name(cls, v): raise ValueError(msg) return v - @validator("host_regex", pre=True) - @classmethod - def _strip_whitespaces(cls, v): - if v and isinstance(v, str): - # Prevents unintended leading & trailing spaces when added - # manually in the database - return v.strip() - return v - @property def twilio_alpha_numeric_sender_id(self) -> str: return self.short_name or self.display_name.replace(string.punctuation, "")[:11] - class Config: - alias_generator = snake_to_camel # to export - allow_population_by_field_name = True - anystr_strip_whitespace = True - extra = Extra.ignore - frozen = True # read-only - orm_mode = True - schema_extra: ClassVar[dict[str, Any]] = { + model_config = ConfigDict( + alias_generator=snake_to_camel, + populate_by_name=True, + str_strip_whitespace=True, + frozen=True, + from_attributes=True, + extra="ignore", + json_schema_extra={ "examples": [ { # fake mandatory @@ -234,7 +233,8 @@ class Config: "is_payment_enabled": False, }, ] - } + }, + ) # helpers ---- @@ -247,7 +247,7 @@ def to_statics(self) -> dict[str, Any]: # SECURITY WARNING: do not expose sensitive information here # keys will be named as e.g. displayName, supportEmail, ... - return self.dict( + return self.model_dump( include={ "display_name": True, "support_email": True, @@ -266,8 +266,8 @@ def to_statics(self) -> dict[str, Any]: def get_template_name_for(self, filename: str) -> str | None: """Checks for field marked with 'x_template_name' that fits the argument""" template_name = filename.removesuffix(".jinja2") - for field in self.__fields__.values(): - if field.field_info.extra.get("x_template_name") == template_name: - template_name_attribute: str = getattr(self, field.name) + for name, field in self.model_fields.items(): + if field.json_schema_extra.get("x_template_name") == template_name: + template_name_attribute: str = getattr(self, name) return template_name_attribute return None diff --git a/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py index 5325f389e9a..1cb7e7c2ed1 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py @@ -15,7 +15,7 @@ Page, ) from models_library.rest_pagination_utils import paginate_data -from pydantic import BaseModel, Extra, Field, NonNegativeInt +from pydantic import ConfigDict, BaseModel, Field, NonNegativeInt from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_body_as, @@ -60,24 +60,18 @@ async def wrapper(request: web.Request) -> web.StreamResponse: class _ProjectCommentsPathParams(BaseModel): project_uuid: ProjectID - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") class _ProjectCommentsWithCommentPathParams(BaseModel): project_uuid: ProjectID comment_id: CommentID - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") class _ProjectCommentsBodyParams(BaseModel): contents: str - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") @routes.post( @@ -119,9 +113,7 @@ class _ListProjectCommentsQueryParams(BaseModel): offset: NonNegativeInt = Field( default=0, description="index to the first item to return (pagination)" ) - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") @routes.get(f"/{VTAG}/projects/{{project_uuid}}/comments", name="list_project_comments") diff --git a/services/web/server/src/simcore_service_webserver/projects/_common_models.py b/services/web/server/src/simcore_service_webserver/projects/_common_models.py index d25a0f6c24b..57c02680f72 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_common_models.py +++ b/services/web/server/src/simcore_service_webserver/projects/_common_models.py @@ -6,7 +6,7 @@ from models_library.projects import ProjectID from models_library.users import UserID -from pydantic import BaseModel, Extra, Field +from pydantic import ConfigDict, BaseModel, Field from servicelib.request_keys import RQT_USERID_KEY from .._constants import RQ_PRODUCT_KEY @@ -19,7 +19,4 @@ class RequestContext(BaseModel): class ProjectPathParams(BaseModel): project_id: ProjectID - - class Config: - allow_population_by_field_name = True - extra = Extra.forbid + model_config = ConfigDict(populate_by_name=True, extra="forbid") diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers_models.py b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers_models.py index 2fdef2fb3e2..982ccdee17b 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers_models.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers_models.py @@ -18,13 +18,9 @@ ) from models_library.workspaces import WorkspaceID from pydantic import ( - BaseModel, - Extra, + TypeAdapter, field_validator, ConfigDict, BaseModel, Field, Json, - parse_obj_as, - root_validator, - validator, ) from servicelib.common_headers import ( UNDEFINED_DEFAULT_SIMCORE_USER_AGENT_VALUE, @@ -56,7 +52,7 @@ class ProjectCreateHeaders(BaseModel): alias=X_SIMCORE_PARENT_NODE_ID, ) - @root_validator + @model_validator(mode="before") @classmethod def check_parent_valid(cls, values: dict[str, Any]) -> dict[str, Any]: if ( @@ -69,9 +65,7 @@ def check_parent_valid(cls, values: dict[str, Any]) -> dict[str, Any]: msg = "Both parent_project_uuid and parent_node_id must be set or both null or both unset" raise ValueError(msg) return values - - class Config: - allow_population_by_field_name = False + model_config = ConfigDict(populate_by_name=False) class ProjectCreateParams(BaseModel): @@ -91,9 +85,7 @@ class ProjectCreateParams(BaseModel): default=False, description="Enables/disables hidden flag. Hidden projects are by default unlisted", ) - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") class ProjectListParams(PageQueryParameters): @@ -105,7 +97,7 @@ class ProjectListParams(PageQueryParameters): default=None, description="Multi column full text search", max_length=100, - example="My Project", + examples=["My Project"], ) folder_id: FolderID | None = Field( default=None, @@ -116,19 +108,19 @@ class ProjectListParams(PageQueryParameters): description="Filter projects in specific workspace. Default filtering is a private workspace.", ) - @validator("search", pre=True) + @field_validator("search", mode="before") @classmethod def search_check_empty_string(cls, v): if not v: return None return v - _null_or_none_str_to_none_validator = validator( - "folder_id", allow_reuse=True, pre=True + _null_or_none_str_to_none_validator = field_validator( + "folder_id", mode="before" )(null_or_none_str_to_none_validator) - _null_or_none_str_to_none_validator2 = validator( - "workspace_id", allow_reuse=True, pre=True + _null_or_none_str_to_none_validator2 = field_validator( + "workspace_id", mode="before" )(null_or_none_str_to_none_validator) @@ -136,11 +128,11 @@ class ProjectListWithOrderByParams(BaseModel): order_by: Json[OrderBy] = Field( # pylint: disable=unsubscriptable-object default=OrderBy(field=IDStr("last_change_date"), direction=OrderDirection.DESC), description="Order by field (type|uuid|name|description|prj_owner|creation_date|last_change_date) and direction (asc|desc). The default sorting order is ascending.", - example='{"field": "prj_owner", "direction": "desc"}', + examples=['{"field": "prj_owner", "direction": "desc"}'], alias="order_by", ) - @validator("order_by", check_fields=False) + @field_validator("order_by", check_fields=False) @classmethod def validate_order_by_field(cls, v): if v.field not in { @@ -155,9 +147,7 @@ def validate_order_by_field(cls, v): msg = f"We do not support ordering by provided field {v.field}" raise ValueError(msg) return v - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") class ProjectListWithJsonStrParams(ProjectListParams, ProjectListWithOrderByParams): @@ -173,15 +163,15 @@ class ProjectListFullSearchParams(PageQueryParameters): default=None, description="Multi column full text search, across all folders and workspaces", max_length=100, - example="My Project", + examples=["My Project"], ) tag_ids: str | None = Field( default=None, description="Search by tag ID (multiple tag IDs may be provided separated by column)", - example="1,3", + examples=["1,3"], ) - _empty_is_none = validator("text", allow_reuse=True, pre=True)( + _empty_is_none = field_validator("text", mode="before")( empty_str_to_none_pre_validator ) @@ -195,7 +185,7 @@ def tag_ids_list(self) -> list[int]: if self.tag_ids: tag_ids_list = list(map(int, self.tag_ids.split(","))) # Validate that the tag_ids_list is indeed a list of integers - parse_obj_as(list[int], tag_ids_list) + TypeAdapter(list[int]).validate_python(tag_ids_list) else: tag_ids_list = [] except ValueError as exc: diff --git a/services/web/server/src/simcore_service_webserver/projects/_folders_db.py b/services/web/server/src/simcore_service_webserver/projects/_folders_db.py index 1ac57057c53..59ea8ebe282 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_folders_db.py +++ b/services/web/server/src/simcore_service_webserver/projects/_folders_db.py @@ -11,7 +11,7 @@ from models_library.folders import FolderID from models_library.projects import ProjectID from models_library.users import UserID -from pydantic import BaseModel, parse_obj_as +from pydantic import BaseModel from simcore_postgres_database.models.projects_to_folders import projects_to_folders from sqlalchemy import func, literal_column from sqlalchemy.sql import select @@ -56,7 +56,7 @@ async def insert_project_to_folder( .returning(literal_column("*")) ) row = await result.first() - return parse_obj_as(ProjectToFolderDB, row) + return ProjectToFolderDB.model_validate(row) async def get_project_to_folder( @@ -81,7 +81,7 @@ async def get_project_to_folder( row = await result.first() if row is None: return None - return parse_obj_as(ProjectToFolderDB, row) + return ProjectToFolderDB.model_validate(row) async def delete_project_to_folder( diff --git a/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py index 0e22c5970b9..2e644a4d598 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py @@ -5,7 +5,7 @@ from models_library.folders import FolderID from models_library.projects import ProjectID from models_library.utils.common_validators import null_or_none_str_to_none_validator -from pydantic import BaseModel, Extra, validator +from pydantic import ConfigDict, BaseModel, field_validator from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import parse_request_path_parameters_as from servicelib.aiohttp.typing_extension import Handler @@ -41,13 +41,11 @@ async def wrapper(request: web.Request) -> web.StreamResponse: class _ProjectsFoldersPathParams(BaseModel): project_id: ProjectID folder_id: FolderID | None - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") # validators - _null_or_none_str_to_none_validator = validator( - "folder_id", allow_reuse=True, pre=True + _null_or_none_str_to_none_validator = field_validator( + "folder_id", mode="before" )(null_or_none_str_to_none_validator) @@ -59,7 +57,7 @@ class Config: @permission_required("project.folders.*") @_handle_projects_folders_exceptions async def replace_project_folder(request: web.Request): - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_ProjectsFoldersPathParams, request) await _folders_api.move_project_into_folder( diff --git a/services/web/server/src/simcore_service_webserver/projects/_groups_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_groups_handlers.py index 85c71d0d62d..a747798100e 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_groups_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_groups_handlers.py @@ -8,7 +8,7 @@ from aiohttp import web from models_library.projects import ProjectID from models_library.users import GroupID -from pydantic import BaseModel, Extra +from pydantic import ConfigDict, BaseModel from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_body_as, @@ -53,18 +53,14 @@ async def wrapper(request: web.Request) -> web.StreamResponse: class _ProjectsGroupsPathParams(BaseModel): project_id: ProjectID group_id: GroupID - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") class _ProjectsGroupsBodyParams(BaseModel): read: bool write: bool delete: bool - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") @routes.post( @@ -74,7 +70,7 @@ class Config: @permission_required("project.access_rights.update") @_handle_projects_groups_exceptions async def create_project_group(request: web.Request): - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_ProjectsGroupsPathParams, request) body_params = await parse_request_body_as(_ProjectsGroupsBodyParams, request) @@ -97,7 +93,7 @@ async def create_project_group(request: web.Request): @permission_required("project.read") @_handle_projects_groups_exceptions async def list_project_groups(request: web.Request): - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) project_groups: list[ @@ -120,7 +116,7 @@ async def list_project_groups(request: web.Request): @permission_required("project.access_rights.update") @_handle_projects_groups_exceptions async def replace_project_group(request: web.Request): - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_ProjectsGroupsPathParams, request) body_params = await parse_request_body_as(_ProjectsGroupsBodyParams, request) @@ -144,7 +140,7 @@ async def replace_project_group(request: web.Request): @permission_required("project.access_rights.update") @_handle_projects_groups_exceptions async def delete_project_group(request: web.Request): - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_ProjectsGroupsPathParams, request) await _groups_api.delete_project_group( diff --git a/services/web/server/src/simcore_service_webserver/projects/_nodes_api.py b/services/web/server/src/simcore_service_webserver/projects/_nodes_api.py index ab6ba4b7d93..7bc4f824738 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_nodes_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/_nodes_api.py @@ -14,15 +14,13 @@ from models_library.projects_nodes_io import NodeID, SimCoreFileLink from models_library.users import UserID from pydantic import ( - BaseModel, + model_validator, BaseModel, Field, HttpUrl, NonNegativeFloat, NonNegativeInt, ValidationError, - parse_obj_as, - root_validator, -) + parse_obj_as) from servicelib.utils import logged_gather from ..application_settings import get_application_settings @@ -96,10 +94,10 @@ class NodeScreenshot(BaseModel): mimetype: str | None = Field( default=None, description="File's media type or None if unknown. SEE https://www.iana.org/assignments/media-types/media-types.xhtml", - example="image/jpeg", + examples=["image/jpeg"], ) - @root_validator(pre=True) + @model_validator(mode="before") @classmethod def guess_mimetype_if_undefined(cls, values): mimetype = values.get("mimetype") diff --git a/services/web/server/src/simcore_service_webserver/projects/_ports_api.py b/services/web/server/src/simcore_service_webserver/projects/_ports_api.py index 95a42f32046..7a24dec5923 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_ports_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/_ports_api.py @@ -24,7 +24,7 @@ jsonschema_validate_data, ) from models_library.utils.services_io import JsonSchemaDict, get_service_io_json_schema -from pydantic import ValidationError +from pydantic import ConfigDict, ValidationError from ..director_v2.api import get_batch_tasks_outputs from .exceptions import InvalidInputValue @@ -163,8 +163,9 @@ def set_inputs_in_project( class _NonStrictPortLink(PortLink): - class Config(PortLink.Config): - allow_population_by_field_name = True + model_config = ConfigDict( + populate_by_name=True + ) class _OutputPortInfo(NamedTuple): diff --git a/services/web/server/src/simcore_service_webserver/projects/_projects_nodes_pricing_unit_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_projects_nodes_pricing_unit_handlers.py index 552869a0404..1e7734f3b1c 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_projects_nodes_pricing_unit_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_projects_nodes_pricing_unit_handlers.py @@ -10,7 +10,7 @@ from models_library.projects import ProjectID from models_library.projects_nodes_io import NodeID, NodeIDStr from models_library.resource_tracker import PricingPlanId, PricingUnitId -from pydantic import BaseModel, Extra +from pydantic import ConfigDict, BaseModel from pydantic.errors import PydanticErrorMixin from servicelib.aiohttp.requests_validation import parse_request_path_parameters_as from servicelib.aiohttp.typing_extension import Handler @@ -99,9 +99,7 @@ class _ProjectNodePricingUnitPathParams(BaseModel): node_id: NodeID pricing_plan_id: PricingPlanId pricing_unit_id: PricingUnitId - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") @routes.put( diff --git a/services/web/server/src/simcore_service_webserver/projects/_wallets_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_wallets_handlers.py index 04c6fd3f218..e4b3293e465 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_wallets_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_wallets_handlers.py @@ -9,7 +9,7 @@ from models_library.api_schemas_webserver.wallets import WalletGet from models_library.projects import ProjectID from models_library.wallets import WalletID -from pydantic import BaseModel, Extra +from pydantic import ConfigDict, BaseModel from servicelib.aiohttp.requests_validation import parse_request_path_parameters_as from servicelib.aiohttp.typing_extension import Handler from simcore_service_webserver.utils_aiohttp import envelope_json_response @@ -69,9 +69,7 @@ async def get_project_wallet(request: web.Request): class _ProjectWalletPathParams(BaseModel): project_id: ProjectID wallet_id: WalletID - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") @routes.put( diff --git a/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py index 6b553a6d3ba..89447a81f5b 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py @@ -5,7 +5,7 @@ from models_library.projects import ProjectID from models_library.utils.common_validators import null_or_none_str_to_none_validator from models_library.workspaces import WorkspaceID -from pydantic import BaseModel, Extra, validator +from pydantic import ConfigDict, BaseModel, field_validator from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import parse_request_path_parameters_as from servicelib.aiohttp.typing_extension import Handler @@ -50,14 +50,12 @@ async def wrapper(request: web.Request) -> web.StreamResponse: class _ProjectWorkspacesPathParams(BaseModel): project_id: ProjectID - workspace_id: WorkspaceID | None - - class Config: - extra = Extra.forbid + workspace_id: WorkspaceID | None = None + model_config = ConfigDict(extra="forbid") # validators - _null_or_none_str_to_none_validator = validator( - "workspace_id", allow_reuse=True, pre=True + _null_or_none_str_to_none_validator = field_validator( + "workspace_id", mode="before" )(null_or_none_str_to_none_validator) diff --git a/services/web/server/src/simcore_service_webserver/projects/models.py b/services/web/server/src/simcore_service_webserver/projects/models.py index 8f4a13c172b..3445e68a3bd 100644 --- a/services/web/server/src/simcore_service_webserver/projects/models.py +++ b/services/web/server/src/simcore_service_webserver/projects/models.py @@ -13,7 +13,7 @@ none_to_empty_str_pre_validator, ) from models_library.workspaces import WorkspaceID -from pydantic import BaseModel, validator +from pydantic import ConfigDict, BaseModel, field_validator from simcore_postgres_database.models.projects import ProjectType, projects ProjectDict: TypeAlias = dict[str, Any] @@ -40,38 +40,34 @@ class ProjectDB(BaseModel): uuid: ProjectID name: str description: str - thumbnail: HttpUrlWithCustomMinLength | None + thumbnail: HttpUrlWithCustomMinLength | None = None prj_owner: UserID creation_date: datetime last_change_date: datetime - ui: StudyUI | None + ui: StudyUI | None = None classifiers: list[ClassifierID] - dev: dict | None + dev: dict | None = None quality: dict[str, Any] published: bool hidden: bool - workspace_id: WorkspaceID | None - - class Config: - orm_mode = True + workspace_id: WorkspaceID | None = None + model_config = ConfigDict(from_attributes=True) # validators - _empty_thumbnail_is_none = validator("thumbnail", allow_reuse=True, pre=True)( + _empty_thumbnail_is_none = field_validator("thumbnail", mode="before")( empty_str_to_none_pre_validator ) - _none_description_is_empty = validator("description", allow_reuse=True, pre=True)( + _none_description_is_empty = field_validator("description", mode="before")( none_to_empty_str_pre_validator ) class UserSpecificProjectDataDB(ProjectDB): - folder_id: FolderID | None - - class Config: - orm_mode = True + folder_id: FolderID | None = None + model_config = ConfigDict(from_attributes=True) -assert set(ProjectDB.__fields__.keys()).issubset( # nosec +assert set(ProjectDB.model_fields.keys()).issubset( # nosec {c.name for c in projects.columns if c.name not in ["access_rights"]} ) @@ -81,20 +77,16 @@ class UserProjectAccessRightsDB(BaseModel): read: bool write: bool delete: bool - - class Config: - orm_mode = True + model_config = ConfigDict(from_attributes=True) class UserProjectAccessRightsWithWorkspace(BaseModel): uid: UserID - workspace_id: WorkspaceID | None # None if it's a private workspace + workspace_id: WorkspaceID | None = None # None if it's a private workspace read: bool write: bool delete: bool - - class Config: - orm_mode = True + model_config = ConfigDict(from_attributes=True) __all__: tuple[str, ...] = ( diff --git a/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_admin_handlers.py b/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_admin_handlers.py index b71317b1aab..bbb435060ac 100644 --- a/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_admin_handlers.py +++ b/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_admin_handlers.py @@ -20,7 +20,7 @@ PricingUnitWithCostUpdate, ) from models_library.users import UserID -from pydantic import BaseModel, Extra, Field +from pydantic import ConfigDict, BaseModel, Field from servicelib.aiohttp.requests_validation import ( parse_request_body_as, parse_request_path_parameters_as, @@ -72,9 +72,7 @@ class _RequestContext(BaseModel): class _GetPricingPlanPathParams(BaseModel): pricing_plan_id: PricingPlanId - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") @routes.get( @@ -256,9 +254,7 @@ async def update_pricing_plan(request: web.Request): class _GetPricingUnitPathParams(BaseModel): pricing_plan_id: PricingPlanId pricing_unit_id: PricingUnitId - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") @routes.get( diff --git a/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_handlers.py b/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_handlers.py index 86072f00e5e..5092262a96f 100644 --- a/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_handlers.py +++ b/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_handlers.py @@ -4,7 +4,7 @@ from models_library.api_schemas_webserver.resource_usage import PricingUnitGet from models_library.resource_tracker import PricingPlanId, PricingUnitId from models_library.users import UserID -from pydantic import BaseModel, Extra, Field +from pydantic import ConfigDict, BaseModel, Field from servicelib.aiohttp.requests_validation import parse_request_path_parameters_as from servicelib.aiohttp.typing_extension import Handler from servicelib.request_keys import RQT_USERID_KEY @@ -49,9 +49,7 @@ class _RequestContext(BaseModel): class _GetPricingPlanUnitPathParams(BaseModel): pricing_plan_id: PricingPlanId pricing_unit_id: PricingUnitId - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") @routes.get( diff --git a/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py b/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py index f265e45faf1..8ddc9804dbc 100644 --- a/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py +++ b/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py @@ -23,14 +23,11 @@ from models_library.users import UserID from models_library.wallets import WalletID from pydantic import ( - BaseModel, - Extra, + field_validator, ConfigDict, BaseModel, Field, Json, NonNegativeInt, - parse_obj_as, - validator, -) + parse_obj_as) from servicelib.aiohttp.requests_validation import parse_request_query_parameters_as from servicelib.aiohttp.typing_extension import Handler from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON @@ -74,7 +71,7 @@ class _ListServicesResourceUsagesQueryParams(BaseModel): order_by: Json[OrderBy] = Field( # pylint: disable=unsubscriptable-object default=OrderBy(field=IDStr("started_at"), direction=OrderDirection.DESC), description=ORDER_BY_DESCRIPTION, - example='{"field": "started_at", "direction": "desc"}', + examples=['{"field": "started_at", "direction": "desc"}'], ) filters: ( Json[ServiceResourceUsagesFilters] # pylint: disable=unsubscriptable-object @@ -82,10 +79,10 @@ class _ListServicesResourceUsagesQueryParams(BaseModel): ) = Field( default=None, description="Filters to process on the resource usages list, encoded as JSON. Currently supports the filtering of 'started_at' field with 'from' and 'until' parameters in ISO 8601 format. The date range specified is inclusive.", - example='{"started_at": {"from": "yyyy-mm-dd", "until": "yyyy-mm-dd"}}', + examples=['{"started_at": {"from": "yyyy-mm-dd", "until": "yyyy-mm-dd"}}'], ) - @validator("order_by", allow_reuse=True) + @field_validator("order_by") @classmethod def validate_order_by_field(cls, v): if v.field not in { @@ -112,9 +109,7 @@ def validate_order_by_field(cls, v): if v.field == "credit_cost": v.field = "osparc_credits" return v - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") class _ListServicesResourceUsagesQueryParamsWithPagination( @@ -129,18 +124,14 @@ class _ListServicesResourceUsagesQueryParamsWithPagination( offset: NonNegativeInt = Field( default=0, description="index to the first item to return (pagination)" ) - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") class _ListServicesAggregatedUsagesQueryParams(PageQueryParameters): aggregated_by: ServicesAggregatedUsagesType time_period: ServicesAggregatedUsagesTimePeriod wallet_id: WalletID - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") # diff --git a/services/web/server/src/simcore_service_webserver/scicrunch/models.py b/services/web/server/src/simcore_service_webserver/scicrunch/models.py index 743f4bd8211..2140f88ea33 100644 --- a/services/web/server/src/simcore_service_webserver/scicrunch/models.py +++ b/services/web/server/src/simcore_service_webserver/scicrunch/models.py @@ -6,7 +6,7 @@ import re from datetime import datetime -from pydantic import BaseModel, Field, validator +from pydantic import field_validator, ConfigDict, BaseModel, Field logger = logging.getLogger(__name__) @@ -58,19 +58,16 @@ class ResearchResource(BaseModel): rrid: str = Field( ..., description="Unique identifier used as classifier, i.e. to tag studies and services", - regex=STRICT_RRID_PATTERN, + pattern=STRICT_RRID_PATTERN, ) name: str description: str - @validator("rrid", pre=True) + @field_validator("rrid", mode="before") @classmethod def format_rrid(cls, v): return normalize_rrid_tags(v, with_prefix=True) - - class Config: - orm_mode = True - anystr_strip_whitespace = True + model_config = ConfigDict(from_attributes=True, str_strip_whitespace=True) # postgres_database.scicrunch_resources ORM -------------------- diff --git a/services/web/server/src/simcore_service_webserver/session/settings.py b/services/web/server/src/simcore_service_webserver/session/settings.py index b5f3c333fa8..e02b1ee3f55 100644 --- a/services/web/server/src/simcore_service_webserver/session/settings.py +++ b/services/web/server/src/simcore_service_webserver/session/settings.py @@ -1,8 +1,8 @@ from typing import Final from aiohttp import web -from pydantic import PositiveInt -from pydantic.class_validators import validator +from pydantic import AliasChoices, field_validator, PositiveInt +from pydantic.class_validators import field_validator from pydantic.fields import Field from pydantic.types import SecretStr from settings_library.base import BaseCustomSettings @@ -22,7 +22,7 @@ class SessionSettings(BaseCustomSettings, MixinSessionSettings): description="Secret key to encrypt cookies. " 'TIP: python3 -c "from cryptography.fernet import *; print(Fernet.generate_key())"', min_length=44, - env=["SESSION_SECRET_KEY", "WEBSERVER_SESSION_SECRET_KEY"], + validation_alias=AliasChoices("SESSION_SECRET_KEY", "WEBSERVER_SESSION_SECRET_KEY"), ) SESSION_ACCESS_TOKENS_EXPIRATION_INTERVAL_SECS: int = Field( @@ -53,12 +53,12 @@ class SessionSettings(BaseCustomSettings, MixinSessionSettings): description="This prevents JavaScript from accessing the session cookie", ) - @validator("SESSION_SECRET_KEY") + @field_validator("SESSION_SECRET_KEY") @classmethod def check_valid_fernet_key(cls, v): return cls.do_check_valid_fernet_key(v) - @validator("SESSION_COOKIE_SAMESITE") + @field_validator("SESSION_COOKIE_SAMESITE") @classmethod def check_valid_samesite_attribute(cls, v): # NOTE: Replacement to `Literal["Strict", "Lax"] | None` due to bug in settings_library/base.py:93: in prepare_field diff --git a/services/web/server/src/simcore_service_webserver/socketio/models.py b/services/web/server/src/simcore_service_webserver/socketio/models.py index 06e5b9014cb..5c348dff159 100644 --- a/services/web/server/src/simcore_service_webserver/socketio/models.py +++ b/services/web/server/src/simcore_service_webserver/socketio/models.py @@ -12,23 +12,21 @@ from models_library.socketio import SocketMessageDict from models_library.users import UserID from models_library.utils.fastapi_encoders import jsonable_encoder -from pydantic import BaseModel, Field +from pydantic import ConfigDict, BaseModel class WebSocketMessageBase(BaseModel): - event_type: str = Field(..., const=True) + event_type: Literal[...] = ... @classmethod def get_event_type(cls) -> str: - _event_type: str = cls.__fields__["event_type"].default + _event_type: str = cls.model_fields["event_type"].default return _event_type @abstractmethod def to_socket_dict(self) -> SocketMessageDict: ... - - class Config: - frozen = True + model_config = ConfigDict(frozen=True) class _WebSocketProjectMixin(BaseModel): @@ -87,7 +85,7 @@ class WebSocketNodeProgress( def from_rabbit_message( cls, message: ProgressRabbitMessageNode ) -> "WebSocketNodeProgress": - return cls.construct( + return cls.model_construct( user_id=message.user_id, project_id=message.project_id, node_id=message.node_id, diff --git a/services/web/server/src/simcore_service_webserver/statics/settings.py b/services/web/server/src/simcore_service_webserver/statics/settings.py index 275def8154b..ed01702170a 100644 --- a/services/web/server/src/simcore_service_webserver/statics/settings.py +++ b/services/web/server/src/simcore_service_webserver/statics/settings.py @@ -7,7 +7,7 @@ import pycountry from aiohttp import web from models_library.utils.change_case import snake_to_camel -from pydantic import AnyHttpUrl, Field, parse_obj_as +from pydantic import AnyHttpUrl, Field, TypeAdapter from settings_library.base import BaseCustomSettings from .._constants import APP_SETTINGS_KEY @@ -121,12 +121,12 @@ def to_statics(self) -> dict[str, Any]: class StaticWebserverModuleSettings(BaseCustomSettings): STATIC_WEBSERVER_URL: AnyHttpUrl = Field( - default=parse_obj_as(AnyHttpUrl, "http://static-webserver:8000"), + default=TypeAdapter(AnyHttpUrl).validate_python("http://static-webserver:8000"), description="url fort static content", - env=[ + validation_alias=AliasChoices( "STATIC_WEBSERVER_URL", "WEBSERVER_STATIC_MODULE_STATIC_WEB_SERVER_URL", # legacy - ], + ), ) diff --git a/services/web/server/src/simcore_service_webserver/storage/schemas.py b/services/web/server/src/simcore_service_webserver/storage/schemas.py index 4c47c99a8ff..b5c71beaa09 100644 --- a/services/web/server/src/simcore_service_webserver/storage/schemas.py +++ b/services/web/server/src/simcore_service_webserver/storage/schemas.py @@ -1,8 +1,8 @@ from enum import Enum -from typing import Any, ClassVar, TypeAlias +from typing import Any, TypeAlias from models_library.api_schemas_storage import TableSynchronisation -from pydantic import BaseModel, Field +from pydantic import ConfigDict, BaseModel, Field # NOTE: storage generates URLs that contain double encoded # slashes, and when applying validation via `StorageFileID` @@ -14,14 +14,14 @@ class FileLocation(BaseModel): name: str | None = None id: float | None = None - - class Config: - schema_extra: ClassVar[dict[str, Any]] = { + model_config = ConfigDict( + json_schema_extra={ "example": { "name": "simcore.s3", "id": 0, }, } + ) class FileLocationArray(BaseModel): @@ -60,14 +60,14 @@ class FileUploadCompleteFuture(BaseModel): class DatasetMetaData(BaseModel): dataset_id: str | None = None display_name: str | None = None - - class Config: - schema_extra: ClassVar[dict[str, Any]] = { + model_config = ConfigDict( + json_schema_extra={ "example": { "dataset_id": "N:id-aaaa", "display_name": "simcore-testing", }, } + ) class DatasetMetaDataArray(BaseModel): @@ -96,7 +96,7 @@ class FileUploadCompleteEnveloped(BaseModel): class FileUploadCompleteFutureEnveloped(BaseModel): data: FileUploadCompleteFuture - error: Any + error: Any = None class DatasetMetaEnvelope(BaseModel): @@ -122,8 +122,8 @@ class FileMetaData(BaseModel): entity_tag: str | None = None is_directory: bool | None = None - class Config: - schema_extra: ClassVar[dict[str, Any]] = { + model_config = ConfigDict( + json_schema_extra={ "example": { "file_uuid": "simcore-testing/105/1000/3", "location_id": "0", @@ -138,6 +138,7 @@ class Config: "is_directory": False, } } + ) class FileMetaDataArray(BaseModel): @@ -152,9 +153,9 @@ class FileMetaEnvelope(BaseModel): class PresignedLink(BaseModel): link: str | None = None - class Config: - schema_extra: ClassVar[dict[str, Any]] = {"example": {"link": "example_link"}} - + model_config = ConfigDict( + json_schema_extra={"example": {"link": "example_link"}} + ) class PresignedLinkEnveloped(BaseModel): data: PresignedLink diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_redirects_handlers.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_redirects_handlers.py index 05757c80468..988dd0c8831 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_redirects_handlers.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_redirects_handlers.py @@ -12,7 +12,7 @@ from models_library.projects import ProjectID from models_library.projects_nodes_io import NodeID from models_library.services import ServiceKey, ServiceVersion -from pydantic import BaseModel, Extra, ValidationError, validator +from pydantic import field_validator, ConfigDict, BaseModel, ValidationError from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import parse_request_query_parameters_as from servicelib.aiohttp.typing_extension import Handler @@ -153,15 +153,13 @@ async def wrapper(request: web.Request) -> web.StreamResponse: class ServiceQueryParams(ServiceParams): - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") class FileQueryParams(FileParams): - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") - @validator("file_type") + @field_validator("file_type") @classmethod def ensure_extension_upper_and_dotless(cls, v): # NOTE: see filetype constraint-check @@ -172,14 +170,13 @@ def ensure_extension_upper_and_dotless(cls, v): class ServiceAndFileParams(FileQueryParams, ServiceParams): - class Config: - # Optional configuration to exclude duplicates from schema - schema_extra = { + model_config = ConfigDict( + json_schema_extra={ "allOf": [ {"$ref": "#/definitions/FileParams"}, {"$ref": "#/definitions/ServiceParams"}, ] - } + }) class ViewerQueryParams(BaseModel): @@ -190,13 +187,13 @@ class ViewerQueryParams(BaseModel): @staticmethod def from_viewer(viewer: ViewerInfo) -> "ViewerQueryParams": # can safely construct w/o validation from a viewer - return ViewerQueryParams.construct( + return ViewerQueryParams.model_construct( file_type=viewer.filetype, viewer_key=viewer.key, viewer_version=viewer.version, ) - @validator("file_type") + @field_validator("file_type") @classmethod def ensure_extension_upper_and_dotless(cls, v): # NOTE: see filetype constraint-check diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_rest_handlers.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_rest_handlers.py index 9f66cd460b0..98616e47f68 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_rest_handlers.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_rest_handlers.py @@ -3,13 +3,12 @@ """ import logging from dataclasses import asdict -from typing import Any, ClassVar from aiohttp import web from aiohttp.web import Request from models_library.services import ServiceKey from models_library.services_types import ServiceVersion -from pydantic import BaseModel, Field, ValidationError, parse_obj_as, validator +from pydantic import TypeAdapter, field_validator, ConfigDict, BaseModel, Field, ValidationError from pydantic.networks import HttpUrl from .._meta import API_VTAG @@ -32,11 +31,11 @@ def _compose_file_and_service_dispatcher_prefix_url( request: web.Request, viewer: ViewerInfo ) -> HttpUrl: """This is denoted PREFIX URL because it needs to append extra query parameters""" - params = ViewerQueryParams.from_viewer(viewer).dict() + params = ViewerQueryParams.from_viewer(viewer).model_dump() absolute_url = request.url.join( request.app.router["get_redirection_to_viewer"].url_for().with_query(**params) ) - absolute_url_: HttpUrl = parse_obj_as(HttpUrl, f"{absolute_url}") + absolute_url_: HttpUrl = TypeAdapter(HttpUrl).validate_python(f"{absolute_url}") return absolute_url_ @@ -50,7 +49,7 @@ def _compose_service_only_dispatcher_prefix_url( absolute_url = request.url.join( request.app.router["get_redirection_to_viewer"].url_for().with_query(**params) ) - absolute_url_: HttpUrl = parse_obj_as(HttpUrl, f"{absolute_url}") + absolute_url_: HttpUrl = TypeAdapter(HttpUrl).validate_python(f"{absolute_url}") return absolute_url_ @@ -125,15 +124,14 @@ def create(cls, meta: ServiceMetaData, request: web.Request): **asdict(meta), ) - @validator("file_extensions") + @field_validator("file_extensions") @classmethod def remove_dot_prefix_from_extension(cls, v): if v: return [ext.removeprefix(".") for ext in v] return v - - class Config: - schema_extra: ClassVar[dict[str, Any]] = { + model_config = ConfigDict( + json_schema_extra={ "example": { "key": "simcore/services/dynamic/sim4life", "title": "Sim4Life Mattermost", @@ -143,6 +141,7 @@ class Config: "view_url": "https://host.com/view?viewer_key=simcore/services/dynamic/raw-graphs&viewer_version=1.2.3", } } + ) # diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/settings.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/settings.py index a79c4865f12..d1a448777c7 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/settings.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/settings.py @@ -3,7 +3,7 @@ from aiohttp import web from common_library.pydantic_validators import validate_numeric_string_as_timedelta -from pydantic import ByteSize, HttpUrl, parse_obj_as, validator +from pydantic import TypeAdapter, field_validator, ConfigDict, ByteSize, HttpUrl from pydantic.fields import Field from servicelib.aiohttp.application_keys import APP_SETTINGS_KEY from settings_library.base import BaseCustomSettings @@ -22,22 +22,22 @@ class StudiesDispatcherSettings(BaseCustomSettings): ) STUDIES_DEFAULT_SERVICE_THUMBNAIL: HttpUrl = Field( - default=parse_obj_as(HttpUrl, "https://via.placeholder.com/170x120.png"), + default=TypeAdapter(HttpUrl).validate_python("https://via.placeholder.com/170x120.png"), description="Default thumbnail for services or dispatch project with a service", ) STUDIES_DEFAULT_FILE_THUMBNAIL: HttpUrl = Field( - default=parse_obj_as(HttpUrl, "https://via.placeholder.com/170x120.png"), + default=TypeAdapter(HttpUrl).validate_python("https://via.placeholder.com/170x120.png"), description="Default thumbnail for dispatch projects with only data (i.e. file-picker)", ) STUDIES_MAX_FILE_SIZE_ALLOWED: ByteSize = Field( - default=parse_obj_as(ByteSize, "50Mib"), + default=TypeAdapter(ByteSize).validate_python("50Mib"), description="Limits the size of the files that can be dispatched" "Note that the accuracy of the file size is not guaranteed and this limit might be surpassed", ) - @validator("STUDIES_GUEST_ACCOUNT_LIFETIME") + @field_validator("STUDIES_GUEST_ACCOUNT_LIFETIME") @classmethod def _is_positive_lifetime(cls, v): if v and isinstance(v, timedelta) and v.total_seconds() <= 0: @@ -54,14 +54,14 @@ def is_login_required(self): _validate_studies_guest_account_lifetime = validate_numeric_string_as_timedelta( "STUDIES_GUEST_ACCOUNT_LIFETIME" ) - - class Config: - schema_extra: ClassVar[dict[str, Any]] = { + model_config = ConfigDict( + json_schema_extra={ "example": { "STUDIES_GUEST_ACCOUNT_LIFETIME": "2 1:10:00", # 2 days 1h and 10 mins "STUDIES_ACCESS_ANONYMOUS_ALLOWED": "1", }, } + ) def get_plugin_settings(app: web.Application) -> StudiesDispatcherSettings: diff --git a/services/web/server/src/simcore_service_webserver/users/_notifications.py b/services/web/server/src/simcore_service_webserver/users/_notifications.py index 256e521f89c..39e6fda9208 100644 --- a/services/web/server/src/simcore_service_webserver/users/_notifications.py +++ b/services/web/server/src/simcore_service_webserver/users/_notifications.py @@ -6,7 +6,7 @@ from models_library.products import ProductName from models_library.users import UserID from models_library.utils.enums import StrAutoEnum -from pydantic import BaseModel, NonNegativeInt, validator +from pydantic import field_validator, ConfigDict, BaseModel, NonNegativeInt MAX_NOTIFICATIONS_FOR_USER_TO_SHOW: Final[NonNegativeInt] = 10 MAX_NOTIFICATIONS_FOR_USER_TO_KEEP: Final[NonNegativeInt] = 100 @@ -33,7 +33,7 @@ class BaseUserNotification(BaseModel): date: datetime product: Literal["UNDEFINED"] | ProductName = "UNDEFINED" - @validator("category", pre=True) + @field_validator("category", mode="before") @classmethod def category_to_upper(cls, value: str) -> str: return value.upper() @@ -58,10 +58,9 @@ class UserNotification(BaseUserNotification): def create_from_request_data( cls, request_data: UserNotificationCreate ) -> "UserNotification": - return cls.construct(id=f"{uuid4()}", read=False, **request_data.dict()) - - class Config: - schema_extra: ClassVar[dict[str, Any]] = { + return cls.model_construct(id=f"{uuid4()}", read=False, **request_data.model_dump()) + model_config = ConfigDict( + json_schema_extra={ "examples": [ { "id": "3fb96d89-ff5d-4d27-b5aa-d20d46e20eb8", @@ -120,3 +119,4 @@ class Config: }, ] } + ) diff --git a/services/web/server/src/simcore_service_webserver/users/_schemas.py b/services/web/server/src/simcore_service_webserver/users/_schemas.py index 1dd4f59992f..19aa761a124 100644 --- a/services/web/server/src/simcore_service_webserver/users/_schemas.py +++ b/services/web/server/src/simcore_service_webserver/users/_schemas.py @@ -12,7 +12,7 @@ from models_library.api_schemas_webserver._base import InputSchema, OutputSchema from models_library.emails import LowerCaseEmailStr from models_library.products import ProductName -from pydantic import Field, root_validator, validator +from pydantic import ConfigDict, field_validator, model_validator, Field, field_validator from simcore_postgres_database.models.users import UserStatus @@ -43,7 +43,7 @@ class UserProfile(OutputSchema): description="List of products this users is included or None if fields is unset", ) - @validator("status") + @field_validator("status") @classmethod def _consistency_check(cls, v, values): registered = values["registered"] @@ -74,11 +74,12 @@ class PreUserProfile(InputSchema): description="Keeps extra information provided in the request form. At most MAX_NUM_EXTRAS fields", ) - class Config(InputSchema.Config): - anystr_strip_whitespace = True - max_anystr_length = 200 + model_config = ConfigDict( + str_strip_whitespace=True, + str_max_length=200 + ) - @root_validator(pre=True) + @model_validator(mode="before") @classmethod def _preprocess_aliases_and_extras(cls, values): # multiple aliases for "institution" @@ -111,7 +112,7 @@ def _preprocess_aliases_and_extras(cls, values): return values - @validator("first_name", "last_name", "institution", pre=True) + @field_validator("first_name", "last_name", "institution", mode="before") @classmethod def _pre_normalize_given_names(cls, v): if v: @@ -120,7 +121,7 @@ def _pre_normalize_given_names(cls, v): return re.sub(r"\b\w+\b", lambda m: m.group(0).capitalize(), name) return v - @validator("country", pre=True) + @field_validator("country", mode="before") @classmethod def _pre_check_and_normalize_country(cls, v): if v: @@ -131,4 +132,4 @@ def _pre_check_and_normalize_country(cls, v): return v -assert set(PreUserProfile.__fields__).issubset(UserProfile.__fields__) # nosec +assert set(PreUserProfile.model_fields).issubset(UserProfile.model_fields) # nosec diff --git a/services/web/server/src/simcore_service_webserver/users/schemas.py b/services/web/server/src/simcore_service_webserver/users/schemas.py index 53c9ee9b756..79a63cfa77a 100644 --- a/services/web/server/src/simcore_service_webserver/users/schemas.py +++ b/services/web/server/src/simcore_service_webserver/users/schemas.py @@ -7,8 +7,7 @@ from models_library.api_schemas_webserver.users_preferences import AggregatedPreferences from models_library.emails import LowerCaseEmailStr from models_library.users import FirstNameStr, LastNameStr, UserID -from models_library.utils.json_serialization import json_dumps -from pydantic import BaseModel, Field, root_validator, validator +from pydantic import field_validator, model_validator, ConfigDict, BaseModel, Field from simcore_postgres_database.models.users import UserRole from ..utils import gravatar_hash @@ -28,13 +27,14 @@ class ThirdPartyToken(BaseModel): token_key: UUID = Field(..., description="basic token key") token_secret: UUID | None = None - class Config: - schema_extra: ClassVar[dict[str, Any]] = { + model_config = ConfigDict( + json_schema_extra={ "example": { "service": "github-api-v1", "token_key": "5f21abf5-c596-47b7-bfd1-c0e436ef1107", } } + ) class TokenCreate(ThirdPartyToken): @@ -50,13 +50,14 @@ class ProfileUpdate(BaseModel): first_name: FirstNameStr | None = None last_name: LastNameStr | None = None - class Config: - schema_extra: ClassVar[dict[str, Any]] = { + model_config = ConfigDict( + json_schema_extra={ "example": { "first_name": "Pedro", "last_name": "Crespo", } } + ) class ProfileGet(BaseModel): @@ -74,13 +75,11 @@ class ProfileGet(BaseModel): ) preferences: AggregatedPreferences - class Config: + model_config = ConfigDict( # NOTE: old models have an hybrid between snake and camel cases! # Should be unified at some point - allow_population_by_field_name = True - json_dumps = json_dumps - - schema_extra: ClassVar[dict[str, Any]] = { + populate_by_name=True, + json_schema_extra={ "examples": [ { "id": 1, @@ -98,8 +97,9 @@ class Config: }, ] } + ) - @root_validator(pre=True) + @model_validator(mode="before") @classmethod def _auto_generate_gravatar(cls, values): gravatar_id = values.get("gravatar_id") @@ -108,7 +108,7 @@ def _auto_generate_gravatar(cls, values): values["gravatar_id"] = gravatar_hash(email) return values - @validator("role", pre=True) + @field_validator("role", mode="before") @classmethod def _to_upper_string(cls, v): if isinstance(v, str): diff --git a/services/web/server/src/simcore_service_webserver/utils_aiohttp.py b/services/web/server/src/simcore_service_webserver/utils_aiohttp.py index ae35a58ee6f..82179deb5a2 100644 --- a/services/web/server/src/simcore_service_webserver/utils_aiohttp.py +++ b/services/web/server/src/simcore_service_webserver/utils_aiohttp.py @@ -9,7 +9,6 @@ from models_library.generics import Envelope from models_library.utils.json_serialization import json_dumps from pydantic import BaseModel, Field -from pydantic.generics import GenericModel from servicelib.common_headers import X_FORWARDED_PROTO from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from servicelib.rest_constants import RESPONSE_MODEL_POLICY @@ -71,7 +70,7 @@ def envelope_json_response( enveloped = Envelope[Any](data=obj) return web.Response( - text=json_dumps(enveloped.dict(**RESPONSE_MODEL_POLICY)), + text=json_dumps(enveloped.model_dump(**RESPONSE_MODEL_POLICY)), content_type=MIMETYPE_APPLICATION_JSON, status=status_cls.status_code, ) @@ -116,7 +115,7 @@ def create_redirect_to_page_response( PageParameters = TypeVar("PageParameters", bound=BaseModel) -class NextPage(GenericModel, Generic[PageParameters]): +class NextPage(BaseModel, Generic[PageParameters]): """ This is the body of a 2XX response to pass the front-end what kind of page shall be display next and some information about it diff --git a/services/web/server/src/simcore_service_webserver/version_control/_handlers.py b/services/web/server/src/simcore_service_webserver/version_control/_handlers.py index 0cf849effb0..595bcb0dcdc 100644 --- a/services/web/server/src/simcore_service_webserver/version_control/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/version_control/_handlers.py @@ -4,7 +4,7 @@ from models_library.projects import ProjectID from models_library.rest_pagination import Page, PageQueryParameters from models_library.rest_pagination_utils import paginate_data -from pydantic import BaseModel, validator +from pydantic import field_validator, BaseModel from servicelib.aiohttp.requests_validation import ( parse_request_body_as, parse_request_path_parameters_as, @@ -46,7 +46,7 @@ class _CheckpointsPathParam(BaseModel): project_uuid: ProjectID ref_id: RefID - @validator("ref_id", pre=True) + @field_validator("ref_id", mode="before") @classmethod def _normalize_refid(cls, v): if v and v == "HEAD": diff --git a/services/web/server/src/simcore_service_webserver/version_control/models.py b/services/web/server/src/simcore_service_webserver/version_control/models.py index a562459547e..de0e74bc637 100644 --- a/services/web/server/src/simcore_service_webserver/version_control/models.py +++ b/services/web/server/src/simcore_service_webserver/version_control/models.py @@ -5,7 +5,7 @@ from models_library.basic_types import SHA1Str from models_library.projects import ProjectID from models_library.projects_nodes import Node -from pydantic import BaseModel, Field, PositiveInt, StrictBool, StrictFloat, StrictInt +from pydantic import ConfigDict, BaseModel, Field, PositiveInt, StrictBool, StrictFloat, StrictInt from pydantic.networks import HttpUrl BuiltinTypes: TypeAlias = Union[StrictBool, StrictInt, StrictFloat, str] @@ -51,9 +51,7 @@ def from_commit_log(cls, commit: RowProxy, tags: list[RowProxy]) -> "Checkpoint" class WorkbenchView(BaseModel): """A view (i.e. read-only and visual) of the project's workbench""" - - class Config: - orm_mode = True + model_config = ConfigDict(from_attributes=True) # NOTE: Tmp replacing UUIDS by str due to a problem serializing to json UUID keys # in the response https://github.com/samuelcolvin/pydantic/issues/2096#issuecomment-814860206 diff --git a/services/web/server/src/simcore_service_webserver/wallets/_groups_handlers.py b/services/web/server/src/simcore_service_webserver/wallets/_groups_handlers.py index 1115a239d62..7e0064bb158 100644 --- a/services/web/server/src/simcore_service_webserver/wallets/_groups_handlers.py +++ b/services/web/server/src/simcore_service_webserver/wallets/_groups_handlers.py @@ -8,7 +8,7 @@ from aiohttp import web from models_library.users import GroupID, UserID from models_library.wallets import WalletID -from pydantic import BaseModel, Extra, Field +from pydantic import ConfigDict, BaseModel, Field from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_body_as, @@ -60,18 +60,14 @@ async def wrapper(request: web.Request) -> web.StreamResponse: class _WalletsGroupsPathParams(BaseModel): wallet_id: WalletID group_id: GroupID - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") class _WalletsGroupsBodyParams(BaseModel): read: bool write: bool delete: bool - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") @routes.post( diff --git a/services/web/server/src/simcore_service_webserver/workspaces/_groups_db.py b/services/web/server/src/simcore_service_webserver/workspaces/_groups_db.py index daeba51ae80..1fd21bc4338 100644 --- a/services/web/server/src/simcore_service_webserver/workspaces/_groups_db.py +++ b/services/web/server/src/simcore_service_webserver/workspaces/_groups_db.py @@ -9,7 +9,7 @@ from aiohttp import web from models_library.users import GroupID from models_library.workspaces import WorkspaceID -from pydantic import BaseModel +from pydantic import ConfigDict, BaseModel from simcore_postgres_database.models.workspaces_access_rights import ( workspaces_access_rights, ) @@ -31,9 +31,7 @@ class WorkspaceGroupGetDB(BaseModel): delete: bool created: datetime modified: datetime - - class Config: - orm_mode = True + model_config = ConfigDict(from_attributes=True) ## DB API diff --git a/services/web/server/src/simcore_service_webserver/workspaces/_groups_handlers.py b/services/web/server/src/simcore_service_webserver/workspaces/_groups_handlers.py index d4ae7c4b74f..cf2e421a724 100644 --- a/services/web/server/src/simcore_service_webserver/workspaces/_groups_handlers.py +++ b/services/web/server/src/simcore_service_webserver/workspaces/_groups_handlers.py @@ -8,7 +8,7 @@ from aiohttp import web from models_library.users import GroupID, UserID from models_library.workspaces import WorkspaceID -from pydantic import BaseModel, Extra, Field +from pydantic import ConfigDict, BaseModel, Field from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_body_as, @@ -60,18 +60,14 @@ async def wrapper(request: web.Request) -> web.StreamResponse: class _WorkspacesGroupsPathParams(BaseModel): workspace_id: WorkspaceID group_id: GroupID - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") class _WorkspacesGroupsBodyParams(BaseModel): read: bool write: bool delete: bool - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") @routes.post( diff --git a/services/web/server/src/simcore_service_webserver/workspaces/_workspaces_handlers.py b/services/web/server/src/simcore_service_webserver/workspaces/_workspaces_handlers.py index fa9a2e4aa67..3a22237865b 100644 --- a/services/web/server/src/simcore_service_webserver/workspaces/_workspaces_handlers.py +++ b/services/web/server/src/simcore_service_webserver/workspaces/_workspaces_handlers.py @@ -14,7 +14,7 @@ from models_library.rest_pagination_utils import paginate_data from models_library.users import UserID from models_library.workspaces import WorkspaceID -from pydantic import Extra, Field, Json, parse_obj_as, validator +from pydantic import field_validator, ConfigDict, Field, Json from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( RequestParams, @@ -75,11 +75,11 @@ class WorkspacesListWithJsonStrQueryParams(PageQueryParameters): order_by: Json[OrderBy] = Field( default=OrderBy(field=IDStr("modified"), direction=OrderDirection.DESC), description="Order by field (modified_at|name|description) and direction (asc|desc). The default sorting order is ascending.", - example='{"field": "name", "direction": "desc"}', + examples=['{"field": "name", "direction": "desc"}'], alias="order_by", ) - @validator("order_by", check_fields=False) + @field_validator("order_by", check_fields=False) @classmethod def validate_order_by_field(cls, v): if v.field not in { @@ -92,9 +92,7 @@ def validate_order_by_field(cls, v): if v.field == "modified_at": v.field = "modified" return v - - class Config: - extra = Extra.forbid + model_config = ConfigDict(extra="forbid") @routes.post(f"/{VTAG}/workspaces", name="create_workspace") @@ -102,7 +100,7 @@ class Config: @permission_required("workspaces.*") @handle_workspaces_exceptions async def create_workspace(request: web.Request): - req_ctx = WorkspacesRequestContext.parse_obj(request) + req_ctx = WorkspacesRequestContext.model_validate(request) body_params = await parse_request_body_as(CreateWorkspaceBodyParams, request) workspace: WorkspaceGet = await _workspaces_api.create_workspace( @@ -122,7 +120,7 @@ async def create_workspace(request: web.Request): @permission_required("workspaces.*") @handle_workspaces_exceptions async def list_workspaces(request: web.Request): - req_ctx = WorkspacesRequestContext.parse_obj(request) + req_ctx = WorkspacesRequestContext.model_validate(request) query_params: WorkspacesListWithJsonStrQueryParams = ( parse_request_query_parameters_as(WorkspacesListWithJsonStrQueryParams, request) ) @@ -133,10 +131,10 @@ async def list_workspaces(request: web.Request): product_name=req_ctx.product_name, offset=query_params.offset, limit=query_params.limit, - order_by=parse_obj_as(OrderBy, query_params.order_by), + order_by=OrderBy.model_validate(query_params.order_by), ) - page = Page[WorkspaceGet].parse_obj( + page = Page[WorkspaceGet].model_validate( paginate_data( chunk=workspaces.items, request_url=request.url, @@ -146,7 +144,7 @@ async def list_workspaces(request: web.Request): ) ) return web.Response( - text=page.json(**RESPONSE_MODEL_POLICY), + text=page.model_dump_json(**RESPONSE_MODEL_POLICY), content_type=MIMETYPE_APPLICATION_JSON, ) @@ -156,7 +154,7 @@ async def list_workspaces(request: web.Request): @permission_required("workspaces.*") @handle_workspaces_exceptions async def get_workspace(request: web.Request): - req_ctx = WorkspacesRequestContext.parse_obj(request) + req_ctx = WorkspacesRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(WorkspacesPathParams, request) workspace: WorkspaceGet = await _workspaces_api.get_workspace( @@ -177,7 +175,7 @@ async def get_workspace(request: web.Request): @permission_required("workspaces.*") @handle_workspaces_exceptions async def replace_workspace(request: web.Request): - req_ctx = WorkspacesRequestContext.parse_obj(request) + req_ctx = WorkspacesRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(WorkspacesPathParams, request) body_params = await parse_request_body_as(PutWorkspaceBodyParams, request) @@ -201,7 +199,7 @@ async def replace_workspace(request: web.Request): @permission_required("workspaces.*") @handle_workspaces_exceptions async def delete_workspace(request: web.Request): - req_ctx = WorkspacesRequestContext.parse_obj(request) + req_ctx = WorkspacesRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(WorkspacesPathParams, request) await _workspaces_api.delete_workspace( diff --git a/services/web/server/tests/unit/isolated/test_studies_dispatcher_core.py b/services/web/server/tests/unit/isolated/test_studies_dispatcher_core.py index 8faada91005..ef842a25a98 100644 --- a/services/web/server/tests/unit/isolated/test_studies_dispatcher_core.py +++ b/services/web/server/tests/unit/isolated/test_studies_dispatcher_core.py @@ -12,7 +12,7 @@ import pytest from models_library.projects import Project, ProjectID from models_library.projects_nodes_io import NodeID -from pydantic import validator +from pydantic import field_validator from pydantic.main import BaseModel from pydantic.networks import HttpUrl from pytest_simcore.helpers.webserver_fake_services_data import list_fake_file_consumers @@ -46,11 +46,11 @@ async def test_create_project_with_viewer(view: dict[str, Any]): assert list(project.workbench.keys()) # converts into equivalent Dict - project_in: dict = json.loads(project.json(exclude_none=True, by_alias=True)) + project_in: dict = json.loads(project.model_dump_json(exclude_none=True, by_alias=True)) print(json.dumps(project_in, indent=2)) # This operation is done exactly before adding to the database in projects_handlers.create_projects - Project.parse_obj(project_in) + Project.model_validate(project_in) def test_url_quoting_and_validation(): @@ -63,7 +63,7 @@ def test_url_quoting_and_validation(): class M(BaseModel): url: HttpUrl - @validator("url", pre=True) + @field_validator("url", mode="before") @classmethod def unquote_url(cls, v): w = urllib.parse.unquote(v) @@ -71,14 +71,14 @@ def unquote_url(cls, v): w = w.replace(SPACE, "%20") return w - M.parse_obj( + M.model_validate( { # encoding %20 as %2520 "url": "https://raw.githubusercontent.com/pcrespov/osparc-sample-studies/master/files%2520samples/sample.ipynb" } ) - obj2 = M.parse_obj( + obj2 = M.model_validate( { # encoding space as %20 "url": "https://raw.githubusercontent.com/pcrespov/osparc-sample-studies/master/files%20samples/sample.ipynb" @@ -86,7 +86,7 @@ def unquote_url(cls, v): ) url_with_url_in_query = "http://127.0.0.1:9081/view?file_type=IPYNB&viewer_key=simcore/services/dynamic/jupyter-octave-python-math&viewer_version=1.6.9&file_size=1&download_link=https://raw.githubusercontent.com/pcrespov/osparc-sample-studies/master/files%2520samples/sample.ipynb" - obj4 = M.parse_obj({"url": URL(url_with_url_in_query).query["download_link"]}) + obj4 = M.model_validate({"url": URL(url_with_url_in_query).query["download_link"]}) assert obj2.url.path == obj4.url.path @@ -94,7 +94,7 @@ def unquote_url(cls, v): "https://raw.githubusercontent.com/pcrespov/osparc-sample-studies/master/files%20samples/sample.ipynb" ) M(url=quoted_url) - M.parse_obj({"url": url_with_url_in_query}) + M.model_validate({"url": url_with_url_in_query}) assert ( URL(url_with_url_in_query).query["download_link"] diff --git a/services/web/server/tests/unit/isolated/test_utils_rate_limiting.py b/services/web/server/tests/unit/isolated/test_utils_rate_limiting.py index 5e2b5a891b0..6568b1b7db4 100644 --- a/services/web/server/tests/unit/isolated/test_utils_rate_limiting.py +++ b/services/web/server/tests/unit/isolated/test_utils_rate_limiting.py @@ -10,8 +10,9 @@ from aiohttp import web from aiohttp.test_utils import TestClient from aiohttp.web_exceptions import HTTPOk, HTTPTooManyRequests -from pydantic import ValidationError, conint, parse_obj_as +from pydantic import Field, TypeAdapter, ValidationError from simcore_service_webserver.utils_rate_limiting import global_rate_limit_route +from typing_extensions import Annotated TOTAL_TEST_TIME = 1 # secs MAX_NUM_REQUESTS = 3 @@ -110,7 +111,7 @@ async def test_global_rate_limit_route(requests_per_second: float, client: TestC for t in tasks: if retry_after := t.result().headers.get("Retry-After"): try: - parse_obj_as(conint(ge=1), retry_after) + TypeAdapter(Annotated[int, Field(ge=1)]).validate_python(retry_after) except ValidationError as err: failed.append((retry_after, f"{err}")) assert not failed diff --git a/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control.py b/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control.py index ed04b3728e2..614bdca0209 100644 --- a/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control.py +++ b/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control.py @@ -4,16 +4,14 @@ from models_library.projects import NodesDict -from pydantic import BaseModel +from pydantic import ConfigDict, BaseModel from simcore_service_webserver.projects.models import ProjectDict from simcore_service_webserver.version_control.db import compute_workbench_checksum class WorkbenchModel(BaseModel): __root__: NodesDict - - class Config: - allow_population_by_field_name = True + model_config = ConfigDict(populate_by_name=True) def test_compute_workbench_checksum(fake_project: ProjectDict): @@ -21,7 +19,7 @@ def test_compute_workbench_checksum(fake_project: ProjectDict): # as a dict sha1_w_dict = compute_workbench_checksum(fake_project["workbench"]) - workbench = WorkbenchModel.parse_obj(fake_project["workbench"]) + workbench = WorkbenchModel.model_validate(fake_project["workbench"]) # with pydantic models, i.e. Nodes # From 9979a3fac844299b91a9c7415e9c32f66650403a Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 23 Oct 2024 15:40:35 +0200 Subject: [PATCH 003/165] add common-library dependency --- services/web/server/requirements/_base.in | 1 + services/web/server/requirements/ci.txt | 1 + services/web/server/requirements/dev.txt | 1 + services/web/server/requirements/prod.txt | 1 + 4 files changed, 4 insertions(+) diff --git a/services/web/server/requirements/_base.in b/services/web/server/requirements/_base.in index 8d5ba7d34d8..308a1604cb3 100644 --- a/services/web/server/requirements/_base.in +++ b/services/web/server/requirements/_base.in @@ -9,6 +9,7 @@ # - Added as constraints instead of requirements in order to avoid polluting base.txt # - Will be installed when prod.txt or dev.txt # +--requirement ../../../../packages/common-library/requirements/_base.in --requirement ../../../../packages/models-library/requirements/_base.in --requirement ../../../../packages/postgres-database/requirements/_base.in --requirement ../../../../packages/settings-library/requirements/_base.in diff --git a/services/web/server/requirements/ci.txt b/services/web/server/requirements/ci.txt index f9917eb3748..bf55a2ed211 100644 --- a/services/web/server/requirements/ci.txt +++ b/services/web/server/requirements/ci.txt @@ -14,6 +14,7 @@ --requirement _tools.txt # installs this repo's packages +simcore-common-library @ ../../../packages/common-library simcore-models-library @ ../../../packages/models-library simcore-postgres-database @ ../../../packages/postgres-database simcore-settings-library @ ../../../packages/settings-library diff --git a/services/web/server/requirements/dev.txt b/services/web/server/requirements/dev.txt index b62c7127482..fdc9cb27429 100644 --- a/services/web/server/requirements/dev.txt +++ b/services/web/server/requirements/dev.txt @@ -12,6 +12,7 @@ --requirement _tools.txt # installs this repo's packages +--editable ../../../packages/common-library/ --editable ../../../packages/models-library/ --editable ../../../packages/postgres-database/ --editable ../../../packages/settings-library/ diff --git a/services/web/server/requirements/prod.txt b/services/web/server/requirements/prod.txt index 9494dd12c30..2ccad765e49 100644 --- a/services/web/server/requirements/prod.txt +++ b/services/web/server/requirements/prod.txt @@ -10,6 +10,7 @@ --requirement _base.txt # installs this repo's packages +simcore-common-library @ ../../../packages/common-library simcore-models-library @ ../../../packages/models-library simcore-postgres-database @ ../../../packages/postgres-database simcore-settings-library @ ../../../packages/settings-library From 29ab6c2cdf0f0d7f0b673abf4c9742be1627be5b Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 23 Oct 2024 15:43:42 +0200 Subject: [PATCH 004/165] fix import --- .../web/server/src/simcore_service_webserver/errors.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/errors.py b/services/web/server/src/simcore_service_webserver/errors.py index 173699f5888..ac21f882297 100644 --- a/services/web/server/src/simcore_service_webserver/errors.py +++ b/services/web/server/src/simcore_service_webserver/errors.py @@ -1,8 +1,5 @@ -from typing import Any - -from models_library.errors_classes import OsparcErrorMixin +from common_library.errors_classes import OsparcErrorMixin class WebServerBaseError(OsparcErrorMixin, Exception): - def __init__(self, **ctx: Any) -> None: - super().__init__(**ctx) + """WebServer base error.""" From a75e832272c05451acaab99efc07e026cc72e70d Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 23 Oct 2024 15:48:05 +0200 Subject: [PATCH 005/165] fix import --- packages/service-library/src/servicelib/logging_errors.py | 2 +- packages/service-library/tests/test_logging_errors.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/service-library/src/servicelib/logging_errors.py b/packages/service-library/src/servicelib/logging_errors.py index 926581a3f2c..322a9934b5a 100644 --- a/packages/service-library/src/servicelib/logging_errors.py +++ b/packages/service-library/src/servicelib/logging_errors.py @@ -3,7 +3,7 @@ from typing import Any, TypedDict from models_library.error_codes import ErrorCodeStr -from models_library.errors_classes import OsparcErrorMixin +from common_library.errors_classes import OsparcErrorMixin from .logging_utils import LogExtra, get_log_record_extra diff --git a/packages/service-library/tests/test_logging_errors.py b/packages/service-library/tests/test_logging_errors.py index 432d2421d17..932d410fb0a 100644 --- a/packages/service-library/tests/test_logging_errors.py +++ b/packages/service-library/tests/test_logging_errors.py @@ -4,7 +4,7 @@ import pytest from models_library.error_codes import create_error_code -from models_library.errors_classes import OsparcErrorMixin +from common_library.errors_classes import OsparcErrorMixin from servicelib.logging_errors import ( create_troubleshotting_log_kwargs, create_troubleshotting_log_message, From dc1f60d9eea20d12183337ada954b5e19ebf248d Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 23 Oct 2024 15:55:10 +0200 Subject: [PATCH 006/165] continue fixing --- .../src/simcore_postgres_database/models/products.py | 3 ++- .../src/simcore_service_webserver/session/settings.py | 7 ++++--- .../src/simcore_service_webserver/statics/settings.py | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/postgres-database/src/simcore_postgres_database/models/products.py b/packages/postgres-database/src/simcore_postgres_database/models/products.py index 03e137528ec..a71906ace59 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/products.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/products.py @@ -6,11 +6,12 @@ """ import json -from typing import Literal, TypedDict +from typing import Literal import sqlalchemy as sa from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.sql import func +from typing_extensions import TypedDict from .base import metadata from .groups import groups diff --git a/services/web/server/src/simcore_service_webserver/session/settings.py b/services/web/server/src/simcore_service_webserver/session/settings.py index e02b1ee3f55..f71620e7c95 100644 --- a/services/web/server/src/simcore_service_webserver/session/settings.py +++ b/services/web/server/src/simcore_service_webserver/session/settings.py @@ -1,8 +1,7 @@ from typing import Final from aiohttp import web -from pydantic import AliasChoices, field_validator, PositiveInt -from pydantic.class_validators import field_validator +from pydantic import AliasChoices, PositiveInt, field_validator from pydantic.fields import Field from pydantic.types import SecretStr from settings_library.base import BaseCustomSettings @@ -22,7 +21,9 @@ class SessionSettings(BaseCustomSettings, MixinSessionSettings): description="Secret key to encrypt cookies. " 'TIP: python3 -c "from cryptography.fernet import *; print(Fernet.generate_key())"', min_length=44, - validation_alias=AliasChoices("SESSION_SECRET_KEY", "WEBSERVER_SESSION_SECRET_KEY"), + validation_alias=AliasChoices( + "SESSION_SECRET_KEY", "WEBSERVER_SESSION_SECRET_KEY" + ), ) SESSION_ACCESS_TOKENS_EXPIRATION_INTERVAL_SECS: int = Field( diff --git a/services/web/server/src/simcore_service_webserver/statics/settings.py b/services/web/server/src/simcore_service_webserver/statics/settings.py index ed01702170a..46471b2f235 100644 --- a/services/web/server/src/simcore_service_webserver/statics/settings.py +++ b/services/web/server/src/simcore_service_webserver/statics/settings.py @@ -7,7 +7,7 @@ import pycountry from aiohttp import web from models_library.utils.change_case import snake_to_camel -from pydantic import AnyHttpUrl, Field, TypeAdapter +from pydantic import AliasChoices, AnyHttpUrl, Field, TypeAdapter from settings_library.base import BaseCustomSettings from .._constants import APP_SETTINGS_KEY From fdb7238ded0adfc8929287380f89cea6394981bb Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 23 Oct 2024 15:57:26 +0200 Subject: [PATCH 007/165] fix arbitrary type field --- .../web/server/src/simcore_service_webserver/projects/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/models.py b/services/web/server/src/simcore_service_webserver/projects/models.py index 3445e68a3bd..c0ca8954f9e 100644 --- a/services/web/server/src/simcore_service_webserver/projects/models.py +++ b/services/web/server/src/simcore_service_webserver/projects/models.py @@ -51,7 +51,7 @@ class ProjectDB(BaseModel): published: bool hidden: bool workspace_id: WorkspaceID | None = None - model_config = ConfigDict(from_attributes=True) + model_config = ConfigDict(from_attributes=True, arbitrary_types_allowed=True) # validators _empty_thumbnail_is_none = field_validator("thumbnail", mode="before")( From c1db89ad5a62c53b71ad306ed927698018e2ab8b Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 23 Oct 2024 23:51:36 +0200 Subject: [PATCH 008/165] continue fixing --- .../hypothesis_type_strategies.py | 10 +++---- .../_rabbitmq_exclusive_queue_consumers.py | 14 +++++----- .../projects/_crud_handlers_models.py | 14 +++++++--- .../scicrunch/_rest.py | 10 +++---- .../scicrunch/settings.py | 8 +++--- .../storage/schemas.py | 19 +++++++------- .../simcore_service_webserver/tags/schemas.py | 8 +++--- .../src/simcore_service_webserver/utils.py | 3 ++- .../unit/isolated/test_dynamic_scheduler.py | 26 ++++++++++++------- .../test_projects__nodes_resources.py | 12 ++++++--- .../unit/isolated/test_user_notifications.py | 12 +++++---- 11 files changed, 80 insertions(+), 56 deletions(-) diff --git a/packages/pytest-simcore/src/pytest_simcore/hypothesis_type_strategies.py b/packages/pytest-simcore/src/pytest_simcore/hypothesis_type_strategies.py index ad80ab57774..d158a0694b5 100644 --- a/packages/pytest-simcore/src/pytest_simcore/hypothesis_type_strategies.py +++ b/packages/pytest-simcore/src/pytest_simcore/hypothesis_type_strategies.py @@ -1,9 +1,5 @@ -from hypothesis import provisional -from hypothesis import strategies as st -from pydantic import AnyHttpUrl, AnyUrl, HttpUrl - # FIXME: For now it seems the pydantic hypothesis plugin does not provide strategies for these types. # therefore we currently provide it -st.register_type_strategy(AnyUrl, provisional.urls()) -st.register_type_strategy(HttpUrl, provisional.urls()) -st.register_type_strategy(AnyHttpUrl, provisional.urls()) +# st.register_type_strategy(AnyUrl, provisional.urls()) +# st.register_type_strategy(HttpUrl, provisional.urls()) +# st.register_type_strategy(AnyHttpUrl, provisional.urls()) diff --git a/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_exclusive_queue_consumers.py b/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_exclusive_queue_consumers.py index d9a6b1f0861..449f3ecebec 100644 --- a/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_exclusive_queue_consumers.py +++ b/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_exclusive_queue_consumers.py @@ -13,7 +13,7 @@ ) from models_library.socketio import SocketMessageDict from models_library.users import GroupID -from pydantic import parse_raw_as +from pydantic import TypeAdapter from servicelib.logging_utils import log_catch, log_context from servicelib.rabbitmq import RabbitMQClient from servicelib.utils import logged_gather @@ -58,17 +58,19 @@ async def _convert_to_node_update_event( "data": project["workbench"][f"{message.node_id}"], }, ) - _logger.warning("node not found: '%s'", message.dict()) + _logger.warning("node not found: '%s'", message.model_dump()) except ProjectNotFoundError: - _logger.warning("project not found: '%s'", message.dict()) + _logger.warning("project not found: '%s'", message.model_dump()) return None async def _progress_message_parser(app: web.Application, data: bytes) -> bool: rabbit_message: ( ProgressRabbitMessageNode | ProgressRabbitMessageProject - ) = parse_raw_as( - ProgressRabbitMessageNode | ProgressRabbitMessageProject, data # type: ignore[arg-type] # from pydantic v2 --> https://github.com/pydantic/pydantic/discussions/4950 + ) = TypeAdapter( + ProgressRabbitMessageNode | ProgressRabbitMessageProject + ).validate_json( + data ) message: SocketMessageDict | None = None if isinstance(rabbit_message, ProgressRabbitMessageProject): @@ -126,7 +128,7 @@ async def _events_message_parser(app: web.Application, data: bytes) -> bool: async def _osparc_credits_message_parser(app: web.Application, data: bytes) -> bool: - rabbit_message = parse_raw_as(WalletCreditsMessage, data) + rabbit_message = TypeAdapter(WalletCreditsMessage).validate_json(data) wallet_groups = await wallets_api.list_wallet_groups_with_read_access_by_wallet( app, wallet_id=rabbit_message.wallet_id ) diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers_models.py b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers_models.py index 982ccdee17b..b6f40146269 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers_models.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers_models.py @@ -18,9 +18,13 @@ ) from models_library.workspaces import WorkspaceID from pydantic import ( - TypeAdapter, field_validator, ConfigDict, BaseModel, + BaseModel, + ConfigDict, Field, Json, + TypeAdapter, + field_validator, + model_validator, ) from servicelib.common_headers import ( UNDEFINED_DEFAULT_SIMCORE_USER_AGENT_VALUE, @@ -65,6 +69,7 @@ def check_parent_valid(cls, values: dict[str, Any]) -> dict[str, Any]: msg = "Both parent_project_uuid and parent_node_id must be set or both null or both unset" raise ValueError(msg) return values + model_config = ConfigDict(populate_by_name=False) @@ -115,9 +120,9 @@ def search_check_empty_string(cls, v): return None return v - _null_or_none_str_to_none_validator = field_validator( - "folder_id", mode="before" - )(null_or_none_str_to_none_validator) + _null_or_none_str_to_none_validator = field_validator("folder_id", mode="before")( + null_or_none_str_to_none_validator + ) _null_or_none_str_to_none_validator2 = field_validator( "workspace_id", mode="before" @@ -147,6 +152,7 @@ def validate_order_by_field(cls, v): msg = f"We do not support ordering by provided field {v.field}" raise ValueError(msg) return v + model_config = ConfigDict(extra="forbid") diff --git a/services/web/server/src/simcore_service_webserver/scicrunch/_rest.py b/services/web/server/src/simcore_service_webserver/scicrunch/_rest.py index 70e4963fc68..fd2f3d243a1 100644 --- a/services/web/server/src/simcore_service_webserver/scicrunch/_rest.py +++ b/services/web/server/src/simcore_service_webserver/scicrunch/_rest.py @@ -18,7 +18,7 @@ from typing import Any from aiohttp import ClientSession -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, RootModel from yarl import URL from .models import ResourceHit @@ -49,7 +49,7 @@ class ResourceView(BaseModel): @classmethod def from_response_payload(cls, payload: dict): - assert payload["success"] == True # nosec + assert payload["success"] is True # nosec return cls(**payload["data"]) @property @@ -72,8 +72,8 @@ def get_resource_url(self): return URL(str(self._get_field("Resource URL"))) -class ListOfResourceHits(BaseModel): - __root__: list[ResourceHit] +class ListOfResourceHits(RootModel[list[ResourceHit]]): + ... # REQUESTS @@ -120,4 +120,4 @@ async def autocomplete_by_name( ) as resp: body = await resp.json() assert body.get("success") # nosec - return ListOfResourceHits.parse_obj(body.get("data", [])) + return ListOfResourceHits.model_validate(body.get("data", [])) diff --git a/services/web/server/src/simcore_service_webserver/scicrunch/settings.py b/services/web/server/src/simcore_service_webserver/scicrunch/settings.py index ecc027374c0..0bf88e69b05 100644 --- a/services/web/server/src/simcore_service_webserver/scicrunch/settings.py +++ b/services/web/server/src/simcore_service_webserver/scicrunch/settings.py @@ -1,5 +1,5 @@ from aiohttp import web -from pydantic import Field, HttpUrl, SecretStr, parse_obj_as +from pydantic import Field, HttpUrl, SecretStr, TypeAdapter from settings_library.base import BaseCustomSettings from .._constants import APP_SETTINGS_KEY @@ -11,7 +11,7 @@ class SciCrunchSettings(BaseCustomSettings): SCICRUNCH_API_BASE_URL: HttpUrl = Field( - default=parse_obj_as(HttpUrl, f"{SCICRUNCH_DEFAULT_URL}/api/1"), + default=TypeAdapter(HttpUrl).validate_python(f"{SCICRUNCH_DEFAULT_URL}/api/1"), description="Base url to scicrunch API's entrypoint", ) @@ -20,7 +20,9 @@ class SciCrunchSettings(BaseCustomSettings): SCICRUNCH_API_KEY: SecretStr SCICRUNCH_RESOLVER_BASE_URL: HttpUrl = Field( - default=parse_obj_as(HttpUrl, f"{SCICRUNCH_DEFAULT_URL}/resolver"), + default=TypeAdapter(HttpUrl).validate_python( + f"{SCICRUNCH_DEFAULT_URL}/resolver" + ), description="Base url to scicrunch resolver entrypoint", ) diff --git a/services/web/server/src/simcore_service_webserver/storage/schemas.py b/services/web/server/src/simcore_service_webserver/storage/schemas.py index b5c71beaa09..e1e97392274 100644 --- a/services/web/server/src/simcore_service_webserver/storage/schemas.py +++ b/services/web/server/src/simcore_service_webserver/storage/schemas.py @@ -2,7 +2,7 @@ from typing import Any, TypeAlias from models_library.api_schemas_storage import TableSynchronisation -from pydantic import ConfigDict, BaseModel, Field +from pydantic import BaseModel, ConfigDict, Field, RootModel # NOTE: storage generates URLs that contain double encoded # slashes, and when applying validation via `StorageFileID` @@ -24,8 +24,8 @@ class FileLocation(BaseModel): ) -class FileLocationArray(BaseModel): - __root__: list[FileLocation] +class FileLocationArray(RootModel[list[FileLocation]]): + ... class Links(BaseModel): @@ -70,8 +70,8 @@ class DatasetMetaData(BaseModel): ) -class DatasetMetaDataArray(BaseModel): - __root__: list[DatasetMetaData] +class DatasetMetaDataArray(RootModel[list[DatasetMetaData]]): + ... class FileLocationEnveloped(BaseModel): @@ -141,8 +141,8 @@ class FileMetaData(BaseModel): ) -class FileMetaDataArray(BaseModel): - __root__: list[FileMetaData] +class FileMetaDataArray(RootModel[list[FileMetaData]]): + ... class FileMetaEnvelope(BaseModel): @@ -153,9 +153,8 @@ class FileMetaEnvelope(BaseModel): class PresignedLink(BaseModel): link: str | None = None - model_config = ConfigDict( - json_schema_extra={"example": {"link": "example_link"}} - ) + model_config = ConfigDict(json_schema_extra={"example": {"link": "example_link"}}) + class PresignedLinkEnveloped(BaseModel): data: PresignedLink diff --git a/services/web/server/src/simcore_service_webserver/tags/schemas.py b/services/web/server/src/simcore_service_webserver/tags/schemas.py index 01663e0d337..0851c99f99a 100644 --- a/services/web/server/src/simcore_service_webserver/tags/schemas.py +++ b/services/web/server/src/simcore_service_webserver/tags/schemas.py @@ -1,9 +1,10 @@ import re from datetime import datetime +from typing import Annotated from models_library.api_schemas_webserver._base import InputSchema, OutputSchema from models_library.users import GroupID, UserID -from pydantic import ConstrainedStr, Field, PositiveInt +from pydantic import Field, PositiveInt, StringConstraints from servicelib.aiohttp.requests_validation import RequestParams, StrictRequestParams from servicelib.request_keys import RQT_USERID_KEY from simcore_postgres_database.utils_tags import TagDict @@ -17,8 +18,9 @@ class TagPathParams(StrictRequestParams): tag_id: PositiveInt -class ColorStr(ConstrainedStr): - regex = re.compile(r"^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$") +ColorStr = Annotated[ + str, StringConstraints(pattern=re.compile(r"^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$")) +] class TagUpdate(InputSchema): diff --git a/services/web/server/src/simcore_service_webserver/utils.py b/services/web/server/src/simcore_service_webserver/utils.py index 6e7e7fced67..3f9bcbde6d5 100644 --- a/services/web/server/src/simcore_service_webserver/utils.py +++ b/services/web/server/src/simcore_service_webserver/utils.py @@ -10,11 +10,12 @@ import tracemalloc from datetime import datetime from pathlib import Path -from typing import Any, TypedDict, cast +from typing import Any, cast import orjson from models_library.basic_types import SHA1Str from models_library.error_codes import ErrorCodeStr +from typing_extensions import TypedDict _CURRENT_DIR = ( Path(sys.argv[0] if __name__ == "__main__" else __file__).resolve().parent diff --git a/services/web/server/tests/unit/isolated/test_dynamic_scheduler.py b/services/web/server/tests/unit/isolated/test_dynamic_scheduler.py index 6308141d254..944a958baf2 100644 --- a/services/web/server/tests/unit/isolated/test_dynamic_scheduler.py +++ b/services/web/server/tests/unit/isolated/test_dynamic_scheduler.py @@ -47,18 +47,23 @@ def mock_rpc_client( @pytest.fixture def dynamic_service_start() -> DynamicServiceStart: - return DynamicServiceStart.parse_obj( - DynamicServiceStart.Config.schema_extra["example"] + return DynamicServiceStart.model_validate( + DynamicServiceStart.model_config["json_schema_extra"]["example"] ) @pytest.mark.parametrize( "expected_response", [ - *[NodeGet.parse_obj(x) for x in NodeGet.Config.schema_extra["examples"]], - NodeGetIdle.parse_obj(NodeGetIdle.Config.schema_extra["example"]), - DynamicServiceGet.parse_obj( - DynamicServiceGet.Config.schema_extra["examples"][0] + *[ + NodeGet.model_validate(x) + for x in NodeGet.model_config["json_schema_extra"]["examples"] + ], + NodeGetIdle.model_validate( + NodeGetIdle.model_config["json_schema_extra"]["example"] + ), + DynamicServiceGet.model_validate( + DynamicServiceGet.model_config["json_schema_extra"]["examples"][0] ), ], ) @@ -98,9 +103,12 @@ async def test_get_service_status_raises_rpc_server_error( @pytest.mark.parametrize( "expected_response", [ - *[NodeGet.parse_obj(x) for x in NodeGet.Config.schema_extra["examples"]], - DynamicServiceGet.parse_obj( - DynamicServiceGet.Config.schema_extra["examples"][0] + *[ + NodeGet.model_validate(x) + for x in NodeGet.model_config["json_schema_extra"]["examples"] + ], + DynamicServiceGet.model_validate( + DynamicServiceGet.model_config["json_schema_extra"]["examples"][0] ), ], ) diff --git a/services/web/server/tests/unit/isolated/test_projects__nodes_resources.py b/services/web/server/tests/unit/isolated/test_projects__nodes_resources.py index 12f6bfc23b4..259c4ba0c3f 100644 --- a/services/web/server/tests/unit/isolated/test_projects__nodes_resources.py +++ b/services/web/server/tests/unit/isolated/test_projects__nodes_resources.py @@ -18,7 +18,9 @@ "resources", [ parse_obj_as(ServiceResourcesDict, example) - for example in ServiceResourcesDictHelpers.Config.schema_extra["examples"] + for example in ServiceResourcesDictHelpers.model_config["json_schema_extra"][ + "examples" + ] ], ) def test_check_can_update_service_resources_with_same_does_not_raise( @@ -32,7 +34,9 @@ def test_check_can_update_service_resources_with_same_does_not_raise( "resources", [ parse_obj_as(ServiceResourcesDict, example) - for example in ServiceResourcesDictHelpers.Config.schema_extra["examples"] + for example in ServiceResourcesDictHelpers.model_config["json_schema_extra"][ + "examples" + ] ], ) def test_check_can_update_service_resources_with_invalid_container_name_raises( @@ -51,7 +55,9 @@ def test_check_can_update_service_resources_with_invalid_container_name_raises( "resources", [ parse_obj_as(ServiceResourcesDict, example) - for example in ServiceResourcesDictHelpers.Config.schema_extra["examples"] + for example in ServiceResourcesDictHelpers.model_config["json_schema_extra"][ + "examples" + ] ], ) def test_check_can_update_service_resources_with_invalid_image_name_raises( diff --git a/services/web/server/tests/unit/isolated/test_user_notifications.py b/services/web/server/tests/unit/isolated/test_user_notifications.py index d606a84297f..1970f9d19c8 100644 --- a/services/web/server/tests/unit/isolated/test_user_notifications.py +++ b/services/web/server/tests/unit/isolated/test_user_notifications.py @@ -12,7 +12,9 @@ ) -@pytest.mark.parametrize("raw_data", UserNotification.Config.schema_extra["examples"]) +@pytest.mark.parametrize( + "raw_data", UserNotification.model_config["json_schema_extra"]["examples"] +) def test_user_notification(raw_data: dict[str, Any]): assert UserNotification.parse_obj(raw_data) @@ -26,7 +28,7 @@ def test_get_notification_key(user_id: UserID): "request_data", [ pytest.param( - UserNotificationCreate.parse_obj( + UserNotificationCreate.model_validate( { "user_id": "1", "category": NotificationCategory.NEW_ORGANIZATION, @@ -40,7 +42,7 @@ def test_get_notification_key(user_id: UserID): id="normal_usage", ), pytest.param( - UserNotificationCreate.parse_obj( + UserNotificationCreate.model_validate( { "user_id": "1", "category": NotificationCategory.NEW_ORGANIZATION, @@ -55,7 +57,7 @@ def test_get_notification_key(user_id: UserID): id="read_is_always_set_false", ), pytest.param( - UserNotificationCreate.parse_obj( + UserNotificationCreate.model_validate( { "id": "some_id", "user_id": "1", @@ -70,7 +72,7 @@ def test_get_notification_key(user_id: UserID): id="a_new_id_is_alway_recreated", ), pytest.param( - UserNotificationCreate.parse_obj( + UserNotificationCreate.model_validate( { "id": "some_id", "user_id": "1", From 9586ea8e2fe72c402f1f078f24bd586b9e43f0f9 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 00:06:12 +0200 Subject: [PATCH 009/165] continue fixing --- .../{test_statics.py => test_isolated_statics.py} | 0 .../tests/unit/with_dbs/02/test_project_lock.py | 12 ++++++------ 2 files changed, 6 insertions(+), 6 deletions(-) rename services/web/server/tests/unit/isolated/{test_statics.py => test_isolated_statics.py} (100%) diff --git a/services/web/server/tests/unit/isolated/test_statics.py b/services/web/server/tests/unit/isolated/test_isolated_statics.py similarity index 100% rename from services/web/server/tests/unit/isolated/test_statics.py rename to services/web/server/tests/unit/isolated/test_isolated_statics.py diff --git a/services/web/server/tests/unit/with_dbs/02/test_project_lock.py b/services/web/server/tests/unit/with_dbs/02/test_project_lock.py index f70a44c7bbc..5c33586e151 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_project_lock.py +++ b/services/web/server/tests/unit/with_dbs/02/test_project_lock.py @@ -12,7 +12,7 @@ from models_library.projects_access import Owner from models_library.projects_state import ProjectLocked, ProjectStatus from models_library.users import UserID -from pydantic import parse_raw_as +from pydantic import TypeAdapter from simcore_service_webserver.projects.exceptions import ProjectLockError from simcore_service_webserver.projects.lock import ( PROJECT_REDIS_LOCK_KEY, @@ -51,7 +51,7 @@ async def test_lock_project( PROJECT_REDIS_LOCK_KEY.format(project_uuid) ) assert redis_value - lock_value = parse_raw_as(ProjectLocked, redis_value) + lock_value = TypeAdapter(ProjectLocked).validate_json(redis_value) assert lock_value == ProjectLocked( value=True, owner=Owner(user_id=user_id, **user_fullname), @@ -137,7 +137,7 @@ async def test_is_project_locked( faker: Faker, ): assert client.app - assert await is_project_locked(client.app, project_uuid) == False + assert await is_project_locked(client.app, project_uuid) is False user_name: FullNameDict = { "first_name": faker.first_name(), "last_name": faker.last_name(), @@ -149,7 +149,7 @@ async def test_is_project_locked( user_id=user_id, user_fullname=user_name, ): - assert await is_project_locked(client.app, project_uuid) == True + assert await is_project_locked(client.app, project_uuid) is True @pytest.mark.parametrize( @@ -170,9 +170,9 @@ async def test_get_project_locked_state( ): assert client.app # no lock - assert await get_project_locked_state(client.app, project_uuid) == None + assert await get_project_locked_state(client.app, project_uuid) is None - assert await is_project_locked(client.app, project_uuid) == False + assert await is_project_locked(client.app, project_uuid) is False user_name: FullNameDict = { "first_name": faker.first_name(), "last_name": faker.last_name(), From e7ba88a93ff70368e3438f67a0136452c3a81e88 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 09:19:36 +0200 Subject: [PATCH 010/165] continue fixing --- .../application_settings_utils.py | 7 ++++--- .../src/simcore_service_webserver/payments/settings.py | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/application_settings_utils.py b/services/web/server/src/simcore_service_webserver/application_settings_utils.py index 9123c8ad574..9843e84afdd 100644 --- a/services/web/server/src/simcore_service_webserver/application_settings_utils.py +++ b/services/web/server/src/simcore_service_webserver/application_settings_utils.py @@ -10,6 +10,7 @@ from typing import Any from aiohttp import web +from common_library.pydantic_fields_extension import get_type, is_nullable from pydantic.types import SecretStr from servicelib.aiohttp.typing_extension import Handler @@ -200,10 +201,10 @@ def convert_to_environ_vars( # noqa: C901, PLR0915, PLR0912 def _set_if_disabled(field_name, section): # Assumes that by default is enabled enabled = section.get("enabled", True) - field = ApplicationSettings.__fields__[field_name] + field = ApplicationSettings.model_fields[field_name] if not enabled: - envs[field_name] = "null" if field.allow_none else "0" - elif field.type_ == bool: + envs[field_name] = "null" if is_nullable(field) else "0" + elif get_type(field) == bool: envs[field_name] = "1" if main := cfg.get("main"): diff --git a/services/web/server/src/simcore_service_webserver/payments/settings.py b/services/web/server/src/simcore_service_webserver/payments/settings.py index db40f5007a9..8553e508b76 100644 --- a/services/web/server/src/simcore_service_webserver/payments/settings.py +++ b/services/web/server/src/simcore_service_webserver/payments/settings.py @@ -9,6 +9,7 @@ PositiveInt, SecretStr, TypeAdapter, + ValidationInfo, field_validator, ) from settings_library.base import BaseCustomSettings @@ -101,8 +102,8 @@ def _payments_cannot_be_faken_in_production(cls, v): @field_validator("PAYMENTS_AUTORECHARGE_DEFAULT_MONTHLY_LIMIT") @classmethod - def _monthly_limit_greater_than_top_up(cls, v, values): - top_up = values["PAYMENTS_AUTORECHARGE_DEFAULT_TOP_UP_AMOUNT"] + def _monthly_limit_greater_than_top_up(cls, v, info: ValidationInfo): + top_up = info.data["PAYMENTS_AUTORECHARGE_DEFAULT_TOP_UP_AMOUNT"] if v < 2 * top_up: msg = "PAYMENTS_AUTORECHARGE_DEFAULT_MONTHLY_LIMIT (={v}) should be at least twice PAYMENTS_AUTORECHARGE_DEFAULT_TOP_UP_AMOUNT ({top_up})" raise ValueError(msg) From c5dd024bfd72c3fdab92d47cded051624f06d156 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 09:43:43 +0200 Subject: [PATCH 011/165] fix settings validator --- .../simcore_service_webserver/application_settings.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/application_settings.py b/services/web/server/src/simcore_service_webserver/application_settings.py index c01e5bd96e5..ef0b3d2781d 100644 --- a/services/web/server/src/simcore_service_webserver/application_settings.py +++ b/services/web/server/src/simcore_service_webserver/application_settings.py @@ -270,11 +270,11 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings): @model_validator(mode="after") @classmethod - def build_vcs_release_url_if_unset(cls, values): - release_url = values.get("SIMCORE_VCS_RELEASE_URL") + def build_vcs_release_url_if_unset(cls, v): + release_url = v.SIMCORE_VCS_RELEASE_URL if release_url is None and ( - vsc_release_tag := values.get("SIMCORE_VCS_RELEASE_TAG") + vsc_release_tag := v.SIMCORE_VCS_RELEASE_TAG ): if vsc_release_tag == "latest": release_url = ( @@ -282,9 +282,9 @@ def build_vcs_release_url_if_unset(cls, values): ) else: release_url = f"https://github.com/ITISFoundation/osparc-simcore/releases/tag/{vsc_release_tag}" - values["SIMCORE_VCS_RELEASE_URL"] = release_url + v.SIMCORE_VCS_RELEASE_URL = release_url - return values + return v @field_validator( # List of plugins under-development (keep up-to-date) From 1e97eadde6bf340a2dacdaec80581b34aba4f6b1 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 09:53:56 +0200 Subject: [PATCH 012/165] continue fixing --- .../director_v2/settings.py | 10 +++++----- .../meta_modeling/_results.py | 2 +- .../studies_dispatcher/_projects.py | 20 +++++++++---------- .../studies_dispatcher/settings.py | 6 +++--- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/director_v2/settings.py b/services/web/server/src/simcore_service_webserver/director_v2/settings.py index d182ad6df28..21cb368ff50 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/settings.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/settings.py @@ -6,7 +6,7 @@ from aiohttp import ClientSession, ClientTimeout, web from models_library.basic_types import VersionTag -from pydantic import Field, PositiveInt +from pydantic import AliasChoices, Field, PositiveInt from servicelib.aiohttp.application_keys import APP_CLIENT_SESSION_KEY from settings_library.base import BaseCustomSettings from settings_library.basic_types import PortInt @@ -36,9 +36,9 @@ def base_url(self) -> URL: DIRECTOR_V2_RESTART_DYNAMIC_SERVICE_TIMEOUT: PositiveInt = Field( 1 * _MINUTE, description="timeout of containers restart", - envs=[ + validation_alias=AliasChoices( "DIRECTOR_V2_RESTART_DYNAMIC_SERVICE_TIMEOUT", - ], + ), ) DIRECTOR_V2_STORAGE_SERVICE_UPLOAD_DOWNLOAD_TIMEOUT: PositiveInt = Field( @@ -49,9 +49,9 @@ def base_url(self) -> URL: "such payloads it is required to have long timeouts which " "allow the service to finish the operation." ), - envs=[ + validation_alias=AliasChoices( "DIRECTOR_V2_DYNAMIC_SERVICE_DATA_UPLOAD_DOWNLOAD_TIMEOUT", - ], + ), ) def get_service_retrieve_timeout(self) -> ClientTimeout: diff --git a/services/web/server/src/simcore_service_webserver/meta_modeling/_results.py b/services/web/server/src/simcore_service_webserver/meta_modeling/_results.py index e4c695687e8..150c2b8f680 100644 --- a/services/web/server/src/simcore_service_webserver/meta_modeling/_results.py +++ b/services/web/server/src/simcore_service_webserver/meta_modeling/_results.py @@ -110,5 +110,5 @@ def extract_project_results(workbench: dict[str, Any]) -> ExtractedResults: values = node["outputs"] results[noid], labels[noid] = values, label - res = ExtractedResults(progress=progress, labels=labels, values=results) # type: ignore[arg-type] + res = ExtractedResults(progress=progress, labels=labels, values=results) return res diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py index e4b71213ee6..14a1c212552 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py @@ -18,7 +18,7 @@ from models_library.projects_nodes_io import DownloadLink, NodeID, PortLink from models_library.projects_ui import StudyUI from models_library.services import ServiceKey, ServiceVersion -from pydantic import AnyUrl, HttpUrl, parse_obj_as +from pydantic import AnyUrl, HttpUrl, TypeAdapter from servicelib.logging_utils import log_decorator from ..projects.db import ProjectDBAPI @@ -32,10 +32,10 @@ _logger = logging.getLogger(__name__) -_FILE_PICKER_KEY: ServiceKey = parse_obj_as( - ServiceKey, "simcore/services/frontend/file-picker" +_FILE_PICKER_KEY: ServiceKey = TypeAdapter(ServiceKey).validate_python( + "simcore/services/frontend/file-picker" ) -_FILE_PICKER_VERSION: ServiceVersion = parse_obj_as(ServiceVersion, "1.0.0") +_FILE_PICKER_VERSION: ServiceVersion = TypeAdapter(ServiceVersion).validate_python("1.0.0") def _generate_nodeids(project_id: ProjectID) -> tuple[NodeID, NodeID]: @@ -55,12 +55,12 @@ def _create_file_picker(download_link: str, output_label: str | None): # also to name the file in case it is downloaded data = {} - data["downloadLink"] = url = parse_obj_as(AnyUrl, download_link) + data["downloadLink"] = url = TypeAdapter(AnyUrl).validate_python(download_link) if output_label: data["label"] = Path(output_label).name elif url.path: data["label"] = Path(url.path).name - output = DownloadLink.parse_obj(data) + output = DownloadLink.model_validate(data) output_id = "outFile" node = Node( @@ -69,7 +69,7 @@ def _create_file_picker(download_link: str, output_label: str | None): label="File Picker", inputs={}, inputNodes=[], - outputs={output_id: output}, # type: ignore[dict-item] + outputs={output_id: output}, progress=0, ) return node, output_id @@ -94,12 +94,12 @@ def _create_project( uuid=project_id, name=name, description=description, - thumbnail=thumbnail, # type: ignore[arg-type] + thumbnail=thumbnail, prjOwner=owner.email, accessRights={owner.primary_gid: access_rights}, # type: ignore[dict-item] creationDate=DateTimeStr(now_str()), lastChangeDate=DateTimeStr(now_str()), - workbench=workbench, # type: ignore[arg-type] + workbench=workbench, ui=StudyUI(workbench=workbench_ui), # type: ignore[arg-type] ) @@ -153,7 +153,7 @@ def _create_project_with_filepicker_and_service( version=viewer_info.version, label=viewer_info.label, inputs={ - viewer_info.input_port_key: PortLink( # type: ignore[dict-item] + viewer_info.input_port_key: PortLink( nodeUuid=file_picker_id, output=file_picker_output_id, ) diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/settings.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/settings.py index d1a448777c7..38e3cf20969 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/settings.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/settings.py @@ -1,10 +1,10 @@ from datetime import timedelta -from typing import Any, ClassVar from aiohttp import web from common_library.pydantic_validators import validate_numeric_string_as_timedelta -from pydantic import TypeAdapter, field_validator, ConfigDict, ByteSize, HttpUrl +from pydantic import TypeAdapter, field_validator, ByteSize, HttpUrl from pydantic.fields import Field +from pydantic_settings import SettingsConfigDict from servicelib.aiohttp.application_keys import APP_SETTINGS_KEY from settings_library.base import BaseCustomSettings @@ -54,7 +54,7 @@ def is_login_required(self): _validate_studies_guest_account_lifetime = validate_numeric_string_as_timedelta( "STUDIES_GUEST_ACCOUNT_LIFETIME" ) - model_config = ConfigDict( + model_config = SettingsConfigDict( json_schema_extra={ "example": { "STUDIES_GUEST_ACCOUNT_LIFETIME": "2 1:10:00", # 2 days 1h and 10 mins From 82ba4bdd59aa9426ac53fa3d27dac034c516425e Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 10:27:35 +0200 Subject: [PATCH 013/165] remove deprecated --- .../src/simcore_service_webserver/application_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/application_settings.py b/services/web/server/src/simcore_service_webserver/application_settings.py index ef0b3d2781d..a02b35910cc 100644 --- a/services/web/server/src/simcore_service_webserver/application_settings.py +++ b/services/web/server/src/simcore_service_webserver/application_settings.py @@ -437,7 +437,7 @@ def setup_settings(app: web.Application) -> ApplicationSettings: app[APP_SETTINGS_KEY] = settings _logger.debug( "Captured app settings:\n%s", - app[APP_SETTINGS_KEY].json(indent=1, sort_keys=True), + app[APP_SETTINGS_KEY].model_dump_json(indent=1, sort_keys=True), ) return settings From be74c419a829997179f1a0ebce0a63b48b94eac9 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 10:31:28 +0200 Subject: [PATCH 014/165] fix model_config attr --- .../server/tests/unit/isolated/test_garbage_collector_core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/server/tests/unit/isolated/test_garbage_collector_core.py b/services/web/server/tests/unit/isolated/test_garbage_collector_core.py index 0d84fbb534c..920dfb2b035 100644 --- a/services/web/server/tests/unit/isolated/test_garbage_collector_core.py +++ b/services/web/server/tests/unit/isolated/test_garbage_collector_core.py @@ -123,8 +123,8 @@ async def test_remove_orphaned_services_with_no_running_services_does_nothing( @pytest.fixture def faker_dynamic_service_get() -> Callable[[], DynamicServiceGet]: def _() -> DynamicServiceGet: - return DynamicServiceGet.parse_obj( - DynamicServiceGet.Config.schema_extra["examples"][1] + return DynamicServiceGet.model_validate( + DynamicServiceGet.model_config["json_schema_extra"]["examples"][1] ) return _ From 8015258133536a15629b1fc73c4fab9a7b797c8f Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 11:08:22 +0200 Subject: [PATCH 015/165] remove deprecated --- .../activity/_handlers.py | 4 +-- .../api_keys/_api.py | 2 +- .../api_keys/_handlers.py | 6 ++-- .../simcore_service_webserver/catalog/_api.py | 2 +- .../catalog/_handlers.py | 8 ++--- .../catalog/client.py | 6 ++-- .../clusters/_handlers.py | 24 ++++++------- .../diagnostics/_handlers.py | 8 ++--- .../director_v2/_api_utils.py | 6 ++-- .../director_v2/_core_computations.py | 20 +++++------ .../director_v2/_handlers.py | 2 +- .../groups/_handlers.py | 2 +- .../login/_registration.py | 2 +- .../login/_registration_handlers.py | 2 +- .../long_running_tasks.py | 2 +- .../meta_modeling/_handlers.py | 4 +-- .../meta_modeling/_iterations.py | 6 ++-- .../payments/_methods_api.py | 2 +- .../payments/_onetime_api.py | 2 +- .../products/_handlers.py | 14 ++++---- .../products/_invitations_handlers.py | 2 +- .../projects/_comments_handlers.py | 14 ++++---- .../projects/_crud_api_create.py | 2 +- .../projects/_crud_api_read.py | 2 +- .../projects/_crud_handlers.py | 28 +++++++-------- .../projects/_db_utils.py | 2 +- .../projects/_metadata_handlers.py | 4 +-- .../projects/_nodes_handlers.py | 28 +++++++-------- .../projects/_ports_api.py | 6 ++-- .../projects/_ports_handlers.py | 8 ++--- .../_projects_nodes_pricing_unit_handlers.py | 6 ++-- .../projects/_states_handlers.py | 6 ++-- .../projects/_wallets_handlers.py | 6 ++-- .../projects/_workspaces_handlers.py | 4 +-- .../projects/projects_api.py | 4 +-- .../_pricing_plans_admin_handlers.py | 20 +++++------ .../resource_usage/_pricing_plans_handlers.py | 4 +-- .../resource_usage/_service_runs_handlers.py | 18 ++++++---- .../scicrunch/_resolver.py | 4 +-- .../storage/_handlers.py | 4 +-- .../simcore_service_webserver/storage/api.py | 12 +++---- .../tags/_handlers.py | 8 ++--- .../users/_handlers.py | 8 ++--- .../users/_notifications_handlers.py | 8 ++--- .../users/_preferences_api.py | 2 +- .../users/_preferences_db.py | 2 +- .../users/_preferences_handlers.py | 2 +- .../users/_tokens_handlers.py | 8 ++--- .../version_control/_core.py | 2 +- .../version_control/_handlers.py | 22 ++++++------ .../wallets/_groups_handlers.py | 10 +++--- .../wallets/_handlers.py | 10 +++--- .../wallets/_payments_handlers.py | 30 ++++++++-------- .../workspaces/_groups_handlers.py | 10 +++--- services/web/server/tests/conftest.py | 2 +- .../02/scicrunch/test_scicrunch__rest.py | 2 +- .../unit/isolated/test_catalog_api_units.py | 24 ++++++------- .../tests/unit/isolated/test_groups_models.py | 4 +-- .../unit/isolated/test_products_model.py | 12 ++++--- .../unit/isolated/test_projects_utils.py | 4 +-- .../unit/isolated/test_storage_schemas.py | 2 +- .../test_studies_dispatcher_models.py | 2 +- .../test_studies_dispatcher_settings.py | 2 +- .../unit/isolated/test_user_notifications.py | 4 +-- .../tests/unit/isolated/test_users_models.py | 4 +-- .../01/clusters/test_clusters_handlers.py | 14 ++++---- .../test_studies_dispatcher_handlers.py | 2 +- .../test_studies_dispatcher_projects.py | 2 +- .../01/test_catalog_handlers__pricing_plan.py | 2 +- .../01/test_catalog_handlers__services.py | 7 ++-- ...st_catalog_handlers__services_resources.py | 2 +- .../server/tests/unit/with_dbs/02/conftest.py | 2 +- .../unit/with_dbs/02/test_announcements.py | 6 ++-- .../02/test_projects_cancellations.py | 2 +- .../02/test_projects_crud_handlers__clone.py | 2 +- ...handlers__clone_in_workspace_and_folder.py | 2 +- .../02/test_projects_nodes_handler.py | 28 ++++++++++----- ...st_projects_nodes_pricing_unit_handlers.py | 2 +- .../02/test_projects_ports_handlers.py | 2 +- .../02/test_projects_states_handlers.py | 6 ++-- .../unit/with_dbs/03/folders/test_folders.py | 10 +++--- .../unit/with_dbs/03/invitations/conftest.py | 4 +-- ...login_handlers_registration_invitations.py | 6 ++-- .../test_products__invitations_handlers.py | 6 ++-- .../03/login/test_login_registration.py | 2 +- .../login/test_login_registration_handlers.py | 2 +- .../test_meta_modeling_iterations.py | 10 +++--- .../test_admin_pricing_plans.py | 29 +++++++++++----- .../03/resource_usage/test_pricing_plans.py | 4 +-- .../tests/unit/with_dbs/03/test_email.py | 4 +-- .../tests/unit/with_dbs/03/test_users.py | 8 +++-- .../with_dbs/03/test_users__notifications.py | 2 +- .../with_dbs/03/version_control/conftest.py | 2 +- .../test_version_control_handlers.py | 34 +++++++++---------- .../with_dbs/03/wallets/payments/conftest.py | 2 +- .../03/wallets/payments/test_payments.py | 6 ++-- .../wallets/payments/test_payments_methods.py | 18 +++++----- .../with_dbs/03/workspaces/test_workspaces.py | 4 +-- 98 files changed, 380 insertions(+), 346 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/activity/_handlers.py b/services/web/server/src/simcore_service_webserver/activity/_handlers.py index ba7ec32557a..4e87c8f3bc0 100644 --- a/services/web/server/src/simcore_service_webserver/activity/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/activity/_handlers.py @@ -4,7 +4,7 @@ import aiohttp import aiohttp.web from models_library.api_schemas_webserver.activity import ActivityStatusDict -from pydantic import parse_obj_as +from pydantic import TypeAdapter from servicelib.aiohttp.client_session import get_client_session from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from servicelib.request_keys import RQT_USERID_KEY @@ -73,5 +73,5 @@ async def get_activity_status(request: aiohttp.web.Request): if not res: raise aiohttp.web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) - assert parse_obj_as(ActivityStatusDict, res) is not None # nosec + assert TypeAdapter(ActivityStatusDict).validate_python(res) is not None # nosec return dict(res) diff --git a/services/web/server/src/simcore_service_webserver/api_keys/_api.py b/services/web/server/src/simcore_service_webserver/api_keys/_api.py index 9a46ad9f512..9bbe56f7c6f 100644 --- a/services/web/server/src/simcore_service_webserver/api_keys/_api.py +++ b/services/web/server/src/simcore_service_webserver/api_keys/_api.py @@ -70,7 +70,7 @@ async def get_api_key( ) -> ApiKeyGet | None: repo = ApiKeyRepo.create_from_app(app) row = await repo.get(display_name=name, user_id=user_id, product_name=product_name) - return ApiKeyGet.parse_obj(row) if row else None + return ApiKeyGet.model_validate(row) if row else None async def get_or_create_api_key( diff --git a/services/web/server/src/simcore_service_webserver/api_keys/_handlers.py b/services/web/server/src/simcore_service_webserver/api_keys/_handlers.py index 627d733d9c7..6327a57ca54 100644 --- a/services/web/server/src/simcore_service_webserver/api_keys/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/api_keys/_handlers.py @@ -32,7 +32,7 @@ class _RequestContext(RequestParams): @login_required @permission_required("user.apikey.*") async def list_api_keys(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) api_keys_names = await _api.list_api_keys( request.app, user_id=req_ctx.user_id, @@ -45,7 +45,7 @@ async def list_api_keys(request: web.Request): @login_required @permission_required("user.apikey.*") async def create_api_key(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) new = await parse_request_body_as(ApiKeyCreate, request) try: data = await _api.create_api_key( @@ -67,7 +67,7 @@ async def create_api_key(request: web.Request): @login_required @permission_required("user.apikey.*") async def delete_api_key(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) # NOTE: SEE https://github.com/ITISFoundation/osparc-simcore/issues/4920 body = await request.json() diff --git a/services/web/server/src/simcore_service_webserver/catalog/_api.py b/services/web/server/src/simcore_service_webserver/catalog/_api.py index b89c79b6dbf..ebd04b788cf 100644 --- a/services/web/server/src/simcore_service_webserver/catalog/_api.py +++ b/services/web/server/src/simcore_service_webserver/catalog/_api.py @@ -155,7 +155,7 @@ async def update_service_v2( user_id=user_id, service_key=service_key, service_version=service_version, - update=ServiceUpdateV2.parse_obj(update_data), + update=ServiceUpdateV2.model_validate(update_data), ) data = jsonable_encoder(service, exclude_unset=True) diff --git a/services/web/server/src/simcore_service_webserver/catalog/_handlers.py b/services/web/server/src/simcore_service_webserver/catalog/_handlers.py index d5aad19f958..6c697e45cbf 100644 --- a/services/web/server/src/simcore_service_webserver/catalog/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/catalog/_handlers.py @@ -90,7 +90,7 @@ async def list_services_latest(request: Request): user_id=request_ctx.user_id, product_name=request_ctx.product_name, unit_registry=request_ctx.unit_registry, - page_params=PageQueryParameters.construct( + page_params=PageQueryParameters.model_construct( offset=query_params.offset, limit=query_params.limit ), ) @@ -98,7 +98,7 @@ async def list_services_latest(request: Request): assert page_meta.limit == query_params.limit # nosec assert page_meta.offset == query_params.offset # nosec - page = Page[CatalogServiceGet].parse_obj( + page = Page[CatalogServiceGet].model_validate( paginate_data( chunk=page_items, request_url=request.url, @@ -133,7 +133,7 @@ async def get_service(request: Request): service_version=path_params.service_version, ) - return envelope_json_response(CatalogServiceGet.parse_obj(service)) + return envelope_json_response(CatalogServiceGet.model_validate(service)) @routes.patch( @@ -164,7 +164,7 @@ async def update_service(request: Request): unit_registry=request_ctx.unit_registry, ) - return envelope_json_response(CatalogServiceGet.parse_obj(updated)) + return envelope_json_response(CatalogServiceGet.model_validate(updated)) @routes.get( diff --git a/services/web/server/src/simcore_service_webserver/catalog/client.py b/services/web/server/src/simcore_service_webserver/catalog/client.py index 8a8f6083252..386ae811da0 100644 --- a/services/web/server/src/simcore_service_webserver/catalog/client.py +++ b/services/web/server/src/simcore_service_webserver/catalog/client.py @@ -19,7 +19,7 @@ ) from models_library.services_resources import ServiceResourcesDict from models_library.users import UserID -from pydantic import parse_obj_as +from pydantic import TypeAdapter from servicelib.aiohttp import status from servicelib.aiohttp.client_session import get_client_session from servicelib.rest_constants import X_PRODUCT_NAME_HEADER @@ -146,7 +146,7 @@ async def get_service_resources( async with session.get(url) as resp: resp.raise_for_status() dict_response = await resp.json() - return parse_obj_as(ServiceResourcesDict, dict_response) + return TypeAdapter(ServiceResourcesDict).validate_python(dict_response) async def get_service_access_rights( @@ -168,7 +168,7 @@ async def get_service_access_rights( ) as resp: resp.raise_for_status() body = await resp.json() - return ServiceAccessRightsGet.parse_obj(body) + return ServiceAccessRightsGet.model_validate(body) async def update_service( diff --git a/services/web/server/src/simcore_service_webserver/clusters/_handlers.py b/services/web/server/src/simcore_service_webserver/clusters/_handlers.py index 70752da883b..41d64a2d5ef 100644 --- a/services/web/server/src/simcore_service_webserver/clusters/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/clusters/_handlers.py @@ -11,7 +11,7 @@ ClusterPing, ) from models_library.users import UserID -from pydantic import BaseModel, Field, parse_obj_as +from pydantic import BaseModel, Field, TypeAdapter from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_body_as, @@ -78,7 +78,7 @@ class _RequestContext(BaseModel): @permission_required("clusters.create") @_handle_cluster_exceptions async def create_cluster(request: web.Request) -> web.Response: - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) new_cluster = await parse_request_body_as(ClusterCreate, request) created_cluster = await director_v2_api.create_cluster( @@ -94,13 +94,13 @@ async def create_cluster(request: web.Request) -> web.Response: @permission_required("clusters.read") @_handle_cluster_exceptions async def list_clusters(request: web.Request) -> web.Response: - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) clusters = await director_v2_api.list_clusters( app=request.app, user_id=req_ctx.user_id, ) - assert parse_obj_as(list[ClusterGet], clusters) is not None # nosec + assert TypeAdapter(list[ClusterGet]).validate_python(clusters) is not None # nosec return envelope_json_response(clusters) @@ -109,7 +109,7 @@ async def list_clusters(request: web.Request) -> web.Response: @permission_required("clusters.read") @_handle_cluster_exceptions async def get_cluster(request: web.Request) -> web.Response: - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ClusterPathParams, request) cluster = await director_v2_api.get_cluster( @@ -117,7 +117,7 @@ async def get_cluster(request: web.Request) -> web.Response: user_id=req_ctx.user_id, cluster_id=path_params.cluster_id, ) - assert parse_obj_as(ClusterGet, cluster) is not None # nosec + assert ClusterGet.model_validate(cluster) is not None # nosec return envelope_json_response(cluster) @@ -126,7 +126,7 @@ async def get_cluster(request: web.Request) -> web.Response: @permission_required("clusters.write") @_handle_cluster_exceptions async def update_cluster(request: web.Request) -> web.Response: - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ClusterPathParams, request) cluster_patch = await parse_request_body_as(ClusterPatch, request) @@ -137,7 +137,7 @@ async def update_cluster(request: web.Request) -> web.Response: cluster_patch=cluster_patch, ) - assert parse_obj_as(ClusterGet, updated_cluster) is not None # nosec + assert ClusterGet.model_validate(updated_cluster) is not None # nosec return envelope_json_response(updated_cluster) @@ -146,7 +146,7 @@ async def update_cluster(request: web.Request) -> web.Response: @permission_required("clusters.delete") @_handle_cluster_exceptions async def delete_cluster(request: web.Request) -> web.Response: - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ClusterPathParams, request) await director_v2_api.delete_cluster( @@ -165,7 +165,7 @@ async def delete_cluster(request: web.Request) -> web.Response: @permission_required("clusters.read") @_handle_cluster_exceptions async def get_cluster_details(request: web.Request) -> web.Response: - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ClusterPathParams, request) cluster_details = await director_v2_api.get_cluster_details( @@ -173,7 +173,7 @@ async def get_cluster_details(request: web.Request) -> web.Response: user_id=req_ctx.user_id, cluster_id=path_params.cluster_id, ) - assert parse_obj_as(ClusterDetails, cluster_details) is not None # nosec + assert ClusterDetails.model_validate(cluster_details) is not None # nosec return envelope_json_response(cluster_details) @@ -199,7 +199,7 @@ async def ping_cluster(request: web.Request) -> web.Response: @permission_required("clusters.read") @_handle_cluster_exceptions async def ping_cluster_cluster_id(request: web.Request) -> web.Response: - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ClusterPathParams, request) await director_v2_api.ping_specific_cluster( diff --git a/services/web/server/src/simcore_service_webserver/diagnostics/_handlers.py b/services/web/server/src/simcore_service_webserver/diagnostics/_handlers.py index 13154bf5723..6ad898bcef2 100644 --- a/services/web/server/src/simcore_service_webserver/diagnostics/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/diagnostics/_handlers.py @@ -10,7 +10,7 @@ from aiohttp import ClientError, ClientSession, web from models_library.app_diagnostics import AppStatusCheck from models_library.utils.pydantic_tools_extension import FieldNotRequired -from pydantic import BaseModel, parse_obj_as +from pydantic import BaseModel from servicelib.aiohttp.client_session import get_client_session from servicelib.aiohttp.requests_validation import parse_request_query_parameters_as from servicelib.utils import logged_gather @@ -62,7 +62,7 @@ async def get_app_diagnostics(request: web.Request): top_tracemalloc=get_tracemalloc_info(top=query_params.top_tracemalloc) ) - assert parse_obj_as(StatusDiagnosticsGet, data) is not None # nosec + assert StatusDiagnosticsGet.model_validate(data) is not None # nosec return envelope_json_response(data) @@ -99,7 +99,7 @@ def _get_client_session_info(): return info - check = AppStatusCheck.parse_obj( + check = AppStatusCheck.model_validate( { "app_name": APP_NAME, "version": API_VERSION, @@ -150,7 +150,7 @@ async def _check_resource_usage_tracker(): reraise=False, ) - return envelope_json_response(check.dict(exclude_unset=True)) + return envelope_json_response(check.model_dump(exclude_unset=True)) @routes.get(f"/{api_version_prefix}/status/{{service_name}}", name="get_service_status") diff --git a/services/web/server/src/simcore_service_webserver/director_v2/_api_utils.py b/services/web/server/src/simcore_service_webserver/director_v2/_api_utils.py index e9bbca91c50..74bc8e8ee14 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/_api_utils.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/_api_utils.py @@ -2,7 +2,7 @@ from models_library.projects import ProjectID from models_library.users import UserID from models_library.wallets import WalletID, WalletInfo -from pydantic import parse_obj_as +from pydantic import TypeAdapter from ..application_settings import get_application_settings from ..products.api import Product @@ -35,7 +35,9 @@ async def get_wallet_info( ) if user_default_wallet_preference is None: raise UserDefaultWalletNotFoundError(uid=user_id) - project_wallet_id = parse_obj_as(WalletID, user_default_wallet_preference.value) + project_wallet_id = TypeAdapter(WalletID).validate_python( + user_default_wallet_preference.value + ) await projects_api.connect_wallet_to_project( app, product_name=product_name, diff --git a/services/web/server/src/simcore_service_webserver/director_v2/_core_computations.py b/services/web/server/src/simcore_service_webserver/director_v2/_core_computations.py index 950d92fa2eb..3cdefd8426e 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/_core_computations.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/_core_computations.py @@ -26,7 +26,7 @@ from models_library.projects_pipeline import ComputationTask from models_library.users import UserID from models_library.utils.fastapi_encoders import jsonable_encoder -from pydantic import parse_obj_as +from pydantic import TypeAdapter from pydantic.types import PositiveInt from servicelib.aiohttp import status from servicelib.logging_utils import log_decorator @@ -182,7 +182,7 @@ async def get_computation_task( computation_task_out_dict = await request_director_v2( app, "GET", backend_url, expected_status=web.HTTPOk ) - task_out = ComputationTask.parse_obj(computation_task_out_dict) + task_out = ComputationTask.model_validate(computation_task_out_dict) _logger.debug("found computation task: %s", f"{task_out=}") return task_out except DirectorServiceError as exc: @@ -226,7 +226,7 @@ async def create_cluster( url=(settings.base_url / "clusters").update_query(user_id=int(user_id)), expected_status=web.HTTPCreated, data=json.loads( - new_cluster.json( + new_cluster.model_dump_json( by_alias=True, exclude_unset=True, encoder=create_json_encoder_wo_secrets(ClusterCreate), @@ -234,7 +234,7 @@ async def create_cluster( ), ) assert isinstance(cluster, dict) # nosec - assert parse_obj_as(ClusterGet, cluster) is not None # nosec + assert ClusterGet.model_validate(cluster) is not None # nosec return cluster @@ -248,7 +248,7 @@ async def list_clusters(app: web.Application, user_id: UserID) -> list[DataType] ) assert isinstance(clusters, list) # nosec - assert parse_obj_as(list[ClusterGet], clusters) is not None # nosec + assert TypeAdapter(list[ClusterGet]).validate_python(clusters) is not None # nosec return clusters @@ -276,7 +276,7 @@ async def get_cluster( ) assert isinstance(cluster, dict) # nosec - assert parse_obj_as(ClusterGet, cluster) is not None # nosec + assert ClusterGet.model_validate(cluster) is not None # nosec return cluster @@ -304,7 +304,7 @@ async def get_cluster_details( }, ) assert isinstance(cluster, dict) # nosec - assert parse_obj_as(ClusterDetails, cluster) is not None # nosec + assert ClusterDetails.model_validate(cluster) is not None # nosec return cluster @@ -323,7 +323,7 @@ async def update_cluster( ), expected_status=web.HTTPOk, data=json.loads( - cluster_patch.json( + cluster_patch.model_dump_json( by_alias=True, exclude_unset=True, encoder=create_json_encoder_wo_secrets(ClusterPatch), @@ -342,7 +342,7 @@ async def update_cluster( ) assert isinstance(cluster, dict) # nosec - assert parse_obj_as(ClusterGet, cluster) is not None # nosec + assert ClusterGet.model_validate(cluster) is not None # nosec return cluster @@ -378,7 +378,7 @@ async def ping_cluster(app: web.Application, cluster_ping: ClusterPing) -> None: url=settings.base_url / "clusters:ping", expected_status=web.HTTPNoContent, data=json.loads( - cluster_ping.json( + cluster_ping.model_dump_json( by_alias=True, exclude_unset=True, encoder=create_json_encoder_wo_secrets(ClusterPing), diff --git a/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py b/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py index b84eb68dfd9..6a1a925ffff 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py @@ -190,7 +190,7 @@ async def start_computation(request: web.Request) -> web.Response: @permission_required("services.pipeline.*") @permission_required("project.read") async def stop_computation(request: web.Request) -> web.Response: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) computations = ComputationsApi(request.app) run_policy = get_project_run_policy(request.app) assert run_policy # nosec diff --git a/services/web/server/src/simcore_service_webserver/groups/_handlers.py b/services/web/server/src/simcore_service_webserver/groups/_handlers.py index efa3bd906e6..b817533c0fd 100644 --- a/services/web/server/src/simcore_service_webserver/groups/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/groups/_handlers.py @@ -196,7 +196,7 @@ async def add_group_user(request: web.Request): """ Adds a user in an organization group """ - req_ctx = _GroupsRequestContext.parse_obj(request) + req_ctx = _GroupsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_GroupPathParams, request) new_user_in_group = await request.json() diff --git a/services/web/server/src/simcore_service_webserver/login/_registration.py b/services/web/server/src/simcore_service_webserver/login/_registration.py index bf9e09fcd30..c6d5d14007e 100644 --- a/services/web/server/src/simcore_service_webserver/login/_registration.py +++ b/services/web/server/src/simcore_service_webserver/login/_registration.py @@ -299,7 +299,7 @@ async def check_and_consume_invitation( # database-type invitations if confirmation_token := await validate_confirmation_code(invitation_code, db, cfg): try: - invitation_data: InvitationData = _InvitationValidator.parse_obj( + invitation_data: InvitationData = _InvitationValidator.model_validate( confirmation_token ).data return invitation_data diff --git a/services/web/server/src/simcore_service_webserver/login/_registration_handlers.py b/services/web/server/src/simcore_service_webserver/login/_registration_handlers.py index 869fa7a2973..42e8229e7a6 100644 --- a/services/web/server/src/simcore_service_webserver/login/_registration_handlers.py +++ b/services/web/server/src/simcore_service_webserver/login/_registration_handlers.py @@ -98,7 +98,7 @@ class _AuthenticatedContext(BaseModel): @login_required @permission_required("user.profile.delete") async def unregister_account(request: web.Request): - req_ctx = _AuthenticatedContext.parse_obj(request) + req_ctx = _AuthenticatedContext.model_validate(request) body = await parse_request_body_as(UnregisterCheck, request) product: Product = get_current_product(request) diff --git a/services/web/server/src/simcore_service_webserver/long_running_tasks.py b/services/web/server/src/simcore_service_webserver/long_running_tasks.py index a7e4e8c725b..2f42f18927e 100644 --- a/services/web/server/src/simcore_service_webserver/long_running_tasks.py +++ b/services/web/server/src/simcore_service_webserver/long_running_tasks.py @@ -28,7 +28,7 @@ async def _test_task_context_decorator( request: web.Request, ) -> web.StreamResponse: """this task context callback tries to get the user_id from the query if available""" - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) request[RQT_LONG_RUNNING_TASKS_CONTEXT_KEY] = jsonable_encoder(req_ctx) return await handler(request) diff --git a/services/web/server/src/simcore_service_webserver/meta_modeling/_handlers.py b/services/web/server/src/simcore_service_webserver/meta_modeling/_handlers.py index 7b2007cdb94..4a3ef629cac 100644 --- a/services/web/server/src/simcore_service_webserver/meta_modeling/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/meta_modeling/_handlers.py @@ -292,7 +292,7 @@ async def list_project_iterations(request: web.Request) -> web.Response: for item in iterations_range.items ] - page = Page[ProjectIterationItem].parse_obj( + page = Page[ProjectIterationItem].model_validate( paginate_data( chunk=page_items, request_url=request.url, @@ -395,7 +395,7 @@ def _get_project_results(project_id) -> ExtractedResults: for item in iterations_range.items ] - page = Page[ProjectIterationResultItem].parse_obj( + page = Page[ProjectIterationResultItem].model_validate( paginate_data( chunk=page_items, request_url=request.url, diff --git a/services/web/server/src/simcore_service_webserver/meta_modeling/_iterations.py b/services/web/server/src/simcore_service_webserver/meta_modeling/_iterations.py index c4b16f12caf..1663d7ac96d 100644 --- a/services/web/server/src/simcore_service_webserver/meta_modeling/_iterations.py +++ b/services/web/server/src/simcore_service_webserver/meta_modeling/_iterations.py @@ -156,7 +156,7 @@ def from_tag_name( ) -> Optional["ProjectIteration"]: """Parses iteration info from tag name""" try: - return cls.parse_obj(parse_iteration_tag_name(tag_name)) + return cls.model_validate(parse_iteration_tag_name(tag_name)) except ValidationError as err: if return_none_if_fails: _logger.debug("%s", f"{err=}") @@ -218,7 +218,7 @@ async def get_or_create_runnable_projects( raise web.HTTPForbidden(reason="Unauthenticated request") from err project_nodes: dict[NodeID, Node] = { - nid: Node.parse_obj(n) for nid, n in project["workbench"].items() + nid: Node.model_validate(n) for nid, n in project["workbench"].items() } # init returns @@ -326,7 +326,7 @@ async def get_runnable_projects_ids( project: ProjectDict = await vc_repo.get_project(str(project_uuid)) assert project["uuid"] == str(project_uuid) # nosec project_nodes: dict[NodeID, Node] = { - nid: Node.parse_obj(n) for nid, n in project["workbench"].items() + nid: Node.model_validate(n) for nid, n in project["workbench"].items() } # init returns diff --git a/services/web/server/src/simcore_service_webserver/payments/_methods_api.py b/services/web/server/src/simcore_service_webserver/payments/_methods_api.py index a1eac2b440d..bf6c4a32a0b 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_methods_api.py +++ b/services/web/server/src/simcore_service_webserver/payments/_methods_api.py @@ -56,7 +56,7 @@ def _to_api_model( ) -> PaymentMethodGet: assert entry.completed_at # nosec - return PaymentMethodGet.parse_obj( + return PaymentMethodGet.model_validate( { **payment_method_details_from_gateway, "idr": entry.payment_method_id, diff --git a/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py b/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py index f54f48403bb..bff07528d2c 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py +++ b/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py @@ -62,7 +62,7 @@ def _to_api_model( if transaction.invoice_url: data["invoice_url"] = transaction.invoice_url - return PaymentTransaction.parse_obj(data) + return PaymentTransaction.model_validate(data) @log_decorator(_logger, level=logging.INFO) diff --git a/services/web/server/src/simcore_service_webserver/products/_handlers.py b/services/web/server/src/simcore_service_webserver/products/_handlers.py index bfdabef6d6f..bd845e379ec 100644 --- a/services/web/server/src/simcore_service_webserver/products/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/products/_handlers.py @@ -5,7 +5,7 @@ from models_library.api_schemas_webserver.product import GetCreditPrice, GetProduct from models_library.basic_types import IDStr from models_library.users import UserID -from pydantic import Extra, Field +from pydantic import Field from servicelib.aiohttp.requests_validation import ( RequestParams, StrictRequestParams, @@ -36,12 +36,12 @@ class _ProductsRequestContext(RequestParams): @login_required @permission_required("product.price.read") async def _get_current_product_price(request: web.Request): - req_ctx = _ProductsRequestContext.parse_obj(request) + req_ctx = _ProductsRequestContext.model_validate(request) price_info = await _api.get_current_product_credit_price_info(request) credit_price = GetCreditPrice( product_name=req_ctx.product_name, - usd_per_credit=price_info.usd_per_credit if price_info else None, # type: ignore[arg-type] + usd_per_credit=price_info.usd_per_credit if price_info else None, min_payment_amount_usd=price_info.min_payment_amount_usd # type: ignore[arg-type] if price_info else None, @@ -57,7 +57,7 @@ class _ProductsRequestParams(StrictRequestParams): @login_required @permission_required("product.details.*") async def _get_product(request: web.Request): - req_ctx = _ProductsRequestContext.parse_obj(request) + req_ctx = _ProductsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_ProductsRequestParams, request) if path_params.product_name == "current": @@ -70,8 +70,8 @@ async def _get_product(request: web.Request): except KeyError as err: raise web.HTTPNotFound(reason=f"{product_name=} not found") from err - assert GetProduct.Config.extra == Extra.ignore # nosec - data = GetProduct(**product.dict(), templates=[]) + assert GetProduct.model_config.extra == "ignore" # nosec + data = GetProduct(**product.model_dump(), templates=[]) return envelope_json_response(data) @@ -86,7 +86,7 @@ class _ProductTemplateParams(_ProductsRequestParams): @login_required @permission_required("product.details.*") async def update_product_template(request: web.Request): - req_ctx = _ProductsRequestContext.parse_obj(request) + req_ctx = _ProductsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_ProductTemplateParams, request) assert req_ctx # nosec diff --git a/services/web/server/src/simcore_service_webserver/products/_invitations_handlers.py b/services/web/server/src/simcore_service_webserver/products/_invitations_handlers.py index a300a4c43e9..4f530d3b566 100644 --- a/services/web/server/src/simcore_service_webserver/products/_invitations_handlers.py +++ b/services/web/server/src/simcore_service_webserver/products/_invitations_handlers.py @@ -35,7 +35,7 @@ class _ProductsRequestContext(RequestParams): @login_required @permission_required("product.invitations.create") async def generate_invitation(request: web.Request): - req_ctx = _ProductsRequestContext.parse_obj(request) + req_ctx = _ProductsRequestContext.model_validate(request) body = await parse_request_body_as(GenerateInvitation, request) _, user_email = await get_user_name_and_email(request.app, user_id=req_ctx.user_id) diff --git a/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py index 1cb7e7c2ed1..8c22479100e 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py @@ -15,7 +15,7 @@ Page, ) from models_library.rest_pagination_utils import paginate_data -from pydantic import ConfigDict, BaseModel, Field, NonNegativeInt +from pydantic import BaseModel, ConfigDict, Field, NonNegativeInt from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_body_as, @@ -81,7 +81,7 @@ class _ProjectCommentsBodyParams(BaseModel): @permission_required("project.read") @_handle_project_comments_exceptions async def create_project_comment(request: web.Request): - req_ctx = RequestContext.parse_obj(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) @@ -121,7 +121,7 @@ class _ListProjectCommentsQueryParams(BaseModel): @permission_required("project.read") @_handle_project_comments_exceptions async def list_project_comments(request: web.Request): - req_ctx = RequestContext.parse_obj(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 @@ -147,7 +147,7 @@ async def list_project_comments(request: web.Request): limit=query_params.limit, ) - page = Page[dict[str, Any]].parse_obj( + page = Page[dict[str, Any]].model_validate( paginate_data( chunk=project_comments, request_url=request.url, @@ -169,7 +169,7 @@ async def list_project_comments(request: web.Request): @login_required @permission_required("project.read") async def update_project_comment(request: web.Request): - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as( _ProjectCommentsWithCommentPathParams, request ) @@ -199,7 +199,7 @@ async def update_project_comment(request: web.Request): @permission_required("project.read") @_handle_project_comments_exceptions async def delete_project_comment(request: web.Request): - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as( _ProjectCommentsWithCommentPathParams, request ) @@ -227,7 +227,7 @@ async def delete_project_comment(request: web.Request): @permission_required("project.read") @_handle_project_comments_exceptions async def get_project_comment(request: web.Request): - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as( _ProjectCommentsWithCommentPathParams, request ) diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py b/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py index 37416912e15..7d4464cebc1 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py @@ -420,7 +420,7 @@ async def create_project( # pylint: disable=too-many-arguments,too-many-branche } # Ensures is like ProjectGet - data = ProjectGet.parse_obj(new_project).data(exclude_unset=True) + data = ProjectGet.model_validate(new_project).data(exclude_unset=True) raise web.HTTPCreated( text=json_dumps({"data": data}), diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py b/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py index 21802e9841d..a589a79bba8 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py @@ -57,7 +57,7 @@ async def _append_fields( } # validate - return model_schema_cls.parse_obj(project).data(exclude_unset=True) + return model_schema_cls.model_validate(project).data(exclude_unset=True) async def list_projects( # pylint: disable=too-many-arguments diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py index 11d1f701b32..e168a11ab70 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py @@ -131,7 +131,7 @@ async def _wrapper(request: web.Request) -> web.StreamResponse: @permission_required("project.create") @permission_required("services.pipeline.*") # due to update_pipeline_db async def create_project(request: web.Request): - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) query_params: ProjectCreateParams = parse_request_query_parameters_as( ProjectCreateParams, request ) @@ -198,7 +198,7 @@ async def list_projects(request: web.Request): web.HTTPUnprocessableEntity: (422) if validation of request parameters fail """ - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) query_params: ProjectListWithJsonStrParams = parse_request_query_parameters_as( ProjectListWithJsonStrParams, request ) @@ -217,7 +217,7 @@ async def list_projects(request: web.Request): workspace_id=query_params.workspace_id, ) - page = Page[ProjectDict].parse_obj( + page = Page[ProjectDict].model_validate( paginate_data( chunk=projects, request_url=request.url, @@ -237,7 +237,7 @@ async def list_projects(request: web.Request): @permission_required("project.read") @_handle_projects_exceptions async def list_projects_full_search(request: web.Request): - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) query_params: ProjectListFullSearchWithJsonStrParams = ( parse_request_query_parameters_as( ProjectListFullSearchWithJsonStrParams, request @@ -256,7 +256,7 @@ async def list_projects_full_search(request: web.Request): tag_ids_list=tag_ids_list, ) - page = Page[ProjectDict].parse_obj( + page = Page[ProjectDict].model_validate( paginate_data( chunk=projects, request_url=request.url, @@ -287,7 +287,7 @@ async def get_active_project(request: web.Request) -> web.Response: web.HTTPUnprocessableEntity: (422) if validation of request parameters fail web.HTTPNotFound: If active project is not found """ - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) query_params: ProjectActiveParams = parse_request_query_parameters_as( ProjectActiveParams, request ) @@ -312,7 +312,7 @@ async def get_active_project(request: web.Request) -> web.Response: # updates project's permalink field await update_or_pop_permalink_in_project(request, project) - data = ProjectGet.parse_obj(project).data(exclude_unset=True) + data = ProjectGet.model_validate(project).data(exclude_unset=True) return web.json_response({"data": data}, dumps=json_dumps) @@ -333,7 +333,7 @@ async def get_project(request: web.Request): web.HTTPNotFound: This project was not found """ - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) user_available_services: list[dict] = await get_services_for_user_in_product( @@ -368,7 +368,7 @@ async def get_project(request: web.Request): # Adds permalink await update_or_pop_permalink_in_project(request, project) - data = ProjectGet.parse_obj(project).data(exclude_unset=True) + data = ProjectGet.model_validate(project).data(exclude_unset=True) return web.json_response({"data": data}, dumps=json_dumps) except ProjectInvalidRightsError as exc: @@ -426,7 +426,7 @@ async def replace_project(request: web.Request): """ db: ProjectDBAPI = ProjectDBAPI.get_from_app_context(request.app) - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) try: @@ -452,7 +452,7 @@ async def replace_project(request: web.Request): ) try: - Project.parse_obj(new_project) # validate + Project.model_validate(new_project) # validate current_project = await projects_api.get_project_for_user( request.app, @@ -565,7 +565,7 @@ async def replace_project(request: web.Request): @permission_required("services.pipeline.*") @_handle_projects_exceptions async def patch_project(request: web.Request): - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) project_patch = await parse_request_body_as(ProjectPatch, request) @@ -602,7 +602,7 @@ async def delete_project(request: web.Request): web.HTTPNoContent: Sucess """ - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) try: @@ -677,7 +677,7 @@ async def delete_project(request: web.Request): @permission_required("project.create") @permission_required("services.pipeline.*") # due to update_pipeline_db async def clone_project(request: web.Request): - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) return await start_long_running_task( diff --git a/services/web/server/src/simcore_service_webserver/projects/_db_utils.py b/services/web/server/src/simcore_service_webserver/projects/_db_utils.py index 8bda162ab6f..9173c2b131d 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_db_utils.py +++ b/services/web/server/src/simcore_service_webserver/projects/_db_utils.py @@ -380,7 +380,7 @@ def patch_workbench( raise ProjectInvalidUsageError # if it's a new node, let's check that it validates try: - Node.parse_obj(new_node_data) + Node.model_validate(new_node_data) patched_project["workbench"][node_key] = new_node_data changed_entries.update({node_key: new_node_data}) except ValidationError as err: diff --git a/services/web/server/src/simcore_service_webserver/projects/_metadata_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_metadata_handlers.py index 614d0ba03b9..802c13f7937 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_metadata_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_metadata_handlers.py @@ -79,7 +79,7 @@ async def wrapper(request: web.Request) -> web.StreamResponse: @permission_required("project.read") @_handle_project_exceptions async def get_project_metadata(request: web.Request) -> web.Response: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) custom_metadata = await _metadata_api.get_project_custom_metadata( @@ -99,7 +99,7 @@ async def get_project_metadata(request: web.Request) -> web.Response: @permission_required("project.update") @_handle_project_exceptions async def update_project_metadata(request: web.Request) -> web.Response: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) update = await parse_request_body_as(ProjectMetadataUpdate, request) diff --git a/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py index fd7a21eaad6..4c3c4194066 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py @@ -145,7 +145,7 @@ class NodePathParams(ProjectPathParams): @permission_required("project.node.create") @_handle_project_nodes_exceptions async def create_node(request: web.Request) -> web.Response: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) body = await parse_request_body_as(NodeCreate, request) @@ -188,7 +188,7 @@ async def create_node(request: web.Request) -> web.Response: @_handle_project_nodes_exceptions # NOTE: Careful, this endpoint is actually "get_node_state," and it doesn't return a Node resource. async def get_node(request: web.Request) -> web.Response: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(NodePathParams, request) # ensure the project exists @@ -226,7 +226,7 @@ async def get_node(request: web.Request) -> web.Response: @permission_required("project.node.update") @_handle_project_nodes_exceptions async def patch_project_node(request: web.Request) -> web.Response: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(NodePathParams, request) node_patch = await parse_request_body_as(NodePatch, request) @@ -247,7 +247,7 @@ async def patch_project_node(request: web.Request) -> web.Response: @permission_required("project.node.delete") @_handle_project_nodes_exceptions async def delete_node(request: web.Request) -> web.Response: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(NodePathParams, request) # ensure the project exists @@ -295,7 +295,7 @@ async def retrieve_node(request: web.Request) -> web.Response: @permission_required("project.node.update") @_handle_project_nodes_exceptions async def update_node_outputs(request: web.Request) -> web.Response: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(NodePathParams, request) node_outputs = await parse_request_body_as(NodeOutputs, request) @@ -323,7 +323,7 @@ async def update_node_outputs(request: web.Request) -> web.Response: @_handle_project_nodes_exceptions async def start_node(request: web.Request) -> web.Response: """Has only effect on nodes associated to dynamic services""" - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(NodePathParams, request) await projects_api.start_project_node( @@ -367,7 +367,7 @@ async def _stop_dynamic_service_task( @_handle_project_nodes_exceptions async def stop_node(request: web.Request) -> web.Response: """Has only effect on nodes associated to dynamic services""" - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(NodePathParams, request) save_state = await has_user_project_access_rights( @@ -430,7 +430,7 @@ async def restart_node(request: web.Request) -> web.Response: @permission_required("project.node.read") @_handle_project_nodes_exceptions async def get_node_resources(request: web.Request) -> web.Response: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(NodePathParams, request) # ensure the project exists @@ -463,7 +463,7 @@ async def get_node_resources(request: web.Request) -> web.Response: @permission_required("project.node.update") @_handle_project_nodes_exceptions async def replace_node_resources(request: web.Request) -> web.Response: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(NodePathParams, request) body = await parse_request_body_as(ServiceResourcesDict, request) @@ -524,7 +524,7 @@ class _ProjectGroupAccess(BaseModel): async def get_project_services_access_for_gid( request: web.Request, ) -> web.Response: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) query_params: _ServicesAccessQuery = parse_request_query_parameters_as( _ServicesAccessQuery, request @@ -640,7 +640,7 @@ class _ProjectNodePreview(BaseModel): @permission_required("project.read") @_handle_project_nodes_exceptions async def list_project_nodes_previews(request: web.Request) -> web.Response: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) assert req_ctx # nosec @@ -650,7 +650,7 @@ async def list_project_nodes_previews(request: web.Request) -> web.Response: project_uuid=f"{path_params.project_id}", user_id=req_ctx.user_id, ) - project = Project.parse_obj(project_data) + project = Project.model_validate(project_data) for node_id, node in project.workbench.items(): screenshots = await get_node_screenshots( @@ -680,7 +680,7 @@ async def list_project_nodes_previews(request: web.Request) -> web.Response: @permission_required("project.read") @_handle_project_nodes_exceptions async def get_project_node_preview(request: web.Request) -> web.Response: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(NodePathParams, request) assert req_ctx # nosec @@ -690,7 +690,7 @@ async def get_project_node_preview(request: web.Request) -> web.Response: user_id=req_ctx.user_id, ) - project = Project.parse_obj(project_data) + project = Project.model_validate(project_data) node = project.workbench.get(NodeIDStr(path_params.node_id)) if node is None: diff --git a/services/web/server/src/simcore_service_webserver/projects/_ports_api.py b/services/web/server/src/simcore_service_webserver/projects/_ports_api.py index 7a24dec5923..637543aa41a 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_ports_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/_ports_api.py @@ -163,9 +163,7 @@ def set_inputs_in_project( class _NonStrictPortLink(PortLink): - model_config = ConfigDict( - populate_by_name=True - ) + model_config = ConfigDict(populate_by_name=True) class _OutputPortInfo(NamedTuple): @@ -182,7 +180,7 @@ def _get_outputs_in_workbench(workbench: dict[NodeID, Node]) -> dict[NodeID, Any if port.node.inputs: try: # Every port is associated to the output of a task - port_link = _NonStrictPortLink.parse_obj( + port_link = _NonStrictPortLink.model_validate( port.node.inputs[KeyIDStr("in_1")] ) # Here we resolve which task and which tasks' output is associated to this port? diff --git a/services/web/server/src/simcore_service_webserver/projects/_ports_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_ports_handlers.py index 0d2fb6f3eca..e34548a9022 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_ports_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_ports_handlers.py @@ -103,7 +103,7 @@ async def _get_validated_workbench_model( @permission_required("project.read") @_handle_project_exceptions async def get_project_inputs(request: web.Request) -> web.Response: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) assert request.app # nosec @@ -129,7 +129,7 @@ async def get_project_inputs(request: web.Request) -> web.Response: @_handle_project_exceptions async def update_project_inputs(request: web.Request) -> web.Response: db: ProjectDBAPI = ProjectDBAPI.get_from_app_context(request.app) - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) inputs_updates = await parse_request_body_as(list[ProjectInputUpdate], request) @@ -192,7 +192,7 @@ async def update_project_inputs(request: web.Request) -> web.Response: @permission_required("project.read") @_handle_project_exceptions async def get_project_outputs(request: web.Request) -> web.Response: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) assert request.app # nosec @@ -239,7 +239,7 @@ class ProjectMetadataPortGet(BaseModel): @permission_required("project.read") @_handle_project_exceptions async def list_project_metadata_ports(request: web.Request) -> web.Response: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) assert request.app # nosec diff --git a/services/web/server/src/simcore_service_webserver/projects/_projects_nodes_pricing_unit_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_projects_nodes_pricing_unit_handlers.py index 1e7734f3b1c..208f3fc782d 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_projects_nodes_pricing_unit_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_projects_nodes_pricing_unit_handlers.py @@ -10,7 +10,7 @@ from models_library.projects import ProjectID from models_library.projects_nodes_io import NodeID, NodeIDStr from models_library.resource_tracker import PricingPlanId, PricingUnitId -from pydantic import ConfigDict, BaseModel +from pydantic import BaseModel, ConfigDict from pydantic.errors import PydanticErrorMixin from servicelib.aiohttp.requests_validation import parse_request_path_parameters_as from servicelib.aiohttp.typing_extension import Handler @@ -64,7 +64,7 @@ async def wrapper(request: web.Request) -> web.StreamResponse: @_handle_projects_nodes_pricing_unit_exceptions async def get_project_node_pricing_unit(request: web.Request): db: ProjectDBAPI = ProjectDBAPI.get_from_app_context(request.app) - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(NodePathParams, request) # ensure the project exists @@ -111,7 +111,7 @@ class _ProjectNodePricingUnitPathParams(BaseModel): @_handle_projects_nodes_pricing_unit_exceptions async def connect_pricing_unit_to_project_node(request: web.Request): db: ProjectDBAPI = ProjectDBAPI.get_from_app_context(request.app) - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as( _ProjectNodePricingUnitPathParams, request ) diff --git a/services/web/server/src/simcore_service_webserver/projects/_states_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_states_handlers.py index ca0725b37b9..07ba932d64b 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_states_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_states_handlers.py @@ -93,7 +93,7 @@ class _OpenProjectQuery(BaseModel): @permission_required("project.open") @_handle_project_exceptions async def open_project(request: web.Request) -> web.Response: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) query_params: _OpenProjectQuery = parse_request_query_parameters_as( _OpenProjectQuery, request @@ -196,7 +196,7 @@ async def open_project(request: web.Request) -> web.Response: @permission_required("project.close") @_handle_project_exceptions async def close_project(request: web.Request) -> web.Response: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) try: @@ -234,7 +234,7 @@ async def close_project(request: web.Request) -> web.Response: @login_required @permission_required("project.read") async def get_project_state(request: web.Request) -> web.Response: - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) # check that project exists and queries state diff --git a/services/web/server/src/simcore_service_webserver/projects/_wallets_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_wallets_handlers.py index e4b3293e465..56e7136d299 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_wallets_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_wallets_handlers.py @@ -9,7 +9,7 @@ from models_library.api_schemas_webserver.wallets import WalletGet from models_library.projects import ProjectID from models_library.wallets import WalletID -from pydantic import ConfigDict, BaseModel +from pydantic import BaseModel, ConfigDict from servicelib.aiohttp.requests_validation import parse_request_path_parameters_as from servicelib.aiohttp.typing_extension import Handler from simcore_service_webserver.utils_aiohttp import envelope_json_response @@ -49,7 +49,7 @@ async def wrapper(request: web.Request) -> web.StreamResponse: @permission_required("project.wallet.*") @_handle_project_wallet_exceptions async def get_project_wallet(request: web.Request): - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) # ensure the project exists @@ -80,7 +80,7 @@ class _ProjectWalletPathParams(BaseModel): @permission_required("project.wallet.*") @_handle_project_wallet_exceptions async def connect_wallet_to_project(request: web.Request): - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_ProjectWalletPathParams, request) # ensure the project exists diff --git a/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py index 89447a81f5b..61e6c011603 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py @@ -5,7 +5,7 @@ from models_library.projects import ProjectID from models_library.utils.common_validators import null_or_none_str_to_none_validator from models_library.workspaces import WorkspaceID -from pydantic import ConfigDict, BaseModel, field_validator +from pydantic import BaseModel, ConfigDict, field_validator from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import parse_request_path_parameters_as from servicelib.aiohttp.typing_extension import Handler @@ -67,7 +67,7 @@ class _ProjectWorkspacesPathParams(BaseModel): @permission_required("project.workspaces.*") @_handle_projects_workspaces_exceptions async def replace_project_workspace(request: web.Request): - req_ctx = RequestContext.parse_obj(request) + req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as( _ProjectWorkspacesPathParams, request ) diff --git a/services/web/server/src/simcore_service_webserver/projects/projects_api.py b/services/web/server/src/simcore_service_webserver/projects/projects_api.py index 8a5e69c39ce..7f271bdb02a 100644 --- a/services/web/server/src/simcore_service_webserver/projects/projects_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/projects_api.py @@ -219,7 +219,7 @@ async def get_project_for_user( gid: access.dict() for gid, access in workspace_db.access_rights.items() } - Project.parse_obj(project) # NOTE: only validates + Project.model_validate(project) # NOTE: only validates return project @@ -798,7 +798,7 @@ async def add_project_node( ProjectNodeCreate( node_id=node_uuid, required_resources=jsonable_encoder(default_resources) ), - Node.parse_obj( + Node.model_validate( { "key": service_key, "version": service_version, diff --git a/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_admin_handlers.py b/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_admin_handlers.py index bbb435060ac..72ed1c10bff 100644 --- a/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_admin_handlers.py +++ b/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_admin_handlers.py @@ -20,7 +20,7 @@ PricingUnitWithCostUpdate, ) from models_library.users import UserID -from pydantic import ConfigDict, BaseModel, Field +from pydantic import BaseModel, ConfigDict, Field from servicelib.aiohttp.requests_validation import ( parse_request_body_as, parse_request_path_parameters_as, @@ -83,7 +83,7 @@ class _GetPricingPlanPathParams(BaseModel): @permission_required("resource-usage.write") @_handle_pricing_plan_admin_exceptions async def list_pricing_plans(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) pricing_plans_list = await admin_api.list_pricing_plans( app=request.app, @@ -114,7 +114,7 @@ async def list_pricing_plans(request: web.Request): @permission_required("resource-usage.write") @_handle_pricing_plan_admin_exceptions async def get_pricing_plan(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_GetPricingPlanPathParams, request) pricing_plan_get = await admin_api.get_pricing_plan( @@ -157,7 +157,7 @@ async def get_pricing_plan(request: web.Request): @permission_required("resource-usage.write") @_handle_pricing_plan_admin_exceptions async def create_pricing_plan(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) body_params = await parse_request_body_as(CreatePricingPlanBodyParams, request) _data = PricingPlanCreate( @@ -206,7 +206,7 @@ async def create_pricing_plan(request: web.Request): @permission_required("resource-usage.write") @_handle_pricing_plan_admin_exceptions async def update_pricing_plan(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_GetPricingPlanPathParams, request) body_params = await parse_request_body_as(UpdatePricingPlanBodyParams, request) @@ -265,7 +265,7 @@ class _GetPricingUnitPathParams(BaseModel): @permission_required("resource-usage.write") @_handle_pricing_plan_admin_exceptions async def get_pricing_unit(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_GetPricingUnitPathParams, request) pricing_unit_get = await admin_api.get_pricing_unit( @@ -295,7 +295,7 @@ async def get_pricing_unit(request: web.Request): @permission_required("resource-usage.write") @_handle_pricing_plan_admin_exceptions async def create_pricing_unit(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_GetPricingPlanPathParams, request) body_params = await parse_request_body_as(CreatePricingUnitBodyParams, request) @@ -334,7 +334,7 @@ async def create_pricing_unit(request: web.Request): @permission_required("resource-usage.write") @_handle_pricing_plan_admin_exceptions async def update_pricing_unit(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_GetPricingUnitPathParams, request) body_params = await parse_request_body_as(UpdatePricingUnitBodyParams, request) @@ -376,7 +376,7 @@ async def update_pricing_unit(request: web.Request): @permission_required("resource-usage.write") @_handle_pricing_plan_admin_exceptions async def list_connected_services_to_pricing_plan(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_GetPricingPlanPathParams, request) connected_services_list = await admin_api.list_connected_services_to_pricing_plan( @@ -405,7 +405,7 @@ async def list_connected_services_to_pricing_plan(request: web.Request): @permission_required("resource-usage.write") @_handle_pricing_plan_admin_exceptions async def connect_service_to_pricing_plan(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_GetPricingPlanPathParams, request) body_params = await parse_request_body_as( ConnectServiceToPricingPlanBodyParams, request diff --git a/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_handlers.py b/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_handlers.py index 5092262a96f..9715efae496 100644 --- a/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_handlers.py +++ b/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_handlers.py @@ -4,7 +4,7 @@ from models_library.api_schemas_webserver.resource_usage import PricingUnitGet from models_library.resource_tracker import PricingPlanId, PricingUnitId from models_library.users import UserID -from pydantic import ConfigDict, BaseModel, Field +from pydantic import BaseModel, ConfigDict, Field from servicelib.aiohttp.requests_validation import parse_request_path_parameters_as from servicelib.aiohttp.typing_extension import Handler from servicelib.request_keys import RQT_USERID_KEY @@ -60,7 +60,7 @@ class _GetPricingPlanUnitPathParams(BaseModel): @permission_required("resource-usage.read") @_handle_resource_usage_exceptions async def get_pricing_plan_unit(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as( _GetPricingPlanUnitPathParams, request ) diff --git a/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py b/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py index 8ddc9804dbc..d687d25a94b 100644 --- a/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py +++ b/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py @@ -23,11 +23,14 @@ from models_library.users import UserID from models_library.wallets import WalletID from pydantic import ( - field_validator, ConfigDict, BaseModel, + BaseModel, + ConfigDict, Field, Json, NonNegativeInt, - parse_obj_as) + field_validator, + parse_obj_as, +) from servicelib.aiohttp.requests_validation import parse_request_query_parameters_as from servicelib.aiohttp.typing_extension import Handler from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON @@ -109,6 +112,7 @@ def validate_order_by_field(cls, v): if v.field == "credit_cost": v.field = "osparc_credits" return v + model_config = ConfigDict(extra="forbid") @@ -146,7 +150,7 @@ class _ListServicesAggregatedUsagesQueryParams(PageQueryParameters): @permission_required("resource-usage.read") @_handle_resource_usage_exceptions async def list_resource_usage_services(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) query_params: _ListServicesResourceUsagesQueryParamsWithPagination = ( parse_request_query_parameters_as( _ListServicesResourceUsagesQueryParamsWithPagination, request @@ -164,7 +168,7 @@ async def list_resource_usage_services(request: web.Request): filters=parse_obj_as(ServiceResourceUsagesFilters | None, query_params.filters), # type: ignore[arg-type] # from pydantic v2 --> https://github.com/pydantic/pydantic/discussions/4950 ) - page = Page[dict[str, Any]].parse_obj( + page = Page[dict[str, Any]].model_validate( paginate_data( chunk=services.items, request_url=request.url, @@ -187,7 +191,7 @@ async def list_resource_usage_services(request: web.Request): @permission_required("resource-usage.read") @_handle_resource_usage_exceptions async def list_osparc_credits_aggregated_usages(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) query_params: _ListServicesAggregatedUsagesQueryParams = ( parse_request_query_parameters_as( _ListServicesAggregatedUsagesQueryParams, request @@ -207,7 +211,7 @@ async def list_osparc_credits_aggregated_usages(request: web.Request): ) ) - page = Page[dict[str, Any]].parse_obj( + page = Page[dict[str, Any]].model_validate( paginate_data( chunk=aggregated_services.items, request_url=request.url, @@ -227,7 +231,7 @@ async def list_osparc_credits_aggregated_usages(request: web.Request): @permission_required("resource-usage.read") @_handle_resource_usage_exceptions async def export_resource_usage_services(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) query_params: _ListServicesResourceUsagesQueryParams = ( parse_request_query_parameters_as( _ListServicesResourceUsagesQueryParams, request diff --git a/services/web/server/src/simcore_service_webserver/scicrunch/_resolver.py b/services/web/server/src/simcore_service_webserver/scicrunch/_resolver.py index efef7f77668..07d62498230 100644 --- a/services/web/server/src/simcore_service_webserver/scicrunch/_resolver.py +++ b/services/web/server/src/simcore_service_webserver/scicrunch/_resolver.py @@ -93,7 +93,7 @@ async def resolve_rrid( body = await resp.json() # process and simplify response - resolved = ResolverResponseBody.parse_obj(body) + resolved = ResolverResponseBody.model_validate(body) if resolved.hits.total == 0: return [] @@ -113,7 +113,7 @@ async def resolve_rrid( items = [] for hit in resolved.hits.hits: try: - items.append(ResolvedItem.parse_obj(hit.source.flatten_dict())) + items.append(ResolvedItem.model_validate(hit.source.flatten_dict())) except ValidationError as err: logger.warning("Skipping unexpected response %s: %s", url, err) diff --git a/services/web/server/src/simcore_service_webserver/storage/_handlers.py b/services/web/server/src/simcore_service_webserver/storage/_handlers.py index f5acb0171b1..ec07a27d449 100644 --- a/services/web/server/src/simcore_service_webserver/storage/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/storage/_handlers.py @@ -237,7 +237,7 @@ class _QueryParams(BaseModel): payload, status = await _forward_request_to_storage(request, "PUT", body=None) data, _ = unwrap_envelope(payload) - file_upload_schema = FileUploadSchema.parse_obj(data) + file_upload_schema = FileUploadSchema.model_validate(data) file_upload_schema.links.complete_upload = _from_storage_url( request, file_upload_schema.links.complete_upload ) @@ -265,7 +265,7 @@ class _PathParams(BaseModel): request, "POST", body=body_item.dict() ) data, _ = unwrap_envelope(payload) - file_upload_complete = FileUploadCompleteResponse.parse_obj(data) + file_upload_complete = FileUploadCompleteResponse.model_validate(data) file_upload_complete.links.state = _from_storage_url( request, file_upload_complete.links.state ) diff --git a/services/web/server/src/simcore_service_webserver/storage/api.py b/services/web/server/src/simcore_service_webserver/storage/api.py index 2ddf66d8907..cc234f02530 100644 --- a/services/web/server/src/simcore_service_webserver/storage/api.py +++ b/services/web/server/src/simcore_service_webserver/storage/api.py @@ -56,7 +56,7 @@ async def get_storage_locations( locations_url = (api_endpoint / "locations").with_query(user_id=user_id) async with session.get(f"{locations_url}") as response: response.raise_for_status() - locations_enveloped = Envelope[FileLocationArray].parse_obj( + locations_enveloped = Envelope[FileLocationArray].model_validate( await response.json() ) assert locations_enveloped.data # nosec @@ -89,9 +89,9 @@ async def get_project_total_size_simcore_s3( ).with_query(user_id=user_id, project_id=f"{project_uuid}") async with session.get(f"{files_metadata_url}") as response: response.raise_for_status() - list_of_files_enveloped = Envelope[list[FileMetaDataGet]].parse_obj( - await response.json() - ) + list_of_files_enveloped = Envelope[ + list[FileMetaDataGet] + ].model_validate(await response.json()) assert list_of_files_enveloped.data is not None # nosec project_size_bytes += sum( file_metadata.file_size @@ -204,7 +204,7 @@ async def get_download_link( async with session.get(f"{url}") as response: response.raise_for_status() download: PresignedLink | None = ( - Envelope[PresignedLink].parse_obj(await response.json()).data + Envelope[PresignedLink].model_validate(await response.json()).data ) assert download is not None # nosec link: HttpUrl = parse_obj_as(HttpUrl, download.link) @@ -227,7 +227,7 @@ async def get_files_in_node_folder( async with session.get(f"{files_metadata_url}") as response: response.raise_for_status() - list_of_files_enveloped = Envelope[list[FileMetaDataGet]].parse_obj( + list_of_files_enveloped = Envelope[list[FileMetaDataGet]].model_validate( await response.json() ) assert list_of_files_enveloped.data is not None # nosec diff --git a/services/web/server/src/simcore_service_webserver/tags/_handlers.py b/services/web/server/src/simcore_service_webserver/tags/_handlers.py index 8862f0320c1..57fee75af46 100644 --- a/services/web/server/src/simcore_service_webserver/tags/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/tags/_handlers.py @@ -58,7 +58,7 @@ async def wrapper(request: web.Request) -> web.StreamResponse: @_handle_tags_exceptions async def create_tag(request: web.Request): assert request.app # nosec - req_ctx = TagRequestContext.parse_obj(request) + req_ctx = TagRequestContext.model_validate(request) new_tag = await parse_request_body_as(TagCreate, request) created = await _api.create_tag( @@ -73,7 +73,7 @@ async def create_tag(request: web.Request): @_handle_tags_exceptions async def list_tags(request: web.Request): - req_ctx = TagRequestContext.parse_obj(request) + req_ctx = TagRequestContext.model_validate(request) got = await _api.list_tags(request.app, user_id=req_ctx.user_id) return envelope_json_response(got) @@ -83,7 +83,7 @@ async def list_tags(request: web.Request): @permission_required("tag.crud.*") @_handle_tags_exceptions async def update_tag(request: web.Request): - req_ctx = TagRequestContext.parse_obj(request) + req_ctx = TagRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(TagPathParams, request) tag_updates = await parse_request_body_as(TagUpdate, request) @@ -101,7 +101,7 @@ async def update_tag(request: web.Request): @permission_required("tag.crud.*") @_handle_tags_exceptions async def delete_tag(request: web.Request): - req_ctx = TagRequestContext.parse_obj(request) + req_ctx = TagRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(TagPathParams, request) await _api.delete_tag( diff --git a/services/web/server/src/simcore_service_webserver/users/_handlers.py b/services/web/server/src/simcore_service_webserver/users/_handlers.py index 3462602f74b..5fb02801702 100644 --- a/services/web/server/src/simcore_service_webserver/users/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/users/_handlers.py @@ -68,7 +68,7 @@ async def wrapper(request: web.Request) -> web.StreamResponse: @login_required @_handle_users_exceptions async def get_my_profile(request: web.Request) -> web.Response: - req_ctx = UsersRequestContext.parse_obj(request) + req_ctx = UsersRequestContext.model_validate(request) profile: ProfileGet = await api.get_user_profile( request.app, req_ctx.user_id, req_ctx.product_name ) @@ -80,7 +80,7 @@ async def get_my_profile(request: web.Request) -> web.Response: @permission_required("user.profile.update") @_handle_users_exceptions async def update_my_profile(request: web.Request) -> web.Response: - req_ctx = UsersRequestContext.parse_obj(request) + req_ctx = UsersRequestContext.model_validate(request) profile_update = await parse_request_body_as(ProfileUpdate, request) await api.update_user_profile( request.app, req_ctx.user_id, profile_update, as_patch=False @@ -105,7 +105,7 @@ class _SearchQueryParams(BaseModel): @permission_required("user.users.*") @_handle_users_exceptions async def search_users(request: web.Request) -> web.Response: - req_ctx = UsersRequestContext.parse_obj(request) + req_ctx = UsersRequestContext.model_validate(request) assert req_ctx.product_name # nosec query_params: _SearchQueryParams = parse_request_query_parameters_as( @@ -126,7 +126,7 @@ async def search_users(request: web.Request) -> web.Response: @permission_required("user.users.*") @_handle_users_exceptions async def pre_register_user(request: web.Request) -> web.Response: - req_ctx = UsersRequestContext.parse_obj(request) + req_ctx = UsersRequestContext.model_validate(request) pre_user_profile = await parse_request_body_as(PreUserProfile, request) try: diff --git a/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py b/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py index 3a9588d39a5..dc78270bea3 100644 --- a/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py +++ b/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py @@ -52,7 +52,7 @@ async def _get_user_notifications( # Filter by product included = [product_name, "UNDEFINED"] filtered_notifications = [n for n in notifications if n["product"] in included] - return [UserNotification.parse_obj(x) for x in filtered_notifications] + return [UserNotification.model_validate(x) for x in filtered_notifications] @routes.get(f"/{API_VTAG}/me/notifications", name="list_user_notifications") @@ -60,7 +60,7 @@ async def _get_user_notifications( @permission_required("user.notifications.read") async def list_user_notifications(request: web.Request) -> web.Response: redis_client = get_redis_user_notifications_client(request.app) - req_ctx = UsersRequestContext.parse_obj(request) + req_ctx = UsersRequestContext.model_validate(request) product_name = get_product_name(request) notifications = await _get_user_notifications( redis_client, req_ctx.user_id, product_name @@ -99,7 +99,7 @@ class _NotificationPathParams(BaseModel): @permission_required("user.notifications.update") async def mark_notification_as_read(request: web.Request) -> web.Response: redis_client = get_redis_user_notifications_client(request.app) - req_ctx = UsersRequestContext.parse_obj(request) + req_ctx = UsersRequestContext.model_validate(request) req_path_params = parse_request_path_parameters_as(_NotificationPathParams, request) body = await parse_request_body_as(UserNotificationPatch, request) @@ -124,7 +124,7 @@ async def mark_notification_as_read(request: web.Request) -> web.Response: @login_required @permission_required("user.permissions.read") async def list_user_permissions(request: web.Request) -> web.Response: - req_ctx = UsersRequestContext.parse_obj(request) + req_ctx = UsersRequestContext.model_validate(request) list_permissions: list[Permission] = await _api.list_user_permissions( request.app, req_ctx.user_id, req_ctx.product_name ) diff --git a/services/web/server/src/simcore_service_webserver/users/_preferences_api.py b/services/web/server/src/simcore_service_webserver/users/_preferences_api.py index 8e17a4a25d4..3215b4bc149 100644 --- a/services/web/server/src/simcore_service_webserver/users/_preferences_api.py +++ b/services/web/server/src/simcore_service_webserver/users/_preferences_api.py @@ -96,7 +96,7 @@ def include_preference(identifier: PreferenceIdentifier) -> bool: return True aggregated_preferences: AggregatedPreferences = { - p.preference_identifier: Preference.parse_obj( + p.preference_identifier: Preference.model_validate( {"value": p.value, "default_value": p.get_default_value()} ) for p in await _get_frontend_user_preferences(app, user_id, product_name) diff --git a/services/web/server/src/simcore_service_webserver/users/_preferences_db.py b/services/web/server/src/simcore_service_webserver/users/_preferences_db.py index 45903403af9..e64ce5e579b 100644 --- a/services/web/server/src/simcore_service_webserver/users/_preferences_db.py +++ b/services/web/server/src/simcore_service_webserver/users/_preferences_db.py @@ -31,7 +31,7 @@ async def get_user_preference( return ( None if preference_payload is None - else preference_class.parse_obj(preference_payload) + else preference_class.model_validate(preference_payload) ) diff --git a/services/web/server/src/simcore_service_webserver/users/_preferences_handlers.py b/services/web/server/src/simcore_service_webserver/users/_preferences_handlers.py index 0c537278f9c..9581b46b335 100644 --- a/services/web/server/src/simcore_service_webserver/users/_preferences_handlers.py +++ b/services/web/server/src/simcore_service_webserver/users/_preferences_handlers.py @@ -55,7 +55,7 @@ async def wrapper(request: web.Request) -> web.StreamResponse: @login_required @_handle_users_exceptions async def set_frontend_preference(request: web.Request) -> web.Response: - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) req_body = await parse_request_body_as(PatchRequestBody, request) req_path_params = parse_request_path_parameters_as(PatchPathParams, request) diff --git a/services/web/server/src/simcore_service_webserver/users/_tokens_handlers.py b/services/web/server/src/simcore_service_webserver/users/_tokens_handlers.py index 40b884c4eb9..9f5dfc941b8 100644 --- a/services/web/server/src/simcore_service_webserver/users/_tokens_handlers.py +++ b/services/web/server/src/simcore_service_webserver/users/_tokens_handlers.py @@ -44,7 +44,7 @@ async def _wrapper(request: web.Request) -> web.StreamResponse: @_handle_tokens_errors @permission_required("user.tokens.*") async def list_tokens(request: web.Request) -> web.Response: - req_ctx = UsersRequestContext.parse_obj(request) + req_ctx = UsersRequestContext.model_validate(request) all_tokens = await _tokens.list_tokens(request.app, req_ctx.user_id) return envelope_json_response(all_tokens) @@ -54,7 +54,7 @@ async def list_tokens(request: web.Request) -> web.Response: @_handle_tokens_errors @permission_required("user.tokens.*") async def create_token(request: web.Request) -> web.Response: - req_ctx = UsersRequestContext.parse_obj(request) + req_ctx = UsersRequestContext.model_validate(request) token_create = await parse_request_body_as(TokenCreate, request) await _tokens.create_token(request.app, req_ctx.user_id, token_create) return envelope_json_response(token_create, web.HTTPCreated) @@ -69,7 +69,7 @@ class _TokenPathParams(BaseModel): @_handle_tokens_errors @permission_required("user.tokens.*") async def get_token(request: web.Request) -> web.Response: - req_ctx = UsersRequestContext.parse_obj(request) + req_ctx = UsersRequestContext.model_validate(request) req_path_params = parse_request_path_parameters_as(_TokenPathParams, request) token = await _tokens.get_token( request.app, req_ctx.user_id, req_path_params.service @@ -82,7 +82,7 @@ async def get_token(request: web.Request) -> web.Response: @_handle_tokens_errors @permission_required("user.tokens.*") async def delete_token(request: web.Request) -> web.Response: - req_ctx = UsersRequestContext.parse_obj(request) + req_ctx = UsersRequestContext.model_validate(request) req_path_params = parse_request_path_parameters_as(_TokenPathParams, request) await _tokens.delete_token(request.app, req_ctx.user_id, req_path_params.service) return web.json_response(status=status.HTTP_204_NO_CONTENT) diff --git a/services/web/server/src/simcore_service_webserver/version_control/_core.py b/services/web/server/src/simcore_service_webserver/version_control/_core.py index 53f10829b48..c8d444339ea 100644 --- a/services/web/server/src/simcore_service_webserver/version_control/_core.py +++ b/services/web/server/src/simcore_service_webserver/version_control/_core.py @@ -136,7 +136,7 @@ async def get_workbench( # prefer actual project to snapshot content = await vc_repo.get_workbench_view(repo_id, commit_id) - return WorkbenchView.parse_obj(content) + return WorkbenchView.model_validate(content) # diff --git a/services/web/server/src/simcore_service_webserver/version_control/_handlers.py b/services/web/server/src/simcore_service_webserver/version_control/_handlers.py index 595bcb0dcdc..315968009f3 100644 --- a/services/web/server/src/simcore_service_webserver/version_control/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/version_control/_handlers.py @@ -4,7 +4,7 @@ from models_library.projects import ProjectID from models_library.rest_pagination import Page, PageQueryParameters from models_library.rest_pagination_utils import paginate_data -from pydantic import field_validator, BaseModel +from pydantic import BaseModel, field_validator from servicelib.aiohttp.requests_validation import ( parse_request_body_as, parse_request_path_parameters_as, @@ -81,7 +81,7 @@ async def _list_repos_handler(request: web.Request): # parse and validate repos_list = [ - RepoApiModel.parse_obj( + RepoApiModel.model_validate( { "url": url_for("list_repos"), **dict(row.items()), @@ -90,7 +90,7 @@ async def _list_repos_handler(request: web.Request): for row in repos_rows ] - page = Page[RepoApiModel].parse_obj( + page = Page[RepoApiModel].model_validate( paginate_data( chunk=repos_list, request_url=request.url, @@ -116,7 +116,7 @@ async def _create_checkpoint_handler(request: web.Request): vc_repo = VersionControlRepository.create_from_request(request) path_params = parse_request_path_parameters_as(_ProjectPathParam, request) - _body = CheckpointNew.parse_obj(await request.json()) + _body = CheckpointNew.model_validate(await request.json()) checkpoint: Checkpoint = await create_checkpoint( vc_repo, @@ -124,7 +124,7 @@ async def _create_checkpoint_handler(request: web.Request): **_body.dict(include={"tag", "message"}), ) - data = CheckpointApiModel.parse_obj( + data = CheckpointApiModel.model_validate( { "url": url_for( "get_checkpoint", @@ -163,7 +163,7 @@ async def _list_checkpoints_handler(request: web.Request): # parse and validate checkpoints_list = [ - CheckpointApiModel.parse_obj( + CheckpointApiModel.model_validate( { "url": url_for( "get_checkpoint", @@ -176,7 +176,7 @@ async def _list_checkpoints_handler(request: web.Request): for checkpoint in checkpoints ] - page = Page[CheckpointApiModel].parse_obj( + page = Page[CheckpointApiModel].model_validate( paginate_data( chunk=checkpoints_list, request_url=request.url, @@ -211,7 +211,7 @@ async def _get_checkpoint_handler(request: web.Request): ref_id=path_params.ref_id, ) - data = CheckpointApiModel.parse_obj( + data = CheckpointApiModel.model_validate( { "url": url_for( "get_checkpoint", @@ -245,7 +245,7 @@ async def _update_checkpoint_annotations_handler(request: web.Request): **update.dict(include={"tag", "message"}, exclude_none=True), ) - data = CheckpointApiModel.parse_obj( + data = CheckpointApiModel.model_validate( { "url": url_for( "get_checkpoint", @@ -277,7 +277,7 @@ async def _checkout_handler(request: web.Request): ref_id=path_params.ref_id, ) - data = CheckpointApiModel.parse_obj( + data = CheckpointApiModel.model_validate( { "url": url_for( "get_checkpoint", @@ -315,7 +315,7 @@ async def _view_project_workbench_handler(request: web.Request): ref_id=checkpoint.id, ) - data = WorkbenchViewApiModel.parse_obj( + data = WorkbenchViewApiModel.model_validate( { # = request.url?? "url": url_for( diff --git a/services/web/server/src/simcore_service_webserver/wallets/_groups_handlers.py b/services/web/server/src/simcore_service_webserver/wallets/_groups_handlers.py index 7e0064bb158..b8477396e26 100644 --- a/services/web/server/src/simcore_service_webserver/wallets/_groups_handlers.py +++ b/services/web/server/src/simcore_service_webserver/wallets/_groups_handlers.py @@ -8,7 +8,7 @@ from aiohttp import web from models_library.users import GroupID, UserID from models_library.wallets import WalletID -from pydantic import ConfigDict, BaseModel, Field +from pydantic import BaseModel, ConfigDict, Field from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_body_as, @@ -77,7 +77,7 @@ class _WalletsGroupsBodyParams(BaseModel): @permission_required("wallets.*") @_handle_wallets_groups_exceptions async def create_wallet_group(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_WalletsGroupsPathParams, request) body_params = await parse_request_body_as(_WalletsGroupsBodyParams, request) @@ -100,7 +100,7 @@ async def create_wallet_group(request: web.Request): @permission_required("wallets.*") @_handle_wallets_groups_exceptions async def list_wallet_groups(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(WalletsPathParams, request) wallets: list[ @@ -123,7 +123,7 @@ async def list_wallet_groups(request: web.Request): @permission_required("wallets.*") @_handle_wallets_groups_exceptions async def update_wallet_group(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_WalletsGroupsPathParams, request) body_params = await parse_request_body_as(_WalletsGroupsBodyParams, request) @@ -147,7 +147,7 @@ async def update_wallet_group(request: web.Request): @permission_required("wallets.*") @_handle_wallets_groups_exceptions async def delete_wallet_group(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_WalletsGroupsPathParams, request) await _groups_api.delete_wallet_group( diff --git a/services/web/server/src/simcore_service_webserver/wallets/_handlers.py b/services/web/server/src/simcore_service_webserver/wallets/_handlers.py index dc6855f2c01..ef933c345bd 100644 --- a/services/web/server/src/simcore_service_webserver/wallets/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/wallets/_handlers.py @@ -128,7 +128,7 @@ class WalletsPathParams(StrictRequestParams): @permission_required("wallets.*") @handle_wallets_exceptions async def create_wallet(request: web.Request): - req_ctx = WalletsRequestContext.parse_obj(request) + req_ctx = WalletsRequestContext.model_validate(request) body_params = await parse_request_body_as(CreateWalletBodyParams, request) wallet: WalletGet = await _api.create_wallet( @@ -148,7 +148,7 @@ async def create_wallet(request: web.Request): @permission_required("wallets.*") @handle_wallets_exceptions async def list_wallets(request: web.Request): - req_ctx = WalletsRequestContext.parse_obj(request) + req_ctx = WalletsRequestContext.model_validate(request) wallets: list[ WalletGetWithAvailableCredits @@ -164,7 +164,7 @@ async def list_wallets(request: web.Request): @permission_required("wallets.*") @handle_wallets_exceptions async def get_default_wallet(request: web.Request): - req_ctx = WalletsRequestContext.parse_obj(request) + req_ctx = WalletsRequestContext.model_validate(request) wallet: WalletGetWithAvailableCredits = ( await _api.get_user_default_wallet_with_available_credits( @@ -179,7 +179,7 @@ async def get_default_wallet(request: web.Request): @permission_required("wallets.*") @handle_wallets_exceptions async def get_wallet(request: web.Request): - req_ctx = WalletsRequestContext.parse_obj(request) + req_ctx = WalletsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(WalletsPathParams, request) wallet: WalletGetWithAvailableCredits = ( @@ -202,7 +202,7 @@ async def get_wallet(request: web.Request): @permission_required("wallets.*") @handle_wallets_exceptions async def update_wallet(request: web.Request): - req_ctx = WalletsRequestContext.parse_obj(request) + req_ctx = WalletsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(WalletsPathParams, request) body_params = await parse_request_body_as(PutWalletBodyParams, request) diff --git a/services/web/server/src/simcore_service_webserver/wallets/_payments_handlers.py b/services/web/server/src/simcore_service_webserver/wallets/_payments_handlers.py index 9a03bc2d2a5..66c73b5a293 100644 --- a/services/web/server/src/simcore_service_webserver/wallets/_payments_handlers.py +++ b/services/web/server/src/simcore_service_webserver/wallets/_payments_handlers.py @@ -65,7 +65,7 @@ @permission_required("wallets.*") @handle_wallets_exceptions async def _create_payment(request: web.Request): - req_ctx = WalletsRequestContext.parse_obj(request) + req_ctx = WalletsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(WalletsPathParams, request) body_params = await parse_request_body_as(CreateWalletPayment, request) @@ -113,7 +113,7 @@ async def _list_all_payments(request: web.Request): be listed here. """ - req_ctx = WalletsRequestContext.parse_obj(request) + req_ctx = WalletsRequestContext.model_validate(request) query_params: PageQueryParameters = parse_request_query_parameters_as( PageQueryParameters, request ) @@ -126,7 +126,7 @@ async def _list_all_payments(request: web.Request): offset=query_params.offset, ) - page = Page[PaymentTransaction].parse_obj( + page = Page[PaymentTransaction].model_validate( paginate_data( chunk=payments, request_url=request.url, @@ -148,7 +148,7 @@ async def _list_all_payments(request: web.Request): @handle_wallets_exceptions async def _get_payment_invoice_link(request: web.Request): """Get invoice for concrete payment""" - req_ctx = WalletsRequestContext.parse_obj(request) + req_ctx = WalletsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(PaymentsPathParams, request) payment_invoice = await get_payment_invoice_url( @@ -174,7 +174,7 @@ class PaymentsPathParams(WalletsPathParams): @permission_required("wallets.*") @handle_wallets_exceptions async def _cancel_payment(request: web.Request): - req_ctx = WalletsRequestContext.parse_obj(request) + req_ctx = WalletsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(PaymentsPathParams, request) await api.cancel_payment_to_wallet( @@ -208,7 +208,7 @@ async def _init_creation_of_payment_method(request: web.Request): """Triggers the creation of a new payment method. Note that creating a payment-method follows the init-prompt-ack flow """ - req_ctx = WalletsRequestContext.parse_obj(request) + req_ctx = WalletsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(WalletsPathParams, request) with log_context( @@ -241,7 +241,7 @@ async def _init_creation_of_payment_method(request: web.Request): @permission_required("wallets.*") @handle_wallets_exceptions async def _cancel_creation_of_payment_method(request: web.Request): - req_ctx = WalletsRequestContext.parse_obj(request) + req_ctx = WalletsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(PaymentMethodsPathParams, request) with log_context( @@ -272,7 +272,7 @@ async def _cancel_creation_of_payment_method(request: web.Request): @permission_required("wallets.*") @handle_wallets_exceptions async def _list_payments_methods(request: web.Request): - req_ctx = WalletsRequestContext.parse_obj(request) + req_ctx = WalletsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(WalletsPathParams, request) payments_methods: list[PaymentMethodGet] = await list_wallet_payment_methods( @@ -292,7 +292,7 @@ async def _list_payments_methods(request: web.Request): @permission_required("wallets.*") @handle_wallets_exceptions async def _get_payment_method(request: web.Request): - req_ctx = WalletsRequestContext.parse_obj(request) + req_ctx = WalletsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(PaymentMethodsPathParams, request) payment_method: PaymentMethodGet = await get_wallet_payment_method( @@ -313,7 +313,7 @@ async def _get_payment_method(request: web.Request): @permission_required("wallets.*") @handle_wallets_exceptions async def _delete_payment_method(request: web.Request): - req_ctx = WalletsRequestContext.parse_obj(request) + req_ctx = WalletsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(PaymentMethodsPathParams, request) await delete_wallet_payment_method( @@ -337,7 +337,7 @@ async def _delete_payment_method(request: web.Request): @permission_required("wallets.*") @handle_wallets_exceptions async def _pay_with_payment_method(request: web.Request): - req_ctx = WalletsRequestContext.parse_obj(request) + req_ctx = WalletsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(PaymentMethodsPathParams, request) body_params = await parse_request_body_as(CreateWalletPayment, request) @@ -409,7 +409,7 @@ async def _notify_payment_completed_after_response(app, user_id, payment): @permission_required("wallets.*") @handle_wallets_exceptions async def _get_wallet_autorecharge(request: web.Request): - req_ctx = WalletsRequestContext.parse_obj(request) + req_ctx = WalletsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(WalletsPathParams, request) auto_recharge = await get_wallet_payment_autorecharge( @@ -426,7 +426,7 @@ async def _get_wallet_autorecharge(request: web.Request): product_name=req_ctx.product_name, ) - return envelope_json_response(GetWalletAutoRecharge.parse_obj(auto_recharge)) + return envelope_json_response(GetWalletAutoRecharge.model_validate(auto_recharge)) @routes.put( @@ -437,7 +437,7 @@ async def _get_wallet_autorecharge(request: web.Request): @permission_required("wallets.*") @handle_wallets_exceptions async def _replace_wallet_autorecharge(request: web.Request): - req_ctx = WalletsRequestContext.parse_obj(request) + req_ctx = WalletsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(WalletsPathParams, request) body_params = await parse_request_body_as(ReplaceWalletAutoRecharge, request) @@ -454,4 +454,4 @@ async def _replace_wallet_autorecharge(request: web.Request): wallet_id=path_params.wallet_id, new=body_params, ) - return envelope_json_response(GetWalletAutoRecharge.parse_obj(udpated)) + return envelope_json_response(GetWalletAutoRecharge.model_validate(udpated)) diff --git a/services/web/server/src/simcore_service_webserver/workspaces/_groups_handlers.py b/services/web/server/src/simcore_service_webserver/workspaces/_groups_handlers.py index cf2e421a724..22572cee19c 100644 --- a/services/web/server/src/simcore_service_webserver/workspaces/_groups_handlers.py +++ b/services/web/server/src/simcore_service_webserver/workspaces/_groups_handlers.py @@ -8,7 +8,7 @@ from aiohttp import web from models_library.users import GroupID, UserID from models_library.workspaces import WorkspaceID -from pydantic import ConfigDict, BaseModel, Field +from pydantic import BaseModel, ConfigDict, Field from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_body_as, @@ -78,7 +78,7 @@ class _WorkspacesGroupsBodyParams(BaseModel): @permission_required("workspaces.*") @_handle_workspaces_groups_exceptions async def create_workspace_group(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_WorkspacesGroupsPathParams, request) body_params = await parse_request_body_as(_WorkspacesGroupsBodyParams, request) @@ -101,7 +101,7 @@ async def create_workspace_group(request: web.Request): @permission_required("workspaces.*") @_handle_workspaces_groups_exceptions async def list_workspace_groups(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(WorkspacesPathParams, request) workspaces: list[ @@ -124,7 +124,7 @@ async def list_workspace_groups(request: web.Request): @permission_required("workspaces.*") @_handle_workspaces_groups_exceptions async def replace_workspace_group(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_WorkspacesGroupsPathParams, request) body_params = await parse_request_body_as(_WorkspacesGroupsBodyParams, request) @@ -148,7 +148,7 @@ async def replace_workspace_group(request: web.Request): @permission_required("workspaces.*") @_handle_workspaces_groups_exceptions async def delete_workspace_group(request: web.Request): - req_ctx = _RequestContext.parse_obj(request) + req_ctx = _RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(_WorkspacesGroupsPathParams, request) await _groups_api.delete_workspace_group( diff --git a/services/web/server/tests/conftest.py b/services/web/server/tests/conftest.py index a57dfd3c852..04b9f1a54f6 100644 --- a/services/web/server/tests/conftest.py +++ b/services/web/server/tests/conftest.py @@ -326,7 +326,7 @@ async def _creator( data, error = await assert_status(result, status.HTTP_200_OK) assert data assert not error - task_status = TaskStatus.parse_obj(data) + task_status = TaskStatus.model_validate(data) assert task_status print(f"<-- status: {task_status.json(indent=2)}") assert task_status.done, "task incomplete" diff --git a/services/web/server/tests/integration/02/scicrunch/test_scicrunch__rest.py b/services/web/server/tests/integration/02/scicrunch/test_scicrunch__rest.py index 014418a25fb..39e507b2fda 100644 --- a/services/web/server/tests/integration/02/scicrunch/test_scicrunch__rest.py +++ b/services/web/server/tests/integration/02/scicrunch/test_scicrunch__rest.py @@ -145,7 +145,7 @@ async def test_scicrunch_get_fields_from_invalid_rrid( async def test_scicrunch_service_autocomplete_by_name(settings: SciCrunchSettings): - expected: list[dict[str, Any]] = ListOfResourceHits.parse_obj( + expected: list[dict[str, Any]] = ListOfResourceHits.model_validate( [ { "rid": "SCR_000860", diff --git a/services/web/server/tests/unit/isolated/test_catalog_api_units.py b/services/web/server/tests/unit/isolated/test_catalog_api_units.py index 479165189d2..39d1824a775 100644 --- a/services/web/server/tests/unit/isolated/test_catalog_api_units.py +++ b/services/web/server/tests/unit/isolated/test_catalog_api_units.py @@ -45,8 +45,8 @@ def test_can_connect_enums(unit_registry: UnitRegistry): } assert can_connect( - from_output=ServiceOutput.parse_obj(enum_port), - to_input=ServiceInput.parse_obj(enum_port), + from_output=ServiceOutput.model_validate(enum_port), + to_input=ServiceInput.model_validate(enum_port), units_registry=unit_registry, ) @@ -71,15 +71,15 @@ def test_can_connect_generic_data_types(unit_registry: UnitRegistry): # data:*/* -> data:text/plain assert can_connect( - from_output=ServiceOutput.parse_obj(file_picker_outfile), - to_input=ServiceInput.parse_obj(input_sleeper_input_1), + from_output=ServiceOutput.model_validate(file_picker_outfile), + to_input=ServiceInput.model_validate(input_sleeper_input_1), units_registry=unit_registry, ) # data:text/plain -> data:*/* assert can_connect( - from_output=ServiceOutput.parse_obj(input_sleeper_input_1), - to_input=ServiceInput.parse_obj(file_picker_outfile), + from_output=ServiceOutput.model_validate(input_sleeper_input_1), + to_input=ServiceInput.model_validate(file_picker_outfile), units_registry=unit_registry, ) @@ -127,15 +127,15 @@ def test_can_connect_no_units_with_units( ): # w/o -> w assert can_connect( - from_output=ServiceOutput.parse_obj(port_without_unit), - to_input=ServiceInput.parse_obj(port_with_unit), + from_output=ServiceOutput.model_validate(port_without_unit), + to_input=ServiceInput.model_validate(port_with_unit), units_registry=unit_registry, ) # w -> w/o assert can_connect( - from_output=ServiceOutput.parse_obj(port_with_unit), - to_input=ServiceInput.parse_obj(port_without_unit), + from_output=ServiceOutput.model_validate(port_with_unit), + to_input=ServiceInput.model_validate(port_without_unit), units_registry=unit_registry, ) @@ -178,8 +178,8 @@ def test_units_compatible( assert ( can_connect( - from_output=ServiceOutput.parse_obj(from_port), - to_input=ServiceInput.parse_obj(to_port), + from_output=ServiceOutput.model_validate(from_port), + to_input=ServiceInput.model_validate(to_port), units_registry=unit_registry, ) == are_compatible diff --git a/services/web/server/tests/unit/isolated/test_groups_models.py b/services/web/server/tests/unit/isolated/test_groups_models.py index 10b49979b1c..5ad6f8863cc 100644 --- a/services/web/server/tests/unit/isolated/test_groups_models.py +++ b/services/web/server/tests/unit/isolated/test_groups_models.py @@ -14,7 +14,7 @@ def test_models_library_and_postgress_database_enums_are_equivalent(): def test_sanitize_legacy_data(): - users_group_1 = UsersGroup.parse_obj( + users_group_1 = UsersGroup.model_validate( { "gid": "27", "label": "A user", @@ -26,7 +26,7 @@ def test_sanitize_legacy_data(): assert users_group_1.thumbnail is None - users_group_2 = UsersGroup.parse_obj( + users_group_2 = UsersGroup.model_validate( { "gid": "27", "label": "A user", diff --git a/services/web/server/tests/unit/isolated/test_products_model.py b/services/web/server/tests/unit/isolated/test_products_model.py index 45ecbd0f4c3..818161eb046 100644 --- a/services/web/server/tests/unit/isolated/test_products_model.py +++ b/services/web/server/tests/unit/isolated/test_products_model.py @@ -34,13 +34,17 @@ def test_product_examples( def test_product_to_static(): - product = Product.parse_obj(Product.Config.schema_extra["examples"][0]) + product = Product.model_validate( + Product.model_config["json_schema_extra"]["examples"][0] + ) assert product.to_statics() == { "displayName": "o²S²PARC", "supportEmail": "support@osparc.io", } - product = Product.parse_obj(Product.Config.schema_extra["examples"][2]) + product = Product.model_validate( + Product.model_config["json_schema_extra"]["examples"][2] + ) assert product.to_statics() == { "displayName": "o²S²PARC FOO", @@ -78,7 +82,7 @@ def test_product_to_static(): def test_product_host_regex_with_spaces(): - data = Product.Config.schema_extra["examples"][2] + data = Product.model_config["json_schema_extra"]["examples"][2] # with leading and trailing spaces and uppercase (tests anystr_strip_whitespace ) data["support_email"] = " fOO@BaR.COM " @@ -88,7 +92,7 @@ def test_product_host_regex_with_spaces(): data["host_regex"] = expected + " " # parsing should strip all whitespaces and normalize email - product = Product.parse_obj(data) + product = Product.model_validate(data) assert product.host_regex.pattern == expected assert product.host_regex.search("osparc.bar.com") diff --git a/services/web/server/tests/unit/isolated/test_projects_utils.py b/services/web/server/tests/unit/isolated/test_projects_utils.py index e83e02e295f..0178882d760 100644 --- a/services/web/server/tests/unit/isolated/test_projects_utils.py +++ b/services/web/server/tests/unit/isolated/test_projects_utils.py @@ -58,7 +58,7 @@ def test_clone_project_document( # # SEE https://swagger.io/docs/specification/data-models/data-types/#Null - assert Project.parse_obj(clone) is not None + assert Project.model_validate(clone) is not None @pytest.mark.parametrize( @@ -145,4 +145,4 @@ def test_validate_project_json_schema(): with open(CURRENT_DIR / "data/project-data.json") as f: project: ProjectDict = json.load(f) - Project.parse_obj(project) + Project.model_validate(project) diff --git a/services/web/server/tests/unit/isolated/test_storage_schemas.py b/services/web/server/tests/unit/isolated/test_storage_schemas.py index c11ce1f1345..31ea4260bb4 100644 --- a/services/web/server/tests/unit/isolated/test_storage_schemas.py +++ b/services/web/server/tests/unit/isolated/test_storage_schemas.py @@ -20,4 +20,4 @@ def test_model_examples( model_cls: type[BaseModel], example_name: int, example_data: Any ): print(example_name, ":", json.dumps(example_data)) - assert model_cls.parse_obj(example_data) + assert model_cls.model_validate(example_data) diff --git a/services/web/server/tests/unit/isolated/test_studies_dispatcher_models.py b/services/web/server/tests/unit/isolated/test_studies_dispatcher_models.py index 0ab58dfd77e..3e14ad9150a 100644 --- a/services/web/server/tests/unit/isolated/test_studies_dispatcher_models.py +++ b/services/web/server/tests/unit/isolated/test_studies_dispatcher_models.py @@ -74,7 +74,7 @@ def file_and_service_params() -> dict[str, Any]: def test_download_link_validators_2(file_and_service_params: dict[str, Any]): - params = ServiceAndFileParams.parse_obj(file_and_service_params) + params = ServiceAndFileParams.model_validate(file_and_service_params) assert params.download_link diff --git a/services/web/server/tests/unit/isolated/test_studies_dispatcher_settings.py b/services/web/server/tests/unit/isolated/test_studies_dispatcher_settings.py index 91364e64beb..e2255bbcf26 100644 --- a/services/web/server/tests/unit/isolated/test_studies_dispatcher_settings.py +++ b/services/web/server/tests/unit/isolated/test_studies_dispatcher_settings.py @@ -21,7 +21,7 @@ def environment(monkeypatch: pytest.MonkeyPatch) -> EnvVarsDict: envs = setenvs_from_dict( monkeypatch, - envs=StudiesDispatcherSettings.Config.schema_extra["example"], + envs=StudiesDispatcherSettings.model_config["json_schema_extra"]["example"], ) return envs diff --git a/services/web/server/tests/unit/isolated/test_user_notifications.py b/services/web/server/tests/unit/isolated/test_user_notifications.py index 1970f9d19c8..3caca391881 100644 --- a/services/web/server/tests/unit/isolated/test_user_notifications.py +++ b/services/web/server/tests/unit/isolated/test_user_notifications.py @@ -16,7 +16,7 @@ "raw_data", UserNotification.model_config["json_schema_extra"]["examples"] ) def test_user_notification(raw_data: dict[str, Any]): - assert UserNotification.parse_obj(raw_data) + assert UserNotification.model_validate(raw_data) @pytest.mark.parametrize("user_id", [10]) @@ -87,7 +87,7 @@ def test_get_notification_key(user_id: UserID): id="category_from_string", ), pytest.param( - UserNotificationCreate.parse_obj( + UserNotificationCreate.model_validate( { "id": "some_id", "user_id": "1", diff --git a/services/web/server/tests/unit/isolated/test_users_models.py b/services/web/server/tests/unit/isolated/test_users_models.py index ef5ee03c7b0..8f3b45cd3df 100644 --- a/services/web/server/tests/unit/isolated/test_users_models.py +++ b/services/web/server/tests/unit/isolated/test_users_models.py @@ -81,7 +81,7 @@ def test_auto_compute_gravatar(faker: Faker): @pytest.mark.parametrize("user_role", [u.name for u in UserRole]) def test_profile_get_role(user_role: str): - for example in ProfileGet.Config.schema_extra["examples"]: + for example in ProfileGet.model_config["json_schema_extra"]["examples"]: data = deepcopy(example) data["role"] = user_role m1 = ProfileGet(**data) @@ -134,5 +134,5 @@ def test_parsing_output_of_get_user_profile(): }, } - profile = ProfileGet.parse_obj(result_from_db_query_and_composition) + profile = ProfileGet.model_validate(result_from_db_query_and_composition) assert "password" not in profile.dict(exclude_unset=True) diff --git a/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py b/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py index c3f6b1d8570..510b3118dca 100644 --- a/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py +++ b/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py @@ -45,19 +45,19 @@ def mocked_director_v2_api(mocker: MockerFixture): "simcore_service_webserver.clusters._handlers.director_v2_api", autospec=True ) - mocked_director_v2_api.create_cluster.return_value = Cluster.parse_obj( - random.choice(Cluster.Config.schema_extra["examples"]) + mocked_director_v2_api.create_cluster.return_value = Cluster.model_validate( + random.choice(Cluster.model_config["json_schema_extra"]["examples"]) ) mocked_director_v2_api.list_clusters.return_value = [] - mocked_director_v2_api.get_cluster.return_value = Cluster.parse_obj( - random.choice(Cluster.Config.schema_extra["examples"]) + mocked_director_v2_api.get_cluster.return_value = Cluster.model_validate( + random.choice(Cluster.model_config["json_schema_extra"]["examples"]) ) mocked_director_v2_api.get_cluster_details.return_value = { "scheduler": {"status": "running"}, "dashboardLink": "https://link.to.dashboard", } - mocked_director_v2_api.update_cluster.return_value = Cluster.parse_obj( - random.choice(Cluster.Config.schema_extra["examples"]) + mocked_director_v2_api.update_cluster.return_value = Cluster.model_validate( + random.choice(Cluster.model_config["json_schema_extra"]["examples"]) ) mocked_director_v2_api.delete_cluster.return_value = None mocked_director_v2_api.ping_cluster.return_value = None @@ -132,7 +132,7 @@ async def test_create_cluster( # we are done here return - created_cluster = Cluster.parse_obj(data) + created_cluster = Cluster.model_validate(data) assert created_cluster diff --git a/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_handlers.py b/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_handlers.py index 7933270984a..dc76115c45b 100644 --- a/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_handlers.py +++ b/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_handlers.py @@ -241,7 +241,7 @@ def test_model_examples( model_cls: type[BaseModel], example_name: int, example_data: Any ): print(example_name, ":", json_dumps(example_data)) - model = model_cls.parse_obj(example_data) + model = model_cls.model_validate(example_data) assert model diff --git a/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_projects.py b/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_projects.py index 48aacf56c6c..cd9bc502089 100644 --- a/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_projects.py +++ b/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_projects.py @@ -106,7 +106,7 @@ async def test_add_new_project_from_model_instance( project_id=project_id, service_id=viewer_id, owner=user, - service_info=ServiceInfo.parse_obj(viewer_info), + service_info=ServiceInfo.model_validate(viewer_info), ) else: project = _create_project_with_filepicker_and_service( diff --git a/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__pricing_plan.py b/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__pricing_plan.py index b328ddc4c7d..f6f683d0c37 100644 --- a/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__pricing_plan.py +++ b/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__pricing_plan.py @@ -32,7 +32,7 @@ def mock_rut_api_responses( service_pricing_plan_get = parse_obj_as( PricingPlanGet, - PricingPlanGet.Config.schema_extra["examples"][0], + PricingPlanGet.model_config["json_schema_extra"]["examples"][0], ) aioresponses_mocker.get( re.compile(f"^{settings.api_base_url}/services/+.+$"), diff --git a/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__services.py b/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__services.py index 396e3a1f8a0..38d10825512 100644 --- a/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__services.py +++ b/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__services.py @@ -56,7 +56,8 @@ async def _list( assert user_id items = parse_obj_as( - list[ServiceGetV2], ServiceGetV2.Config.schema_extra["examples"] + list[ServiceGetV2], + ServiceGetV2.model_config["json_schema_extra"]["examples"], ) total_count = len(items) @@ -80,7 +81,7 @@ async def _get( assert user_id got = parse_obj_as( - ServiceGetV2, ServiceGetV2.Config.schema_extra["examples"][0] + ServiceGetV2, ServiceGetV2.model_config["json_schema_extra"]["examples"][0] ) got.version = service_version got.key = service_key @@ -101,7 +102,7 @@ async def _update( assert user_id got = parse_obj_as( - ServiceGetV2, ServiceGetV2.Config.schema_extra["examples"][0] + ServiceGetV2, ServiceGetV2.model_config["json_schema_extra"]["examples"][0] ) got.version = service_version got.key = service_key diff --git a/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__services_resources.py b/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__services_resources.py index afffca3652a..955a8cb8dd0 100644 --- a/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__services_resources.py +++ b/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__services_resources.py @@ -34,7 +34,7 @@ def mock_catalog_service_api_responses( service_resources = parse_obj_as( ServiceResourcesDict, - ServiceResourcesDictHelpers.Config.schema_extra["examples"][0], + ServiceResourcesDictHelpers.model_config["json_schema_extra"]["examples"][0], ) jsonable_service_resources = ServiceResourcesDictHelpers.create_jsonable( service_resources diff --git a/services/web/server/tests/unit/with_dbs/02/conftest.py b/services/web/server/tests/unit/with_dbs/02/conftest.py index 425756375b1..e33fe523296 100644 --- a/services/web/server/tests/unit/with_dbs/02/conftest.py +++ b/services/web/server/tests/unit/with_dbs/02/conftest.py @@ -41,7 +41,7 @@ def mock_service_resources() -> ServiceResourcesDict: return parse_obj_as( ServiceResourcesDict, - ServiceResourcesDictHelpers.Config.schema_extra["examples"][0], + ServiceResourcesDictHelpers.model_config["json_schema_extra"]["examples"][0], ) diff --git a/services/web/server/tests/unit/with_dbs/02/test_announcements.py b/services/web/server/tests/unit/with_dbs/02/test_announcements.py index cd87e2526c6..19ca7413827 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_announcements.py +++ b/services/web/server/tests/unit/with_dbs/02/test_announcements.py @@ -185,7 +185,7 @@ async def test_list_announcements_filtered( def test_model_examples( model_cls: type[BaseModel], example_name: int, example_data: Any ): - assert model_cls.parse_obj( + assert model_cls.model_validate( example_data ), f"Failed {example_name} : {json.dumps(example_data)}" @@ -193,7 +193,7 @@ def test_model_examples( def test_invalid_announcement(faker: Faker): now = arrow.utcnow() with pytest.raises(ValidationError): - Announcement.parse_obj( + Announcement.model_validate( { "id": "Student_Competition_2023", "products": ["s4llite", "osparc"], @@ -209,7 +209,7 @@ def test_invalid_announcement(faker: Faker): def test_announcement_expired(faker: Faker): now = arrow.utcnow() - model = Announcement.parse_obj( + model = Announcement.model_validate( { "id": "Student_Competition_2023", "products": ["s4llite", "osparc"], diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_cancellations.py b/services/web/server/tests/unit/with_dbs/02/test_projects_cancellations.py index 6c841fa8650..3fe08f2172a 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_cancellations.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_cancellations.py @@ -150,7 +150,7 @@ async def test_copying_large_project_and_retrieving_copy_task( create_url = create_url.with_query(from_study=user_project["uuid"]) resp = await client.post(f"{create_url}", json={}) data, error = await assert_status(resp, expected.accepted) - created_copy_task = TaskGet.parse_obj(data) + created_copy_task = TaskGet.model_validate(data) # list current tasks list_task_url = client.app.router["list_tasks"].url_for() resp = await client.get(f"{list_task_url}") diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone.py index 6ca7392dd4b..28c8db0cfa6 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone.py @@ -49,7 +49,7 @@ async def _request_clone_project(client: TestClient, url: URL) -> ProjectGet: data = await long_running_task.result() assert data is not None - return ProjectGet.parse_obj(data) + return ProjectGet.model_validate(data) @pytest.mark.parametrize(*standard_role_response(), ids=str) diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone_in_workspace_and_folder.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone_in_workspace_and_folder.py index 051f522fcd9..755a1081fcb 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone_in_workspace_and_folder.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone_in_workspace_and_folder.py @@ -80,7 +80,7 @@ async def _request_clone_project(client: TestClient, url: URL) -> ProjectGet: data = await long_running_task.result() assert data is not None - return ProjectGet.parse_obj(data) + return ProjectGet.model_validate(data) @pytest.mark.parametrize( diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py b/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py index 8243228681b..a5b5ad3abdd 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py @@ -77,7 +77,9 @@ async def test_get_node_resources( assert DEFAULT_SINGLE_SERVICE_NAME in node_resources assert ( node_resources - == ServiceResourcesDictHelpers.Config.schema_extra["examples"][0] + == ServiceResourcesDictHelpers.model_config["json_schema_extra"][ + "examples" + ][0] ) else: assert not data @@ -145,7 +147,9 @@ async def test_replace_node_resources_is_forbidden_by_default( response = await client.put( f"{url}", json=ServiceResourcesDictHelpers.create_jsonable( - ServiceResourcesDictHelpers.Config.schema_extra["examples"][0] + ServiceResourcesDictHelpers.model_config["json_schema_extra"][ + "examples" + ][0] ), ) data, error = await assert_status(response, expected) @@ -156,7 +160,9 @@ async def test_replace_node_resources_is_forbidden_by_default( assert DEFAULT_SINGLE_SERVICE_NAME in node_resources assert ( node_resources - == ServiceResourcesDictHelpers.Config.schema_extra["examples"][0] + == ServiceResourcesDictHelpers.model_config["json_schema_extra"][ + "examples" + ][0] ) @@ -183,7 +189,9 @@ async def test_replace_node_resources_is_ok_if_explicitly_authorized( response = await client.put( f"{url}", json=ServiceResourcesDictHelpers.create_jsonable( - ServiceResourcesDictHelpers.Config.schema_extra["examples"][0] + ServiceResourcesDictHelpers.model_config["json_schema_extra"][ + "examples" + ][0] ), ) data, error = await assert_status(response, expected) @@ -194,7 +202,9 @@ async def test_replace_node_resources_is_ok_if_explicitly_authorized( assert DEFAULT_SINGLE_SERVICE_NAME in node_resources assert ( node_resources - == ServiceResourcesDictHelpers.Config.schema_extra["examples"][0] + == ServiceResourcesDictHelpers.model_config["json_schema_extra"][ + "examples" + ][0] ) @@ -218,7 +228,9 @@ async def test_replace_node_resources_raises_422_if_resource_does_not_validate( f"{url}", json=ServiceResourcesDictHelpers.create_jsonable( # NOTE: we apply a different resource set - ServiceResourcesDictHelpers.Config.schema_extra["examples"][1] + ServiceResourcesDictHelpers.model_config["json_schema_extra"][ + "examples" + ][1] ), ) await assert_status(response, expected) @@ -383,8 +395,8 @@ def num_services( self, *args, **kwargs ) -> list[DynamicServiceGet]: # noqa: ARG002 return [ - DynamicServiceGet.parse_obj( - DynamicServiceGet.Config.schema_extra["examples"][1] + DynamicServiceGet.model_validate( + DynamicServiceGet.model_config["json_schema_extra"]["examples"][1] | {"service_uuid": service_uuid, "project_id": user_project["uuid"]} ) for service_uuid in self.running_services_uuids diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_pricing_unit_handlers.py b/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_pricing_unit_handlers.py index 06957402de2..f51877302b6 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_pricing_unit_handlers.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_pricing_unit_handlers.py @@ -99,7 +99,7 @@ def mock_rut_api_responses( settings: ResourceUsageTrackerSettings = get_plugin_settings(client.app) pricing_unit_get_base = parse_obj_as( - PricingUnitGet, PricingUnitGet.Config.schema_extra["examples"][0] + PricingUnitGet, PricingUnitGet.model_config["json_schema_extra"]["examples"][0] ) pricing_unit_get_1 = pricing_unit_get_base.copy() pricing_unit_get_1.pricing_unit_id = _PRICING_UNIT_ID_1 diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_ports_handlers.py b/services/web/server/tests/unit/with_dbs/02/test_projects_ports_handlers.py index ae1b62e0558..06e20946e91 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_ports_handlers.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_ports_handlers.py @@ -276,7 +276,7 @@ async def test_clone_project_and_set_inputs( data = await long_running_task.result() assert data is not None - cloned_project = ProjectGet.parse_obj(data) + cloned_project = ProjectGet.model_validate(data) assert parent_project_id != cloned_project.uuid assert user_project["description"] == cloned_project.description diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py b/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py index 4c8b41e4861..9b61a1bb9fb 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py @@ -1039,10 +1039,10 @@ async def test_project_node_lifetime( # noqa: PLR0915 project_id=user_project["uuid"], node_id=dynamic_node_id ) - node_sample = deepcopy(NodeGet.Config.schema_extra["examples"][1]) + node_sample = deepcopy(NodeGet.model_config["json_schema_extra"]["examples"][1]) mocked_director_v2_api[ "dynamic_scheduler.api.get_dynamic_service" - ].return_value = NodeGet.parse_obj( + ].return_value = NodeGet.model_validate( { **node_sample, "service_state": "running", @@ -1061,7 +1061,7 @@ async def test_project_node_lifetime( # noqa: PLR0915 ) mocked_director_v2_api[ "dynamic_scheduler.api.get_dynamic_service" - ].return_value = NodeGetIdle.parse_obj( + ].return_value = NodeGetIdle.model_validate( { "service_uuid": node_sample["service_uuid"], "service_state": "idle", diff --git a/services/web/server/tests/unit/with_dbs/03/folders/test_folders.py b/services/web/server/tests/unit/with_dbs/03/folders/test_folders.py index 7643afa7367..022e4f73119 100644 --- a/services/web/server/tests/unit/with_dbs/03/folders/test_folders.py +++ b/services/web/server/tests/unit/with_dbs/03/folders/test_folders.py @@ -58,7 +58,7 @@ async def test_folders_full_workflow( url = client.app.router["create_folder"].url_for() resp = await client.post(url.path, json={"name": "My first folder"}) added_folder, _ = await assert_status(resp, status.HTTP_201_CREATED) - assert FolderGet.parse_obj(added_folder) + assert FolderGet.model_validate(added_folder) # list user folders url = client.app.router["list_folders"].url_for() @@ -78,7 +78,7 @@ async def test_folders_full_workflow( ) resp = await client.get(url) data, _ = await assert_status(resp, status.HTTP_200_OK) - assert FolderGet.parse_obj(data) + assert FolderGet.model_validate(data) assert data["folderId"] == added_folder["folderId"] assert data["name"] == "My first folder" @@ -93,7 +93,7 @@ async def test_folders_full_workflow( }, ) data, _ = await assert_status(resp, status.HTTP_200_OK) - assert FolderGet.parse_obj(data) + assert FolderGet.model_validate(data) # list user folders url = client.app.router["list_folders"].url_for() @@ -206,7 +206,7 @@ async def test_sub_folders_full_workflow( }, ) data, _ = await assert_status(resp, status.HTTP_200_OK) - assert FolderGet.parse_obj(data) + assert FolderGet.model_validate(data) # list user root folders base_url = client.app.router["list_folders"].url_for() @@ -396,7 +396,7 @@ async def test_folders_deletion( url = client.app.router["create_folder"].url_for() resp = await client.post(url.path, json={"name": "My first folder"}) root_folder, _ = await assert_status(resp, status.HTTP_201_CREATED) - assert FolderGet.parse_obj(root_folder) + assert FolderGet.model_validate(root_folder) # create a subfolder folder url = client.app.router["create_folder"].url_for() diff --git a/services/web/server/tests/unit/with_dbs/03/invitations/conftest.py b/services/web/server/tests/unit/with_dbs/03/invitations/conftest.py index ea792b8f726..d318e17a96f 100644 --- a/services/web/server/tests/unit/with_dbs/03/invitations/conftest.py +++ b/services/web/server/tests/unit/with_dbs/03/invitations/conftest.py @@ -66,7 +66,7 @@ def fake_osparc_invitation( Emulates an invitation for osparc product """ oas = deepcopy(invitations_service_openapi_specs) - content = ApiInvitationContent.parse_obj( + content = ApiInvitationContent.model_validate( oas["components"]["schemas"]["ApiInvitationContent"]["example"] ) content.product = "osparc" @@ -150,7 +150,7 @@ def _generate(url, **kwargs): return CallbackResult( status=status.HTTP_200_OK, payload=jsonable_encoder( - ApiInvitationContentAndLink.parse_obj( + ApiInvitationContentAndLink.model_validate( { **example, **body, diff --git a/services/web/server/tests/unit/with_dbs/03/invitations/test_login_handlers_registration_invitations.py b/services/web/server/tests/unit/with_dbs/03/invitations/test_login_handlers_registration_invitations.py index 7fa3ee144a7..be73ad487c9 100644 --- a/services/web/server/tests/unit/with_dbs/03/invitations/test_login_handlers_registration_invitations.py +++ b/services/web/server/tests/unit/with_dbs/03/invitations/test_login_handlers_registration_invitations.py @@ -52,7 +52,7 @@ async def test_check_registration_invitation_when_not_required( ) data, _ = await assert_status(response, status.HTTP_200_OK) - invitation = InvitationInfo.parse_obj(data) + invitation = InvitationInfo.model_validate(data) assert invitation.email is None @@ -74,7 +74,7 @@ async def test_check_registration_invitations_with_old_code( ) data, _ = await assert_status(response, status.HTTP_200_OK) - invitation = InvitationInfo.parse_obj(data) + invitation = InvitationInfo.model_validate(data) assert invitation.email is None @@ -100,7 +100,7 @@ async def test_check_registration_invitation_and_get_email( ) data, _ = await assert_status(response, status.HTTP_200_OK) - invitation = InvitationInfo.parse_obj(data) + invitation = InvitationInfo.model_validate(data) assert invitation.email == fake_osparc_invitation.guest diff --git a/services/web/server/tests/unit/with_dbs/03/invitations/test_products__invitations_handlers.py b/services/web/server/tests/unit/with_dbs/03/invitations/test_products__invitations_handlers.py index 0f8a85544f4..749fddb1548 100644 --- a/services/web/server/tests/unit/with_dbs/03/invitations/test_products__invitations_handlers.py +++ b/services/web/server/tests/unit/with_dbs/03/invitations/test_products__invitations_handlers.py @@ -56,7 +56,7 @@ async def test_role_access_to_generate_invitation( ) data, error = await assert_status(response, expected_status) if not error: - got = InvitationGenerated.parse_obj(data) + got = InvitationGenerated.model_validate(data) assert got.guest == guest_email else: assert error @@ -99,7 +99,7 @@ async def test_product_owner_generates_invitation( data, error = await assert_status(response, expected_status) assert not error - got = InvitationGenerated.parse_obj(data) + got = InvitationGenerated.model_validate(data) expected = { "issuer": logged_user["email"][:_MAX_LEN], **request_model.dict(exclude_none=True), @@ -186,7 +186,7 @@ async def test_pre_registration_and_invitation_workflow( response = await client.post("/v0/invitation:generate", json=invitation) data, _ = await assert_status(response, status.HTTP_200_OK) assert data["guest"] == guest_email - got_invitation = InvitationGenerated.parse_obj(data) + got_invitation = InvitationGenerated.model_validate(data) # register user assert got_invitation.invitation_link.fragment diff --git a/services/web/server/tests/unit/with_dbs/03/login/test_login_registration.py b/services/web/server/tests/unit/with_dbs/03/login/test_login_registration.py index 61ade5ec24b..a3b51fe4d2c 100644 --- a/services/web/server/tests/unit/with_dbs/03/login/test_login_registration.py +++ b/services/web/server/tests/unit/with_dbs/03/login/test_login_registration.py @@ -494,7 +494,7 @@ async def test_registraton_with_invitation_for_trial_account( url = client.app.router["get_my_profile"].url_for() response = await client.get(url.path) data, _ = await assert_status(response, status.HTTP_200_OK) - profile = ProfileGet.parse_obj(data) + profile = ProfileGet.model_validate(data) expected = invitation.user["created_at"] + timedelta(days=TRIAL_DAYS) assert profile.expiration_date diff --git a/services/web/server/tests/unit/with_dbs/03/login/test_login_registration_handlers.py b/services/web/server/tests/unit/with_dbs/03/login/test_login_registration_handlers.py index 782d6bba93d..906350725e5 100644 --- a/services/web/server/tests/unit/with_dbs/03/login/test_login_registration_handlers.py +++ b/services/web/server/tests/unit/with_dbs/03/login/test_login_registration_handlers.py @@ -184,7 +184,7 @@ async def test_request_an_account( assert client.app # A form similar to the one in https://github.com/ITISFoundation/osparc-simcore/pull/5378 user_data = { - **AccountRequestInfo.Config.schema_extra["example"]["form"], + **AccountRequestInfo.model_config["json_schema_extra"]["example"]["form"], # fields required in the form "firstName": faker.first_name(), "lastName": faker.last_name(), diff --git a/services/web/server/tests/unit/with_dbs/03/meta_modeling/test_meta_modeling_iterations.py b/services/web/server/tests/unit/with_dbs/03/meta_modeling/test_meta_modeling_iterations.py index 20cb885bdfa..b01c9029f3b 100644 --- a/services/web/server/tests/unit/with_dbs/03/meta_modeling/test_meta_modeling_iterations.py +++ b/services/web/server/tests/unit/with_dbs/03/meta_modeling/test_meta_modeling_iterations.py @@ -131,7 +131,7 @@ async def test_iterators_workflow( project_id=project_data["uuid"] ) for node_id, node_data in modifications["workbench"].items(): - node = Node.parse_obj(node_data) + node = Node.model_validate(node_data) response = await client.post( f"{create_node_url}", json={ @@ -188,7 +188,7 @@ async def _mock_start(project_id, user_id, product_name, **options): f"/v0/projects/{project_uuid}/checkpoint/{head_ref_id}/iterations?offset=0" ) body = await response.json() - first_iterlist = Page[ProjectIterationItem].parse_obj(body).data + first_iterlist = Page[ProjectIterationItem].model_validate(body).data assert len(first_iterlist) == 3 @@ -231,7 +231,7 @@ async def _mock_catalog_get(*args, **kwarg): assert response.status == status.HTTP_200_OK, await response.text() body = await response.json() - assert Page[ProjectIterationResultItem].parse_obj(body).data is not None + assert Page[ProjectIterationResultItem].model_validate(body).data is not None # GET project and MODIFY iterator values---------------------------------------------- # - Change iterations from 0:4 -> HEAD+1 @@ -245,7 +245,7 @@ async def _mock_catalog_get(*args, **kwarg): # Dict keys are usually some sort of identifier, typically a UUID or # and index but nothing prevents a dict from using any other type of key types # - project = Project.parse_obj(body["data"]) + project = Project.model_validate(body["data"]) new_project = project.copy( update={ # TODO: HACK to overcome export from None -> string @@ -290,7 +290,7 @@ async def _mock_catalog_get(*args, **kwarg): ) body = await response.json() assert response.status == status.HTTP_200_OK, f"{body=}" # nosec - second_iterlist = Page[ProjectIterationItem].parse_obj(body).data + second_iterlist = Page[ProjectIterationItem].model_validate(body).data assert len(second_iterlist) == 4 assert len({it.workcopy_project_id for it in second_iterlist}) == len( diff --git a/services/web/server/tests/unit/with_dbs/03/resource_usage/test_admin_pricing_plans.py b/services/web/server/tests/unit/with_dbs/03/resource_usage/test_admin_pricing_plans.py index 6e67883e357..04b30407526 100644 --- a/services/web/server/tests/unit/with_dbs/03/resource_usage/test_admin_pricing_plans.py +++ b/services/web/server/tests/unit/with_dbs/03/resource_usage/test_admin_pricing_plans.py @@ -36,7 +36,8 @@ def mock_rpc_resource_usage_tracker_service_api( autospec=True, return_value=[ parse_obj_as( - PricingPlanGet, PricingPlanGet.Config.schema_extra["examples"][0] + PricingPlanGet, + PricingPlanGet.model_config["json_schema_extra"]["examples"][0], ) ], ), @@ -44,21 +45,24 @@ def mock_rpc_resource_usage_tracker_service_api( "simcore_service_webserver.resource_usage._pricing_plans_admin_api.pricing_plans.get_pricing_plan", autospec=True, return_value=parse_obj_as( - PricingPlanGet, PricingPlanGet.Config.schema_extra["examples"][0] + PricingPlanGet, + PricingPlanGet.model_config["json_schema_extra"]["examples"][0], ), ), "create_pricing_plan": mocker.patch( "simcore_service_webserver.resource_usage._pricing_plans_admin_api.pricing_plans.create_pricing_plan", autospec=True, return_value=parse_obj_as( - PricingPlanGet, PricingPlanGet.Config.schema_extra["examples"][0] + PricingPlanGet, + PricingPlanGet.model_config["json_schema_extra"]["examples"][0], ), ), "update_pricing_plan": mocker.patch( "simcore_service_webserver.resource_usage._pricing_plans_admin_api.pricing_plans.update_pricing_plan", autospec=True, return_value=parse_obj_as( - PricingPlanGet, PricingPlanGet.Config.schema_extra["examples"][0] + PricingPlanGet, + PricingPlanGet.model_config["json_schema_extra"]["examples"][0], ), ), ## Pricing units @@ -66,21 +70,24 @@ def mock_rpc_resource_usage_tracker_service_api( "simcore_service_webserver.resource_usage._pricing_plans_admin_api.pricing_units.get_pricing_unit", autospec=True, return_value=parse_obj_as( - PricingUnitGet, PricingUnitGet.Config.schema_extra["examples"][0] + PricingUnitGet, + PricingUnitGet.model_config["json_schema_extra"]["examples"][0], ), ), "create_pricing_unit": mocker.patch( "simcore_service_webserver.resource_usage._pricing_plans_admin_api.pricing_units.create_pricing_unit", autospec=True, return_value=parse_obj_as( - PricingUnitGet, PricingUnitGet.Config.schema_extra["examples"][0] + PricingUnitGet, + PricingUnitGet.model_config["json_schema_extra"]["examples"][0], ), ), "update_pricing_unit": mocker.patch( "simcore_service_webserver.resource_usage._pricing_plans_admin_api.pricing_units.update_pricing_unit", autospec=True, return_value=parse_obj_as( - PricingUnitGet, PricingUnitGet.Config.schema_extra["examples"][0] + PricingUnitGet, + PricingUnitGet.model_config["json_schema_extra"]["examples"][0], ), ), ## Pricing plan to service @@ -90,7 +97,9 @@ def mock_rpc_resource_usage_tracker_service_api( return_value=[ parse_obj_as( PricingPlanToServiceGet, - PricingPlanToServiceGet.Config.schema_extra["examples"][0], + PricingPlanToServiceGet.model_config["json_schema_extra"][ + "examples" + ][0], ) ], ), @@ -99,7 +108,9 @@ def mock_rpc_resource_usage_tracker_service_api( autospec=True, return_value=parse_obj_as( PricingPlanToServiceGet, - PricingPlanToServiceGet.Config.schema_extra["examples"][0], + PricingPlanToServiceGet.model_config["json_schema_extra"]["examples"][ + 0 + ], ), ), } diff --git a/services/web/server/tests/unit/with_dbs/03/resource_usage/test_pricing_plans.py b/services/web/server/tests/unit/with_dbs/03/resource_usage/test_pricing_plans.py index 7b25e33a799..e48d461ad26 100644 --- a/services/web/server/tests/unit/with_dbs/03/resource_usage/test_pricing_plans.py +++ b/services/web/server/tests/unit/with_dbs/03/resource_usage/test_pricing_plans.py @@ -33,12 +33,12 @@ def mock_rut_api_responses( settings: ResourceUsageTrackerSettings = get_plugin_settings(client.app) pricing_unit_get = parse_obj_as( - PricingUnitGet, PricingUnitGet.Config.schema_extra["examples"][0] + PricingUnitGet, PricingUnitGet.model_config["json_schema_extra"]["examples"][0] ) service_pricing_plan_get = parse_obj_as( PricingPlanGet, - PricingPlanGet.Config.schema_extra["examples"][0], + PricingPlanGet.model_config["json_schema_extra"]["examples"][0], ) aioresponses_mocker.get( diff --git a/services/web/server/tests/unit/with_dbs/03/test_email.py b/services/web/server/tests/unit/with_dbs/03/test_email.py index e2164071c16..d6f7a050239 100644 --- a/services/web/server/tests/unit/with_dbs/03/test_email.py +++ b/services/web/server/tests/unit/with_dbs/03/test_email.py @@ -136,9 +136,9 @@ async def test_email_handlers( assert error is None with pytest.raises(ValidationError): - EmailTestFailed.parse_obj(data) + EmailTestFailed.model_validate(data) - passed = EmailTestPassed.parse_obj(data) + passed = EmailTestPassed.model_validate(data) print(passed.json(indent=1)) diff --git a/services/web/server/tests/unit/with_dbs/03/test_users.py b/services/web/server/tests/unit/with_dbs/03/test_users.py index 80c0c7912af..d37cbd8f0e2 100644 --- a/services/web/server/tests/unit/with_dbs/03/test_users.py +++ b/services/web/server/tests/unit/with_dbs/03/test_users.py @@ -82,12 +82,12 @@ async def test_get_profile( data, error = await assert_status(resp, expected) # check enveloped - e = Envelope[ProfileGet].parse_obj(await resp.json()) + e = Envelope[ProfileGet].model_validate(await resp.json()) assert e.error == error assert e.data.dict(**RESPONSE_MODEL_POLICY) == data if e.data else e.data == data if not error: - profile = ProfileGet.parse_obj(data) + profile = ProfileGet.model_validate(data) product_group = { "accessRights": {"delete": False, "read": False, "write": False}, @@ -250,7 +250,9 @@ def account_request_form(faker: Faker) -> dict[str, Any]: } # keeps in sync fields from example and this fixture - assert set(form) == set(AccountRequestInfo.Config.schema_extra["example"]["form"]) + assert set(form) == set( + AccountRequestInfo.model_config["json_schema_extra"]["example"]["form"] + ) return form diff --git a/services/web/server/tests/unit/with_dbs/03/test_users__notifications.py b/services/web/server/tests/unit/with_dbs/03/test_users__notifications.py index 77aaccade51..70d76a247f5 100644 --- a/services/web/server/tests/unit/with_dbs/03/test_users__notifications.py +++ b/services/web/server/tests/unit/with_dbs/03/test_users__notifications.py @@ -71,7 +71,7 @@ def _create_notification( notification_categories = tuple(NotificationCategory) notification: UserNotification = UserNotification.create_from_request_data( - UserNotificationCreate.parse_obj( + UserNotificationCreate.model_validate( { "user_id": user_id, "category": random.choice(notification_categories), diff --git a/services/web/server/tests/unit/with_dbs/03/version_control/conftest.py b/services/web/server/tests/unit/with_dbs/03/version_control/conftest.py index 7343176760e..5843d32408e 100644 --- a/services/web/server/tests/unit/with_dbs/03/version_control/conftest.py +++ b/services/web/server/tests/unit/with_dbs/03/version_control/conftest.py @@ -207,7 +207,7 @@ async def _go(client: TestClient, project_uuid: UUID) -> None: # add a node node_id = faker.uuid4() - node = Node.parse_obj( + node = Node.model_validate( { "key": f"simcore/services/comp/test_{__name__}", "version": "1.0.0", diff --git a/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_handlers.py b/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_handlers.py index 05ab31ccdf8..078ba287a5e 100644 --- a/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_handlers.py +++ b/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_handlers.py @@ -32,7 +32,7 @@ async def assert_resp_page( assert resp.status == status.HTTP_200_OK, f"Got {await resp.text()}" body = await resp.json() - page = expected_page_cls.parse_obj(body) + page = expected_page_cls.model_validate(body) assert page.meta.total == expected_total assert page.meta.count == expected_count return page @@ -42,7 +42,7 @@ async def assert_status_and_body( resp, expected_cls: HTTPStatus, expected_model: type[BaseModel] ) -> BaseModel: data, _ = await assert_status(resp, expected_cls) - return expected_model.parse_obj(data) + return expected_model.model_validate(data) @pytest.mark.acceptance_test() @@ -59,7 +59,7 @@ async def test_workflow( # get existing project resp = await client.get(f"/{VX}/projects/{project_uuid}") data, _ = await assert_status(resp, status.HTTP_200_OK) - project = Project.parse_obj(data) + project = Project.model_validate(data) assert project.uuid == UUID(project_uuid) # @@ -78,7 +78,7 @@ async def test_workflow( data, _ = await assert_status(resp, status.HTTP_201_CREATED) assert data - checkpoint1 = CheckpointApiModel.parse_obj(data) # NOTE: this is NOT API model + checkpoint1 = CheckpointApiModel.model_validate(data) # NOTE: this is NOT API model # # this project now has a repo @@ -87,20 +87,20 @@ async def test_workflow( resp, expected_page_cls=Page[ProjectDict], expected_total=1, expected_count=1 ) - repo = RepoApiModel.parse_obj(page.data[0]) + repo = RepoApiModel.model_validate(page.data[0]) assert repo.project_uuid == UUID(project_uuid) # GET checkpoint with HEAD resp = await client.get(f"/{VX}/repos/projects/{project_uuid}/checkpoints/HEAD") data, _ = await assert_status(resp, status.HTTP_200_OK) - assert CheckpointApiModel.parse_obj(data) == checkpoint1 + assert CheckpointApiModel.model_validate(data) == checkpoint1 # TODO: GET checkpoint with tag with pytest.raises(aiohttp.ClientResponseError) as excinfo: resp = await client.get(f"/{VX}/repos/projects/{project_uuid}/checkpoints/v1") resp.raise_for_status() - assert CheckpointApiModel.parse_obj(data) == checkpoint1 + assert CheckpointApiModel.model_validate(data) == checkpoint1 assert excinfo.value.status == status.HTTP_501_NOT_IMPLEMENTED @@ -109,7 +109,7 @@ async def test_workflow( f"/{VX}/repos/projects/{project_uuid}/checkpoints/{checkpoint1.id}" ) assert str(resp.url) == checkpoint1.url - assert CheckpointApiModel.parse_obj(data) == checkpoint1 + assert CheckpointApiModel.model_validate(data) == checkpoint1 # LIST checkpoints resp = await client.get(f"/{VX}/repos/projects/{project_uuid}/checkpoints") @@ -120,7 +120,7 @@ async def test_workflow( expected_count=1, ) - assert CheckpointApiModel.parse_obj(page.data[0]) == checkpoint1 + assert CheckpointApiModel.model_validate(page.data[0]) == checkpoint1 # UPDATE checkpoint annotations resp = await client.patch( @@ -128,7 +128,7 @@ async def test_workflow( json={"message": "updated message", "tag": "Version 1"}, ) data, _ = await assert_status(resp, status.HTTP_200_OK) - checkpoint1_updated = CheckpointApiModel.parse_obj(data) + checkpoint1_updated = CheckpointApiModel.model_validate(data) assert checkpoint1.id == checkpoint1_updated.id assert checkpoint1.checksum == checkpoint1_updated.checksum @@ -154,30 +154,30 @@ async def test_workflow( json={"tag": "v2", "message": "new commit"}, ) data, _ = await assert_status(resp, status.HTTP_201_CREATED) - checkpoint2 = CheckpointApiModel.parse_obj(data) + checkpoint2 = CheckpointApiModel.model_validate(data) assert checkpoint2.tags == ("v2",) # GET checkpoint with HEAD resp = await client.get(f"/{VX}/repos/projects/{project_uuid}/checkpoints/HEAD") data, _ = await assert_status(resp, status.HTTP_200_OK) - assert CheckpointApiModel.parse_obj(data) == checkpoint2 + assert CheckpointApiModel.model_validate(data) == checkpoint2 # CHECKOUT resp = await client.post( f"/{VX}/repos/projects/{project_uuid}/checkpoints/{checkpoint1.id}:checkout" ) data, _ = await assert_status(resp, status.HTTP_200_OK) - assert CheckpointApiModel.parse_obj(data) == checkpoint1_updated + assert CheckpointApiModel.model_validate(data) == checkpoint1_updated # GET checkpoint with HEAD resp = await client.get(f"/{VX}/repos/projects/{project_uuid}/checkpoints/HEAD") data, _ = await assert_status(resp, status.HTTP_200_OK) - assert CheckpointApiModel.parse_obj(data) == checkpoint1_updated + assert CheckpointApiModel.model_validate(data) == checkpoint1_updated # get working copy resp = await client.get(f"/{VX}/projects/{project_uuid}") data, _ = await assert_status(resp, status.HTTP_200_OK) - project_wc = Project.parse_obj(data) + project_wc = Project.model_validate(data) assert project_wc.uuid == UUID(project_uuid) assert project_wc != project @@ -193,7 +193,7 @@ async def test_create_checkpoint_without_changes( data, _ = await assert_status(resp, status.HTTP_201_CREATED) assert data - checkpoint1 = CheckpointApiModel.parse_obj(data) # NOTE: this is NOT API model + checkpoint1 = CheckpointApiModel.model_validate(data) # NOTE: this is NOT API model # CREATE checkpoint WITHOUT changes resp = await client.post( @@ -203,7 +203,7 @@ async def test_create_checkpoint_without_changes( data, _ = await assert_status(resp, status.HTTP_201_CREATED) assert data - checkpoint2 = CheckpointApiModel.parse_obj(data) # NOTE: this is NOT API model + checkpoint2 = CheckpointApiModel.model_validate(data) # NOTE: this is NOT API model assert ( checkpoint1 == checkpoint2 diff --git a/services/web/server/tests/unit/with_dbs/03/wallets/payments/conftest.py b/services/web/server/tests/unit/with_dbs/03/wallets/payments/conftest.py index 2b59b77c3b5..c84023dfe9a 100644 --- a/services/web/server/tests/unit/with_dbs/03/wallets/payments/conftest.py +++ b/services/web/server/tests/unit/with_dbs/03/wallets/payments/conftest.py @@ -79,7 +79,7 @@ async def _create(): }, ) data, _ = await assert_status(resp, status.HTTP_201_CREATED) - return WalletGet.parse_obj(data) + return WalletGet.model_validate(data) return _create diff --git a/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments.py b/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments.py index f6519735ed1..0ace542ea86 100644 --- a/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments.py +++ b/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments.py @@ -105,7 +105,7 @@ async def test_one_time_payment_worfklow( data, error = await assert_status(response, expected_status) if not error: - payment = WalletPaymentInitiated.parse_obj(data) + payment = WalletPaymentInitiated.model_validate(data) assert payment.payment_id assert payment.payment_form_url @@ -200,7 +200,7 @@ async def test_multiple_payments( data, error = await assert_status(response, status.HTTP_201_CREATED) assert data assert not error - payment = WalletPaymentInitiated.parse_obj(data) + payment = WalletPaymentInitiated.model_validate(data) if n % 2: transaction = await _ack_creation_of_wallet_payment( @@ -286,7 +286,7 @@ async def test_complete_payment_errors( assert mock_rpc_payments_service_api["init_payment"].called data, _ = await assert_status(response, status.HTTP_201_CREATED) - payment = WalletPaymentInitiated.parse_obj(data) + payment = WalletPaymentInitiated.model_validate(data) # Cannot complete as PENDING with pytest.raises(ValueError): diff --git a/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments_methods.py b/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments_methods.py index 0980e45caa2..89291d5eb56 100644 --- a/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments_methods.py +++ b/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments_methods.py @@ -65,7 +65,7 @@ async def test_payment_method_worfklow( ) data, error = await assert_status(response, status.HTTP_202_ACCEPTED) assert error is None - inited = PaymentMethodInitiated.parse_obj(data) + inited = PaymentMethodInitiated.model_validate(data) assert inited.payment_method_id assert inited.payment_method_form_url.query @@ -140,7 +140,7 @@ async def test_init_and_cancel_payment_method( ) data, error = await assert_status(response, status.HTTP_202_ACCEPTED) assert error is None - inited = PaymentMethodInitiated.parse_obj(data) + inited = PaymentMethodInitiated.model_validate(data) # cancel Create response = await client.post( @@ -165,7 +165,7 @@ async def _add_payment_method( ) data, error = await assert_status(response, status.HTTP_202_ACCEPTED) assert error is None - inited = PaymentMethodInitiated.parse_obj(data) + inited = PaymentMethodInitiated.model_validate(data) await _ack_creation_of_wallet_payment_method( client.app, payment_method_id=inited.payment_method_id, @@ -249,7 +249,7 @@ async def test_wallet_autorecharge( ) data, error = await assert_status(response, expected_status) if not error: - updated_auto_recharge = GetWalletAutoRecharge.parse_obj(data) + updated_auto_recharge = GetWalletAutoRecharge.model_validate(data) assert updated_auto_recharge == GetWalletAutoRecharge( payment_method_id=payment_method_id, min_balance_in_credits=settings.PAYMENTS_AUTORECHARGE_MIN_BALANCE_IN_CREDITS, @@ -263,7 +263,7 @@ async def test_wallet_autorecharge( f"/v0/wallets/{wallet.wallet_id}/auto-recharge", ) data, _ = await assert_status(response, status.HTTP_200_OK) - assert updated_auto_recharge == GetWalletAutoRecharge.parse_obj(data) + assert updated_auto_recharge == GetWalletAutoRecharge.model_validate(data) # payment-methods.auto_recharge response = await client.get(f"/v0/wallets/{wallet.wallet_id}/payments-methods") @@ -305,7 +305,7 @@ async def test_delete_primary_payment_method_in_autorecharge( }, ) data, _ = await assert_status(response, status.HTTP_200_OK) - auto_recharge = GetWalletAutoRecharge.parse_obj(data) + auto_recharge = GetWalletAutoRecharge.model_validate(data) assert auto_recharge.enabled is True assert auto_recharge.payment_method_id == payment_method_id assert auto_recharge.monthly_limit_in_usd == 123 @@ -321,7 +321,7 @@ async def test_delete_primary_payment_method_in_autorecharge( f"/v0/wallets/{wallet.wallet_id}/auto-recharge", ) data, _ = await assert_status(response, status.HTTP_200_OK) - auto_recharge_after_delete = GetWalletAutoRecharge.parse_obj(data) + auto_recharge_after_delete = GetWalletAutoRecharge.model_validate(data) assert auto_recharge_after_delete.payment_method_id is None assert auto_recharge_after_delete.enabled is False @@ -334,7 +334,7 @@ async def test_delete_primary_payment_method_in_autorecharge( f"/v0/wallets/{wallet.wallet_id}/auto-recharge", ) data, _ = await assert_status(response, status.HTTP_200_OK) - auto_recharge = GetWalletAutoRecharge.parse_obj(data) + auto_recharge = GetWalletAutoRecharge.model_validate(data) assert auto_recharge.payment_method_id == new_payment_method_id assert auto_recharge.enabled is False @@ -398,7 +398,7 @@ async def test_one_time_payment_with_payment_method( ) data, error = await assert_status(response, expected_status) if not error: - payment = WalletPaymentInitiated.parse_obj(data) + payment = WalletPaymentInitiated.model_validate(data) assert mock_rpc_payments_service_api["pay_with_payment_method"].called assert payment.payment_id diff --git a/services/web/server/tests/unit/with_dbs/03/workspaces/test_workspaces.py b/services/web/server/tests/unit/with_dbs/03/workspaces/test_workspaces.py index e2ace9daa6a..9365094d679 100644 --- a/services/web/server/tests/unit/with_dbs/03/workspaces/test_workspaces.py +++ b/services/web/server/tests/unit/with_dbs/03/workspaces/test_workspaces.py @@ -59,7 +59,7 @@ async def test_workspaces_workflow( }, ) added_workspace, _ = await assert_status(resp, status.HTTP_201_CREATED) - assert WorkspaceGet.parse_obj(added_workspace) + assert WorkspaceGet.model_validate(added_workspace) # list user workspaces url = client.app.router["list_workspaces"].url_for() @@ -96,7 +96,7 @@ async def test_workspaces_workflow( }, ) data, _ = await assert_status(resp, status.HTTP_200_OK) - assert WorkspaceGet.parse_obj(data) + assert WorkspaceGet.model_validate(data) # list user workspaces url = client.app.router["list_workspaces"].url_for() From 44d97de20674142b9edf60bdb110c05eab965b82 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 11:47:53 +0200 Subject: [PATCH 016/165] continue fixing --- .../director_v2/_core_dynamic_services.py | 6 ++--- .../exporter/_formatter/_sds.py | 7 ++---- .../simcore_service_webserver/groups/_db.py | 5 ++-- .../invitations/_client.py | 6 ++--- .../invitations/_core.py | 6 ++--- .../invitations/settings.py | 4 +-- .../login/_auth_handlers.py | 8 +++--- .../login/_registration_api.py | 8 +++--- .../login/handlers_confirmation.py | 16 ++++++------ .../payments/_methods_api.py | 4 +-- .../payments/_onetime_api.py | 9 ++++--- .../payments/_rpc.py | 25 ++++++++++--------- .../payments/_tasks.py | 5 ++-- .../projects/_comments_db.py | 10 ++++---- .../projects/_crud_api_create.py | 12 +++++---- .../projects/_crud_handlers.py | 3 +-- .../projects/_groups_api.py | 10 +++++--- .../projects/_groups_db.py | 6 ++--- 18 files changed, 78 insertions(+), 72 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/director_v2/_core_dynamic_services.py b/services/web/server/src/simcore_service_webserver/director_v2/_core_dynamic_services.py index 20cf772075e..21793b79376 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/_core_dynamic_services.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/_core_dynamic_services.py @@ -10,7 +10,7 @@ from models_library.api_schemas_directorv2.dynamic_services import DynamicServiceGet from models_library.projects import ProjectID from models_library.services import ServicePortKey -from pydantic import BaseModel, NonNegativeInt, parse_obj_as +from pydantic import BaseModel, NonNegativeInt, TypeAdapter from pydantic.types import PositiveInt from servicelib.logging_utils import log_decorator from yarl import URL @@ -33,7 +33,7 @@ async def list_dynamic_services( project_id: str | None = None, ) -> list[DynamicServiceGet]: params = _Params(user_id=user_id, project_id=project_id) - params_dict = params.dict(exclude_none=True) + params_dict = params.model_dump(exclude_none=True) settings: DirectorV2Settings = get_plugin_settings(app) if params_dict: # Update query doesnt work with no params to unwrap backend_url = (settings.base_url / "dynamic_services").update_query( @@ -49,7 +49,7 @@ async def list_dynamic_services( if services is None: services = [] assert isinstance(services, list) # nosec - return parse_obj_as(list[DynamicServiceGet], services) + return TypeAdapter(list[DynamicServiceGet]).validate_python(services) # NOTE: ANE https://github.com/ITISFoundation/osparc-simcore/issues/3191 diff --git a/services/web/server/src/simcore_service_webserver/exporter/_formatter/_sds.py b/services/web/server/src/simcore_service_webserver/exporter/_formatter/_sds.py index 994d06690de..65749210e77 100644 --- a/services/web/server/src/simcore_service_webserver/exporter/_formatter/_sds.py +++ b/services/web/server/src/simcore_service_webserver/exporter/_formatter/_sds.py @@ -5,7 +5,6 @@ from typing import Any, Final from aiohttp import web -from pydantic import parse_obj_as from servicelib.pools import non_blocking_process_pool_executor from ...catalog.client import get_service @@ -79,8 +78,7 @@ async def _add_rrid_entries( continue rrid_entires.append( - parse_obj_as( - RRIDEntry, + RRIDEntry.model_validate( { "rrid_term": scicrunch_resource.name, "rrid_identifier": scicrunch_resource.rrid, @@ -158,8 +156,7 @@ async def create_sds_directory( _logger.debug("Project data: %s", project_data) # assemble params here - dataset_description_params = parse_obj_as( - DatasetDescriptionParams, + dataset_description_params = DatasetDescriptionParams.model_validate( {"name": project_data["name"], "description": project_data["description"]}, ) diff --git a/services/web/server/src/simcore_service_webserver/groups/_db.py b/services/web/server/src/simcore_service_webserver/groups/_db.py index 38bbb4e7d7c..4eda0ff3e65 100644 --- a/services/web/server/src/simcore_service_webserver/groups/_db.py +++ b/services/web/server/src/simcore_service_webserver/groups/_db.py @@ -6,7 +6,6 @@ from aiopg.sa.result import ResultProxy, RowProxy from models_library.groups import GroupAtDB from models_library.users import GroupID, UserID -from pydantic import parse_obj_as from simcore_postgres_database.utils_products import get_or_create_product_group from sqlalchemy import and_, literal_column from sqlalchemy.dialects.postgresql import insert @@ -117,7 +116,7 @@ async def get_all_user_groups(conn: SAConnection, user_id: UserID) -> list[Group .where(user_to_groups.c.uid == user_id) ) rows = await result.fetchall() or [] - return [parse_obj_as(GroupAtDB, row) for row in rows] + return [GroupAtDB.model_validate(row) for row in rows] async def get_user_group( @@ -409,5 +408,5 @@ async def get_group_from_gid(conn: SAConnection, gid: GroupID) -> GroupAtDB | No row: ResultProxy = await conn.execute(groups.select().where(groups.c.gid == gid)) result = await row.first() if result: - return GroupAtDB.from_orm(result) + return GroupAtDB.model_validate(result) return None diff --git a/services/web/server/src/simcore_service_webserver/invitations/_client.py b/services/web/server/src/simcore_service_webserver/invitations/_client.py index cc427cf28ce..4ca20894b0e 100644 --- a/services/web/server/src/simcore_service_webserver/invitations/_client.py +++ b/services/web/server/src/simcore_service_webserver/invitations/_client.py @@ -10,7 +10,7 @@ ApiInvitationInputs, ) from models_library.utils.fastapi_encoders import jsonable_encoder -from pydantic import AnyHttpUrl, parse_obj_as +from pydantic import AnyHttpUrl from yarl import URL from .._constants import APP_SETTINGS_KEY @@ -86,7 +86,7 @@ async def extract_invitation( url=self._url_vtag("/invitations:extract"), json={"invitation_url": invitation_url}, ) - return parse_obj_as(ApiInvitationContent, await response.json()) + return ApiInvitationContent.model_validate(await response.json()) async def generate_invitation( self, params: ApiInvitationInputs @@ -95,7 +95,7 @@ async def generate_invitation( url=self._url_vtag("/invitations"), json=jsonable_encoder(params), ) - return parse_obj_as(ApiInvitationContentAndLink, await response.json()) + return ApiInvitationContentAndLink.model_validate(await response.json()) # diff --git a/services/web/server/src/simcore_service_webserver/invitations/_core.py b/services/web/server/src/simcore_service_webserver/invitations/_core.py index 2bf18487638..fcd9e619742 100644 --- a/services/web/server/src/simcore_service_webserver/invitations/_core.py +++ b/services/web/server/src/simcore_service_webserver/invitations/_core.py @@ -9,7 +9,7 @@ ApiInvitationInputs, ) from models_library.emails import LowerCaseEmailStr -from pydantic import AnyHttpUrl, ValidationError, parse_obj_as +from pydantic import AnyHttpUrl, TypeAdapter, ValidationError from servicelib.aiohttp import status from ..groups.api import is_user_by_email_in_group @@ -92,7 +92,7 @@ async def validate_invitation_url( with _handle_exceptions_as_invitations_errors(): try: - valid_url = parse_obj_as(AnyHttpUrl, invitation_url) + valid_url = TypeAdapter(AnyHttpUrl).validate_python(invitation_url) except ValidationError as err: raise InvalidInvitationError(reason=MSG_INVALID_INVITATION_URL) from err @@ -143,7 +143,7 @@ async def extract_invitation( with _handle_exceptions_as_invitations_errors(): try: - valid_url = parse_obj_as(AnyHttpUrl, invitation_url) + valid_url = TypeAdapter(AnyHttpUrl).validate_python(invitation_url) except ValidationError as err: raise InvalidInvitationError(reason=MSG_INVALID_INVITATION_URL) from err diff --git a/services/web/server/src/simcore_service_webserver/invitations/settings.py b/services/web/server/src/simcore_service_webserver/invitations/settings.py index 025f89955ff..02755291910 100644 --- a/services/web/server/src/simcore_service_webserver/invitations/settings.py +++ b/services/web/server/src/simcore_service_webserver/invitations/settings.py @@ -8,7 +8,7 @@ from typing import Final from aiohttp import web -from pydantic import Field, SecretStr, parse_obj_as +from pydantic import Field, SecretStr, TypeAdapter from settings_library.base import BaseCustomSettings from settings_library.basic_types import PortInt, VersionTag from settings_library.utils_service import ( @@ -19,7 +19,7 @@ from .._constants import APP_SETTINGS_KEY -_INVITATION_VTAG_V1: Final[VersionTag] = parse_obj_as(VersionTag, "v1") +_INVITATION_VTAG_V1: Final[VersionTag] = TypeAdapter(VersionTag).validate_python("v1") class InvitationsSettings(BaseCustomSettings, MixinServiceSettings): diff --git a/services/web/server/src/simcore_service_webserver/login/_auth_handlers.py b/services/web/server/src/simcore_service_webserver/login/_auth_handlers.py index 95223a55d68..db8ee3421e3 100644 --- a/services/web/server/src/simcore_service_webserver/login/_auth_handlers.py +++ b/services/web/server/src/simcore_service_webserver/login/_auth_handlers.py @@ -4,7 +4,7 @@ from aiohttp.web import RouteTableDef from models_library.authentification import TwoFactorAuthentificationMethod from models_library.emails import LowerCaseEmailStr -from pydantic import BaseModel, Field, PositiveInt, SecretStr, parse_obj_as +from pydantic import BaseModel, Field, PositiveInt, SecretStr, TypeAdapter from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import parse_request_body_as from servicelib.logging_utils import get_log_record_extra, log_context @@ -137,9 +137,9 @@ async def login(request: web.Request): value=user_2fa_authentification_method, ) else: - user_2fa_authentification_method = parse_obj_as( - TwoFactorAuthentificationMethod, user_2fa_preference.value - ) + user_2fa_authentification_method = TypeAdapter( + TwoFactorAuthentificationMethod + ).validate_python(user_2fa_preference.value) if user_2fa_authentification_method == TwoFactorAuthentificationMethod.DISABLED: return await login_granted_response(request, user=user) diff --git a/services/web/server/src/simcore_service_webserver/login/_registration_api.py b/services/web/server/src/simcore_service_webserver/login/_registration_api.py index 1dfd1a5a500..a61356dd952 100644 --- a/services/web/server/src/simcore_service_webserver/login/_registration_api.py +++ b/services/web/server/src/simcore_service_webserver/login/_registration_api.py @@ -9,7 +9,7 @@ from models_library.utils.fastapi_encoders import jsonable_encoder from models_library.utils.json_serialization import json_dumps from PIL.Image import Image -from pydantic import EmailStr, PositiveInt, ValidationError, parse_obj_as +from pydantic import EmailStr, PositiveInt, TypeAdapter, ValidationError from servicelib.utils_secrets import generate_passcode from ..email.utils import send_email_from_template @@ -66,7 +66,9 @@ async def send_account_request_email_to_support( support_email = product.support_email email_template_path = await get_product_template_path(request, template_name) try: - user_email = parse_obj_as(LowerCaseEmailStr, request_form.get("email", None)) + user_email = TypeAdapter(LowerCaseEmailStr).validate_python( + request_form.get("email", None) + ) except ValidationError: user_email = None @@ -80,7 +82,7 @@ async def send_account_request_email_to_support( context={ "host": request.host, "name": "support-team", - "product": product.dict( + "product": product.model_dump( include={ "name", "display_name", diff --git a/services/web/server/src/simcore_service_webserver/login/handlers_confirmation.py b/services/web/server/src/simcore_service_webserver/login/handlers_confirmation.py index c627fb58358..79d77dbd3a7 100644 --- a/services/web/server/src/simcore_service_webserver/login/handlers_confirmation.py +++ b/services/web/server/src/simcore_service_webserver/login/handlers_confirmation.py @@ -12,9 +12,9 @@ Field, PositiveInt, SecretStr, + TypeAdapter, ValidationError, - parse_obj_as, - validator, + field_validator, ) from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( @@ -73,7 +73,7 @@ def _parse_extra_credits_in_usd_or_none( ) -> PositiveInt | None: with suppress(ValidationError, JSONDecodeError): confirmation_data = confirmation.get("data", "EMPTY") or "EMPTY" - invitation = InvitationData.parse_raw(confirmation_data) + invitation = InvitationData.model_validate_json(confirmation_data) return invitation.extra_credits_in_usd return None @@ -110,7 +110,11 @@ async def _handle_confirm_change_email( # update and consume confirmation token await db.delete_confirmation_and_update_user( user_id=user_id, - updates={"email": parse_obj_as(LowerCaseEmailStr, confirmation["data"])}, + updates={ + "email": TypeAdapter(LowerCaseEmailStr).validate_python( + confirmation["data"] + ) + }, confirmation=confirmation, ) @@ -265,9 +269,7 @@ class ResetPasswordConfirmation(InputSchema): password: SecretStr confirm: SecretStr - _password_confirm_match = validator("confirm", allow_reuse=True)( - check_confirm_password_match - ) + _password_confirm_match = field_validator("confirm")(check_confirm_password_match) @routes.post("/v0/auth/reset-password/{code}", name="auth_reset_password_allowed") diff --git a/services/web/server/src/simcore_service_webserver/payments/_methods_api.py b/services/web/server/src/simcore_service_webserver/payments/_methods_api.py index bf6c4a32a0b..e303a382f35 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_methods_api.py +++ b/services/web/server/src/simcore_service_webserver/payments/_methods_api.py @@ -15,7 +15,7 @@ from models_library.products import ProductName from models_library.users import UserID from models_library.wallets import WalletID -from pydantic import HttpUrl, parse_obj_as +from pydantic import HttpUrl, TypeAdapter from servicelib.logging_utils import log_decorator from simcore_postgres_database.models.payments_methods import InitPromptAckFlowState from yarl import URL @@ -97,7 +97,7 @@ async def _fake_init_creation_of_wallet_payment_method( return PaymentMethodInitiated( wallet_id=wallet_id, payment_method_id=payment_method_id, - payment_method_form_url=parse_obj_as(HttpUrl, f"{form_link}"), + payment_method_form_url=TypeAdapter(HttpUrl).validate_python(f"{form_link}"), ) diff --git a/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py b/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py index bff07528d2c..55c43e0d6c7 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py +++ b/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py @@ -15,7 +15,7 @@ from models_library.products import ProductName from models_library.users import UserID from models_library.wallets import WalletID -from pydantic import HttpUrl, parse_obj_as +from pydantic import HttpUrl, TypeAdapter from servicelib.logging_utils import log_decorator from simcore_postgres_database.models.payments_transactions import ( PaymentTransactionState, @@ -128,7 +128,7 @@ async def _ack_creation_of_wallet_payment( assert transaction.completed_at is not None # nosec assert transaction.initiated_at < transaction.completed_at # nosec - _logger.info("Transaction completed: %s", transaction.json(indent=1)) + _logger.info("Transaction completed: %s", transaction.model_dump_json(indent=1)) payment = _to_api_model(transaction) @@ -236,7 +236,10 @@ async def _fake_get_payment_invoice_url( assert wallet_id # nosec return cast( - HttpUrl, parse_obj_as(HttpUrl, f"https://fake-invoice.com/?id={payment_id}") + HttpUrl, + TypeAdapter(HttpUrl).validate_python( + f"https://fake-invoice.com/?id={payment_id}" + ), ) diff --git a/services/web/server/src/simcore_service_webserver/payments/_rpc.py b/services/web/server/src/simcore_service_webserver/payments/_rpc.py index f2b88bc1765..5401568953c 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_rpc.py +++ b/services/web/server/src/simcore_service_webserver/payments/_rpc.py @@ -21,7 +21,7 @@ from models_library.rabbitmq_basic_types import RPCMethodName from models_library.users import UserID from models_library.wallets import WalletID -from pydantic import EmailStr, HttpUrl, parse_obj_as +from pydantic import EmailStr, HttpUrl, TypeAdapter from servicelib.logging_utils import log_decorator from servicelib.rabbitmq import RPC_REQUEST_DEFAULT_TIMEOUT_S @@ -52,7 +52,7 @@ async def init_payment( # pylint: disable=too-many-arguments # NOTE: remote errors are aio_pika.MessageProcessError result = await rpc_client.request( PAYMENTS_RPC_NAMESPACE, - parse_obj_as(RPCMethodName, "init_payment"), + TypeAdapter(RPCMethodName).validate_python("init_payment"), amount_dollars=amount_dollars, target_credits=target_credits, product_name=product_name, @@ -83,7 +83,7 @@ async def cancel_payment( await rpc_client.request( PAYMENTS_RPC_NAMESPACE, - parse_obj_as(RPCMethodName, "cancel_payment"), + TypeAdapter(RPCMethodName).validate_python("cancel_payment"), payment_id=payment_id, user_id=user_id, wallet_id=wallet_id, @@ -104,7 +104,7 @@ async def get_payments_page( result: tuple[int, list[PaymentTransaction]] = await rpc_client.request( PAYMENTS_RPC_NAMESPACE, - parse_obj_as(RPCMethodName, "get_payments_page"), + TypeAdapter(RPCMethodName).validate_python("get_payments_page"), user_id=user_id, product_name=product_name, limit=limit, @@ -112,7 +112,8 @@ async def get_payments_page( timeout_s=2 * RPC_REQUEST_DEFAULT_TIMEOUT_S, ) assert ( # nosec - parse_obj_as(tuple[int, list[PaymentTransaction]], result) is not None + TypeAdapter(tuple[int, list[PaymentTransaction]]).validate_python(result) + is not None ) return result @@ -129,7 +130,7 @@ async def get_payment_invoice_url( result: HttpUrl = await rpc_client.request( PAYMENTS_RPC_NAMESPACE, - parse_obj_as(RPCMethodName, "get_payment_invoice_url"), + TypeAdapter(RPCMethodName).validate_python("get_payment_invoice_url"), user_id=user_id, wallet_id=wallet_id, payment_id=payment_id, @@ -152,7 +153,7 @@ async def init_creation_of_payment_method( result = await rpc_client.request( PAYMENTS_RPC_NAMESPACE, - parse_obj_as(RPCMethodName, "init_creation_of_payment_method"), + TypeAdapter(RPCMethodName).validate_python("init_creation_of_payment_method"), wallet_id=wallet_id, wallet_name=wallet_name, user_id=user_id, @@ -176,7 +177,7 @@ async def cancel_creation_of_payment_method( result = await rpc_client.request( PAYMENTS_RPC_NAMESPACE, - parse_obj_as(RPCMethodName, "cancel_creation_of_payment_method"), + TypeAdapter(RPCMethodName).validate_python("cancel_creation_of_payment_method"), payment_method_id=payment_method_id, user_id=user_id, wallet_id=wallet_id, @@ -196,7 +197,7 @@ async def list_payment_methods( result = await rpc_client.request( PAYMENTS_RPC_NAMESPACE, - parse_obj_as(RPCMethodName, "list_payment_methods"), + TypeAdapter(RPCMethodName).validate_python("list_payment_methods"), user_id=user_id, wallet_id=wallet_id, timeout_s=2 * RPC_REQUEST_DEFAULT_TIMEOUT_S, @@ -217,7 +218,7 @@ async def get_payment_method( result = await rpc_client.request( PAYMENTS_RPC_NAMESPACE, - parse_obj_as(RPCMethodName, "get_payment_method"), + TypeAdapter(RPCMethodName).validate_python("get_payment_method"), payment_method_id=payment_method_id, user_id=user_id, wallet_id=wallet_id, @@ -239,7 +240,7 @@ async def delete_payment_method( result = await rpc_client.request( PAYMENTS_RPC_NAMESPACE, - parse_obj_as(RPCMethodName, "delete_payment_method"), + TypeAdapter(RPCMethodName).validate_python("delete_payment_method"), payment_method_id=payment_method_id, user_id=user_id, wallet_id=wallet_id, @@ -270,7 +271,7 @@ async def pay_with_payment_method( # noqa: PLR0913 # pylint: disable=too-many-a result = await rpc_client.request( PAYMENTS_RPC_NAMESPACE, - parse_obj_as(RPCMethodName, "pay_with_payment_method"), + TypeAdapter(RPCMethodName).validate_python("pay_with_payment_method"), payment_method_id=payment_method_id, amount_dollars=amount_dollars, target_credits=target_credits, diff --git a/services/web/server/src/simcore_service_webserver/payments/_tasks.py b/services/web/server/src/simcore_service_webserver/payments/_tasks.py index d6c8a5719fb..b87465f5f3e 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_tasks.py +++ b/services/web/server/src/simcore_service_webserver/payments/_tasks.py @@ -6,7 +6,7 @@ from aiohttp import web from models_library.api_schemas_webserver.wallets import PaymentID, PaymentMethodID -from pydantic import HttpUrl, parse_obj_as +from pydantic import HttpUrl, TypeAdapter from servicelib.aiohttp.typing_extension import CleanupContextFunc from servicelib.logging_utils import log_decorator from simcore_postgres_database.models.payments_methods import InitPromptAckFlowState @@ -51,8 +51,7 @@ def _create_possible_outcomes(accepted, rejected): accepted={ "completion_state": PaymentTransactionState.SUCCESS, "message": "Succesful payment (fake)", - "invoice_url": parse_obj_as( - HttpUrl, + "invoice_url": TypeAdapter(HttpUrl).validate_python( "https://assets.website-files.com/63206faf68ab2dc3ee3e623b/634ea60a9381021f775e7a28_Placeholder%20PDF.pdf", ), }, diff --git a/services/web/server/src/simcore_service_webserver/projects/_comments_db.py b/services/web/server/src/simcore_service_webserver/projects/_comments_db.py index 102e43971da..0cc52bea1e7 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_comments_db.py +++ b/services/web/server/src/simcore_service_webserver/projects/_comments_db.py @@ -9,7 +9,7 @@ from models_library.projects import ProjectID from models_library.projects_comments import CommentID, ProjectsCommentsDB from models_library.users import UserID -from pydantic import parse_obj_as +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 @@ -32,7 +32,7 @@ async def create_project_comment( .returning(projects_comments.c.comment_id) ) result: tuple[PositiveInt] = await project_comment_id.first() - return parse_obj_as(CommentID, result[0]) + return TypeAdapter(CommentID).validate_python(result[0]) async def list_project_comments( @@ -50,7 +50,7 @@ async def list_project_comments( .limit(limit) ) result = [ - parse_obj_as(ProjectsCommentsDB, row) + ProjectsCommentsDB.model_validate(row) for row in await project_comment_result.fetchall() ] return result @@ -86,7 +86,7 @@ async def update_project_comment( .returning(literal_column("*")) ) result = await project_comment_result.first() - return parse_obj_as(ProjectsCommentsDB, result) + return ProjectsCommentsDB.model_validate(result) async def delete_project_comment(conn, comment_id: CommentID) -> None: @@ -100,4 +100,4 @@ async def get_project_comment(conn, comment_id: CommentID) -> ProjectsCommentsDB projects_comments.select().where(projects_comments.c.comment_id == comment_id) ) result = await project_comment_result.first() - return parse_obj_as(ProjectsCommentsDB, result) + return ProjectsCommentsDB.model_validate(result) diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py b/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py index 7d4464cebc1..9b7cde0fba2 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py @@ -15,7 +15,7 @@ from models_library.utils.fastapi_encoders import jsonable_encoder from models_library.utils.json_serialization import json_dumps from models_library.workspaces import UserWorkspaceAccessRightsDB -from pydantic import parse_obj_as +from pydantic import TypeAdapter from servicelib.aiohttp.long_running_tasks.server import TaskProgress from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from simcore_postgres_database.utils_projects_nodes import ( @@ -157,7 +157,9 @@ async def _copy_files_from_source_project( ): db: ProjectDBAPI = ProjectDBAPI.get_from_app_context(app) needs_lock_source_project: bool = ( - await db.get_project_type(parse_obj_as(ProjectID, source_project["uuid"])) + await db.get_project_type( + TypeAdapter(ProjectID).validate_python(source_project["uuid"]) + ) != ProjectTypeDB.TEMPLATE ) @@ -178,8 +180,7 @@ async def _copy_files_from_source_project( ): task_progress.update( message=long_running_task.progress.message, - percent=parse_obj_as( - ProgressPercent, + percent=TypeAdapter(ProgressPercent).validate_python( ( starting_value + long_running_task.progress.percent * (1.0 - starting_value) @@ -416,7 +417,8 @@ async def create_project( # pylint: disable=too-many-arguments,too-many-branche ) ) new_project["accessRights"] = { - gid: access.dict() for gid, access in workspace_db.access_rights.items() + gid: access.model_dump() + for gid, access in workspace_db.access_rights.items() } # Ensures is like ProjectGet diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py index e168a11ab70..bc3fc0d684f 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py @@ -25,7 +25,6 @@ from models_library.rest_pagination_utils import paginate_data from models_library.utils.fastapi_encoders import jsonable_encoder from models_library.utils.json_serialization import json_dumps -from pydantic import parse_obj_as from servicelib.aiohttp import status from servicelib.aiohttp.long_running_tasks.server import start_long_running_task from servicelib.aiohttp.requests_validation import ( @@ -212,7 +211,7 @@ async def list_projects(request: web.Request): limit=query_params.limit, offset=query_params.offset, search=query_params.search, - order_by=parse_obj_as(OrderBy, query_params.order_by), + order_by=OrderBy.model_validate(query_params.order_by), folder_id=query_params.folder_id, workspace_id=query_params.workspace_id, ) diff --git a/services/web/server/src/simcore_service_webserver/projects/_groups_api.py b/services/web/server/src/simcore_service_webserver/projects/_groups_api.py index 2477c36ecfc..53369af7720 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_groups_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/_groups_api.py @@ -5,7 +5,7 @@ from models_library.products import ProductName from models_library.projects import ProjectID from models_library.users import GroupID, UserID -from pydantic import BaseModel, parse_obj_as +from pydantic import BaseModel from ..users import api as users_api from . import _groups_db as projects_groups_db @@ -53,7 +53,9 @@ async def create_project_group( write=write, delete=delete, ) - project_group_api: ProjectGroupGet = ProjectGroupGet(**project_group_db.dict()) + project_group_api: ProjectGroupGet = ProjectGroupGet( + **project_group_db.model_dump() + ) return project_group_api @@ -78,7 +80,7 @@ async def list_project_groups_by_user_and_project( ] = await projects_groups_db.list_project_groups(app=app, project_id=project_id) project_groups_api: list[ProjectGroupGet] = [ - parse_obj_as(ProjectGroupGet, group) for group in project_groups_db + ProjectGroupGet.model_validate(group) for group in project_groups_db ] return project_groups_api @@ -127,7 +129,7 @@ async def replace_project_group( ) ) - project_api: ProjectGroupGet = ProjectGroupGet(**project_group_db.dict()) + project_api: ProjectGroupGet = ProjectGroupGet(**project_group_db.model_dump()) return project_api diff --git a/services/web/server/src/simcore_service_webserver/projects/_groups_db.py b/services/web/server/src/simcore_service_webserver/projects/_groups_db.py index 8420d71ef7a..26333bc26bc 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_groups_db.py +++ b/services/web/server/src/simcore_service_webserver/projects/_groups_db.py @@ -9,7 +9,7 @@ from aiohttp import web from models_library.projects import ProjectID from models_library.users import GroupID -from pydantic import BaseModel, parse_obj_as +from pydantic import BaseModel, TypeAdapter from simcore_postgres_database.models.project_to_groups import project_to_groups from sqlalchemy import func, literal_column from sqlalchemy.dialects.postgresql import insert as pg_insert @@ -59,7 +59,7 @@ async def create_project_group( .returning(literal_column("*")) ) row = await result.first() - return parse_obj_as(ProjectGroupGetDB, row) + return ProjectGroupGetDB.model_validate(row) async def list_project_groups( @@ -82,7 +82,7 @@ async def list_project_groups( async with get_database_engine(app).acquire() as conn: result = await conn.execute(stmt) rows = await result.fetchall() or [] - return parse_obj_as(list[ProjectGroupGetDB], rows) + return TypeAdapter(list[ProjectGroupGetDB]).validate_python(rows) async def get_project_group( From cf01290a357895153971fb67e97f2a218bf43f73 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 13:32:11 +0200 Subject: [PATCH 017/165] continue fixing --- .../projects/_groups_db.py | 4 +-- .../projects/_metadata_api.py | 4 +-- .../projects/_metadata_db.py | 6 ++-- .../projects/_nodes_api.py | 9 +++--- .../projects/_nodes_handlers.py | 4 +-- .../projects/_ports_handlers.py | 8 +++-- .../simcore_service_webserver/projects/db.py | 8 +++-- .../projects/projects_api.py | 32 +++++++++++-------- .../projects/settings.py | 4 +-- .../projects/utils.py | 6 ++-- .../resource_usage/_client.py | 6 ++-- .../resource_usage/_service_runs_handlers.py | 12 ++++--- .../rest/_handlers.py | 4 +-- .../scicrunch/service_client.py | 8 ++--- .../security/_authz_db.py | 10 ++++-- .../storage/_handlers.py | 6 ++-- .../simcore_service_webserver/storage/api.py | 6 ++-- .../storage/settings.py | 5 ++- .../studies_dispatcher/_catalog.py | 8 ++--- .../studies_dispatcher/_core.py | 6 ++-- .../studies_dispatcher/_models.py | 6 ++-- .../_projects_permalinks.py | 5 ++- .../studies_dispatcher/_users.py | 10 +++--- .../tags/_handlers.py | 4 +-- .../simcore_service_webserver/users/_api.py | 4 +-- .../users/_preferences_api.py | 4 +-- .../simcore_service_webserver/users/api.py | 6 ++-- .../simcore_service_webserver/wallets/_api.py | 12 +++---- .../simcore_service_webserver/wallets/_db.py | 11 +++---- .../wallets/_groups_api.py | 6 ++-- 30 files changed, 123 insertions(+), 101 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/_groups_db.py b/services/web/server/src/simcore_service_webserver/projects/_groups_db.py index 26333bc26bc..5b963b90cdb 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_groups_db.py +++ b/services/web/server/src/simcore_service_webserver/projects/_groups_db.py @@ -113,7 +113,7 @@ async def get_project_group( raise ProjectGroupNotFoundError( reason=f"Project {project_id} group {group_id} not found" ) - return parse_obj_as(ProjectGroupGetDB, row) + return ProjectGroupGetDB.model_validate(row) async def replace_project_group( @@ -144,7 +144,7 @@ async def replace_project_group( raise ProjectGroupNotFoundError( reason=f"Project {project_id} group {group_id} not found" ) - return parse_obj_as(ProjectGroupGetDB, row) + return ProjectGroupGetDB.model_validate(row) async def update_or_insert_project_group( diff --git a/services/web/server/src/simcore_service_webserver/projects/_metadata_api.py b/services/web/server/src/simcore_service_webserver/projects/_metadata_api.py index db27c3359bd..f17c7941a1d 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_metadata_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/_metadata_api.py @@ -6,7 +6,7 @@ from models_library.projects import ProjectID from models_library.projects_nodes_io import NodeID from models_library.users import UserID -from pydantic import parse_obj_as +from pydantic import TypeAdapter from ..db.plugin import get_database_engine from . import _metadata_db @@ -67,7 +67,7 @@ async def set_project_ancestors_from_custom_metadata( if parent_node_idstr := custom_metadata.get("node_id"): # NOTE: backward compatibility with S4l old client - parent_node_id = parse_obj_as(NodeID, parent_node_idstr) + parent_node_id = TypeAdapter(NodeID).validate_python(parent_node_idstr) if parent_node_id == _NIL_NODE_UUID: return diff --git a/services/web/server/src/simcore_service_webserver/projects/_metadata_db.py b/services/web/server/src/simcore_service_webserver/projects/_metadata_db.py index 6a511a8ba4c..2c72a395a5a 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_metadata_db.py +++ b/services/web/server/src/simcore_service_webserver/projects/_metadata_db.py @@ -6,7 +6,7 @@ from models_library.api_schemas_webserver.projects_metadata import MetadataDict from models_library.projects import ProjectID from models_library.projects_nodes_io import NodeID -from pydantic import parse_obj_as +from pydantic import TypeAdapter from simcore_postgres_database import utils_projects_metadata from simcore_postgres_database.utils_projects_metadata import ( DBProjectInvalidAncestorsError, @@ -84,7 +84,7 @@ async def get_project_custom_metadata( connection, project_uuid=project_uuid ) # NOTE: if no metadata in table, it returns None -- which converts here to --> {} - return parse_obj_as(MetadataDict, metadata.custom or {}) + return TypeAdapter(MetadataDict).validate_python(metadata.custom or {}) @_handle_projects_metadata_exceptions @@ -104,7 +104,7 @@ async def set_project_custom_metadata( custom_metadata=custom_metadata, ) - return parse_obj_as(MetadataDict, metadata.custom) + return TypeAdapter(MetadataDict).validate_python(metadata.custom) @_handle_projects_metadata_exceptions diff --git a/services/web/server/src/simcore_service_webserver/projects/_nodes_api.py b/services/web/server/src/simcore_service_webserver/projects/_nodes_api.py index 7bc4f824738..78e48ec3bc7 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_nodes_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/_nodes_api.py @@ -14,13 +14,14 @@ from models_library.projects_nodes_io import NodeID, SimCoreFileLink from models_library.users import UserID from pydantic import ( - model_validator, BaseModel, + BaseModel, Field, HttpUrl, NonNegativeFloat, NonNegativeInt, ValidationError, - parse_obj_as) + model_validator, +) from servicelib.utils import logged_gather from ..application_settings import get_application_settings @@ -171,7 +172,7 @@ async def __get_link( return __get_search_key(file_meta_data), await get_download_link( app, user_id, - parse_obj_as(SimCoreFileLink, {"store": "0", "path": file_meta_data.file_id}), + SimCoreFileLink.model_validate({"store": "0", "path": file_meta_data.file_id}), ) @@ -226,7 +227,7 @@ async def get_node_screenshots( assert node.outputs is not None # nosec - filelink = parse_obj_as(SimCoreFileLink, node.outputs[KeyIDStr("outFile")]) + filelink = SimCoreFileLink.model_validate(node.outputs[KeyIDStr("outFile")]) file_url = await get_download_link(app, user_id, filelink) screenshots.append( diff --git a/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py index 4c3c4194066..71c2efee6c2 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py @@ -32,7 +32,7 @@ from models_library.users import GroupID from models_library.utils.fastapi_encoders import jsonable_encoder from models_library.utils.json_serialization import json_dumps -from pydantic import BaseModel, Field, parse_obj_as +from pydantic import BaseModel, Field from servicelib.aiohttp import status from servicelib.aiohttp.long_running_tasks.server import ( TaskProgress, @@ -177,7 +177,7 @@ async def create_node(request: web.Request) -> web.Response: body.service_id, ) } - assert parse_obj_as(NodeCreated, data) is not None # nosec + assert NodeCreated.model_validate(data) is not None # nosec return envelope_json_response(data, status_cls=web.HTTPCreated) diff --git a/services/web/server/src/simcore_service_webserver/projects/_ports_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_ports_handlers.py index e34548a9022..3a50b5a40c6 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_ports_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_ports_handlers.py @@ -22,7 +22,7 @@ from models_library.utils.fastapi_encoders import jsonable_encoder from models_library.utils.json_serialization import json_dumps from models_library.utils.services_io import JsonSchemaDict -from pydantic import BaseModel, Field, parse_obj_as +from pydantic import BaseModel, Field, TypeAdapter from servicelib.aiohttp.requests_validation import ( parse_request_body_as, parse_request_path_parameters_as, @@ -88,7 +88,7 @@ async def _get_validated_workbench_model( include_state=False, ) - return parse_obj_as(dict[NodeID, Node], project["workbench"]) + return TypeAdapter(dict[NodeID, Node]).validate_python(project["workbench"]) routes = web.RouteTableDef() @@ -169,7 +169,9 @@ async def update_project_inputs(request: web.Request) -> web.Response: partial_workbench_data=jsonable_encoder(partial_workbench_data), ) - workbench = parse_obj_as(dict[NodeID, Node], updated_project["workbench"]) + workbench = TypeAdapter(dict[NodeID, Node]).validate_python( + updated_project["workbench"] + ) inputs: dict[NodeID, Any] = _ports_api.get_project_inputs(workbench) return _web_json_response_enveloped( diff --git a/services/web/server/src/simcore_service_webserver/projects/db.py b/services/web/server/src/simcore_service_webserver/projects/db.py index 10487a80e90..193ddf83f4d 100644 --- a/services/web/server/src/simcore_service_webserver/projects/db.py +++ b/services/web/server/src/simcore_service_webserver/projects/db.py @@ -32,7 +32,7 @@ from models_library.utils.fastapi_encoders import jsonable_encoder from models_library.wallets import WalletDB, WalletID from models_library.workspaces import WorkspaceID -from pydantic import parse_obj_as +from pydantic import TypeAdapter from pydantic.types import PositiveInt from servicelib.aiohttp.application_keys import APP_AIOPG_ENGINE_KEY from servicelib.logging_utils import get_log_record_extra, log_context @@ -254,7 +254,9 @@ async def insert_project( """ # NOTE: tags are removed in convert_to_db_names so we keep it - project_tag_ids = parse_obj_as(list[int], project.get("tags", []).copy()) + project_tag_ids = TypeAdapter(list[int]).validate_python( + project.get("tags", []).copy() + ) insert_values = convert_to_db_names(project) insert_values.update( { @@ -1353,7 +1355,7 @@ async def get_project_wallet( .where(projects_to_wallet.c.project_uuid == f"{project_uuid}") ) row = await result.fetchone() - return parse_obj_as(WalletDB, row) if row else None + return WalletDB.model_validate(row) if row else None async def connect_wallet_to_project( self, diff --git a/services/web/server/src/simcore_service_webserver/projects/projects_api.py b/services/web/server/src/simcore_service_webserver/projects/projects_api.py index 7f271bdb02a..2cb05192640 100644 --- a/services/web/server/src/simcore_service_webserver/projects/projects_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/projects_api.py @@ -63,7 +63,7 @@ from models_library.utils.json_serialization import json_dumps from models_library.wallets import ZERO_CREDITS, WalletID, WalletInfo from models_library.workspaces import UserWorkspaceAccessRightsDB -from pydantic import ByteSize, parse_obj_as +from pydantic import ByteSize, TypeAdapter from servicelib.aiohttp.application_keys import APP_FIRE_AND_FORGET_TASKS_KEY from servicelib.common_headers import ( UNDEFINED_DEFAULT_SIMCORE_USER_AGENT_VALUE, @@ -384,7 +384,7 @@ async def _get_default_pricing_and_hardware_info( _MACHINE_TOTAL_RAM_SAFE_MARGIN_RATIO: Final[ float ] = 0.1 # NOTE: machines always have less available RAM than advertised -_SIDECARS_OPS_SAFE_RAM_MARGIN: Final[ByteSize] = parse_obj_as(ByteSize, "1GiB") +_SIDECARS_OPS_SAFE_RAM_MARGIN: Final[ByteSize] = ByteSize("1GiB") _CPUS_SAFE_MARGIN: Final[float] = 1.4 _MIN_NUM_CPUS: Final[float] = 0.5 @@ -641,8 +641,8 @@ async def _start_dynamic_service( ) if user_default_wallet_preference is None: raise UserDefaultWalletNotFoundError(uid=user_id) - project_wallet_id = parse_obj_as( - WalletID, user_default_wallet_preference.value + project_wallet_id = TypeAdapter(WalletID).validate_python( + user_default_wallet_preference.value ) await connect_wallet_to_project( request.app, @@ -1497,7 +1497,7 @@ async def add_project_states_for_user( project["state"] = ProjectState( locked=lock_state, state=ProjectRunningState(value=running_state) - ).dict(by_alias=True, exclude_unset=True) + ).model_dump(by_alias=True, exclude_unset=True) return project @@ -1515,8 +1515,12 @@ async def is_service_deprecated( app, user_id, service_key, service_version, product_name ) if deprecation_date := service.get("deprecated"): - deprecation_date = parse_obj_as(datetime.datetime, deprecation_date) - deprecation_date_bool: bool = datetime.datetime.utcnow() > deprecation_date + deprecation_date = TypeAdapter(datetime.datetime).validate_python( + deprecation_date + ) + deprecation_date_bool: bool = ( + datetime.datetime.now(datetime.UTC) > deprecation_date + ) return deprecation_date_bool return False @@ -1551,8 +1555,8 @@ async def get_project_node_resources( db = ProjectDBAPI.get_from_app_context(app) try: project_node = await db.get_project_node(project_id, node_id) - node_resources = parse_obj_as( - ServiceResourcesDict, project_node.required_resources + node_resources = TypeAdapter(ServiceResourcesDict).validate_python( + project_node.required_resources ) if not node_resources: # get default resources @@ -1581,8 +1585,8 @@ async def update_project_node_resources( try: # validate the resource are applied to the same container names current_project_node = await db.get_project_node(project_id, node_id) - current_resources = parse_obj_as( - ServiceResourcesDict, current_project_node.required_resources + current_resources = TypeAdapter(ServiceResourcesDict).validate_python( + current_project_node.required_resources ) if not current_resources: # NOTE: this can happen after the migration @@ -1602,7 +1606,9 @@ async def update_project_node_resources( required_resources=jsonable_encoder(resources), check_update_allowed=True, ) - return parse_obj_as(ServiceResourcesDict, project_node.required_resources) + return TypeAdapter(ServiceResourcesDict).validate_python( + project_node.required_resources + ) except ProjectNodesNodeNotFoundError as exc: raise NodeNotFoundError( project_uuid=f"{project_id}", node_uuid=f"{node_id}" @@ -1871,4 +1877,4 @@ async def get_project_inactivity( project_settings.PROJECTS_INACTIVITY_INTERVAL.total_seconds() ), ) - return parse_obj_as(GetProjectInactivityResponse, project_inactivity) + return GetProjectInactivityResponse.model_validate(project_inactivity) diff --git a/services/web/server/src/simcore_service_webserver/projects/settings.py b/services/web/server/src/simcore_service_webserver/projects/settings.py index 8a46b8def4f..8d20cb3fd0a 100644 --- a/services/web/server/src/simcore_service_webserver/projects/settings.py +++ b/services/web/server/src/simcore_service_webserver/projects/settings.py @@ -2,7 +2,7 @@ from aiohttp import web from common_library.pydantic_validators import validate_numeric_string_as_timedelta -from pydantic import ByteSize, Field, NonNegativeInt, parse_obj_as +from pydantic import ByteSize, Field, NonNegativeInt from settings_library.base import BaseCustomSettings from .._constants import APP_SETTINGS_KEY @@ -10,7 +10,7 @@ class ProjectsSettings(BaseCustomSettings): PROJECTS_MAX_COPY_SIZE_BYTES: ByteSize = Field( - parse_obj_as(ByteSize, "30Gib"), + "30Gib", description="defines the maximum authorized project data size" " when copying a project (disable with 0)", ) diff --git a/services/web/server/src/simcore_service_webserver/projects/utils.py b/services/web/server/src/simcore_service_webserver/projects/utils.py index d54bc2b433d..18a02a5fb3c 100644 --- a/services/web/server/src/simcore_service_webserver/projects/utils.py +++ b/services/web/server/src/simcore_service_webserver/projects/utils.py @@ -7,7 +7,7 @@ from models_library.projects_nodes_io import NodeIDStr from models_library.services import ServiceKey -from pydantic import parse_obj_as +from pydantic import TypeAdapter from servicelib.decorators import safe_return from yarl import URL @@ -378,7 +378,9 @@ def default_copy_project_name(name: str) -> str: new_copy_index = 1 if current_copy_index := match.group(2): # we receive something of type "(23)" - new_copy_index = parse_obj_as(int, current_copy_index.strip("()")) + 1 + new_copy_index = ( + TypeAdapter(int).validate_python(current_copy_index.strip("()")) + 1 + ) return f"{match.group(1)}({new_copy_index})" return f"{name} (Copy)" diff --git a/services/web/server/src/simcore_service_webserver/resource_usage/_client.py b/services/web/server/src/simcore_service_webserver/resource_usage/_client.py index eb616b5d209..5b9b5497b42 100644 --- a/services/web/server/src/simcore_service_webserver/resource_usage/_client.py +++ b/services/web/server/src/simcore_service_webserver/resource_usage/_client.py @@ -22,7 +22,7 @@ from models_library.resource_tracker import PricingPlanId, PricingUnitId from models_library.users import UserID from models_library.wallets import WalletID -from pydantic import NonNegativeInt, parse_obj_as +from pydantic import NonNegativeInt from servicelib.aiohttp import status from servicelib.aiohttp.client_session import get_client_session from settings_library.resource_usage_tracker import ResourceUsageTrackerSettings @@ -101,7 +101,7 @@ async def get_default_service_pricing_plan( async with session.get(url) as response: response.raise_for_status() body: dict = await response.json() - return parse_obj_as(PricingPlanGet, body) + return PricingPlanGet.model_validate(body) except ClientResponseError as e: if e.status == status.HTTP_404_NOT_FOUND: raise DefaultPricingPlanNotFoundError from e @@ -130,7 +130,7 @@ async def get_pricing_plan_unit( async with session.get(url) as response: response.raise_for_status() body: dict = await response.json() - return parse_obj_as(PricingUnitGet, body) + return PricingUnitGet.model_validate(body) async def sum_total_available_credits_in_the_wallet( diff --git a/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py b/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py index d687d25a94b..af4d96bee06 100644 --- a/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py +++ b/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py @@ -28,8 +28,8 @@ Field, Json, NonNegativeInt, + TypeAdapter, field_validator, - parse_obj_as, ) from servicelib.aiohttp.requests_validation import parse_request_query_parameters_as from servicelib.aiohttp.typing_extension import Handler @@ -164,8 +164,8 @@ async def list_resource_usage_services(request: web.Request): wallet_id=query_params.wallet_id, offset=query_params.offset, limit=query_params.limit, - order_by=parse_obj_as(OrderBy, query_params.order_by), - filters=parse_obj_as(ServiceResourceUsagesFilters | None, query_params.filters), # type: ignore[arg-type] # from pydantic v2 --> https://github.com/pydantic/pydantic/discussions/4950 + order_by=OrderBy.model_validate(query_params.order_by), + filters=TypeAdapter(ServiceResourceUsagesFilters | None).validate_python(query_params.filters), # type: ignore[arg-type] # from pydantic v2 --> https://github.com/pydantic/pydantic/discussions/4950 ) page = Page[dict[str, Any]].model_validate( @@ -242,7 +242,9 @@ async def export_resource_usage_services(request: web.Request): user_id=req_ctx.user_id, product_name=req_ctx.product_name, wallet_id=query_params.wallet_id, - order_by=parse_obj_as(OrderBy | None, query_params.order_by), # type: ignore[arg-type] # from pydantic v2 --> https://github.com/pydantic/pydantic/discussions/4950 - filters=parse_obj_as(ServiceResourceUsagesFilters | None, query_params.filters), # type: ignore[arg-type] # from pydantic v2 --> https://github.com/pydantic/pydantic/discussions/4950 + order_by=TypeAdapter(OrderBy | None).validate_python(query_params.order_by), + filters=TypeAdapter(ServiceResourceUsagesFilters | None).validate_python( + query_params.filters + ), ) raise web.HTTPFound(location=f"{download_url}") diff --git a/services/web/server/src/simcore_service_webserver/rest/_handlers.py b/services/web/server/src/simcore_service_webserver/rest/_handlers.py index 0cf11ad4810..c1564d1eabc 100644 --- a/services/web/server/src/simcore_service_webserver/rest/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/rest/_handlers.py @@ -8,7 +8,7 @@ from aiohttp import web from models_library.utils.pydantic_tools_extension import FieldNotRequired -from pydantic import BaseModel, parse_obj_as +from pydantic import BaseModel from servicelib.aiohttp import status from .._constants import APP_PUBLIC_CONFIG_PER_PRODUCT, APP_SETTINGS_KEY @@ -104,7 +104,7 @@ async def get_scheduled_maintenance(request: web.Request): if maintenance_data := await redis_client.get(hash_key): assert ( # nosec - parse_obj_as(_ScheduledMaintenanceGet, maintenance_data) is not None + _ScheduledMaintenanceGet.model_validate(maintenance_data) is not None ) return envelope_json_response(maintenance_data) diff --git a/services/web/server/src/simcore_service_webserver/scicrunch/service_client.py b/services/web/server/src/simcore_service_webserver/scicrunch/service_client.py index bcaf413b4db..ec8f43283b3 100644 --- a/services/web/server/src/simcore_service_webserver/scicrunch/service_client.py +++ b/services/web/server/src/simcore_service_webserver/scicrunch/service_client.py @@ -8,7 +8,7 @@ import logging from aiohttp import ClientSession, client_exceptions, web -from pydantic import HttpUrl, ValidationError, parse_obj_as +from pydantic import HttpUrl, TypeAdapter, ValidationError from servicelib.aiohttp.client_session import get_client_session from yarl import URL @@ -90,8 +90,8 @@ def get_search_web_url(self, rrid: str) -> str: def get_resolver_web_url(self, rrid: str) -> HttpUrl: # example https://scicrunch.org/resolver/RRID:AB_90755 - output: HttpUrl = parse_obj_as( - HttpUrl, f"{self.settings.SCICRUNCH_RESOLVER_BASE_URL}/{rrid}" + output: HttpUrl = TypeAdapter(HttpUrl).validate_python( + f"{self.settings.SCICRUNCH_RESOLVER_BASE_URL}/{rrid}" ) return output @@ -171,4 +171,4 @@ async def search_resource(self, name_as: str) -> list[ResourceHit]: # Might be slow and timeout! # Might be good to know that scicrunch.org is not reachable and cannot perform search now? hits = await autocomplete_by_name(name_as, self.client, self.settings) - return hits.__root__ + return hits.root diff --git a/services/web/server/src/simcore_service_webserver/security/_authz_db.py b/services/web/server/src/simcore_service_webserver/security/_authz_db.py index dbb04f7943c..b62baf946f4 100644 --- a/services/web/server/src/simcore_service_webserver/security/_authz_db.py +++ b/services/web/server/src/simcore_service_webserver/security/_authz_db.py @@ -7,7 +7,7 @@ from models_library.basic_types import IdInt from models_library.products import ProductName from models_library.users import UserID -from pydantic import parse_obj_as +from pydantic import TypeAdapter from simcore_postgres_database.models.groups import user_to_groups from simcore_postgres_database.models.products import products from simcore_postgres_database.models.users import UserRole @@ -35,8 +35,12 @@ async def get_active_user_or_none(engine: Engine, email: str) -> AuthInfoDict | ) ) row = await result.fetchone() - assert row is None or parse_obj_as(IdInt, row.id) is not None # nosec - assert row is None or parse_obj_as(UserRole, row.role) is not None # nosec + assert ( + row is None or TypeAdapter(IdInt).validate_python(row.id) is not None + ) # nosec + assert ( + row is None or TypeAdapter(UserRole).validate_python(row.role) is not None + ) # nosec return AuthInfoDict(id=row.id, role=row.role) if row else None diff --git a/services/web/server/src/simcore_service_webserver/storage/_handlers.py b/services/web/server/src/simcore_service_webserver/storage/_handlers.py index ec07a27d449..5f6844d52fa 100644 --- a/services/web/server/src/simcore_service_webserver/storage/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/storage/_handlers.py @@ -14,7 +14,7 @@ ) from models_library.projects_nodes_io import LocationID from models_library.utils.fastapi_encoders import jsonable_encoder -from pydantic import AnyUrl, BaseModel, ByteSize, parse_obj_as +from pydantic import AnyUrl, BaseModel, ByteSize, TypeAdapter from servicelib.aiohttp.client_session import get_client_session from servicelib.aiohttp.requests_validation import ( parse_request_body_as, @@ -74,7 +74,7 @@ def _from_storage_url(request: web.Request, storage_url: AnyUrl) -> AnyUrl: f"/v0/storage{storage_url.path.removeprefix(prefix)}", encoded=True ).with_scheme(request.headers.get(X_FORWARDED_PROTO, request.url.scheme)) - webserver_url: AnyUrl = parse_obj_as(AnyUrl, f"{converted_url}") + webserver_url: AnyUrl = TypeAdapter(AnyUrl).validate_python(f"{converted_url}") return webserver_url @@ -262,7 +262,7 @@ class _PathParams(BaseModel): body_item = await parse_request_body_as(FileUploadCompletionBody, request) payload, status = await _forward_request_to_storage( - request, "POST", body=body_item.dict() + request, "POST", body=body_item.model_dump() ) data, _ = unwrap_envelope(payload) file_upload_complete = FileUploadCompleteResponse.model_validate(data) diff --git a/services/web/server/src/simcore_service_webserver/storage/api.py b/services/web/server/src/simcore_service_webserver/storage/api.py index cc234f02530..8e1ad334beb 100644 --- a/services/web/server/src/simcore_service_webserver/storage/api.py +++ b/services/web/server/src/simcore_service_webserver/storage/api.py @@ -20,7 +20,7 @@ from models_library.projects_nodes_io import LocationID, NodeID, SimCoreFileLink from models_library.users import UserID from models_library.utils.fastapi_encoders import jsonable_encoder -from pydantic import ByteSize, HttpUrl, parse_obj_as +from pydantic import ByteSize, HttpUrl, TypeAdapter from servicelib.aiohttp.client_session import get_client_session from servicelib.aiohttp.long_running_tasks.client import ( LRTask, @@ -97,7 +97,7 @@ async def get_project_total_size_simcore_s3( file_metadata.file_size for file_metadata in list_of_files_enveloped.data ) - return parse_obj_as(ByteSize, project_size_bytes) + return TypeAdapter(ByteSize).validate_python(project_size_bytes) async def copy_data_folders_from_project( @@ -207,7 +207,7 @@ async def get_download_link( Envelope[PresignedLink].model_validate(await response.json()).data ) assert download is not None # nosec - link: HttpUrl = parse_obj_as(HttpUrl, download.link) + link: HttpUrl = TypeAdapter(HttpUrl).validate_python(download.link) return link diff --git a/services/web/server/src/simcore_service_webserver/storage/settings.py b/services/web/server/src/simcore_service_webserver/storage/settings.py index e49e652699d..04ac00f61c3 100644 --- a/services/web/server/src/simcore_service_webserver/storage/settings.py +++ b/services/web/server/src/simcore_service_webserver/storage/settings.py @@ -2,7 +2,6 @@ from aiohttp import web from models_library.basic_types import PortInt, VersionTag -from pydantic import parse_obj_as from settings_library.base import BaseCustomSettings from settings_library.utils_service import DEFAULT_AIOHTTP_PORT, MixinServiceSettings from yarl import URL @@ -12,8 +11,8 @@ class StorageSettings(BaseCustomSettings, MixinServiceSettings): STORAGE_HOST: str = "storage" - STORAGE_PORT: PortInt = parse_obj_as(PortInt, DEFAULT_AIOHTTP_PORT) - STORAGE_VTAG: VersionTag = parse_obj_as(VersionTag, "v0") + STORAGE_PORT: PortInt = DEFAULT_AIOHTTP_PORT + STORAGE_VTAG: VersionTag = "v0" @cached_property def base_url(self) -> URL: diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_catalog.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_catalog.py index 3df62ebd379..9e9d5921d5f 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_catalog.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_catalog.py @@ -9,7 +9,7 @@ from aiopg.sa.engine import Engine from models_library.groups import EVERYONE_GROUP_ID from models_library.services import ServiceKey, ServiceVersion -from pydantic import HttpUrl, PositiveInt, ValidationError, parse_obj_as +from pydantic import HttpUrl, PositiveInt, TypeAdapter, ValidationError from servicelib.logging_utils import log_decorator from simcore_postgres_database.models.services import ( services_access_rights, @@ -97,7 +97,7 @@ async def iter_latest_product_services( ) & (services_meta_data.c.deprecated.is_(None)) & (services_access_rights.c.gid == EVERYONE_GROUP_ID) - & (services_access_rights.c.execute_access == True) + & (services_access_rights.c.execute_access is True) & (services_access_rights.c.product_name == product_name) ) ) @@ -161,7 +161,7 @@ async def validate_requested_service( sa.select(services_consume_filetypes.c.is_guest_allowed) .where( (services_consume_filetypes.c.service_key == service_key) - & (services_consume_filetypes.c.is_guest_allowed == True) + & (services_consume_filetypes.c.is_guest_allowed is True) ) .limit(1) ) @@ -171,7 +171,7 @@ async def validate_requested_service( thumbnail_or_none = None if row.thumbnail is not None: with suppress(ValidationError): - thumbnail_or_none = parse_obj_as(HttpUrl, row.thumbnail) + thumbnail_or_none = TypeAdapter(HttpUrl).validate_python(row.thumbnail) return ValidService( key=service_key, diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_core.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_core.py index dcafdf528de..fe76e1a2855 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_core.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_core.py @@ -7,7 +7,7 @@ from aiohttp import web from models_library.services import ServiceVersion from models_library.utils.pydantic_tools_extension import parse_obj_or_none -from pydantic import ByteSize, ValidationError, parse_obj_as +from pydantic import ByteSize, TypeAdapter, ValidationError from servicelib.logging_utils import log_decorator from simcore_postgres_database.models.services_consume_filetypes import ( services_consume_filetypes, @@ -138,7 +138,9 @@ def _version(column_or_value): row = await result.first() if row: view = ViewerInfo.create_from_db(row) - view.version = parse_obj_as(ServiceVersion, service_version) + view.version = TypeAdapter(ServiceVersion).validate_python( + service_version + ) return view raise IncompatibleService(file_type=file_type) diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_models.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_models.py index 30aa1387269..c697b409c0f 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_models.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_models.py @@ -1,6 +1,6 @@ from aiopg.sa.result import RowProxy from models_library.services import ServiceKey, ServiceVersion -from pydantic import BaseModel, Field, HttpUrl, PositiveInt, parse_obj_as +from pydantic import BaseModel, Field, HttpUrl, PositiveInt, TypeAdapter class ServiceInfo(BaseModel): @@ -10,7 +10,9 @@ class ServiceInfo(BaseModel): label: str = Field(..., description="Display name") thumbnail: HttpUrl = Field( - default=parse_obj_as(HttpUrl, "https://via.placeholder.com/170x120.png") + default=TypeAdapter(HttpUrl).validate_python( + "https://via.placeholder.com/170x120.png" + ) ) is_guest_allowed: bool = True diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects_permalinks.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects_permalinks.py index 055f0f78fcf..9308f4e3c81 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects_permalinks.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects_permalinks.py @@ -4,7 +4,7 @@ import sqlalchemy as sa from aiohttp import web from models_library.projects import ProjectID, ProjectIDStr -from pydantic import HttpUrl, parse_obj_as +from pydantic import HttpUrl, TypeAdapter from simcore_postgres_database.models.project_to_groups import project_to_groups from simcore_postgres_database.models.projects import ProjectType, projects @@ -58,8 +58,7 @@ def create_permalink_for_study( # create url_for = create_url_for_function(request) - permalink = parse_obj_as( - HttpUrl, + permalink = TypeAdapter(HttpUrl).validate_python( url_for(route_name="get_redirection_to_study_page", id=f"{project_uuid}"), ) diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_users.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_users.py index c9ff40adbd9..811628a719b 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_users.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_users.py @@ -12,12 +12,12 @@ import secrets import string from contextlib import suppress -from datetime import datetime +from datetime import UTC, datetime import redis.asyncio as aioredis from aiohttp import web from models_library.emails import LowerCaseEmailStr -from pydantic import BaseModel, parse_obj_as +from pydantic import BaseModel, TypeAdapter from redis.exceptions import LockNotOwnedError from servicelib.aiohttp.application_keys import APP_FIRE_AND_FORGET_TASKS_KEY from servicelib.logging_utils import log_decorator @@ -80,9 +80,11 @@ async def create_temporary_guest_user(request: web.Request): random_user_name = "".join( secrets.choice(string.ascii_lowercase) for _ in range(10) ) - email = parse_obj_as(LowerCaseEmailStr, f"{random_user_name}@guest-at-osparc.io") + email = TypeAdapter(LowerCaseEmailStr).validate_python( + f"{random_user_name}@guest-at-osparc.io" + ) password = generate_password(length=12) - expires_at = datetime.utcnow() + settings.STUDIES_GUEST_ACCOUNT_LIFETIME + expires_at = datetime.now(UTC) + settings.STUDIES_GUEST_ACCOUNT_LIFETIME # GUEST_USER_RC_LOCK: # diff --git a/services/web/server/src/simcore_service_webserver/tags/_handlers.py b/services/web/server/src/simcore_service_webserver/tags/_handlers.py index 57fee75af46..24dff16d066 100644 --- a/services/web/server/src/simcore_service_webserver/tags/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/tags/_handlers.py @@ -1,7 +1,7 @@ import functools from aiohttp import web -from pydantic import parse_obj_as +from pydantic import TypeAdapter from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_body_as, @@ -124,7 +124,7 @@ async def list_tag_groups(request: web.Request): path_params = parse_request_path_parameters_as(TagPathParams, request) assert path_params # nosec - assert envelope_json_response(parse_obj_as(list[TagGroupGet], [])) + assert envelope_json_response(TypeAdapter(list[TagGroupGet]).validate_python([])) raise NotImplementedError diff --git a/services/web/server/src/simcore_service_webserver/users/_api.py b/services/web/server/src/simcore_service_webserver/users/_api.py index a054bfe5927..46ffb99e84a 100644 --- a/services/web/server/src/simcore_service_webserver/users/_api.py +++ b/services/web/server/src/simcore_service_webserver/users/_api.py @@ -6,7 +6,7 @@ from models_library.emails import LowerCaseEmailStr from models_library.payments import UserInvoiceAddress from models_library.users import UserBillingDetails, UserID -from pydantic import parse_obj_as +from pydantic import TypeAdapter from simcore_postgres_database.models.users import UserStatus from ..db.plugin import get_database_engine @@ -50,7 +50,7 @@ async def get_user_credentials( ) return UserCredentialsTuple( - email=parse_obj_as(LowerCaseEmailStr, row.email), + email=TypeAdapter(LowerCaseEmailStr).validate_python(row.email), password_hash=row.password_hash, display_name=row.first_name or row.name.capitalize(), ) diff --git a/services/web/server/src/simcore_service_webserver/users/_preferences_api.py b/services/web/server/src/simcore_service_webserver/users/_preferences_api.py index 3215b4bc149..fb55ac58d2f 100644 --- a/services/web/server/src/simcore_service_webserver/users/_preferences_api.py +++ b/services/web/server/src/simcore_service_webserver/users/_preferences_api.py @@ -13,7 +13,7 @@ PreferenceName, ) from models_library.users import UserID -from pydantic import NonNegativeInt, parse_obj_as +from pydantic import NonNegativeInt, TypeAdapter from servicelib.utils import logged_gather from simcore_postgres_database.utils_groups_extra_properties import ( GroupExtraPropertiesRepo, @@ -130,6 +130,6 @@ async def set_frontend_user_preference( await _preferences_db.set_user_preference( app, user_id=user_id, - preference=parse_obj_as(preference_class, {"value": value}), # type: ignore[arg-type] # GitHK this is suspicious + preference=TypeAdapter(preference_class).validate_python({"value": value}), # type: ignore[arg-type] # GitHK this is suspicious product_name=product_name, ) diff --git a/services/web/server/src/simcore_service_webserver/users/api.py b/services/web/server/src/simcore_service_webserver/users/api.py index 52736a1e8d8..50dfdc4e12d 100644 --- a/services/web/server/src/simcore_service_webserver/users/api.py +++ b/services/web/server/src/simcore_service_webserver/users/api.py @@ -16,7 +16,7 @@ from models_library.basic_types import IDStr from models_library.products import ProductName from models_library.users import GroupID, UserID -from pydantic import EmailStr, ValidationError, parse_obj_as +from pydantic import EmailStr, TypeAdapter, ValidationError from simcore_postgres_database.models.users import UserRole from simcore_postgres_database.utils_groups_extra_properties import ( GroupExtraPropertiesNotFoundError, @@ -38,7 +38,7 @@ def _parse_as_user(user_id: Any) -> UserID: try: - return parse_obj_as(UserID, user_id) + return TypeAdapter(UserID).validate_python(user_id) except ValidationError as err: raise UserNotFoundError(uid=user_id) from err @@ -159,7 +159,7 @@ async def update_user_profile( user_id = _parse_as_user(user_id) async with get_database_engine(app).acquire() as conn: - to_update = update.dict( + to_update = update.model_dump( include={ "first_name", "last_name", diff --git a/services/web/server/src/simcore_service_webserver/wallets/_api.py b/services/web/server/src/simcore_service_webserver/wallets/_api.py index 8a528fe5db2..dff96a2b93d 100644 --- a/services/web/server/src/simcore_service_webserver/wallets/_api.py +++ b/services/web/server/src/simcore_service_webserver/wallets/_api.py @@ -13,7 +13,7 @@ from models_library.products import ProductName from models_library.users import UserID from models_library.wallets import UserWalletDB, WalletDB, WalletID, WalletStatus -from pydantic import parse_obj_as +from pydantic import TypeAdapter from ..resource_usage.api import get_wallet_total_available_credits from ..users import api as users_api @@ -42,7 +42,7 @@ async def create_wallet( thumbnail=thumbnail, product_name=product_name, ) - wallet_api: WalletGet = parse_obj_as(WalletGet, wallet_db) + wallet_api: WalletGet = WalletGet.model_validate(wallet_db) return wallet_api @@ -122,7 +122,7 @@ async def get_user_default_wallet_with_available_credits( ) if user_default_wallet_preference is None: raise UserDefaultWalletNotFoundError(uid=user_id) - default_wallet_id = parse_obj_as(WalletID, user_default_wallet_preference.value) + default_wallet_id = TypeAdapter(WalletID).validate_python(user_default_wallet_preference.value) return await get_wallet_with_available_credits_by_user_and_wallet( app, user_id=user_id, wallet_id=default_wallet_id, product_name=product_name ) @@ -136,7 +136,7 @@ async def list_wallets_for_user( user_wallets: list[UserWalletDB] = await db.list_wallets_for_user( app=app, user_id=user_id, product_name=product_name ) - return parse_obj_as(list[WalletGet], user_wallets) + return TypeAdapter(list[WalletGet]).validate_python(user_wallets) async def any_wallet_owned_by_user( @@ -193,7 +193,7 @@ async def update_wallet( product_name=product_name, ) - wallet_api: WalletGet = parse_obj_as(WalletGet, wallet_db) + wallet_api: WalletGet = WalletGet.model_validate(wallet_db) return wallet_api @@ -263,5 +263,5 @@ async def get_wallet_with_permissions_by_user( app=app, user_id=user_id, wallet_id=wallet_id, product_name=product_name ) - permissions: WalletGetPermissions = parse_obj_as(WalletGetPermissions, wallet) + permissions: WalletGetPermissions = WalletGetPermissions.model_validate(wallet) return permissions diff --git a/services/web/server/src/simcore_service_webserver/wallets/_db.py b/services/web/server/src/simcore_service_webserver/wallets/_db.py index 467bc69e437..413b68ff84f 100644 --- a/services/web/server/src/simcore_service_webserver/wallets/_db.py +++ b/services/web/server/src/simcore_service_webserver/wallets/_db.py @@ -9,7 +9,6 @@ from models_library.products import ProductName from models_library.users import GroupID, UserID from models_library.wallets import UserWalletDB, WalletDB, WalletID, WalletStatus -from pydantic import parse_obj_as from simcore_postgres_database.models.groups import user_to_groups from simcore_postgres_database.models.wallet_to_groups import wallet_to_groups from simcore_postgres_database.models.wallets import wallets @@ -47,7 +46,7 @@ async def create_wallet( .returning(literal_column("*")) ) row = await result.first() - return parse_obj_as(WalletDB, row) + return WalletDB.model_validate(row) _SELECTION_ARGS = ( @@ -98,7 +97,7 @@ async def list_wallets_for_user( async with get_database_engine(app).acquire() as conn: result = await conn.execute(stmt) rows = await result.fetchall() or [] - output: list[UserWalletDB] = [parse_obj_as(UserWalletDB, row) for row in rows] + output: list[UserWalletDB] = [UserWalletDB.model_validate(row) for row in rows] return output @@ -160,7 +159,7 @@ async def get_wallet_for_user( wallet_id=wallet_id, product_name=product_name, ) - return parse_obj_as(UserWalletDB, row) + return UserWalletDB.model_validate(row) async def get_wallet( @@ -188,7 +187,7 @@ async def get_wallet( row = await result.first() if row is None: raise WalletNotFoundError(reason=f"Wallet {wallet_id} not found.") - return parse_obj_as(WalletDB, row) + return WalletDB.model_validate(row) async def update_wallet( @@ -219,7 +218,7 @@ async def update_wallet( row = await result.first() if row is None: raise WalletNotFoundError(reason=f"Wallet {wallet_id} not found.") - return parse_obj_as(WalletDB, row) + return WalletDB.model_validate(row) async def delete_wallet( diff --git a/services/web/server/src/simcore_service_webserver/wallets/_groups_api.py b/services/web/server/src/simcore_service_webserver/wallets/_groups_api.py index 98dcd40058b..8d2703f3a5f 100644 --- a/services/web/server/src/simcore_service_webserver/wallets/_groups_api.py +++ b/services/web/server/src/simcore_service_webserver/wallets/_groups_api.py @@ -5,7 +5,7 @@ from models_library.products import ProductName from models_library.users import GroupID, UserID from models_library.wallets import UserWalletDB, WalletID -from pydantic import BaseModel, parse_obj_as +from pydantic import BaseModel from ..users import api as users_api from . import _db as wallets_db @@ -89,7 +89,7 @@ async def list_wallet_groups_by_user_and_wallet( ] = await wallets_groups_db.list_wallet_groups(app=app, wallet_id=wallet_id) wallet_groups_api: list[WalletGroupGet] = [ - parse_obj_as(WalletGroupGet, group) for group in wallet_groups_db + WalletGroupGet.model_validate(group) for group in wallet_groups_db ] return wallet_groups_api @@ -105,7 +105,7 @@ async def list_wallet_groups_with_read_access_by_wallet( ] = await wallets_groups_db.list_wallet_groups(app=app, wallet_id=wallet_id) wallet_groups_api: list[WalletGroupGet] = [ - parse_obj_as(WalletGroupGet, group) + WalletGroupGet.model_validate(group) for group in wallet_groups_db if group.read is True ] From 2b1282689acaf630c04c1c71eafb1b16233779db Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 14:31:14 +0200 Subject: [PATCH 018/165] continue fixing --- .../wallets/_groups_db.py | 10 ++--- .../workspaces/_groups_api.py | 6 +-- .../isolated/test_application_settings.py | 4 +- .../unit/isolated/test_projects__nodes_api.py | 6 +-- .../test_projects__nodes_resources.py | 12 +++--- .../tests/unit/isolated/test_security_api.py | 4 +- .../unit/isolated/test_statics_settings.py | 6 +-- .../test_studies_dispatcher_models.py | 4 +- .../test_studies_dispatcher_handlers.py | 8 ++-- .../unit/with_dbs/01/test_api_keys_rpc.py | 12 +++--- .../01/test_catalog_handlers__pricing_plan.py | 4 +- .../01/test_catalog_handlers__services.py | 21 +++++----- ...st_catalog_handlers__services_resources.py | 5 +-- .../server/tests/unit/with_dbs/02/conftest.py | 7 ++-- .../02/test_projects_cancellations.py | 10 ++--- .../02/test_projects_crud_handlers.py | 20 +++++----- .../02/test_projects_crud_handlers__clone.py | 8 ++-- .../02/test_projects_metadata_handlers.py | 40 +++++++++---------- .../02/test_projects_nodes_handler.py | 15 ++++--- ...st_projects_nodes_pricing_unit_handlers.py | 5 +-- .../02/test_projects_ports_handlers.py | 8 ++-- .../02/test_projects_wallet_handlers.py | 3 +- .../03/login/test_login_2fa_resend.py | 3 +- .../with_dbs/03/products/test_products_rpc.py | 12 +++--- .../test_admin_pricing_plans.py | 28 +++++-------- .../03/resource_usage/test_pricing_plans.py | 8 ++-- .../test_usage_services__export.py | 8 ++-- .../unit/with_dbs/03/test_storage_handlers.py | 33 ++++++++------- .../with_dbs/03/test_users__notifications.py | 10 +++-- .../03/wallets/payments/test_payments.py | 12 +++--- .../wallets/payments/test_payments_methods.py | 10 +++-- .../03/wallets/payments/test_payments_rpc.py | 6 +-- .../server/tests/unit/with_dbs/conftest.py | 4 +- 33 files changed, 170 insertions(+), 182 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/wallets/_groups_db.py b/services/web/server/src/simcore_service_webserver/wallets/_groups_db.py index f9d42cc6ddd..949978a470f 100644 --- a/services/web/server/src/simcore_service_webserver/wallets/_groups_db.py +++ b/services/web/server/src/simcore_service_webserver/wallets/_groups_db.py @@ -9,7 +9,7 @@ from aiohttp import web from models_library.users import GroupID from models_library.wallets import WalletID -from pydantic import BaseModel, parse_obj_as +from pydantic import BaseModel, TypeAdapter from simcore_postgres_database.models.wallet_to_groups import wallet_to_groups from sqlalchemy import func, literal_column from sqlalchemy.sql import select @@ -58,7 +58,7 @@ async def create_wallet_group( .returning(literal_column("*")) ) row = await result.first() - return parse_obj_as(WalletGroupGetDB, row) + return WalletGroupGetDB.model_validate(row) async def list_wallet_groups( @@ -81,7 +81,7 @@ async def list_wallet_groups( async with get_database_engine(app).acquire() as conn: result = await conn.execute(stmt) rows = await result.fetchall() or [] - return parse_obj_as(list[WalletGroupGetDB], rows) + return TypeAdapter(list[WalletGroupGetDB]).validate_python(rows) async def get_wallet_group( @@ -112,7 +112,7 @@ async def get_wallet_group( raise WalletGroupNotFoundError( reason=f"Wallet {wallet_id} group {group_id} not found" ) - return parse_obj_as(WalletGroupGetDB, row) + return WalletGroupGetDB.model_validate(row) async def update_wallet_group( @@ -143,7 +143,7 @@ async def update_wallet_group( raise WalletGroupNotFoundError( reason=f"Wallet {wallet_id} group {group_id} not found" ) - return parse_obj_as(WalletGroupGetDB, row) + return WalletGroupGetDB.model_validate(row) async def delete_wallet_group( diff --git a/services/web/server/src/simcore_service_webserver/workspaces/_groups_api.py b/services/web/server/src/simcore_service_webserver/workspaces/_groups_api.py index 0ec1e44618e..d69d33f6974 100644 --- a/services/web/server/src/simcore_service_webserver/workspaces/_groups_api.py +++ b/services/web/server/src/simcore_service_webserver/workspaces/_groups_api.py @@ -5,7 +5,7 @@ from models_library.products import ProductName from models_library.users import GroupID, UserID from models_library.workspaces import UserWorkspaceAccessRightsDB, WorkspaceID -from pydantic import BaseModel, parse_obj_as +from pydantic import BaseModel from ..users import api as users_api from . import _groups_db as workspaces_groups_db @@ -84,7 +84,7 @@ async def list_workspace_groups_by_user_and_workspace( ) workspace_groups_api: list[WorkspaceGroupGet] = [ - parse_obj_as(WorkspaceGroupGet, group) for group in workspace_groups_db + WorkspaceGroupGet.model_validate(group) for group in workspace_groups_db ] return workspace_groups_api @@ -102,7 +102,7 @@ async def list_workspace_groups_with_read_access_by_workspace( ) workspace_groups_api: list[WorkspaceGroupGet] = [ - parse_obj_as(WorkspaceGroupGet, group) + WorkspaceGroupGet.model_validate(group) for group in workspace_groups_db if group.read is True ] diff --git a/services/web/server/tests/unit/isolated/test_application_settings.py b/services/web/server/tests/unit/isolated/test_application_settings.py index 65fe54ff483..ecd54e14b8e 100644 --- a/services/web/server/tests/unit/isolated/test_application_settings.py +++ b/services/web/server/tests/unit/isolated/test_application_settings.py @@ -7,7 +7,7 @@ import pytest from aiohttp import web from models_library.utils.json_serialization import json_dumps -from pydantic import HttpUrl, parse_obj_as +from pydantic import HttpUrl, TypeAdapter from pytest_simcore.helpers.typing_env import EnvVarsDict from simcore_service_webserver.application_settings import ( APP_SETTINGS_KEY, @@ -97,7 +97,7 @@ def test_settings_to_client_statics_plugins( ) assert statics["vcsReleaseTag"] - assert parse_obj_as(HttpUrl, statics["vcsReleaseUrl"]) + assert TypeAdapter(HttpUrl).validate_python(statics["vcsReleaseUrl"]) assert set(statics["pluginsDisabled"]) == (disable_plugins | {"WEBSERVER_CLUSTERS"}) diff --git a/services/web/server/tests/unit/isolated/test_projects__nodes_api.py b/services/web/server/tests/unit/isolated/test_projects__nodes_api.py index ef58b4b2451..e7e4bd8a926 100644 --- a/services/web/server/tests/unit/isolated/test_projects__nodes_api.py +++ b/services/web/server/tests/unit/isolated/test_projects__nodes_api.py @@ -3,7 +3,6 @@ import pytest from models_library.api_schemas_storage import FileMetaDataGet -from pydantic import parse_obj_as from simcore_service_webserver.projects._nodes_api import ( _SUPPORTED_PREVIEW_FILE_EXTENSIONS, _FileWithThumbnail, @@ -12,13 +11,12 @@ _PROJECT_ID = uuid4() _NODE_ID = uuid4() -_UTC_NOW = datetime.datetime.now(tz=datetime.timezone.utc) +_UTC_NOW = datetime.datetime.now(tz=datetime.UTC) def _c(file_name: str) -> FileMetaDataGet: """simple converter utility""" - return parse_obj_as( - FileMetaDataGet, + return FileMetaDataGet.model_validate( { "file_uuid": f"{_PROJECT_ID}/{_NODE_ID}/{file_name}", "location_id": 0, diff --git a/services/web/server/tests/unit/isolated/test_projects__nodes_resources.py b/services/web/server/tests/unit/isolated/test_projects__nodes_resources.py index 259c4ba0c3f..70ca1ce3b9d 100644 --- a/services/web/server/tests/unit/isolated/test_projects__nodes_resources.py +++ b/services/web/server/tests/unit/isolated/test_projects__nodes_resources.py @@ -5,7 +5,7 @@ ServiceResourcesDict, ServiceResourcesDictHelpers, ) -from pydantic import parse_obj_as +from pydantic import TypeAdapter from simcore_service_webserver.projects._nodes_utils import ( validate_new_service_resources, ) @@ -17,7 +17,7 @@ @pytest.mark.parametrize( "resources", [ - parse_obj_as(ServiceResourcesDict, example) + TypeAdapter(ServiceResourcesDict).validate_python(example) for example in ServiceResourcesDictHelpers.model_config["json_schema_extra"][ "examples" ] @@ -33,7 +33,7 @@ def test_check_can_update_service_resources_with_same_does_not_raise( @pytest.mark.parametrize( "resources", [ - parse_obj_as(ServiceResourcesDict, example) + TypeAdapter(ServiceResourcesDict).validate_python(example) for example in ServiceResourcesDictHelpers.model_config["json_schema_extra"][ "examples" ] @@ -54,7 +54,7 @@ def test_check_can_update_service_resources_with_invalid_container_name_raises( @pytest.mark.parametrize( "resources", [ - parse_obj_as(ServiceResourcesDict, example) + TypeAdapter(ServiceResourcesDict).validate_python(example) for example in ServiceResourcesDictHelpers.model_config["json_schema_extra"][ "examples" ] @@ -64,7 +64,9 @@ def test_check_can_update_service_resources_with_invalid_image_name_raises( resources: ServiceResourcesDict, ): new_resources = { - resource_name: resource_data.copy(update={"image": "some-invalid-image-name"}) + resource_name: resource_data.model_copy( + update={"image": "some-invalid-image-name"} + ) for resource_name, resource_data in resources.items() } with pytest.raises( diff --git a/services/web/server/tests/unit/isolated/test_security_api.py b/services/web/server/tests/unit/isolated/test_security_api.py index e60cab4985b..3d76a58a24a 100644 --- a/services/web/server/tests/unit/isolated/test_security_api.py +++ b/services/web/server/tests/unit/isolated/test_security_api.py @@ -17,7 +17,7 @@ from aiohttp_session import get_session from models_library.emails import LowerCaseEmailStr from models_library.products import ProductName -from pydantic import parse_obj_as +from pydantic import TypeAdapter from pytest_mock import MockerFixture from pytest_simcore.helpers.typing_env import EnvVarsDict from servicelib.aiohttp import status @@ -167,7 +167,7 @@ async def _set_other_product(request: web.Request): async def _login(request: web.Request): product_name = await _get_product_name(request) body = await request.json() - email = parse_obj_as(LowerCaseEmailStr, body["email"]) + email = TypeAdapter(LowerCaseEmailStr).validate_python(body["email"]) # Permission in this product: Has user access to product? if product_name not in registered_users[email]["registered_products"]: diff --git a/services/web/server/tests/unit/isolated/test_statics_settings.py b/services/web/server/tests/unit/isolated/test_statics_settings.py index 376a8330eb9..30d9035e05d 100644 --- a/services/web/server/tests/unit/isolated/test_statics_settings.py +++ b/services/web/server/tests/unit/isolated/test_statics_settings.py @@ -4,7 +4,7 @@ import json -from pydantic import AnyHttpUrl, BaseModel, parse_obj_as +from pydantic import AnyHttpUrl, BaseModel, TypeAdapter from simcore_service_webserver.statics.settings import ( _THIRD_PARTY_REFERENCES, FrontEndAppSettings, @@ -23,7 +23,7 @@ class OsparcDependency(BaseModel): def test_valid_osparc_dependencies(): - deps = parse_obj_as(list[OsparcDependency], _THIRD_PARTY_REFERENCES) + deps = TypeAdapter(list[OsparcDependency]).validate_python(_THIRD_PARTY_REFERENCES) assert deps @@ -36,7 +36,7 @@ def test_frontend_app_settings(mock_env_devel_environment: dict[str, str]): statics = settings.to_statics() assert json.dumps(statics) - parse_obj_as(list[OsparcDependency], statics["thirdPartyReferences"]) + TypeAdapter(list[OsparcDependency]).validate_python(statics["thirdPartyReferences"]) def test_static_webserver_module_settings(mock_env_devel_environment: dict[str, str]): diff --git a/services/web/server/tests/unit/isolated/test_studies_dispatcher_models.py b/services/web/server/tests/unit/isolated/test_studies_dispatcher_models.py index 3e14ad9150a..06f216362d7 100644 --- a/services/web/server/tests/unit/isolated/test_studies_dispatcher_models.py +++ b/services/web/server/tests/unit/isolated/test_studies_dispatcher_models.py @@ -11,7 +11,7 @@ import pytest from aiohttp.test_utils import make_mocked_request from models_library.utils.pydantic_tools_extension import parse_obj_or_none -from pydantic import ByteSize, parse_obj_as +from pydantic import ByteSize from servicelib.aiohttp.requests_validation import parse_request_query_parameters_as from simcore_service_webserver.studies_dispatcher._models import ( FileParams, @@ -23,7 +23,7 @@ ) from yarl import URL -_SIZEBYTES = parse_obj_as(ByteSize, "3MiB") +_SIZEBYTES = ByteSize("3MiB") # SEE https://github.com/ITISFoundation/osparc-simcore/issues/3951#issuecomment-1489992645 # AWS download links have query arg diff --git a/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_handlers.py b/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_handlers.py index dc76115c45b..1f4ddb6dc14 100644 --- a/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_handlers.py +++ b/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_handlers.py @@ -18,7 +18,7 @@ from aioresponses import aioresponses from models_library.projects_state import ProjectLocked, ProjectStatus from models_library.utils.json_serialization import json_dumps -from pydantic import BaseModel, ByteSize, parse_obj_as +from pydantic import BaseModel, ByteSize, TypeAdapter from pytest_mock import MockerFixture from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.webserver_login import UserInfoDict, UserRole @@ -253,7 +253,7 @@ async def test_api_list_services(client: TestClient): data, error = await assert_status(response, status.HTTP_200_OK) - services = parse_obj_as(list[ServiceGet], data) + services = TypeAdapter(list[ServiceGet]).validate_python(data) assert services # latest versions of services with everyone + ospar-product (see stmt_create_services_access_rights) @@ -350,7 +350,7 @@ def redirect_url(redirect_type: str, client: TestClient) -> URL: if redirect_type == "service_and_file": query = { "file_name": "users.csv", - "file_size": parse_obj_as(ByteSize, "100KB"), + "file_size": ByteSize("100KB"), "file_type": "CSV", "viewer_key": "simcore/services/dynamic/raw-graphs", "viewer_version": "2.11.1", @@ -366,7 +366,7 @@ def redirect_url(redirect_type: str, client: TestClient) -> URL: elif redirect_type == "file_only": query = { "file_name": "users.csv", - "file_size": parse_obj_as(ByteSize, "1MiB"), + "file_size": ByteSize("1MiB"), "file_type": "CSV", "download_link": URL( "https://raw.githubusercontent.com/ITISFoundation/osparc-simcore/8987c95d0ca0090e14f3a5b52db724fa24114cf5/services/storage/tests/data/users.csv" diff --git a/services/web/server/tests/unit/with_dbs/01/test_api_keys_rpc.py b/services/web/server/tests/unit/with_dbs/01/test_api_keys_rpc.py index 7ad51c739d7..51467f3c822 100644 --- a/services/web/server/tests/unit/with_dbs/01/test_api_keys_rpc.py +++ b/services/web/server/tests/unit/with_dbs/01/test_api_keys_rpc.py @@ -12,7 +12,7 @@ from models_library.api_schemas_webserver.auth import ApiKeyCreate from models_library.products import ProductName from models_library.rabbitmq_basic_types import RPCMethodName -from pydantic import parse_obj_as +from pydantic import TypeAdapter from pytest_mock import MockerFixture from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict from pytest_simcore.helpers.typing_env import EnvVarsDict @@ -104,7 +104,7 @@ async def test_api_key_get( for api_key_name in fake_user_api_keys: result = await rpc_client.request( WEBSERVER_RPC_NAMESPACE, - parse_obj_as(RPCMethodName, "api_key_get"), + TypeAdapter(RPCMethodName).validate_python("api_key_get"), product_name=osparc_product_name, user_id=logged_user["id"], name=api_key_name, @@ -124,7 +124,7 @@ async def test_api_keys_workflow( # creating a key created_api_key = await rpc_client.request( WEBSERVER_RPC_NAMESPACE, - parse_obj_as(RPCMethodName, "create_api_keys"), + TypeAdapter(RPCMethodName).validate_python("create_api_keys"), product_name=osparc_product_name, user_id=logged_user["id"], new=ApiKeyCreate(display_name=key_name, expiration=None), @@ -134,7 +134,7 @@ async def test_api_keys_workflow( # query the key is still present queried_api_key = await rpc_client.request( WEBSERVER_RPC_NAMESPACE, - parse_obj_as(RPCMethodName, "api_key_get"), + TypeAdapter(RPCMethodName).validate_python("api_key_get"), product_name=osparc_product_name, user_id=logged_user["id"], name=key_name, @@ -146,7 +146,7 @@ async def test_api_keys_workflow( # remove the key delete_key_result = await rpc_client.request( WEBSERVER_RPC_NAMESPACE, - parse_obj_as(RPCMethodName, "delete_api_keys"), + TypeAdapter(RPCMethodName).validate_python("delete_api_keys"), product_name=osparc_product_name, user_id=logged_user["id"], name=key_name, @@ -156,7 +156,7 @@ async def test_api_keys_workflow( # key no longer present query_missing_query = await rpc_client.request( WEBSERVER_RPC_NAMESPACE, - parse_obj_as(RPCMethodName, "api_key_get"), + TypeAdapter(RPCMethodName).validate_python("api_key_get"), product_name=osparc_product_name, user_id=logged_user["id"], name=key_name, diff --git a/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__pricing_plan.py b/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__pricing_plan.py index f6f683d0c37..35733d100e6 100644 --- a/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__pricing_plan.py +++ b/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__pricing_plan.py @@ -13,7 +13,6 @@ PricingPlanGet, ) from models_library.utils.fastapi_encoders import jsonable_encoder -from pydantic import parse_obj_as from pytest_simcore.aioresponses_mocker import AioResponsesMock from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.webserver_login import UserInfoDict @@ -30,8 +29,7 @@ def mock_rut_api_responses( assert client.app settings: ResourceUsageTrackerSettings = get_plugin_settings(client.app) - service_pricing_plan_get = parse_obj_as( - PricingPlanGet, + service_pricing_plan_get = PricingPlanGet.model_validate( PricingPlanGet.model_config["json_schema_extra"]["examples"][0], ) aioresponses_mocker.get( diff --git a/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__services.py b/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__services.py index 38d10825512..96ada757900 100644 --- a/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__services.py +++ b/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__services.py @@ -19,7 +19,7 @@ from models_library.services_types import ServiceKey, ServiceVersion from models_library.users import UserID from models_library.utils.fastapi_encoders import jsonable_encoder -from pydantic import NonNegativeInt, parse_obj_as +from pydantic import NonNegativeInt, TypeAdapter from pytest_mock import MockerFixture from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict @@ -55,8 +55,7 @@ async def _list( assert product_name assert user_id - items = parse_obj_as( - list[ServiceGetV2], + items = TypeAdapter(list[ServiceGetV2]).validate_python( ServiceGetV2.model_config["json_schema_extra"]["examples"], ) total_count = len(items) @@ -80,8 +79,8 @@ async def _get( assert product_name assert user_id - got = parse_obj_as( - ServiceGetV2, ServiceGetV2.model_config["json_schema_extra"]["examples"][0] + got = ServiceGetV2.model_validate( + ServiceGetV2.model_config["json_schema_extra"]["examples"][0] ) got.version = service_version got.key = service_key @@ -101,12 +100,12 @@ async def _update( assert product_name assert user_id - got = parse_obj_as( - ServiceGetV2, ServiceGetV2.model_config["json_schema_extra"]["examples"][0] + got = ServiceGetV2.model_validate( + ServiceGetV2.model_config["json_schema_extra"]["examples"][0] ) got.version = service_version got.key = service_key - return got.copy(update=update.dict(exclude_unset=True)) + return got.model_copy(update=update.model_dump(exclude_unset=True)) return { "list_services_paginated": mocker.patch( @@ -147,7 +146,7 @@ async def test_list_services_latest( assert data assert error is None - model = parse_obj_as(Page[CatalogServiceGet], data) + model = Page[CatalogServiceGet].model_validate(data) assert model assert model.data assert len(model.data) == model.meta.count @@ -181,7 +180,7 @@ async def test_get_and_patch_service( assert data assert error is None - model = parse_obj_as(CatalogServiceGet, data) + model = CatalogServiceGet.model_validate(data) assert model.key == service_key assert model.version == service_version @@ -206,7 +205,7 @@ async def test_get_and_patch_service( assert data assert error is None - model = parse_obj_as(CatalogServiceGet, data) + model = CatalogServiceGet.model_validate(data) assert model.key == service_key assert model.version == service_version assert model.name == update.name diff --git a/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__services_resources.py b/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__services_resources.py index 955a8cb8dd0..5c65109ef0a 100644 --- a/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__services_resources.py +++ b/services/web/server/tests/unit/with_dbs/01/test_catalog_handlers__services_resources.py @@ -13,7 +13,7 @@ ServiceResourcesDict, ServiceResourcesDictHelpers, ) -from pydantic import parse_obj_as +from pydantic import TypeAdapter from pytest_simcore.aioresponses_mocker import AioResponsesMock from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.webserver_login import UserInfoDict @@ -32,8 +32,7 @@ def mock_catalog_service_api_responses( url_pattern = re.compile(f"^{settings.base_url}+/.+$") - service_resources = parse_obj_as( - ServiceResourcesDict, + service_resources = TypeAdapter(ServiceResourcesDict).validate_python( ServiceResourcesDictHelpers.model_config["json_schema_extra"]["examples"][0], ) jsonable_service_resources = ServiceResourcesDictHelpers.create_jsonable( diff --git a/services/web/server/tests/unit/with_dbs/02/conftest.py b/services/web/server/tests/unit/with_dbs/02/conftest.py index e33fe523296..9eda857afd0 100644 --- a/services/web/server/tests/unit/with_dbs/02/conftest.py +++ b/services/web/server/tests/unit/with_dbs/02/conftest.py @@ -24,7 +24,7 @@ ServiceResourcesDict, ServiceResourcesDictHelpers, ) -from pydantic import parse_obj_as +from pydantic import TypeAdapter from pytest_mock import MockerFixture from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict @@ -39,8 +39,7 @@ @pytest.fixture def mock_service_resources() -> ServiceResourcesDict: - return parse_obj_as( - ServiceResourcesDict, + return TypeAdapter(ServiceResourcesDict).validate_python( ServiceResourcesDictHelpers.model_config["json_schema_extra"]["examples"][0], ) @@ -505,4 +504,4 @@ def workbench_db_column() -> dict[str, Any]: @pytest.fixture def workbench(workbench_db_column: dict[str, Any]) -> dict[NodeID, Node]: # convert to model - return parse_obj_as(dict[NodeID, Node], workbench_db_column) + return TypeAdapter(dict[NodeID, Node]).validate_python(workbench_db_column) diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_cancellations.py b/services/web/server/tests/unit/with_dbs/02/test_projects_cancellations.py index 3fe08f2172a..74c932ca600 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_cancellations.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_cancellations.py @@ -9,7 +9,7 @@ import pytest from aiohttp.test_utils import TestClient -from pydantic import ByteSize, parse_obj_as +from pydantic import ByteSize, TypeAdapter from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict from pytest_simcore.helpers.typing_env import EnvVarsDict @@ -157,7 +157,7 @@ async def test_copying_large_project_and_retrieving_copy_task( data, error = await assert_status(resp, expected.ok) assert data assert not error - list_of_tasks = parse_obj_as(list[TaskGet], data) + list_of_tasks = TypeAdapter(list[TaskGet]).validate_python(data) assert len(list_of_tasks) == 1 task = list_of_tasks[0] assert task.task_name == f"POST {create_url}" @@ -290,9 +290,9 @@ async def test_copying_too_large_project_returns_422( large_project_total_size = ( app_settings.WEBSERVER_PROJECTS.PROJECTS_MAX_COPY_SIZE_BYTES + 1 ) - storage_subsystem_mock.get_project_total_size_simcore_s3.return_value = ( - parse_obj_as(ByteSize, large_project_total_size) - ) + storage_subsystem_mock.get_project_total_size_simcore_s3.return_value = TypeAdapter( + ByteSize + ).validate_python(large_project_total_size) # POST /v0/projects await request_create_project( diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers.py index ec856a80f05..c3611eaa82d 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers.py @@ -23,7 +23,7 @@ from models_library.projects_state import ProjectState from models_library.services import ServiceKey from models_library.utils.fastapi_encoders import jsonable_encoder -from pydantic import parse_obj_as +from pydantic import TypeAdapter from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.webserver_login import UserInfoDict from pytest_simcore.helpers.webserver_parametrizations import ( @@ -173,10 +173,10 @@ async def _assert_get_same_project( assert data == project if project_state: - assert parse_obj_as(ProjectState, project_state) + assert ProjectState.model_validate(project_state) if project_permalink: - assert parse_obj_as(ProjectPermalink, project_permalink) + assert ProjectPermalink.model_validate(project_permalink) assert folder_id is None @@ -231,7 +231,7 @@ async def test_list_projects( assert not ProjectState( **project_state ).locked.value, "Templates are not locked" - assert parse_obj_as(ProjectPermalink, project_permalink) + assert ProjectPermalink.model_validate(project_permalink) # standard project project_state = data[1].pop("state") @@ -274,7 +274,7 @@ async def test_list_projects( assert not ProjectState( **project_state ).locked.value, "Templates are not locked" - assert parse_obj_as(ProjectPermalink, project_permalink) + assert ProjectPermalink.model_validate(project_permalink) @pytest.fixture(scope="session") @@ -454,7 +454,7 @@ async def test_new_project_from_template( if new_project: # check uuid replacement for node_name in new_project["workbench"]: - parse_obj_as(uuidlib.UUID, node_name) + TypeAdapter(uuidlib.UUID).validate_python(node_name) @pytest.mark.parametrize(*standard_user_role_response()) @@ -483,7 +483,7 @@ async def test_new_project_from_other_study( # check uuid replacement assert new_project["name"].endswith("(Copy)") for node_name in new_project["workbench"]: - parse_obj_as(uuidlib.UUID, node_name) + TypeAdapter(uuidlib.UUID).validate_python(node_name) @pytest.mark.parametrize(*standard_user_role_response()) @@ -537,7 +537,7 @@ async def test_new_project_from_template_with_body( # check uuid replacement for node_name in project["workbench"]: - parse_obj_as(uuidlib.UUID, node_name) + TypeAdapter(uuidlib.UUID).validate_python(node_name) @pytest.mark.parametrize(*standard_user_role_response()) @@ -593,7 +593,7 @@ async def test_new_template_from_project( # check uuid replacement for node_name in template_project["workbench"]: - parse_obj_as(uuidlib.UUID, node_name) + TypeAdapter(uuidlib.UUID).validate_python(node_name) # do the same with a body predefined = { @@ -653,7 +653,7 @@ async def test_new_template_from_project( # check uuid replacement for node_name in template_project["workbench"]: - parse_obj_as(uuidlib.UUID, node_name) + TypeAdapter(uuidlib.UUID).validate_python(node_name) # PUT -------- diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone.py index 28c8db0cfa6..657b19e20d6 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone.py @@ -14,7 +14,7 @@ from faker import Faker from models_library.api_schemas_webserver.projects import ProjectGet from models_library.projects import ProjectID -from pydantic import parse_obj_as +from pydantic import TypeAdapter from pytest_simcore.helpers.webserver_login import UserInfoDict from pytest_simcore.helpers.webserver_parametrizations import ( MockedStorageSubsystem, @@ -105,9 +105,9 @@ async def test_clone_project( # check whether it's a clone assert ProjectID(project["uuid"]) != cloned_project.uuid assert project["description"] == cloned_project.description - assert parse_obj_as(datetime, project["creationDate"]) < parse_obj_as( - datetime, cloned_project.creation_date - ) + assert TypeAdapter(datetime).validate_python(project["creationDate"]) < TypeAdapter( + datetime + ).validate_python(cloned_project.creation_date) assert len(project["workbench"]) == len(cloned_project.workbench) assert set(project["workbench"].keys()) != set( diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_metadata_handlers.py b/services/web/server/tests/unit/with_dbs/02/test_projects_metadata_handlers.py index 7a7056d7bbc..c960a86fa13 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_metadata_handlers.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_metadata_handlers.py @@ -18,7 +18,7 @@ ) from models_library.projects import ProjectID from models_library.projects_nodes_io import NodeID -from pydantic import parse_obj_as +from pydantic import TypeAdapter from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.webserver_login import UserInfoDict from pytest_simcore.helpers.webserver_parametrizations import ( @@ -83,12 +83,12 @@ async def test_custom_metadata_handlers( project_id=user_project["uuid"] ) response = await client.patch( - f"{url}", json=ProjectMetadataUpdate(custom=custom_metadata).dict() + f"{url}", json=ProjectMetadataUpdate(custom=custom_metadata).model_dump() ) data, _ = await assert_status(response, expected_status_code=expected.ok) - assert parse_obj_as(ProjectMetadataGet, data).custom == custom_metadata + assert ProjectMetadataGet.model_validate(data).custom == custom_metadata # delete project url = client.app.router["delete_project"].url_for(project_id=user_project["uuid"]) @@ -138,9 +138,9 @@ async def test_new_project_with_parent_project_node( ) assert parent_project - parent_project_uuid = parse_obj_as(ProjectID, parent_project["uuid"]) - parent_node_id = parse_obj_as( - NodeID, random.choice(list(parent_project["workbench"])) # noqa: S311 + parent_project_uuid = TypeAdapter(ProjectID).validate_python(parent_project["uuid"]) + parent_node_id = TypeAdapter(NodeID).validate_python( + random.choice(list(parent_project["workbench"])) # noqa: S311 ) child_project = await request_create_project( client, @@ -175,10 +175,10 @@ async def test_new_project_with_parent_project_node( project_id=child_project["uuid"] ) response = await client.patch( - f"{url}", json=ProjectMetadataUpdate(custom=custom_metadata).dict() + f"{url}", json=ProjectMetadataUpdate(custom=custom_metadata).model_dump() ) data, _ = await assert_status(response, expected_status_code=status.HTTP_200_OK) - assert parse_obj_as(ProjectMetadataGet, data).custom == custom_metadata + assert ProjectMetadataGet.model_validate(data).custom == custom_metadata # check child project has parent unchanged async with aiopg_engine.acquire() as connection: project_db_metadata = await get_db_project_metadata( @@ -216,13 +216,13 @@ async def test_new_project_with_invalid_parent_project_node( ) assert parent_project - parent_project_uuid = parse_obj_as(ProjectID, parent_project["uuid"]) - parent_node_id = parse_obj_as( - NodeID, random.choice(list(parent_project["workbench"])) # noqa: S311 + parent_project_uuid = TypeAdapter(ProjectID).validate_python(parent_project["uuid"]) + parent_node_id = TypeAdapter(NodeID).validate_python( + random.choice(list(parent_project["workbench"])) # noqa: S311 ) # creating with random project UUID should fail - random_project_uuid = parse_obj_as(ProjectID, faker.uuid4()) + random_project_uuid = TypeAdapter(ProjectID).validate_python(faker.uuid4()) child_project = await request_create_project( client, expected.accepted, @@ -235,7 +235,7 @@ async def test_new_project_with_invalid_parent_project_node( assert not child_project # creating with a random node ID should fail too - random_node_id = parse_obj_as(NodeID, faker.uuid4()) + random_node_id = TypeAdapter(NodeID).validate_python(faker.uuid4()) child_project = await request_create_project( client, expected.accepted, @@ -259,7 +259,7 @@ async def test_new_project_with_invalid_parent_project_node( assert not child_project # creating with only a parent node ID should fail too - random_node_id = parse_obj_as(NodeID, faker.uuid4()) + random_node_id = TypeAdapter(NodeID).validate_python(faker.uuid4()) child_project = await request_create_project( client, expected.unprocessable, @@ -320,10 +320,10 @@ async def test_set_project_parent_backward_compatibility( project_id=child_project["uuid"] ) response = await client.patch( - f"{url}", json=ProjectMetadataUpdate(custom=custom_metadata).dict() + f"{url}", json=ProjectMetadataUpdate(custom=custom_metadata).model_dump() ) data, _ = await assert_status(response, expected_status_code=status.HTTP_200_OK) - assert parse_obj_as(ProjectMetadataGet, data).custom == custom_metadata + assert ProjectMetadataGet.model_validate(data).custom == custom_metadata # check child project has parent set correctly async with aiopg_engine.acquire() as connection: project_db_metadata = await get_db_project_metadata( @@ -362,7 +362,7 @@ async def test_update_project_metadata_backward_compatibility_with_same_project_ project_id=child_project["uuid"] ) response = await client.patch( - f"{url}", json=ProjectMetadataUpdate(custom=custom_metadata).dict() + f"{url}", json=ProjectMetadataUpdate(custom=custom_metadata).model_dump() ) await assert_status(response, expected_status_code=expected.ok) @@ -377,7 +377,7 @@ async def test_update_project_metadata_backward_compatibility_with_same_project_ project_id=child_project["uuid"] ) response = await client.patch( - f"{url}", json=ProjectMetadataUpdate(custom=custom_metadata).dict() + f"{url}", json=ProjectMetadataUpdate(custom=custom_metadata).model_dump() ) await assert_status(response, expected_status_code=expected.ok) @@ -427,10 +427,10 @@ async def test_update_project_metadata_s4lacad_backward_compatibility_passing_ni project_id=child_project["uuid"] ) response = await client.patch( - f"{url}", json=ProjectMetadataUpdate(custom=custom_metadata).dict() + f"{url}", json=ProjectMetadataUpdate(custom=custom_metadata).model_dump() ) data, _ = await assert_status(response, expected_status_code=status.HTTP_200_OK) - assert parse_obj_as(ProjectMetadataGet, data).custom == custom_metadata + assert ProjectMetadataGet.model_validate(data).custom == custom_metadata # check project has no parent async with aiopg_engine.acquire() as connection: diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py b/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py index a5b5ad3abdd..778738b052e 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py @@ -33,7 +33,7 @@ ServiceResourcesDictHelpers, ) from models_library.utils.fastapi_encoders import jsonable_encoder -from pydantic import NonNegativeFloat, NonNegativeInt, parse_obj_as +from pydantic import NonNegativeFloat, NonNegativeInt, TypeAdapter from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict from pytest_simcore.helpers.webserver_parametrizations import ( @@ -72,7 +72,7 @@ async def test_get_node_resources( data, error = await assert_status(response, expected) if data: assert not error - node_resources = parse_obj_as(ServiceResourcesDict, data) + node_resources = TypeAdapter(ServiceResourcesDict).validate_python(data) assert node_resources assert DEFAULT_SINGLE_SERVICE_NAME in node_resources assert ( @@ -155,7 +155,7 @@ async def test_replace_node_resources_is_forbidden_by_default( data, error = await assert_status(response, expected) if data: assert not error - node_resources = parse_obj_as(ServiceResourcesDict, data) + node_resources = TypeAdapter(ServiceResourcesDict).validate_python(data) assert node_resources assert DEFAULT_SINGLE_SERVICE_NAME in node_resources assert ( @@ -197,7 +197,7 @@ async def test_replace_node_resources_is_ok_if_explicitly_authorized( data, error = await assert_status(response, expected) if data: assert not error - node_resources = parse_obj_as(ServiceResourcesDict, data) + node_resources = TypeAdapter(ServiceResourcesDict).validate_python(data) assert node_resources assert DEFAULT_SINGLE_SERVICE_NAME in node_resources assert ( @@ -940,8 +940,7 @@ def mock_storage_calls(aioresponses_mocker: aioresponses, faker: Faker) -> None: payload=jsonable_encoder( Envelope[list[FileMetaDataGet]]( data=[ - parse_obj_as( - FileMetaDataGet, + FileMetaDataGet.model_validate( { "file_uuid": file_uuid, "location_id": 0, @@ -991,7 +990,7 @@ async def test_read_project_nodes_previews( assert not error assert len(data) == 3 - nodes_previews = parse_obj_as(list[_ProjectNodePreview], data) + nodes_previews = TypeAdapter(list[_ProjectNodePreview]).validate_python(data) # GET node's preview for node_preview in nodes_previews: @@ -1007,4 +1006,4 @@ async def test_read_project_nodes_previews( status.HTTP_200_OK, ) - assert parse_obj_as(_ProjectNodePreview, data) == node_preview + assert _ProjectNodePreview.model_validate(data) == node_preview diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_pricing_unit_handlers.py b/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_pricing_unit_handlers.py index f51877302b6..10367ac9155 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_pricing_unit_handlers.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_pricing_unit_handlers.py @@ -18,7 +18,6 @@ PricingUnitGet, ) from models_library.utils.fastapi_encoders import jsonable_encoder -from pydantic import parse_obj_as from pytest_mock.plugin import MockerFixture from pytest_simcore.aioresponses_mocker import AioResponsesMock from pytest_simcore.helpers.assert_checks import assert_status @@ -98,8 +97,8 @@ def mock_rut_api_responses( assert client.app settings: ResourceUsageTrackerSettings = get_plugin_settings(client.app) - pricing_unit_get_base = parse_obj_as( - PricingUnitGet, PricingUnitGet.model_config["json_schema_extra"]["examples"][0] + pricing_unit_get_base = PricingUnitGet.model_validate( + PricingUnitGet.model_config["json_schema_extra"]["examples"][0] ) pricing_unit_get_1 = pricing_unit_get_base.copy() pricing_unit_get_1.pricing_unit_id = _PRICING_UNIT_ID_1 diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_ports_handlers.py b/services/web/server/tests/unit/with_dbs/02/test_projects_ports_handlers.py index 06e20946e91..8a82df500b6 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_ports_handlers.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_ports_handlers.py @@ -15,7 +15,7 @@ from models_library.api_schemas_directorv2.comp_tasks import TasksOutputs from models_library.api_schemas_webserver.projects import ProjectGet from models_library.utils.fastapi_encoders import jsonable_encoder -from pydantic import parse_obj_as +from pydantic import TypeAdapter from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.webserver_fake_ports_data import ( PROJECTS_METADATA_PORTS_RESPONSE_BODY_DATA, @@ -280,9 +280,9 @@ async def test_clone_project_and_set_inputs( assert parent_project_id != cloned_project.uuid assert user_project["description"] == cloned_project.description - assert parse_obj_as(datetime, user_project["creationDate"]) < parse_obj_as( - datetime, cloned_project.creation_date - ) + assert TypeAdapter(datetime).validate_python( + user_project["creationDate"] + ) < TypeAdapter(datetime).validate_python(cloned_project.creation_date) # - set_inputs project_clone_id ---------------------------------------------- job_inputs_values = {"X": 42} # like JobInputs.values diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_wallet_handlers.py b/services/web/server/tests/unit/with_dbs/02/test_projects_wallet_handlers.py index 9443f773c03..269358a4a9f 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_wallet_handlers.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_wallet_handlers.py @@ -12,7 +12,6 @@ import sqlalchemy as sa from aiohttp.test_utils import TestClient from models_library.api_schemas_webserver.wallets import WalletGet -from pydantic import parse_obj_as from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.webserver_login import LoggedUser, UserInfoDict from servicelib.aiohttp import status @@ -93,7 +92,7 @@ def setup_wallets_db( ) .returning(sa.literal_column("*")) ) - output.append(parse_obj_as(WalletGet, result.fetchone())) + output.append(WalletGet.model_validate(result.fetchone())) yield output con.execute(wallets.delete()) diff --git a/services/web/server/tests/unit/with_dbs/03/login/test_login_2fa_resend.py b/services/web/server/tests/unit/with_dbs/03/login/test_login_2fa_resend.py index 7139811a6b1..d94e2ffa623 100644 --- a/services/web/server/tests/unit/with_dbs/03/login/test_login_2fa_resend.py +++ b/services/web/server/tests/unit/with_dbs/03/login/test_login_2fa_resend.py @@ -6,7 +6,6 @@ import pytest import sqlalchemy as sa from aiohttp.test_utils import TestClient -from pydantic import parse_obj_as from pytest_mock import MockFixture from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.monkeypatch_envs import EnvVarsDict, setenvs_from_dict @@ -106,7 +105,7 @@ async def test_resend_2fa_workflow( }, ) data, _ = await assert_status(response, status.HTTP_202_ACCEPTED) - next_page = parse_obj_as(NextPage[CodePageParams], data) + next_page = NextPage[CodePageParams].model_validate(data) assert next_page.name == CODE_2FA_SMS_CODE_REQUIRED assert next_page.parameters.expiration_2fa > 0 diff --git a/services/web/server/tests/unit/with_dbs/03/products/test_products_rpc.py b/services/web/server/tests/unit/with_dbs/03/products/test_products_rpc.py index 3de1f7a95c8..4505a6f4e3e 100644 --- a/services/web/server/tests/unit/with_dbs/03/products/test_products_rpc.py +++ b/services/web/server/tests/unit/with_dbs/03/products/test_products_rpc.py @@ -10,7 +10,7 @@ from models_library.api_schemas_webserver import WEBSERVER_RPC_NAMESPACE from models_library.products import CreditResultGet, ProductName from models_library.rabbitmq_basic_types import RPCMethodName -from pydantic import parse_obj_as +from pydantic import TypeAdapter from pytest_mock import MockerFixture from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict from pytest_simcore.helpers.typing_env import EnvVarsDict @@ -70,26 +70,26 @@ async def test_get_credit_amount( ): result = await rpc_client.request( WEBSERVER_RPC_NAMESPACE, - parse_obj_as(RPCMethodName, "get_credit_amount"), + TypeAdapter(RPCMethodName).validate_python("get_credit_amount"), dollar_amount=Decimal(900), product_name="s4l", ) - credit_result = parse_obj_as(CreditResultGet, result) + credit_result = CreditResultGet.model_validate(result) assert credit_result.credit_amount == 100 result = await rpc_client.request( WEBSERVER_RPC_NAMESPACE, - parse_obj_as(RPCMethodName, "get_credit_amount"), + TypeAdapter(RPCMethodName).validate_python("get_credit_amount"), dollar_amount=Decimal(900), product_name="tis", ) - credit_result = parse_obj_as(CreditResultGet, result) + credit_result = CreditResultGet.model_validate(result) assert credit_result.credit_amount == 180 with pytest.raises(RPCServerError) as exc_info: await rpc_client.request( WEBSERVER_RPC_NAMESPACE, - parse_obj_as(RPCMethodName, "get_credit_amount"), + TypeAdapter(RPCMethodName).validate_python("get_credit_amount"), dollar_amount=Decimal(900), product_name="osparc", ) diff --git a/services/web/server/tests/unit/with_dbs/03/resource_usage/test_admin_pricing_plans.py b/services/web/server/tests/unit/with_dbs/03/resource_usage/test_admin_pricing_plans.py index 04b30407526..ad508f523e4 100644 --- a/services/web/server/tests/unit/with_dbs/03/resource_usage/test_admin_pricing_plans.py +++ b/services/web/server/tests/unit/with_dbs/03/resource_usage/test_admin_pricing_plans.py @@ -17,7 +17,6 @@ PricingUnitGet, ) from models_library.resource_tracker import PricingPlanClassification -from pydantic import parse_obj_as from pytest_mock import MockerFixture from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.webserver_login import UserInfoDict @@ -35,8 +34,7 @@ def mock_rpc_resource_usage_tracker_service_api( "simcore_service_webserver.resource_usage._pricing_plans_admin_api.pricing_plans.list_pricing_plans", autospec=True, return_value=[ - parse_obj_as( - PricingPlanGet, + PricingPlanGet.model_validate( PricingPlanGet.model_config["json_schema_extra"]["examples"][0], ) ], @@ -44,24 +42,21 @@ def mock_rpc_resource_usage_tracker_service_api( "get_pricing_plan": mocker.patch( "simcore_service_webserver.resource_usage._pricing_plans_admin_api.pricing_plans.get_pricing_plan", autospec=True, - return_value=parse_obj_as( - PricingPlanGet, + return_value=PricingPlanGet.model_validate( PricingPlanGet.model_config["json_schema_extra"]["examples"][0], ), ), "create_pricing_plan": mocker.patch( "simcore_service_webserver.resource_usage._pricing_plans_admin_api.pricing_plans.create_pricing_plan", autospec=True, - return_value=parse_obj_as( - PricingPlanGet, + return_value=PricingPlanGet.model_validate( PricingPlanGet.model_config["json_schema_extra"]["examples"][0], ), ), "update_pricing_plan": mocker.patch( "simcore_service_webserver.resource_usage._pricing_plans_admin_api.pricing_plans.update_pricing_plan", autospec=True, - return_value=parse_obj_as( - PricingPlanGet, + return_value=PricingPlanGet.model_validate( PricingPlanGet.model_config["json_schema_extra"]["examples"][0], ), ), @@ -69,24 +64,21 @@ def mock_rpc_resource_usage_tracker_service_api( "get_pricing_unit": mocker.patch( "simcore_service_webserver.resource_usage._pricing_plans_admin_api.pricing_units.get_pricing_unit", autospec=True, - return_value=parse_obj_as( - PricingUnitGet, + return_value=PricingUnitGet.model_validate( PricingUnitGet.model_config["json_schema_extra"]["examples"][0], ), ), "create_pricing_unit": mocker.patch( "simcore_service_webserver.resource_usage._pricing_plans_admin_api.pricing_units.create_pricing_unit", autospec=True, - return_value=parse_obj_as( - PricingUnitGet, + return_value=PricingUnitGet.model_validate( PricingUnitGet.model_config["json_schema_extra"]["examples"][0], ), ), "update_pricing_unit": mocker.patch( "simcore_service_webserver.resource_usage._pricing_plans_admin_api.pricing_units.update_pricing_unit", autospec=True, - return_value=parse_obj_as( - PricingUnitGet, + return_value=PricingUnitGet.model_validate( PricingUnitGet.model_config["json_schema_extra"]["examples"][0], ), ), @@ -95,8 +87,7 @@ def mock_rpc_resource_usage_tracker_service_api( "simcore_service_webserver.resource_usage._pricing_plans_admin_api.pricing_plans.list_connected_services_to_pricing_plan_by_pricing_plan", autospec=True, return_value=[ - parse_obj_as( - PricingPlanToServiceGet, + PricingPlanToServiceGet.model_validate( PricingPlanToServiceGet.model_config["json_schema_extra"][ "examples" ][0], @@ -106,8 +97,7 @@ def mock_rpc_resource_usage_tracker_service_api( "connect_service_to_pricing_plan": mocker.patch( "simcore_service_webserver.resource_usage._pricing_plans_admin_api.pricing_plans.connect_service_to_pricing_plan", autospec=True, - return_value=parse_obj_as( - PricingPlanToServiceGet, + return_value=PricingPlanToServiceGet.model_validate( PricingPlanToServiceGet.model_config["json_schema_extra"]["examples"][ 0 ], diff --git a/services/web/server/tests/unit/with_dbs/03/resource_usage/test_pricing_plans.py b/services/web/server/tests/unit/with_dbs/03/resource_usage/test_pricing_plans.py index e48d461ad26..70114820036 100644 --- a/services/web/server/tests/unit/with_dbs/03/resource_usage/test_pricing_plans.py +++ b/services/web/server/tests/unit/with_dbs/03/resource_usage/test_pricing_plans.py @@ -15,7 +15,6 @@ PricingUnitGet, ) from models_library.utils.fastapi_encoders import jsonable_encoder -from pydantic import parse_obj_as from pytest_simcore.aioresponses_mocker import AioResponsesMock from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.webserver_login import UserInfoDict @@ -32,12 +31,11 @@ def mock_rut_api_responses( assert client.app settings: ResourceUsageTrackerSettings = get_plugin_settings(client.app) - pricing_unit_get = parse_obj_as( - PricingUnitGet, PricingUnitGet.model_config["json_schema_extra"]["examples"][0] + pricing_unit_get = PricingUnitGet.model_validate( + PricingUnitGet.model_config["json_schema_extra"]["examples"][0] ) - service_pricing_plan_get = parse_obj_as( - PricingPlanGet, + service_pricing_plan_get = PricingPlanGet.model_validate( PricingPlanGet.model_config["json_schema_extra"]["examples"][0], ) diff --git a/services/web/server/tests/unit/with_dbs/03/resource_usage/test_usage_services__export.py b/services/web/server/tests/unit/with_dbs/03/resource_usage/test_usage_services__export.py index 6a80bccca0d..3ac7ffaa665 100644 --- a/services/web/server/tests/unit/with_dbs/03/resource_usage/test_usage_services__export.py +++ b/services/web/server/tests/unit/with_dbs/03/resource_usage/test_usage_services__export.py @@ -16,7 +16,7 @@ from aiohttp.test_utils import TestClient from models_library.resource_tracker import ServiceResourceUsagesFilters from models_library.rest_ordering import OrderBy -from pydantic import AnyUrl, parse_obj_as +from pydantic import AnyUrl, TypeAdapter from pytest_mock.plugin import MockerFixture from pytest_simcore.helpers.webserver_login import UserInfoDict from servicelib.aiohttp import status @@ -29,7 +29,7 @@ def mock_export_usage_services(mocker: MockerFixture) -> MagicMock: return mocker.patch( "simcore_service_webserver.resource_usage._service_runs_api.service_runs.export_service_runs", spec=True, - return_value=parse_obj_as(AnyUrl, "https://www.google.com/"), + return_value=TypeAdapter(AnyUrl).validate_python("https://www.google.com/"), ) @@ -115,5 +115,5 @@ async def test_list_service_usage( assert mock_export_usage_services.called args = mock_export_usage_services.call_args[1] - assert args["order_by"] == parse_obj_as(OrderBy, _order_by) - assert args["filters"] == parse_obj_as(ServiceResourceUsagesFilters, _filter) + assert args["order_by"] == OrderBy.model_validate(_order_by) + assert args["filters"] == ServiceResourceUsagesFilters.model_validate(_filter) diff --git a/services/web/server/tests/unit/with_dbs/03/test_storage_handlers.py b/services/web/server/tests/unit/with_dbs/03/test_storage_handlers.py index 28e7d0590c9..c7495866c8f 100644 --- a/services/web/server/tests/unit/with_dbs/03/test_storage_handlers.py +++ b/services/web/server/tests/unit/with_dbs/03/test_storage_handlers.py @@ -17,7 +17,7 @@ FileUploadLinks, FileUploadSchema, ) -from pydantic import AnyUrl, ByteSize, parse_obj_as +from pydantic import AnyUrl, ByteSize, TypeAdapter from pytest_mock import MockerFixture from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict from pytest_simcore.helpers.typing_env import EnvVarsDict @@ -59,7 +59,7 @@ async def _resp(*args, **kwargs) -> tuple[Any, int]: ) def _resolve(*args, **kwargs) -> AnyUrl: - return parse_obj_as(AnyUrl, "http://private-url") + return TypeAdapter(AnyUrl).validate_python("http://private-url") mocker.patch( "simcore_service_webserver.storage._handlers._from_storage_url", @@ -69,16 +69,19 @@ def _resolve(*args, **kwargs) -> AnyUrl: MOCK_FILE_UPLOAD_SCHEMA = FileUploadSchema( - chunk_size=parse_obj_as(ByteSize, "5GiB"), - urls=[parse_obj_as(AnyUrl, "s3://file_id")], + chunk_size=ByteSize("5GiB"), + urls=[TypeAdapter(AnyUrl).validate_python("s3://file_id")], links=FileUploadLinks( - abort_upload=parse_obj_as(AnyUrl, "http://private-url/operation:abort"), - complete_upload=parse_obj_as(AnyUrl, "http://private-url/operation:complete"), + abort_upload=TypeAdapter(AnyUrl).validate_python( + "http://private-url/operation:abort" + ), + complete_upload=TypeAdapter(AnyUrl).validate_python( + "http://private-url/operation:complete" + ), ), ) -MOCK_FILE_UPLOAD_SCHEMA = parse_obj_as( - FileUploadSchema, +MOCK_FILE_UPLOAD_SCHEMA = FileUploadSchema.model_validate( { "chunk_size": "5", "urls": ["s3://file_id"], @@ -90,8 +93,8 @@ def _resolve(*args, **kwargs) -> AnyUrl: ) -MOCK_FILE_UPLOAD_COMPLETE_RESPONSE = parse_obj_as( - FileUploadCompleteResponse, {"links": {"state": "http://private-url"}} +MOCK_FILE_UPLOAD_COMPLETE_RESPONSE = FileUploadCompleteResponse.model_validate( + {"links": {"state": "http://private-url"}} ) @@ -124,7 +127,7 @@ def _resolve(*args, **kwargs) -> AnyUrl: "PUT", "/v0/storage/locations/0/files/{file_id}", None, - json.loads(MOCK_FILE_UPLOAD_SCHEMA.json()), + json.loads(MOCK_FILE_UPLOAD_SCHEMA.model_dump_json()), id="upload_file", ), pytest.param( @@ -145,14 +148,14 @@ def _resolve(*args, **kwargs) -> AnyUrl: "POST", "/v0/storage/locations/0/files/{file_id}:complete", {"parts": []}, - json.loads(MOCK_FILE_UPLOAD_COMPLETE_RESPONSE.json()), + json.loads(MOCK_FILE_UPLOAD_COMPLETE_RESPONSE.model_dump_json()), id="complete_upload_file", ), pytest.param( "POST", "/v0/storage/locations/0/files/{file_id}:complete/futures/RANDOM_FUTURE_ID", None, - json.loads(MOCK_FILE_UPLOAD_SCHEMA.json()), + json.loads(MOCK_FILE_UPLOAD_SCHEMA.model_dump_json()), id="is_completed_upload_file", ), ], @@ -208,7 +211,7 @@ def test_url_storage_resolver_helpers(faker: Faker, app_environment: EnvVarsDict # storage -> web web_url: AnyUrl = _from_storage_url( - web_request, parse_obj_as(AnyUrl, str(storage_url)) + web_request, TypeAdapter(AnyUrl).validate_python(f"{storage_url}") ) assert storage_url.host != web_url.host @@ -216,4 +219,4 @@ def test_url_storage_resolver_helpers(faker: Faker, app_environment: EnvVarsDict assert isinstance(storage_url, URL) # this is a bit inconvenient assert isinstance(web_url, AnyUrl) - assert str(web_url) == str(web_request.url) + assert f"{web_url}" == f"{web_request.url}" diff --git a/services/web/server/tests/unit/with_dbs/03/test_users__notifications.py b/services/web/server/tests/unit/with_dbs/03/test_users__notifications.py index 70d76a247f5..36b0aec8276 100644 --- a/services/web/server/tests/unit/with_dbs/03/test_users__notifications.py +++ b/services/web/server/tests/unit/with_dbs/03/test_users__notifications.py @@ -18,7 +18,7 @@ import redis.asyncio as aioredis from aiohttp.test_utils import TestClient from models_library.products import ProductName -from pydantic import parse_obj_as +from pydantic import TypeAdapter from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.monkeypatch_envs import EnvVarsDict, setenvs_from_dict from pytest_simcore.helpers.webserver_login import UserInfoDict @@ -154,7 +154,9 @@ async def test_list_user_notifications( response = await client.get(url.path) json_response = await response.json() - result = parse_obj_as(list[UserNotification], json_response["data"]) + result = TypeAdapter(list[UserNotification]).validate_python( + json_response["data"] + ) # noqa: F821 assert len(result) <= MAX_NOTIFICATIONS_FOR_USER_TO_SHOW assert result == list( reversed(created_notifications[:MAX_NOTIFICATIONS_FOR_USER_TO_SHOW]) @@ -444,7 +446,7 @@ async def test_list_permissions( data, error = await assert_status(resp, expected_response) if data: assert not error - list_of_permissions = parse_obj_as(list[PermissionGet], data) + list_of_permissions = TypeAdapter(list[PermissionGet]).validate_python(data) assert ( len(list_of_permissions) == 1 ), "for now there is only 1 permission, but when we sync frontend/backend permissions there will be more" @@ -475,7 +477,7 @@ async def test_list_permissions_with_overriden_extra_properties( data, error = await assert_status(resp, expected_response) assert data assert not error - list_of_permissions = parse_obj_as(list[PermissionGet], data) + list_of_permissions = TypeAdapter(list[PermissionGet]).validate_python(data) filtered_permissions = list( filter( lambda x: x.name == "override_services_specifications", list_of_permissions diff --git a/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments.py b/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments.py index 0ace542ea86..0963efe0b70 100644 --- a/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments.py +++ b/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments.py @@ -17,7 +17,7 @@ WalletPaymentInitiated, ) from models_library.rest_pagination import Page -from pydantic import parse_obj_as +from pydantic import TypeAdapter from pytest_mock import MockerFixture from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.webserver_login import LoggedUser, NewUser, UserInfoDict @@ -134,7 +134,7 @@ async def test_one_time_payment_worfklow( response = await client.get("/v0/wallets/-/payments") data, error = await assert_status(response, status.HTTP_200_OK) - page = parse_obj_as(Page[PaymentTransaction], data) + page = Page[PaymentTransaction].model_validate(data) assert page.data assert page.meta.total == 1 @@ -233,7 +233,7 @@ async def test_multiple_payments( response = await client.get("/v0/wallets/-/payments") data, error = await assert_status(response, status.HTTP_200_OK) - page = parse_obj_as(Page[PaymentTransaction], data) + page = Page[PaymentTransaction].model_validate(data) assert page.meta.total == num_payments all_transactions = {t.payment_id: t for t in page.data} @@ -362,9 +362,11 @@ async def test_payment_not_found( def test_payment_transaction_state_and_literals_are_in_sync(): - state_literals = PaymentTransaction.__fields__["state"].type_ + state_literals = PaymentTransaction.model_fields["state"].type_ assert ( - parse_obj_as(list[state_literals], [f"{s}" for s in PaymentTransactionState]) + TypeAdapter(list[state_literals]).validate_python( + [f"{s}" for s in PaymentTransactionState] + ) is not None ) diff --git a/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments_methods.py b/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments_methods.py index 89291d5eb56..d10109a5295 100644 --- a/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments_methods.py +++ b/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments_methods.py @@ -22,7 +22,7 @@ ) from models_library.rest_pagination import Page from models_library.wallets import WalletID -from pydantic import parse_obj_as +from pydantic import TypeAdapter from pytest_mock import MockerFixture from pytest_simcore.helpers.assert_checks import assert_status from servicelib.aiohttp import status @@ -103,7 +103,7 @@ async def test_payment_method_worfklow( data, _ = await assert_status(response, status.HTTP_200_OK) assert mock_rpc_payments_service_api["list_payment_methods"].called - wallet_payments_methods = parse_obj_as(list[PaymentMethodGet], data) + wallet_payments_methods = TypeAdapter(list[PaymentMethodGet]).validate_python(data) assert wallet_payments_methods == [payment_method] # Delete @@ -268,7 +268,9 @@ async def test_wallet_autorecharge( # payment-methods.auto_recharge response = await client.get(f"/v0/wallets/{wallet.wallet_id}/payments-methods") data, _ = await assert_status(response, status.HTTP_200_OK) - wallet_payment_methods = parse_obj_as(list[PaymentMethodGet], data) + wallet_payment_methods = TypeAdapter(list[PaymentMethodGet]).validate_python( + data + ) for payment_method in wallet_payment_methods: assert payment_method.auto_recharge == ( @@ -417,7 +419,7 @@ async def test_one_time_payment_with_payment_method( response = await client.get("/v0/wallets/-/payments") data, error = await assert_status(response, status.HTTP_200_OK) - page = parse_obj_as(Page[PaymentTransaction], data) + page = Page[PaymentTransaction].model_validate(data) assert page.data assert page.meta.total == 1 diff --git a/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments_rpc.py b/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments_rpc.py index 756c008adba..af0f7d304ca 100644 --- a/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments_rpc.py +++ b/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments_rpc.py @@ -12,7 +12,7 @@ from models_library.api_schemas_webserver import WEBSERVER_RPC_NAMESPACE from models_library.payments import InvoiceDataGet from models_library.rabbitmq_basic_types import RPCMethodName -from pydantic import parse_obj_as +from pydantic import TypeAdapter from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict from pytest_simcore.helpers.typing_env import EnvVarsDict from pytest_simcore.helpers.webserver_login import UserInfoDict @@ -77,11 +77,11 @@ async def test_one_time_payment_worfklow( result = await rpc_client.request( WEBSERVER_RPC_NAMESPACE, - parse_obj_as(RPCMethodName, "get_invoice_data"), + TypeAdapter(RPCMethodName).validate_python("get_invoice_data"), user_id=logged_user["id"], dollar_amount=Decimal(900), product_name="osparc", ) - invoice_data_get = parse_obj_as(InvoiceDataGet, result) + invoice_data_get = InvoiceDataGet.model_validate(result) assert invoice_data_get assert len(invoice_data_get.user_invoice_address.country) == 2 diff --git a/services/web/server/tests/unit/with_dbs/conftest.py b/services/web/server/tests/unit/with_dbs/conftest.py index f4f527179b1..c5078809d3a 100644 --- a/services/web/server/tests/unit/with_dbs/conftest.py +++ b/services/web/server/tests/unit/with_dbs/conftest.py @@ -38,7 +38,7 @@ from models_library.api_schemas_directorv2.dynamic_services import DynamicServiceGet from models_library.products import ProductName from models_library.services_enums import ServiceState -from pydantic import ByteSize, parse_obj_as +from pydantic import ByteSize from pytest_mock import MockerFixture from pytest_simcore.helpers.dict_tools import ConfigDict from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict @@ -369,7 +369,7 @@ async def _mock_result(): mock3 = mocker.patch( "simcore_service_webserver.projects._crud_api_create.get_project_total_size_simcore_s3", autospec=True, - return_value=parse_obj_as(ByteSize, "1Gib"), + return_value=ByteSize("1Gib"), ) return MockedStorageSubsystem(mock, mock1, mock2, mock3) From a50f41acbf86c21ef15c6006c21e462484dc8db1 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 14:51:56 +0200 Subject: [PATCH 019/165] fix deprecated dict() --- .../application_settings.py | 10 +++++----- .../simcore_service_webserver/catalog/_api.py | 16 ++++++++-------- .../catalog/_handlers.py | 10 +++++----- .../director_v2/_handlers.py | 2 +- .../simcore_service_webserver/email/_handlers.py | 2 +- .../groups/_handlers.py | 2 +- .../src/simcore_service_webserver/groups/api.py | 4 ++-- .../simcore_service_webserver/login/plugin.py | 2 +- .../simcore_service_webserver/login/settings.py | 5 ++++- .../meta_modeling/_iterations.py | 2 +- .../_rabbitmq_exclusive_queue_consumers.py | 2 +- .../_rabbitmq_nonexclusive_queue_consumers.py | 4 ++-- .../products/_invitations_handlers.py | 2 +- .../projects/_comments_api.py | 7 ++++--- .../projects/_crud_api_create.py | 2 +- .../projects/_crud_api_read.py | 2 +- .../projects/_crud_handlers.py | 2 +- .../projects/_nodes_handlers.py | 2 +- .../projects/_ports_handlers.py | 2 +- .../projects/_states_handlers.py | 2 +- .../projects/_tags_api.py | 6 ++++-- .../projects/_wallets_api.py | 4 +++- .../projects/projects_api.py | 3 ++- .../scicrunch/_resolver.py | 2 +- .../simcore_service_webserver/scicrunch/db.py | 4 ++-- .../statics/settings.py | 2 +- .../studies_dispatcher/_rest_handlers.py | 16 ++++++++++++---- .../src/simcore_service_webserver/tags/_api.py | 4 ++-- .../src/simcore_service_webserver/users/_api.py | 2 +- .../simcore_service_webserver/users/_handlers.py | 4 ++-- .../users/_notifications_handlers.py | 2 +- .../version_control/_handlers.py | 16 ++++++++-------- .../version_control/vc_changes.py | 2 +- .../simcore_service_webserver/wallets/_api.py | 10 ++++++---- .../wallets/_groups_api.py | 10 +++++----- .../workspaces/_groups_api.py | 6 ++++-- .../02/notifications/test_rabbitmq_consumers.py | 4 ++-- .../02/scicrunch/test_scicrunch__rest.py | 4 ++-- .../tests/unit/isolated/test_login_settings.py | 4 ++-- .../tests/unit/isolated/test_users_models.py | 12 ++++++------ .../with_dbs/02/test_projects_states_handlers.py | 2 +- .../unit/with_dbs/03/invitations/conftest.py | 2 +- ...st_login_handlers_registration_invitations.py | 6 +++--- .../test_products__invitations_handlers.py | 8 ++++---- .../test_meta_modeling_iterations.py | 2 +- .../server/tests/unit/with_dbs/03/test_users.py | 12 +++++++----- .../version_control/test_version_control_core.py | 4 ++-- .../test_version_control_handlers.py | 2 +- 48 files changed, 130 insertions(+), 107 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/application_settings.py b/services/web/server/src/simcore_service_webserver/application_settings.py index a02b35910cc..dccfe7a019a 100644 --- a/services/web/server/src/simcore_service_webserver/application_settings.py +++ b/services/web/server/src/simcore_service_webserver/application_settings.py @@ -97,7 +97,9 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings): SIMCORE_VCS_RELEASE_URL: AnyHttpUrl | None = Field( default=None, description="URL to release notes", - examples=["https://github.com/ITISFoundation/osparc-simcore/releases/tag/staging_ResistanceIsFutile10"], + examples=[ + "https://github.com/ITISFoundation/osparc-simcore/releases/tag/staging_ResistanceIsFutile10" + ], ) SWARM_STACK_NAME: str | None = Field( @@ -273,9 +275,7 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings): def build_vcs_release_url_if_unset(cls, v): release_url = v.SIMCORE_VCS_RELEASE_URL - if release_url is None and ( - vsc_release_tag := v.SIMCORE_VCS_RELEASE_TAG - ): + if release_url is None and (vsc_release_tag := v.SIMCORE_VCS_RELEASE_TAG): if vsc_release_tag == "latest": release_url = ( "https://github.com/ITISFoundation/osparc-simcore/commits/master/" @@ -378,7 +378,7 @@ def _export_by_alias(self, **kwargs) -> dict[str, Any]: def config_alias_generator(s): return s.lower() - data: dict[str, Any] = self.dict(**kwargs) + data: dict[str, Any] = self.model_dump(**kwargs) current_keys = list(data.keys()) for key in current_keys: diff --git a/services/web/server/src/simcore_service_webserver/catalog/_api.py b/services/web/server/src/simcore_service_webserver/catalog/_api.py index ebd04b788cf..3b02e74f679 100644 --- a/services/web/server/src/simcore_service_webserver/catalog/_api.py +++ b/services/web/server/src/simcore_service_webserver/catalog/_api.py @@ -284,8 +284,8 @@ async def get_compatible_inputs_given_source_output( from_service_key, from_service_version, from_output_key, ctx ) - from_output: ServiceOutput = ServiceOutput.construct( - **service_output.dict(include=ServiceOutput.__fields__.keys()) + from_output: ServiceOutput = ServiceOutput.model_construct( + **service_output.model_dump(include=ServiceOutput.model_fields.keys()) ) # N inputs @@ -293,8 +293,8 @@ async def get_compatible_inputs_given_source_output( def iter_service_inputs() -> Iterator[tuple[ServiceInputKey, ServiceInput]]: for service_input in service_inputs: - yield service_input.key_id, ServiceInput.construct( - **service_input.dict(include=ServiceInput.__fields__.keys()) + yield service_input.key_id, ServiceInput.model_construct( + **service_input.model_dump(include=ServiceInput.model_fields.keys()) ) # check @@ -352,16 +352,16 @@ async def get_compatible_outputs_given_target_input( def iter_service_outputs() -> Iterator[tuple[ServiceOutputKey, ServiceOutput]]: for service_output in service_outputs: - yield service_output.key_id, ServiceOutput.construct( - **service_output.dict(include=ServiceOutput.__fields__.keys()) + yield service_output.key_id, ServiceOutput.model_construct( + **service_output.model_dump(include=ServiceOutput.model_fields.keys()) ) # 1 input service_input = await get_service_input( to_service_key, to_service_version, to_input_key, ctx ) - to_input: ServiceInput = ServiceInput.construct( - **service_input.dict(include=ServiceInput.__fields__.keys()) + to_input: ServiceInput = ServiceInput.model_construct( + **service_input.model_dump(include=ServiceInput.model_fields.keys()) ) # check diff --git a/services/web/server/src/simcore_service_webserver/catalog/_handlers.py b/services/web/server/src/simcore_service_webserver/catalog/_handlers.py index 6c697e45cbf..61a04130511 100644 --- a/services/web/server/src/simcore_service_webserver/catalog/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/catalog/_handlers.py @@ -160,7 +160,7 @@ async def update_service(request: Request): product_name=request_ctx.product_name, service_key=path_params.service_key, service_version=path_params.service_version, - update_data=update.dict(exclude_unset=True), + update_data=update.model_dump(exclude_unset=True), unit_registry=request_ctx.unit_registry, ) @@ -182,7 +182,7 @@ async def list_service_inputs(request: Request): path_params.service_key, path_params.service_version, ctx ) - data = [m.dict(**RESPONSE_MODEL_POLICY) for m in response_model] + data = [m.model_dump(**RESPONSE_MODEL_POLICY) for m in response_model] return await asyncio.get_event_loop().run_in_executor( None, envelope_json_response, data ) @@ -210,7 +210,7 @@ async def get_service_input(request: Request): ctx, ) - data = response_model.dict(**RESPONSE_MODEL_POLICY) + data = response_model.model_dump(**RESPONSE_MODEL_POLICY) return await asyncio.get_event_loop().run_in_executor( None, envelope_json_response, data ) @@ -265,7 +265,7 @@ async def list_service_outputs(request: Request): path_params.service_key, path_params.service_version, ctx ) - data = [m.dict(**RESPONSE_MODEL_POLICY) for m in response_model] + data = [m.model_dump(**RESPONSE_MODEL_POLICY) for m in response_model] return await asyncio.get_event_loop().run_in_executor( None, envelope_json_response, data ) @@ -293,7 +293,7 @@ async def get_service_output(request: Request): ctx, ) - data = response_model.dict(**RESPONSE_MODEL_POLICY) + data = response_model.model_dump(**RESPONSE_MODEL_POLICY) return await asyncio.get_event_loop().run_in_executor( None, envelope_json_response, data ) diff --git a/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py b/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py index 6a1a925ffff..99b3b2c28cf 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py @@ -254,7 +254,7 @@ async def get_computation(request: web.Request) -> web.Response: for c in list_computation_tasks ) return web.json_response( - data={"data": list_computation_tasks[0].dict(by_alias=True)}, + data={"data": list_computation_tasks[0].model_dump(by_alias=True)}, dumps=json_dumps, ) except DirectorServiceError as exc: diff --git a/services/web/server/src/simcore_service_webserver/email/_handlers.py b/services/web/server/src/simcore_service_webserver/email/_handlers.py index 829f58e4a0b..e052bb1e24e 100644 --- a/services/web/server/src/simcore_service_webserver/email/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/email/_handlers.py @@ -100,7 +100,7 @@ async def test_email(request: web.Request): return envelope_json_response( EmailTestPassed( - fixtures=body.dict(), + fixtures=body.model_dump(), info={ "email-server": info, "email-headers": message.items(), diff --git a/services/web/server/src/simcore_service_webserver/groups/_handlers.py b/services/web/server/src/simcore_service_webserver/groups/_handlers.py index b817533c0fd..c80e3cc3231 100644 --- a/services/web/server/src/simcore_service_webserver/groups/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/groups/_handlers.py @@ -374,7 +374,7 @@ async def add_scicrunch_resource(request: web.Request): # insert new or if exists, then update await repo.upsert(resource) - return envelope_json_response(resource.dict()) + return envelope_json_response(resource.model_dump()) @routes.get( diff --git a/services/web/server/src/simcore_service_webserver/groups/api.py b/services/web/server/src/simcore_service_webserver/groups/api.py index e0a837f8399..ec0a615874e 100644 --- a/services/web/server/src/simcore_service_webserver/groups/api.py +++ b/services/web/server/src/simcore_service_webserver/groups/api.py @@ -32,7 +32,7 @@ async def list_all_user_groups(app: web.Application, user_id: UserID) -> list[Gr async with get_database_engine(app).acquire() as conn: groups_db = await _db.get_all_user_groups(conn, user_id=user_id) - return [Group.construct(**group.dict()) for group in groups_db] + return [Group.construct(**group.model_dump()) for group in groups_db] async def get_user_group( @@ -199,5 +199,5 @@ async def get_group_from_gid(app: web.Application, gid: GroupID) -> Group | None group_db = await _db.get_group_from_gid(conn, gid=gid) if group_db: - return Group.construct(**group_db.dict()) + return Group.construct(**group_db.model_dump()) return None diff --git a/services/web/server/src/simcore_service_webserver/login/plugin.py b/services/web/server/src/simcore_service_webserver/login/plugin.py index 174bcd55f4a..ef0c77c2f18 100644 --- a/services/web/server/src/simcore_service_webserver/login/plugin.py +++ b/services/web/server/src/simcore_service_webserver/login/plugin.py @@ -68,7 +68,7 @@ def setup_login_storage(app: web.Application): def _setup_login_options(app: web.Application): settings: SMTPSettings = get_email_plugin_settings(app) - cfg = settings.dict() + cfg = settings.model_dump() if INDEX_RESOURCE_NAME in app.router: cfg["LOGIN_REDIRECT"] = f"{app.router[INDEX_RESOURCE_NAME].url_for()}" diff --git a/services/web/server/src/simcore_service_webserver/login/settings.py b/services/web/server/src/simcore_service_webserver/login/settings.py index 307e5424cff..8efaf2440be 100644 --- a/services/web/server/src/simcore_service_webserver/login/settings.py +++ b/services/web/server/src/simcore_service_webserver/login/settings.py @@ -94,7 +94,10 @@ def create_from_composition( """ For the LoginSettings, product-specific settings override app-specifics settings """ - composed_settings = {**app_login_settings.dict(), **product_login_settings} + composed_settings = { + **app_login_settings.model_dump(), + **product_login_settings, + } if "two_factor_enabled" in composed_settings: # legacy safe diff --git a/services/web/server/src/simcore_service_webserver/meta_modeling/_iterations.py b/services/web/server/src/simcore_service_webserver/meta_modeling/_iterations.py index 1663d7ac96d..51d2a80daed 100644 --- a/services/web/server/src/simcore_service_webserver/meta_modeling/_iterations.py +++ b/services/web/server/src/simcore_service_webserver/meta_modeling/_iterations.py @@ -280,7 +280,7 @@ async def get_or_create_runnable_projects( project["workbench"].update( { # converts model in dict patching first thumbnail - nid: n.copy(update={"thumbnail": n.thumbnail or ""}).dict( + nid: n.copy(update={"thumbnail": n.thumbnail or ""}).model_dump( by_alias=True, exclude_unset=True ) for nid, n in updated_nodes.items() diff --git a/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_exclusive_queue_consumers.py b/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_exclusive_queue_consumers.py index 449f3ecebec..f4178305384 100644 --- a/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_exclusive_queue_consumers.py +++ b/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_exclusive_queue_consumers.py @@ -103,7 +103,7 @@ async def _log_message_parser(app: web.Application, data: bytes) -> bool: rabbit_message.user_id, message=SocketMessageDict( event_type=SOCKET_IO_LOG_EVENT, - data=rabbit_message.dict(exclude={"user_id", "channel_name"}), + data=rabbit_message.model_dump(exclude={"user_id", "channel_name"}), ), ignore_queue=True, ) diff --git a/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_nonexclusive_queue_consumers.py b/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_nonexclusive_queue_consumers.py index 0a8e04b5e7f..cd47a062f2e 100644 --- a/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_nonexclusive_queue_consumers.py +++ b/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_nonexclusive_queue_consumers.py @@ -28,7 +28,7 @@ async def _instrumentation_message_parser(app: web.Application, data: bytes) -> service_started( app, **{ - key: rabbit_message.dict()[key] + key: rabbit_message.model_dump()[key] for key in MONITOR_SERVICE_STARTED_LABELS }, ) @@ -36,7 +36,7 @@ async def _instrumentation_message_parser(app: web.Application, data: bytes) -> service_stopped( app, **{ - key: rabbit_message.dict()[key] + key: rabbit_message.model_dump()[key] for key in MONITOR_SERVICE_STOPPED_LABELS }, ) diff --git a/services/web/server/src/simcore_service_webserver/products/_invitations_handlers.py b/services/web/server/src/simcore_service_webserver/products/_invitations_handlers.py index 4f530d3b566..bbb340b5fa6 100644 --- a/services/web/server/src/simcore_service_webserver/products/_invitations_handlers.py +++ b/services/web/server/src/simcore_service_webserver/products/_invitations_handlers.py @@ -67,4 +67,4 @@ async def generate_invitation(request: web.Request): created=generated.created, invitation_link=f"{invitation_link}", # type: ignore[arg-type] ) - return envelope_json_response(invitation.dict(exclude_none=True)) + return envelope_json_response(invitation.model_dump(exclude_none=True)) diff --git a/services/web/server/src/simcore_service_webserver/projects/_comments_api.py b/services/web/server/src/simcore_service_webserver/projects/_comments_api.py index a3626d099bb..55cfedac30c 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_comments_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/_comments_api.py @@ -43,7 +43,8 @@ async def list_project_comments( ProjectsCommentsDB ] = await db.list_project_comments(project_uuid, offset, limit) projects_comments_api_model = [ - ProjectsCommentsAPI(**comment.dict()) for comment in projects_comments_db_model + ProjectsCommentsAPI(**comment.model_dump()) + for comment in projects_comments_db_model ] return projects_comments_api_model @@ -70,7 +71,7 @@ async def update_project_comment( comment_id, project_uuid, contents ) projects_comments_api_model = ProjectsCommentsAPI( - **projects_comments_db_model.dict() + **projects_comments_db_model.model_dump() ) return projects_comments_api_model @@ -90,6 +91,6 @@ async def get_project_comment( comment_id ) projects_comments_api_model = ProjectsCommentsAPI( - **projects_comments_db_model.dict() + **projects_comments_db_model.model_dump() ) return projects_comments_api_model diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py b/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py index 9b7cde0fba2..e70c1f75516 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py @@ -139,7 +139,7 @@ def _mapped_node_id(node: ProjectNode) -> NodeID: node_id=_mapped_node_id(node), **{ k: v - for k, v in node.dict().items() + for k, v in node.model_dump().items() if k in ProjectNodeCreate.get_field_names(exclude={"node_id"}) }, ) diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py b/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py index a589a79bba8..4c09f81efe1 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py @@ -53,7 +53,7 @@ async def _append_fields( # replace project access rights (if project is in workspace) if workspace_access_rights: project["accessRights"] = { - gid: access.dict() for gid, access in workspace_access_rights.items() + gid: access.model_dump() for gid, access in workspace_access_rights.items() } # validate diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py index bc3fc0d684f..82d6c4c2cf4 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py @@ -154,7 +154,7 @@ async def create_project(request: web.Request): ProjectCreateNew | ProjectCopyOverride | EmptyModel, request # type: ignore[arg-type] # from pydantic v2 --> https://github.com/pydantic/pydantic/discussions/4950 ) predefined_project = ( - project_create.dict( + project_create.model_dump( exclude_unset=True, by_alias=True, exclude_none=True, diff --git a/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py index 71c2efee6c2..93b77eca3f1 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py @@ -623,7 +623,7 @@ async def get_project_services_access_for_gid( inaccessible_services=project_inaccessible_services, ) - return envelope_json_response(project_group_access.dict(exclude_none=True)) + return envelope_json_response(project_group_access.model_dump(exclude_none=True)) class _ProjectNodePreview(BaseModel): diff --git a/services/web/server/src/simcore_service_webserver/projects/_ports_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_ports_handlers.py index 3a50b5a40c6..b9c5750a6f9 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_ports_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_ports_handlers.py @@ -148,7 +148,7 @@ async def update_project_inputs(request: web.Request) -> web.Response: raise web.HTTPBadRequest(reason=f"Invalid input key [{node_id}]") workbench[node_id].outputs = {KeyIDStr("out_1"): input_update.value} - partial_workbench_data[node_id] = workbench[node_id].dict( + partial_workbench_data[node_id] = workbench[node_id].model_dump( include={"outputs"}, exclude_unset=True ) diff --git a/services/web/server/src/simcore_service_webserver/projects/_states_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_states_handlers.py index 07ba932d64b..b2f5e46381c 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_states_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_states_handlers.py @@ -245,4 +245,4 @@ async def get_project_state(request: web.Request) -> web.Response: include_state=True, ) project_state = ProjectState(**validated_project["state"]) - return envelope_json_response(project_state.dict()) + return envelope_json_response(project_state.model_dump()) diff --git a/services/web/server/src/simcore_service_webserver/projects/_tags_api.py b/services/web/server/src/simcore_service_webserver/projects/_tags_api.py index ba4be3c5fb4..c8e0937dbdb 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_tags_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/_tags_api.py @@ -45,7 +45,8 @@ async def add_tag( ) ) project["accessRights"] = { - gid: access.dict() for gid, access in workspace_db.access_rights.items() + gid: access.model_dump() + for gid, access in workspace_db.access_rights.items() } return project @@ -79,7 +80,8 @@ async def remove_tag( ) ) project["accessRights"] = { - gid: access.dict() for gid, access in workspace_db.access_rights.items() + gid: access.model_dump() + for gid, access in workspace_db.access_rights.items() } return project diff --git a/services/web/server/src/simcore_service_webserver/projects/_wallets_api.py b/services/web/server/src/simcore_service_webserver/projects/_wallets_api.py index 6c5a27bed9e..85bc9cf43a3 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_wallets_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/_wallets_api.py @@ -12,7 +12,9 @@ async def get_project_wallet(app, project_id: ProjectID): db: ProjectDBAPI = ProjectDBAPI.get_from_app_context(app) wallet_db: WalletDB | None = await db.get_project_wallet(project_uuid=project_id) - wallet: WalletGet | None = WalletGet(**wallet_db.dict()) if wallet_db else None + wallet: WalletGet | None = ( + WalletGet(**wallet_db.model_dump()) if wallet_db else None + ) return wallet diff --git a/services/web/server/src/simcore_service_webserver/projects/projects_api.py b/services/web/server/src/simcore_service_webserver/projects/projects_api.py index 2cb05192640..46ea5b6a1f6 100644 --- a/services/web/server/src/simcore_service_webserver/projects/projects_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/projects_api.py @@ -216,7 +216,8 @@ async def get_project_for_user( ) ) project["accessRights"] = { - gid: access.dict() for gid, access in workspace_db.access_rights.items() + gid: access.model_dump() + for gid, access in workspace_db.access_rights.items() } Project.model_validate(project) # NOTE: only validates diff --git a/services/web/server/src/simcore_service_webserver/scicrunch/_resolver.py b/services/web/server/src/simcore_service_webserver/scicrunch/_resolver.py index 07d62498230..2b02576e980 100644 --- a/services/web/server/src/simcore_service_webserver/scicrunch/_resolver.py +++ b/services/web/server/src/simcore_service_webserver/scicrunch/_resolver.py @@ -41,7 +41,7 @@ class HitSource(BaseModel): def flatten_dict(self) -> dict[str, Any]: """Used as an output""" - return {**self.item.dict(), **self.rrid.dict()} + return {**self.item.model_dump(), **self.rrid.model_dump()} class HitDetail(BaseModel): diff --git a/services/web/server/src/simcore_service_webserver/scicrunch/db.py b/services/web/server/src/simcore_service_webserver/scicrunch/db.py index 476e320f73d..94f06853d29 100644 --- a/services/web/server/src/simcore_service_webserver/scicrunch/db.py +++ b/services/web/server/src/simcore_service_webserver/scicrunch/db.py @@ -53,12 +53,12 @@ async def get(self, rrid: str) -> ResearchResourceAtdB | None: async def get_resource(self, rrid: str) -> ResearchResource | None: resource: ResearchResourceAtdB | None = await self.get(rrid) if resource: - return ResearchResource(**resource.dict()) + return ResearchResource(**resource.model_dump()) return resource async def upsert(self, resource: ResearchResource): async with self._engine.acquire() as conn: - values = resource.dict(exclude_unset=True) + values = resource.model_dump(exclude_unset=True) stmt = ( sa_pg_insert(scicrunch_resources) diff --git a/services/web/server/src/simcore_service_webserver/statics/settings.py b/services/web/server/src/simcore_service_webserver/statics/settings.py index 46471b2f235..7a9c13f4e33 100644 --- a/services/web/server/src/simcore_service_webserver/statics/settings.py +++ b/services/web/server/src/simcore_service_webserver/statics/settings.py @@ -93,7 +93,7 @@ class FrontEndAppSettings(BaseCustomSettings): # NOTE: for the moment, None but left here for future use def to_statics(self) -> dict[str, Any]: - data = self.dict( + data = self.model_dump( exclude_none=True, by_alias=True, ) diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_rest_handlers.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_rest_handlers.py index 98616e47f68..b003ad55963 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_rest_handlers.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_rest_handlers.py @@ -8,7 +8,14 @@ from aiohttp.web import Request from models_library.services import ServiceKey from models_library.services_types import ServiceVersion -from pydantic import TypeAdapter, field_validator, ConfigDict, BaseModel, Field, ValidationError +from pydantic import ( + BaseModel, + ConfigDict, + Field, + TypeAdapter, + ValidationError, + field_validator, +) from pydantic.networks import HttpUrl from .._meta import API_VTAG @@ -45,7 +52,7 @@ def _compose_service_only_dispatcher_prefix_url( params = ViewerQueryParams( viewer_key=ServiceKey(service_key), viewer_version=ServiceVersion(service_version), - ).dict(exclude_none=True, exclude_unset=True) + ).model_dump(exclude_none=True, exclude_unset=True) absolute_url = request.url.join( request.app.router["get_redirection_to_viewer"].url_for().with_query(**params) ) @@ -130,6 +137,7 @@ def remove_dot_prefix_from_extension(cls, v): if v: return [ext.removeprefix(".") for ext in v] return v + model_config = ConfigDict( json_schema_extra={ "example": { @@ -176,7 +184,7 @@ async def list_viewers(request: Request): file_type: str | None = request.query.get("file_type", None) viewers = [ - Viewer.create(request, viewer).dict() + Viewer.create(request, viewer).model_dump() for viewer in await list_viewers_info(request.app, file_type=file_type) ] return envelope_json_response(viewers) @@ -188,7 +196,7 @@ async def list_default_viewers(request: Request): file_type: str | None = request.query.get("file_type", None) viewers = [ - Viewer.create(request, viewer).dict() + Viewer.create(request, viewer).model_dump() for viewer in await list_viewers_info( request.app, file_type=file_type, only_default=True ) diff --git a/services/web/server/src/simcore_service_webserver/tags/_api.py b/services/web/server/src/simcore_service_webserver/tags/_api.py index 6f3a74853e7..dacedc603f7 100644 --- a/services/web/server/src/simcore_service_webserver/tags/_api.py +++ b/services/web/server/src/simcore_service_webserver/tags/_api.py @@ -22,7 +22,7 @@ async def create_tag( read=True, write=True, delete=True, - **new_tag.dict(exclude_unset=True), + **new_tag.model_dump(exclude_unset=True), ) return TagGet.from_db(tag) @@ -46,7 +46,7 @@ async def update_tag( tag = await repo.update( user_id=user_id, tag_id=tag_id, - **tag_updates.dict(exclude_unset=True), + **tag_updates.model_dump(exclude_unset=True), ) return TagGet.from_db(tag) diff --git a/services/web/server/src/simcore_service_webserver/users/_api.py b/services/web/server/src/simcore_service_webserver/users/_api.py index 46ffb99e84a..458366367f5 100644 --- a/services/web/server/src/simcore_service_webserver/users/_api.py +++ b/services/web/server/src/simcore_service_webserver/users/_api.py @@ -116,7 +116,7 @@ async def pre_register_user( if found: raise AlreadyPreRegisteredError(num_found=len(found), email=profile.email) - details = profile.dict( + details = profile.model_dump( include={ "first_name", "last_name", diff --git a/services/web/server/src/simcore_service_webserver/users/_handlers.py b/services/web/server/src/simcore_service_webserver/users/_handlers.py index 5fb02801702..4d69e9ffaab 100644 --- a/services/web/server/src/simcore_service_webserver/users/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/users/_handlers.py @@ -117,7 +117,7 @@ async def search_users(request: web.Request) -> web.Response: ) return envelope_json_response( - [_.dict(**_RESPONSE_MODEL_MINIMAL_POLICY) for _ in found] + [_.model_dump(**_RESPONSE_MODEL_MINIMAL_POLICY) for _ in found] ) @@ -134,7 +134,7 @@ async def pre_register_user(request: web.Request) -> web.Response: request.app, profile=pre_user_profile, creator_user_id=req_ctx.user_id ) return envelope_json_response( - user_profile.dict(**_RESPONSE_MODEL_MINIMAL_POLICY) + user_profile.model_dump(**_RESPONSE_MODEL_MINIMAL_POLICY) ) except AlreadyPreRegisteredError as err: raise web.HTTPConflict(reason=f"{err}") from err diff --git a/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py b/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py index dc78270bea3..480f0348fe5 100644 --- a/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py +++ b/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py @@ -130,7 +130,7 @@ async def list_user_permissions(request: web.Request) -> web.Response: ) return envelope_json_response( [ - PermissionGet.construct(_fields_set=p.__fields_set__, **p.dict()) + PermissionGet.construct(_fields_set=p.__fields_set__, **p.model_dump()) for p in list_permissions ] ) diff --git a/services/web/server/src/simcore_service_webserver/version_control/_handlers.py b/services/web/server/src/simcore_service_webserver/version_control/_handlers.py index 315968009f3..2c16489067e 100644 --- a/services/web/server/src/simcore_service_webserver/version_control/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/version_control/_handlers.py @@ -121,7 +121,7 @@ async def _create_checkpoint_handler(request: web.Request): checkpoint: Checkpoint = await create_checkpoint( vc_repo, project_uuid=path_params.project_uuid, - **_body.dict(include={"tag", "message"}), + **_body.model_dump(include={"tag", "message"}), ) data = CheckpointApiModel.model_validate( @@ -131,7 +131,7 @@ async def _create_checkpoint_handler(request: web.Request): project_uuid=path_params.project_uuid, ref_id=checkpoint.id, ), - **checkpoint.dict(), + **checkpoint.model_dump(), } ) return envelope_json_response(data, status_cls=web.HTTPCreated) @@ -170,7 +170,7 @@ async def _list_checkpoints_handler(request: web.Request): project_uuid=path_params.project_uuid, ref_id=checkpoint.id, ), - **checkpoint.dict(), + **checkpoint.model_dump(), } ) for checkpoint in checkpoints @@ -218,7 +218,7 @@ async def _get_checkpoint_handler(request: web.Request): project_uuid=path_params.project_uuid, ref_id=checkpoint.id, ), - **checkpoint.dict(**RESPONSE_MODEL_POLICY), + **checkpoint.model_dump(**RESPONSE_MODEL_POLICY), } ) return envelope_json_response(data) @@ -242,7 +242,7 @@ async def _update_checkpoint_annotations_handler(request: web.Request): vc_repo, project_uuid=path_params.project_uuid, ref_id=path_params.ref_id, - **update.dict(include={"tag", "message"}, exclude_none=True), + **update.model_dump(include={"tag", "message"}, exclude_none=True), ) data = CheckpointApiModel.model_validate( @@ -252,7 +252,7 @@ async def _update_checkpoint_annotations_handler(request: web.Request): project_uuid=path_params.project_uuid, ref_id=checkpoint.id, ), - **checkpoint.dict(**RESPONSE_MODEL_POLICY), + **checkpoint.model_dump(**RESPONSE_MODEL_POLICY), } ) return envelope_json_response(data) @@ -284,7 +284,7 @@ async def _checkout_handler(request: web.Request): project_uuid=path_params.project_uuid, ref_id=checkpoint.id, ), - **checkpoint.dict(**RESPONSE_MODEL_POLICY), + **checkpoint.model_dump(**RESPONSE_MODEL_POLICY), } ) return envelope_json_response(data) @@ -328,7 +328,7 @@ async def _view_project_workbench_handler(request: web.Request): project_uuid=path_params.project_uuid, ref_id=checkpoint.id, ), - **view.dict(**RESPONSE_MODEL_POLICY), + **view.model_dump(**RESPONSE_MODEL_POLICY), } ) diff --git a/services/web/server/src/simcore_service_webserver/version_control/vc_changes.py b/services/web/server/src/simcore_service_webserver/version_control/vc_changes.py index 50d2cae1e76..cc3559c118b 100644 --- a/services/web/server/src/simcore_service_webserver/version_control/vc_changes.py +++ b/services/web/server/src/simcore_service_webserver/version_control/vc_changes.py @@ -31,7 +31,7 @@ def compute_workbench_checksum(workbench: dict[str, Any]) -> SHA1Str: checksum = compute_sha1_on_small_dataset( { - k: node.dict( + k: node.model_dump( exclude_unset=True, exclude_defaults=True, exclude_none=True, diff --git a/services/web/server/src/simcore_service_webserver/wallets/_api.py b/services/web/server/src/simcore_service_webserver/wallets/_api.py index dff96a2b93d..c2af4074378 100644 --- a/services/web/server/src/simcore_service_webserver/wallets/_api.py +++ b/services/web/server/src/simcore_service_webserver/wallets/_api.py @@ -122,7 +122,9 @@ async def get_user_default_wallet_with_available_credits( ) if user_default_wallet_preference is None: raise UserDefaultWalletNotFoundError(uid=user_id) - default_wallet_id = TypeAdapter(WalletID).validate_python(user_default_wallet_preference.value) + default_wallet_id = TypeAdapter(WalletID).validate_python( + user_default_wallet_preference.value + ) return await get_wallet_with_available_credits_by_user_and_wallet( app, user_id=user_id, wallet_id=default_wallet_id, product_name=product_name ) @@ -178,7 +180,7 @@ async def update_wallet( user_id=user_id, wallet_id=wallet_id, product_name=product_name, - user_acces_rights_on_wallet=wallet.dict( + user_acces_rights_on_wallet=wallet.model_dump( include={"read", "write", "delete"} ), ) @@ -212,7 +214,7 @@ async def delete_wallet( user_id=user_id, wallet_id=wallet_id, product_name=product_name, - user_acces_rights_on_wallet=wallet.dict( + user_acces_rights_on_wallet=wallet.model_dump( include={"read", "write", "delete"} ), ) @@ -235,7 +237,7 @@ async def get_wallet_by_user( user_id=user_id, wallet_id=wallet_id, product_name=product_name, - user_acces_rights_on_wallet=wallet.dict( + user_acces_rights_on_wallet=wallet.model_dump( include={"read", "write", "delete"} ), ) diff --git a/services/web/server/src/simcore_service_webserver/wallets/_groups_api.py b/services/web/server/src/simcore_service_webserver/wallets/_groups_api.py index 8d2703f3a5f..0990a91b93a 100644 --- a/services/web/server/src/simcore_service_webserver/wallets/_groups_api.py +++ b/services/web/server/src/simcore_service_webserver/wallets/_groups_api.py @@ -45,7 +45,7 @@ async def create_wallet_group( user_id=user_id, wallet_id=wallet_id, product_name=product_name, - user_acces_rights_on_wallet=wallet.dict( + user_acces_rights_on_wallet=wallet.model_dump( include={"read", "write", "delete"} ), ) @@ -58,7 +58,7 @@ async def create_wallet_group( write=write, delete=delete, ) - wallet_group_api: WalletGroupGet = WalletGroupGet(**wallet_group_db.dict()) + wallet_group_api: WalletGroupGet = WalletGroupGet(**wallet_group_db.model_dump()) return wallet_group_api @@ -79,7 +79,7 @@ async def list_wallet_groups_by_user_and_wallet( user_id=user_id, wallet_id=wallet_id, product_name=product_name, - user_acces_rights_on_wallet=wallet.dict( + user_acces_rights_on_wallet=wallet.model_dump( include={"read", "write", "delete"} ), ) @@ -140,7 +140,7 @@ async def update_wallet_group( user_id=user_id, wallet_id=wallet_id, product_name=product_name, - user_acces_rights_on_wallet=wallet.dict( + user_acces_rights_on_wallet=wallet.model_dump( include={"read", "write", "delete"} ), ) @@ -154,7 +154,7 @@ async def update_wallet_group( delete=delete, ) - wallet_api: WalletGroupGet = WalletGroupGet(**wallet_group_db.dict()) + wallet_api: WalletGroupGet = WalletGroupGet(**wallet_group_db.model_dump()) return wallet_api diff --git a/services/web/server/src/simcore_service_webserver/workspaces/_groups_api.py b/services/web/server/src/simcore_service_webserver/workspaces/_groups_api.py index d69d33f6974..f3a0ab3f0ff 100644 --- a/services/web/server/src/simcore_service_webserver/workspaces/_groups_api.py +++ b/services/web/server/src/simcore_service_webserver/workspaces/_groups_api.py @@ -56,7 +56,7 @@ async def create_workspace_group( ) ) workspace_group_api: WorkspaceGroupGet = WorkspaceGroupGet( - **workspace_group_db.dict() + **workspace_group_db.model_dump() ) return workspace_group_api @@ -147,7 +147,9 @@ async def update_workspace_group( ) ) - workspace_api: WorkspaceGroupGet = WorkspaceGroupGet(**workspace_group_db.dict()) + workspace_api: WorkspaceGroupGet = WorkspaceGroupGet( + **workspace_group_db.model_dump() + ) return workspace_api diff --git a/services/web/server/tests/integration/02/notifications/test_rabbitmq_consumers.py b/services/web/server/tests/integration/02/notifications/test_rabbitmq_consumers.py index c15921c7d5d..f1d4aa62187 100644 --- a/services/web/server/tests/integration/02/notifications/test_rabbitmq_consumers.py +++ b/services/web/server/tests/integration/02/notifications/test_rabbitmq_consumers.py @@ -294,7 +294,7 @@ async def test_log_workflow_only_receives_messages_if_subscribed( log_message.user_id, message={ "event_type": SOCKET_IO_LOG_EVENT, - "data": log_message.dict(exclude={"user_id", "channel_name"}), + "data": log_message.model_dump(exclude={"user_id", "channel_name"}), }, ignore_queue=True, ), @@ -493,7 +493,7 @@ async def test_instrumentation_workflow( mocked_metrics_method, mock.call( client.app, - **rabbit_message.dict(include=set(included_labels)), + **rabbit_message.model_dump(include=set(included_labels)), ), ) diff --git a/services/web/server/tests/integration/02/scicrunch/test_scicrunch__rest.py b/services/web/server/tests/integration/02/scicrunch/test_scicrunch__rest.py index 39e507b2fda..a6671355826 100644 --- a/services/web/server/tests/integration/02/scicrunch/test_scicrunch__rest.py +++ b/services/web/server/tests/integration/02/scicrunch/test_scicrunch__rest.py @@ -159,7 +159,7 @@ async def test_scicrunch_service_autocomplete_by_name(settings: SciCrunchSetting }, {"rid": "SCR_014398", "original_id": "SCR_014398", "name": "GNU Octave"}, ] - ).dict()["__root__"] + ).model_dump()["__root__"] async with ClientSession() as client: @@ -167,6 +167,6 @@ async def test_scicrunch_service_autocomplete_by_name(settings: SciCrunchSetting resource_hits = await autocomplete_by_name("octave", client, settings) - hits = resource_hits.dict()["__root__"] + hits = resource_hits.model_dump()["__root__"] assert expected == hits, f"for {guess_name}" diff --git a/services/web/server/tests/unit/isolated/test_login_settings.py b/services/web/server/tests/unit/isolated/test_login_settings.py index b6872fce92d..ebb7ad92337 100644 --- a/services/web/server/tests/unit/isolated/test_login_settings.py +++ b/services/web/server/tests/unit/isolated/test_login_settings.py @@ -121,12 +121,12 @@ def test_smtp_settings(mock_env_devel_environment: dict[str, Any]): settings = SMTPSettings.create_from_envs() - cfg = settings.dict(exclude_unset=True) + cfg = settings.model_dump(exclude_unset=True) for env_name in cfg: assert env_name in os.environ - cfg = settings.dict() + cfg = settings.model_dump() config = LoginOptions(**cfg) print(config.json(indent=1)) diff --git a/services/web/server/tests/unit/isolated/test_users_models.py b/services/web/server/tests/unit/isolated/test_users_models.py index 8f3b45cd3df..fd0bbabbd90 100644 --- a/services/web/server/tests/unit/isolated/test_users_models.py +++ b/services/web/server/tests/unit/isolated/test_users_models.py @@ -26,12 +26,12 @@ def test_user_models_examples( assert model_instance, f"Failed with {name}" model_enveloped = Envelope[model_cls].from_data( - model_instance.dict(by_alias=True) + model_instance.model_dump(by_alias=True) ) model_array_enveloped = Envelope[list[model_cls]].from_data( [ - model_instance.dict(by_alias=True), - model_instance.dict(by_alias=True), + model_instance.model_dump(by_alias=True), + model_instance.model_dump(by_alias=True), ] ) @@ -52,7 +52,7 @@ def test_profile_get_expiration_date(faker: Faker): assert fake_expiration.date() == profile.expiration_date - body = jsonable_encoder(profile.dict(exclude_unset=True, by_alias=True)) + body = jsonable_encoder(profile.model_dump(exclude_unset=True, by_alias=True)) assert body["expirationDate"] == fake_expiration.date().isoformat() @@ -68,7 +68,7 @@ def test_auto_compute_gravatar(faker: Faker): ) envelope = Envelope[Any](data=profile) - data = envelope.dict(**RESPONSE_MODEL_POLICY)["data"] + data = envelope.model_dump(**RESPONSE_MODEL_POLICY)["data"] assert data["gravatar_id"] assert data["id"] == profile.id @@ -135,4 +135,4 @@ def test_parsing_output_of_get_user_profile(): } profile = ProfileGet.model_validate(result_from_db_query_and_composition) - assert "password" not in profile.dict(exclude_unset=True) + assert "password" not in profile.model_dump(exclude_unset=True) diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py b/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py index 9b61a1bb9fb..e089fe3e521 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py @@ -225,7 +225,7 @@ async def _assert_project_state_updated( jsonable_encoder( { "project_uuid": shared_project["uuid"], - "data": p_state.dict(by_alias=True, exclude_unset=True), + "data": p_state.model_dump(by_alias=True, exclude_unset=True), } ) ) diff --git a/services/web/server/tests/unit/with_dbs/03/invitations/conftest.py b/services/web/server/tests/unit/with_dbs/03/invitations/conftest.py index d318e17a96f..b2b5f01a8d2 100644 --- a/services/web/server/tests/unit/with_dbs/03/invitations/conftest.py +++ b/services/web/server/tests/unit/with_dbs/03/invitations/conftest.py @@ -121,7 +121,7 @@ def _extract(url, **kwargs): "invitation" ] # if nothing is encoded in fake_code, just return fake_osparc_invitation - body = fake_osparc_invitation.dict() + body = fake_osparc_invitation.model_dump() with suppress(Exception): decoded = json.loads(binascii.unhexlify(fake_code).decode()) body.update(decoded) diff --git a/services/web/server/tests/unit/with_dbs/03/invitations/test_login_handlers_registration_invitations.py b/services/web/server/tests/unit/with_dbs/03/invitations/test_login_handlers_registration_invitations.py index be73ad487c9..8c4daca29df 100644 --- a/services/web/server/tests/unit/with_dbs/03/invitations/test_login_handlers_registration_invitations.py +++ b/services/web/server/tests/unit/with_dbs/03/invitations/test_login_handlers_registration_invitations.py @@ -48,7 +48,7 @@ async def test_check_registration_invitation_when_not_required( response = await client.post( "/v0/auth/register/invitations:check", - json=InvitationCheck(invitation="*" * 100).dict(), + json=InvitationCheck(invitation="*" * 100).model_dump(), ) data, _ = await assert_status(response, status.HTTP_200_OK) @@ -70,7 +70,7 @@ async def test_check_registration_invitations_with_old_code( response = await client.post( "/v0/auth/register/invitations:check", - json=InvitationCheck(invitation="short-code").dict(), + json=InvitationCheck(invitation="short-code").model_dump(), ) data, _ = await assert_status(response, status.HTTP_200_OK) @@ -96,7 +96,7 @@ async def test_check_registration_invitation_and_get_email( response = await client.post( "/v0/auth/register/invitations:check", - json=InvitationCheck(invitation="*" * 105).dict(), + json=InvitationCheck(invitation="*" * 105).model_dump(), ) data, _ = await assert_status(response, status.HTTP_200_OK) diff --git a/services/web/server/tests/unit/with_dbs/03/invitations/test_products__invitations_handlers.py b/services/web/server/tests/unit/with_dbs/03/invitations/test_products__invitations_handlers.py index 749fddb1548..f816a8535b0 100644 --- a/services/web/server/tests/unit/with_dbs/03/invitations/test_products__invitations_handlers.py +++ b/services/web/server/tests/unit/with_dbs/03/invitations/test_products__invitations_handlers.py @@ -92,7 +92,7 @@ async def test_product_owner_generates_invitation( # request response = await client.post( "/v0/invitation:generate", - json=request_model.dict(exclude_none=True), + json=request_model.model_dump(exclude_none=True), ) # checks @@ -102,9 +102,9 @@ async def test_product_owner_generates_invitation( got = InvitationGenerated.model_validate(data) expected = { "issuer": logged_user["email"][:_MAX_LEN], - **request_model.dict(exclude_none=True), + **request_model.model_dump(exclude_none=True), } - assert got.dict(include=set(expected), by_alias=False) == expected + assert got.model_dump(include=set(expected), by_alias=False) == expected product_base_url = f"{client.make_url('/')}" assert got.invitation_link.startswith(product_base_url) @@ -150,7 +150,7 @@ async def test_pre_registration_and_invitation_workflow( guest=guest_email, trial_account_days=None, extra_credits_in_usd=10, - ).dict() + ).model_dump() # Search user -> nothing response = await client.get("/v0/users:search", params={"email": guest_email}) diff --git a/services/web/server/tests/unit/with_dbs/03/meta_modeling/test_meta_modeling_iterations.py b/services/web/server/tests/unit/with_dbs/03/meta_modeling/test_meta_modeling_iterations.py index b01c9029f3b..1b7d70a30ef 100644 --- a/services/web/server/tests/unit/with_dbs/03/meta_modeling/test_meta_modeling_iterations.py +++ b/services/web/server/tests/unit/with_dbs/03/meta_modeling/test_meta_modeling_iterations.py @@ -262,7 +262,7 @@ async def _mock_catalog_get(*args, **kwarg): response = await client.put( f"/v0/projects/{project_uuid}", - data=json_dumps(new_project.dict(**REQUEST_MODEL_POLICY)), + data=json_dumps(new_project.model_dump(**REQUEST_MODEL_POLICY)), ) assert response.status == status.HTTP_200_OK, await response.text() diff --git a/services/web/server/tests/unit/with_dbs/03/test_users.py b/services/web/server/tests/unit/with_dbs/03/test_users.py index d37cbd8f0e2..318681b30fa 100644 --- a/services/web/server/tests/unit/with_dbs/03/test_users.py +++ b/services/web/server/tests/unit/with_dbs/03/test_users.py @@ -84,7 +84,9 @@ async def test_get_profile( # check enveloped e = Envelope[ProfileGet].model_validate(await resp.json()) assert e.error == error - assert e.data.dict(**RESPONSE_MODEL_POLICY) == data if e.data else e.data == data + assert ( + e.data.model_dump(**RESPONSE_MODEL_POLICY) == data if e.data else e.data == data + ) if not error: profile = ProfileGet.model_validate(data) @@ -105,7 +107,7 @@ async def test_get_profile( assert profile.role == user_role.name assert profile.groups - got_profile_groups = profile.groups.dict(**RESPONSE_MODEL_POLICY) + got_profile_groups = profile.groups.model_dump(**RESPONSE_MODEL_POLICY) assert got_profile_groups["me"] == primary_group assert got_profile_groups["all"] == all_group @@ -294,7 +296,7 @@ async def test_search_and_pre_registration( "registered": True, "status": UserStatus.ACTIVE, } - assert got.dict(include=set(expected)) == expected + assert got.model_dump(include=set(expected)) == expected # NOT in `users` and ONLY `users_pre_registration_details` @@ -309,7 +311,7 @@ async def test_search_and_pre_registration( assert len(found) == 1 got = UserProfile(**found[0]) - assert got.dict(include={"registered", "status"}) == { + assert got.model_dump(include={"registered", "status"}) == { "registered": False, "status": None, } @@ -331,7 +333,7 @@ async def test_search_and_pre_registration( found, _ = await assert_status(resp, status.HTTP_200_OK) assert len(found) == 1 got = UserProfile(**found[0]) - assert got.dict(include={"registered", "status"}) == { + assert got.model_dump(include={"registered", "status"}) == { "registered": True, "status": new_user["status"].name, } diff --git a/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_core.py b/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_core.py index 5b660286cea..705b0458188 100644 --- a/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_core.py +++ b/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_core.py @@ -71,9 +71,9 @@ async def test_workflow( vc_repo, project_uuid, HEAD, message="updated message" ) - assert checkpoint2_updated.dict(exclude={"message"}) == checkpoint2.dict( + assert checkpoint2_updated.model_dump( exclude={"message"} - ) + ) == checkpoint2.model_dump(exclude={"message"}) # ------------------------------------- # checking out to v1 diff --git a/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_handlers.py b/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_handlers.py index 078ba287a5e..e7f2a44a0dd 100644 --- a/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_handlers.py +++ b/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_handlers.py @@ -142,7 +142,7 @@ async def test_workflow( data, _ = await assert_status(resp, status.HTTP_200_OK) assert ( data["workbench"] - == project.dict(exclude_none=True, exclude_unset=True)["workbench"] + == project.model_dump(exclude_none=True, exclude_unset=True)["workbench"] ) # do some changes in project From 6b9d7fae851a6ad7f6e57735d556ba7ca0405f9e Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 15:18:37 +0200 Subject: [PATCH 020/165] fix deprecated json() --- services/web/server/src/simcore_service_webserver/cli.py | 3 ++- .../server/src/simcore_service_webserver/email/_core.py | 2 +- .../src/simcore_service_webserver/email/_handlers.py | 2 +- .../folders/_folders_handlers.py | 2 +- .../src/simcore_service_webserver/login/_registration.py | 3 ++- .../simcore_service_webserver/meta_modeling/_handlers.py | 4 ++-- .../projects/_comments_handlers.py | 2 +- .../simcore_service_webserver/projects/_crud_handlers.py | 4 ++-- .../src/simcore_service_webserver/projects/_nodes_api.py | 2 +- .../simcore_service_webserver/projects/projects_api.py | 2 +- .../simcore_service_webserver/resource_usage/_client.py | 2 +- .../resource_usage/_service_runs_handlers.py | 4 ++-- .../studies_dispatcher/_projects.py | 8 ++++++-- .../users/_notifications_handlers.py | 4 ++-- .../version_control/_handlers.py | 4 ++-- services/web/server/tests/conftest.py | 2 +- .../isolated/notifications/test_rabbitmq_consumers.py | 4 ++-- .../tests/unit/isolated/test_application_settings.py | 2 +- .../unit/isolated/test_application_settings_utils.py | 2 +- .../web/server/tests/unit/isolated/test_login_settings.py | 2 +- .../web/server/tests/unit/isolated/test_security_api.py | 2 +- .../unit/with_dbs/01/clusters/test_clusters_handlers.py | 4 +++- .../tests/unit/with_dbs/01/studies_dispatcher/conftest.py | 2 +- .../test_studies_dispatcher_handlers.py | 4 +++- services/web/server/tests/unit/with_dbs/01/test_groups.py | 2 +- .../web/server/tests/unit/with_dbs/01/test_statics.py | 2 +- .../server/tests/unit/with_dbs/03/invitations/conftest.py | 2 +- .../server/tests/unit/with_dbs/03/login/test_login_2fa.py | 2 +- .../tests/unit/with_dbs/03/login/test_login_2fa_resend.py | 2 +- .../tests/unit/with_dbs/03/login/test_login_auth.py | 2 +- .../tests/unit/with_dbs/03/products/test_products_db.py | 4 ++-- .../tests/unit/with_dbs/03/resource_usage/conftest.py | 2 +- .../server/tests/unit/with_dbs/03/test__openapi_specs.py | 2 +- services/web/server/tests/unit/with_dbs/03/test_email.py | 2 +- .../web/server/tests/unit/with_dbs/03/test_socketio.py | 2 +- services/web/server/tests/unit/with_dbs/03/test_users.py | 4 ++-- .../tests/unit/with_dbs/03/test_users__notifications.py | 2 +- 37 files changed, 56 insertions(+), 46 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/cli.py b/services/web/server/src/simcore_service_webserver/cli.py index bbd9b327a9b..8dd1182b83f 100644 --- a/services/web/server/src/simcore_service_webserver/cli.py +++ b/services/web/server/src/simcore_service_webserver/cli.py @@ -67,7 +67,8 @@ async def app_factory() -> web.Application: assert app_settings.SC_BUILD_TARGET # nosec _logger.info( - "Application settings: %s", app_settings.json(indent=2, sort_keys=True) + "Application settings: %s", + app_settings.model_dump_json(indent=2, sort_keys=True), ) app, _ = _setup_app_from_settings(app_settings) diff --git a/services/web/server/src/simcore_service_webserver/email/_core.py b/services/web/server/src/simcore_service_webserver/email/_core.py index 0c2329cac54..39070a4ced7 100644 --- a/services/web/server/src/simcore_service_webserver/email/_core.py +++ b/services/web/server/src/simcore_service_webserver/email/_core.py @@ -36,7 +36,7 @@ async def _do_send_mail( WARNING: _do_send_mail is mocked so be careful when changing the signature or name !! """ - _logger.debug("Email configuration %s", settings.json(indent=1)) + _logger.debug("Email configuration %s", settings.model_dump_json(indeent=1)) if settings.SMTP_PORT == 587: # NOTE: aiosmtplib does not handle port 587 correctly this is a workaround diff --git a/services/web/server/src/simcore_service_webserver/email/_handlers.py b/services/web/server/src/simcore_service_webserver/email/_handlers.py index e052bb1e24e..84126852347 100644 --- a/services/web/server/src/simcore_service_webserver/email/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/email/_handlers.py @@ -111,7 +111,7 @@ async def test_email(request: web.Request): except Exception as err: # pylint: disable=broad-except logger.exception( "test_email failed for %s", - f"{settings.json(indent=1)}", + f"{settings.model_dump_json(indent=1)}", ) return envelope_json_response( EmailTestFailed.create_from_exception(error=err, test_name="test_email") diff --git a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py index d965678037b..857eb56ef57 100644 --- a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py +++ b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py @@ -183,7 +183,7 @@ async def list_folders(request: web.Request): ) ) return web.Response( - text=page.json(**RESPONSE_MODEL_POLICY), + text=page.model_dump_json(**RESPONSE_MODEL_POLICY), content_type=MIMETYPE_APPLICATION_JSON, ) diff --git a/services/web/server/src/simcore_service_webserver/login/_registration.py b/services/web/server/src/simcore_service_webserver/login/_registration.py index c6d5d14007e..df7b251373d 100644 --- a/services/web/server/src/simcore_service_webserver/login/_registration.py +++ b/services/web/server/src/simcore_service_webserver/login/_registration.py @@ -286,7 +286,8 @@ async def check_and_consume_invitation( ) _logger.info( - "Consuming invitation from service:\n%s", content.json(indent=1) + "Consuming invitation from service:\n%s", + content.model_dump_json(indent=1), ) return InvitationData( issuer=content.issuer, diff --git a/services/web/server/src/simcore_service_webserver/meta_modeling/_handlers.py b/services/web/server/src/simcore_service_webserver/meta_modeling/_handlers.py index 4a3ef629cac..847395e6acd 100644 --- a/services/web/server/src/simcore_service_webserver/meta_modeling/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/meta_modeling/_handlers.py @@ -302,7 +302,7 @@ async def list_project_iterations(request: web.Request) -> web.Response: ) ) return web.Response( - text=page.json(**RESPONSE_MODEL_POLICY), + text=page.model_dump_json(**RESPONSE_MODEL_POLICY), content_type="application/json", ) @@ -405,6 +405,6 @@ def _get_project_results(project_id) -> ExtractedResults: ) ) return web.Response( - text=page.json(**RESPONSE_MODEL_POLICY), + text=page.model_dump_json(**RESPONSE_MODEL_POLICY), content_type="application/json", ) diff --git a/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py index 8c22479100e..6ad8b290ba0 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py @@ -157,7 +157,7 @@ async def list_project_comments(request: web.Request): ) ) return web.Response( - text=page.json(**RESPONSE_MODEL_POLICY), + text=page.model_dump_json(**RESPONSE_MODEL_POLICY), content_type=MIMETYPE_APPLICATION_JSON, ) diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py index 82d6c4c2cf4..25ed73b66d0 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py @@ -226,7 +226,7 @@ async def list_projects(request: web.Request): ) ) return web.Response( - text=page.json(**RESPONSE_MODEL_POLICY), + text=page.model_dump_json(**RESPONSE_MODEL_POLICY), content_type=MIMETYPE_APPLICATION_JSON, ) @@ -265,7 +265,7 @@ async def list_projects_full_search(request: web.Request): ) ) return web.Response( - text=page.json(**RESPONSE_MODEL_POLICY), + text=page.model_dump_json(**RESPONSE_MODEL_POLICY), content_type=MIMETYPE_APPLICATION_JSON, ) diff --git a/services/web/server/src/simcore_service_webserver/projects/_nodes_api.py b/services/web/server/src/simcore_service_webserver/projects/_nodes_api.py index 78e48ec3bc7..4815ae19d03 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_nodes_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/_nodes_api.py @@ -239,7 +239,7 @@ async def get_node_screenshots( except (KeyError, ValidationError, ClientError) as err: _logger.warning( "Skipping fake node. Unable to create link from file-picker %s: %s", - node.json(indent=1), + node.model_dump_json(indent=1), err, ) diff --git a/services/web/server/src/simcore_service_webserver/projects/projects_api.py b/services/web/server/src/simcore_service_webserver/projects/projects_api.py index 46ea5b6a1f6..8d2ebd60843 100644 --- a/services/web/server/src/simcore_service_webserver/projects/projects_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/projects_api.py @@ -1490,7 +1490,7 @@ async def add_project_states_for_user( if prj_node is None: continue node_state_dict = json.loads( - node_state.json(by_alias=True, exclude_unset=True) + node_state.model_dump_json(by_alias=True, exclude_unset=True) ) prj_node.setdefault("state", {}).update(node_state_dict) prj_node_progress = node_state_dict.get("progress", None) or 0 diff --git a/services/web/server/src/simcore_service_webserver/resource_usage/_client.py b/services/web/server/src/simcore_service_webserver/resource_usage/_client.py index 5b9b5497b42..63d5187a7d5 100644 --- a/services/web/server/src/simcore_service_webserver/resource_usage/_client.py +++ b/services/web/server/src/simcore_service_webserver/resource_usage/_client.py @@ -151,7 +151,7 @@ async def sum_total_available_credits_in_the_wallet( async with session.post(url) as response: response.raise_for_status() body: dict = await response.json() - return WalletTotalCredits.construct(**body) + return WalletTotalCredits.model_construct(**body) async def add_credits_to_wallet( diff --git a/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py b/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py index af4d96bee06..b760b517fe5 100644 --- a/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py +++ b/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py @@ -178,7 +178,7 @@ async def list_resource_usage_services(request: web.Request): ) ) return web.Response( - text=page.json(**RESPONSE_MODEL_POLICY), + text=page.model_dump_json(**RESPONSE_MODEL_POLICY), content_type=MIMETYPE_APPLICATION_JSON, ) @@ -221,7 +221,7 @@ async def list_osparc_credits_aggregated_usages(request: web.Request): ) ) return web.Response( - text=page.json(**RESPONSE_MODEL_POLICY), + text=page.model_dump_json(**RESPONSE_MODEL_POLICY), content_type=MIMETYPE_APPLICATION_JSON, ) diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py index 14a1c212552..5f6029215ff 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py @@ -35,7 +35,9 @@ _FILE_PICKER_KEY: ServiceKey = TypeAdapter(ServiceKey).validate_python( "simcore/services/frontend/file-picker" ) -_FILE_PICKER_VERSION: ServiceVersion = TypeAdapter(ServiceVersion).validate_python("1.0.0") +_FILE_PICKER_VERSION: ServiceVersion = TypeAdapter(ServiceVersion).validate_python( + "1.0.0" +) def _generate_nodeids(project_id: ProjectID) -> tuple[NodeID, NodeID]: @@ -194,7 +196,9 @@ async def _add_new_project( db: ProjectDBAPI = app[APP_PROJECT_DBAPI] # validated project is transform in dict via json to use only primitive types - project_in: dict = json.loads(project.json(exclude_none=True, by_alias=True)) + project_in: dict = json.loads( + project.model_dump_json(exclude_none=True, by_alias=True) + ) # update metadata (uuid, timestamps, ownership) and save _project_db: dict = await db.insert_project( diff --git a/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py b/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py index 480f0348fe5..357ff18b3c7 100644 --- a/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py +++ b/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py @@ -80,7 +80,7 @@ async def create_user_notification(request: web.Request) -> web.Response: # insert at the head of the list and discard extra notifications redis_client = get_redis_user_notifications_client(request.app) async with redis_client.pipeline(transaction=True) as pipe: - pipe.lpush(key, user_notification.json()) + pipe.lpush(key, user_notification.model_dump_json()) pipe.ltrim(key, 0, MAX_NOTIFICATIONS_FOR_USER_TO_KEEP - 1) await pipe.execute() @@ -113,7 +113,7 @@ async def mark_notification_as_read(request: web.Request) -> web.Response: if req_path_params.notification_id == user_notification.id: user_notification.read = body.read await handle_redis_returns_union_types( - redis_client.lset(key, k, user_notification.json()) + redis_client.lset(key, k, user_notification.model_dump_json()) ) return web.json_response(status=status.HTTP_204_NO_CONTENT) diff --git a/services/web/server/src/simcore_service_webserver/version_control/_handlers.py b/services/web/server/src/simcore_service_webserver/version_control/_handlers.py index 2c16489067e..22b6270019c 100644 --- a/services/web/server/src/simcore_service_webserver/version_control/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/version_control/_handlers.py @@ -100,7 +100,7 @@ async def _list_repos_handler(request: web.Request): ) ) return web.Response( - text=page.json(**RESPONSE_MODEL_POLICY), + text=page.model_dump_json(**RESPONSE_MODEL_POLICY), content_type="application/json", ) @@ -186,7 +186,7 @@ async def _list_checkpoints_handler(request: web.Request): ) ) return web.Response( - text=page.json(**RESPONSE_MODEL_POLICY), + text=page.model_dump_json(**RESPONSE_MODEL_POLICY), content_type="application/json", ) diff --git a/services/web/server/tests/conftest.py b/services/web/server/tests/conftest.py index 04b9f1a54f6..2a7729014ea 100644 --- a/services/web/server/tests/conftest.py +++ b/services/web/server/tests/conftest.py @@ -328,7 +328,7 @@ async def _creator( assert not error task_status = TaskStatus.model_validate(data) assert task_status - print(f"<-- status: {task_status.json(indent=2)}") + print(f"<-- status: {task_status.model_dump_json(indent=2)}") assert task_status.done, "task incomplete" print( f"-- project creation completed: {json.dumps(attempt.retry_state.retry_object.statistics, indent=2)}" diff --git a/services/web/server/tests/unit/isolated/notifications/test_rabbitmq_consumers.py b/services/web/server/tests/unit/isolated/notifications/test_rabbitmq_consumers.py index 62840490d68..4a9bc655df2 100644 --- a/services/web/server/tests/unit/isolated/notifications/test_rabbitmq_consumers.py +++ b/services/web/server/tests/unit/isolated/notifications/test_rabbitmq_consumers.py @@ -32,7 +32,7 @@ node_id=UUID("6925403d-5464-4d92-9ec9-72c5793ca203"), progress_type=ProgressType.SERVICE_OUTPUTS_PULLING, report=ProgressReport(actual_value=0.4, total=1), - ).json(), + ).model_dump_json(), SocketMessageDict( event_type=WebSocketNodeProgress.get_event_type(), data={ @@ -56,7 +56,7 @@ user_id=123, progress_type=ProgressType.PROJECT_CLOSING, report=ProgressReport(actual_value=0.4, total=1), - ).json(), + ).model_dump_json(), SocketMessageDict( event_type=WebSocketNodeProgress.get_event_type(), data={ diff --git a/services/web/server/tests/unit/isolated/test_application_settings.py b/services/web/server/tests/unit/isolated/test_application_settings.py index ecd54e14b8e..32487b234ce 100644 --- a/services/web/server/tests/unit/isolated/test_application_settings.py +++ b/services/web/server/tests/unit/isolated/test_application_settings.py @@ -25,7 +25,7 @@ def app_settings( # init and validation happens here settings = setup_settings(app) print("envs\n", json.dumps(mock_webserver_service_environment, indent=1)) - print("settings:\n", settings.json(indent=1)) + print("settings:\n", settings.model_dump_json(indent=1)) assert APP_SETTINGS_KEY in app assert app[APP_SETTINGS_KEY] == settings diff --git a/services/web/server/tests/unit/isolated/test_application_settings_utils.py b/services/web/server/tests/unit/isolated/test_application_settings_utils.py index f4f0f901199..2897b8bf358 100644 --- a/services/web/server/tests/unit/isolated/test_application_settings_utils.py +++ b/services/web/server/tests/unit/isolated/test_application_settings_utils.py @@ -19,7 +19,7 @@ def test_settings_infered_from_default_tests_config( settings = ApplicationSettings.create_from_envs() - print("settings=\n", settings.json(indent=1, sort_keys=True)) + print("settings=\n", settings.model_dump_json(indent=1, sort_keys=True)) infered_config = convert_to_app_config(settings) diff --git a/services/web/server/tests/unit/isolated/test_login_settings.py b/services/web/server/tests/unit/isolated/test_login_settings.py index ebb7ad92337..3c189e4bff8 100644 --- a/services/web/server/tests/unit/isolated/test_login_settings.py +++ b/services/web/server/tests/unit/isolated/test_login_settings.py @@ -129,7 +129,7 @@ def test_smtp_settings(mock_env_devel_environment: dict[str, Any]): cfg = settings.model_dump() config = LoginOptions(**cfg) - print(config.json(indent=1)) + print(config.model_dump_json(indent=1)) assert not hasattr(config, "SMTP_SENDER"), "was deprecated and now we use product" diff --git a/services/web/server/tests/unit/isolated/test_security_api.py b/services/web/server/tests/unit/isolated/test_security_api.py index 3d76a58a24a..079fa68e529 100644 --- a/services/web/server/tests/unit/isolated/test_security_api.py +++ b/services/web/server/tests/unit/isolated/test_security_api.py @@ -219,7 +219,7 @@ def client( # mocks 'setup_session': patch to avoid setting up all ApplicationSettings session_settings = SessionSettings.create_from_envs() - print(session_settings.json(indent=1)) + print(session_settings.model_dump_json(indent=1)) mocker.patch( "simcore_service_webserver.session.plugin.get_plugin_settings", autospec=True, diff --git a/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py b/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py index 510b3118dca..b53ab561736 100644 --- a/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py +++ b/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py @@ -120,7 +120,9 @@ async def test_create_cluster( url = client.app.router["create_cluster"].url_for() rsp = await client.post( f"{url}", - json=json.loads(cluster_create.json(by_alias=True, exclude_unset=True)), + json=json.loads( + cluster_create.model_dump_json(by_alias=True, exclude_unset=True) + ), ) data, error = await assert_status( rsp, diff --git a/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/conftest.py b/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/conftest.py index d80957cca89..703e30fa541 100644 --- a/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/conftest.py +++ b/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/conftest.py @@ -56,7 +56,7 @@ def app_environment(app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatc setup_logging(level=logging.DEBUG, log_format_local_dev_enabled=True) plugin_settings = StudiesDispatcherSettings.create_from_envs() - print(plugin_settings.json(indent=1)) + print(plugin_settings.model_dump_json(indent=1)) return {**app_environment, **envs_plugins, **envs_studies_dispatcher} diff --git a/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_handlers.py b/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_handlers.py index 1f4ddb6dc14..3134e1f2c5c 100644 --- a/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_handlers.py +++ b/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_handlers.py @@ -78,7 +78,9 @@ def web_server(redis_service: RedisSettings, web_server: TestServer) -> TestServ # # Extends web_server to start redis_service # - print("Redis service started with settings: ", redis_service.json(indent=1)) + print( + "Redis service started with settings: ", redis_service.model_dump_json(indent=1) + ) return web_server diff --git a/services/web/server/tests/unit/with_dbs/01/test_groups.py b/services/web/server/tests/unit/with_dbs/01/test_groups.py index f6e41225ff7..95682c21f56 100644 --- a/services/web/server/tests/unit/with_dbs/01/test_groups.py +++ b/services/web/server/tests/unit/with_dbs/01/test_groups.py @@ -64,7 +64,7 @@ def client( app = create_safe_application(cfg) settings = setup_settings(app) - print(settings.json(indent=1)) + print(settings.model_dump_json(indent=1)) setup_db(app) setup_session(app) diff --git a/services/web/server/tests/unit/with_dbs/01/test_statics.py b/services/web/server/tests/unit/with_dbs/01/test_statics.py index f6cd1191577..1edb437b20a 100644 --- a/services/web/server/tests/unit/with_dbs/01/test_statics.py +++ b/services/web/server/tests/unit/with_dbs/01/test_statics.py @@ -68,7 +68,7 @@ def client( app = create_safe_application(cfg) settings = setup_settings(app) - print(settings.json(indent=1)) + print(settings.model_dump_json(indent=1)) setup_rest(app) setup_db(app) diff --git a/services/web/server/tests/unit/with_dbs/03/invitations/conftest.py b/services/web/server/tests/unit/with_dbs/03/invitations/conftest.py index b2b5f01a8d2..bbc5e83f8a8 100644 --- a/services/web/server/tests/unit/with_dbs/03/invitations/conftest.py +++ b/services/web/server/tests/unit/with_dbs/03/invitations/conftest.py @@ -212,5 +212,5 @@ def app_environment( ) # tests envs - print(ApplicationSettings.create_from_envs().json(indent=2)) + print(ApplicationSettings.create_from_envs().model_dump_json(indent=2)) return envs diff --git a/services/web/server/tests/unit/with_dbs/03/login/test_login_2fa.py b/services/web/server/tests/unit/with_dbs/03/login/test_login_2fa.py index d6dc34bcdfe..5cb85785419 100644 --- a/services/web/server/tests/unit/with_dbs/03/login/test_login_2fa.py +++ b/services/web/server/tests/unit/with_dbs/03/login/test_login_2fa.py @@ -50,7 +50,7 @@ def app_environment(app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatc "LOGIN_2FA_CODE_EXPIRATION_SEC": "60", }, ) - print(ApplicationSettings.create_from_envs().json(indent=1)) + print(ApplicationSettings.create_from_envs().model_dump_json(indent=1)) return {**app_environment, **envs_login} diff --git a/services/web/server/tests/unit/with_dbs/03/login/test_login_2fa_resend.py b/services/web/server/tests/unit/with_dbs/03/login/test_login_2fa_resend.py index d94e2ffa623..2cf5b63eb24 100644 --- a/services/web/server/tests/unit/with_dbs/03/login/test_login_2fa_resend.py +++ b/services/web/server/tests/unit/with_dbs/03/login/test_login_2fa_resend.py @@ -28,7 +28,7 @@ def app_environment(app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatc }, ) - print(ApplicationSettings.create_from_envs().json(indent=2)) + print(ApplicationSettings.create_from_envs().model_dump_json(indent=2)) return {**app_environment, **envs_login} diff --git a/services/web/server/tests/unit/with_dbs/03/login/test_login_auth.py b/services/web/server/tests/unit/with_dbs/03/login/test_login_auth.py index c73020d0638..ec6713d3894 100644 --- a/services/web/server/tests/unit/with_dbs/03/login/test_login_auth.py +++ b/services/web/server/tests/unit/with_dbs/03/login/test_login_auth.py @@ -30,7 +30,7 @@ def test_login_plugin_setup_succeeded(client: TestClient): assert client.app - print(client.app[APP_SETTINGS_KEY].json(indent=1, sort_keys=True)) + print(client.app[APP_SETTINGS_KEY].model_dump_json(indent=1, sort_keys=True)) # this should raise AssertionError if not succeedd settings = get_plugin_settings(client.app) diff --git a/services/web/server/tests/unit/with_dbs/03/products/test_products_db.py b/services/web/server/tests/unit/with_dbs/03/products/test_products_db.py index 4cd80c74a16..bd399948c14 100644 --- a/services/web/server/tests/unit/with_dbs/03/products/test_products_db.py +++ b/services/web/server/tests/unit/with_dbs/03/products/test_products_db.py @@ -138,9 +138,9 @@ async def test_product_repository_get_product( } # check RowProxy -> pydantic's Product - product = Product.from_orm(product_row) + product = Product.model_validate(product_row) - print(product.json(indent=1)) + print(product.model_dump_json(indent=1)) # product repo assert product_repository.engine diff --git a/services/web/server/tests/unit/with_dbs/03/resource_usage/conftest.py b/services/web/server/tests/unit/with_dbs/03/resource_usage/conftest.py index 4dc9da94974..a4691fcc3a2 100644 --- a/services/web/server/tests/unit/with_dbs/03/resource_usage/conftest.py +++ b/services/web/server/tests/unit/with_dbs/03/resource_usage/conftest.py @@ -17,7 +17,7 @@ def app_environment(app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatch): - # print( ApplicationSettings.create_from_envs().json(indent=1 ) + # print( ApplicationSettings.create_from_envs().model_dump_json((indent=1 ) return app_environment | setenvs_from_dict( monkeypatch, diff --git a/services/web/server/tests/unit/with_dbs/03/test__openapi_specs.py b/services/web/server/tests/unit/with_dbs/03/test__openapi_specs.py index ebd268074ab..eafb6ed29b9 100644 --- a/services/web/server/tests/unit/with_dbs/03/test__openapi_specs.py +++ b/services/web/server/tests/unit/with_dbs/03/test__openapi_specs.py @@ -56,7 +56,7 @@ def app(app_environment: EnvVarsDict) -> web.Application: # - all plugins are setup but app is NOT started (i.e events are not triggered) # app_ = create_application() - print(get_application_settings(app_).json(indent=1)) + print(get_application_settings(app_).model_dump_json(indent=1)) return app_ diff --git a/services/web/server/tests/unit/with_dbs/03/test_email.py b/services/web/server/tests/unit/with_dbs/03/test_email.py index d6f7a050239..c208162d318 100644 --- a/services/web/server/tests/unit/with_dbs/03/test_email.py +++ b/services/web/server/tests/unit/with_dbs/03/test_email.py @@ -139,7 +139,7 @@ async def test_email_handlers( EmailTestFailed.model_validate(data) passed = EmailTestPassed.model_validate(data) - print(passed.json(indent=1)) + print(passed.model_dump_json(indent=1)) class IndexParser(HTMLParser): diff --git a/services/web/server/tests/unit/with_dbs/03/test_socketio.py b/services/web/server/tests/unit/with_dbs/03/test_socketio.py index 05be09f7749..699ff0ccef9 100644 --- a/services/web/server/tests/unit/with_dbs/03/test_socketio.py +++ b/services/web/server/tests/unit/with_dbs/03/test_socketio.py @@ -46,7 +46,7 @@ def app_environment(app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatc }, ) - print(ApplicationSettings.create_from_envs().json(indent=1)) + print(ApplicationSettings.create_from_envs().model_dump_json(indent=1)) return app_environment | overrides diff --git a/services/web/server/tests/unit/with_dbs/03/test_users.py b/services/web/server/tests/unit/with_dbs/03/test_users.py index 318681b30fa..6fa9a78fd44 100644 --- a/services/web/server/tests/unit/with_dbs/03/test_users.py +++ b/services/web/server/tests/unit/with_dbs/03/test_users.py @@ -360,7 +360,7 @@ def test_preuserprofile_parse_model_from_request_form_data( # pre-processors pre_user_profile = PreUserProfile(**data) - print(pre_user_profile.json(indent=1)) + print(pre_user_profile.model_dump_json(indent=1)) # institution aliases assert pre_user_profile.institution == account_request_form["company"] @@ -405,6 +405,6 @@ def test_preuserprofile_pre_given_names( account_request_form["lastName"] = given_name pre_user_profile = PreUserProfile(**account_request_form) - print(pre_user_profile.json(indent=1)) + print(pre_user_profile.model_dump_json(indent=1)) assert pre_user_profile.first_name in ["Pedro-Luis", "Pedro Luis"] assert pre_user_profile.first_name == pre_user_profile.last_name diff --git a/services/web/server/tests/unit/with_dbs/03/test_users__notifications.py b/services/web/server/tests/unit/with_dbs/03/test_users__notifications.py index 36b0aec8276..6a70478ce78 100644 --- a/services/web/server/tests/unit/with_dbs/03/test_users__notifications.py +++ b/services/web/server/tests/unit/with_dbs/03/test_users__notifications.py @@ -104,7 +104,7 @@ async def _create_notifications( redis_key = get_notification_key(user_id) if user_notifications: for notification in user_notifications: - await redis_client.lpush(redis_key, notification.json()) + await redis_client.lpush(redis_key, notification.model_dump_json()) yield user_notifications From 335ff84c390ae3cfc1285c75114fb2d4f228aa8e Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 15:37:35 +0200 Subject: [PATCH 021/165] fix type --- .../src/simcore_service_webserver/projects/projects_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/projects_api.py b/services/web/server/src/simcore_service_webserver/projects/projects_api.py index 8d2ebd60843..856b5251639 100644 --- a/services/web/server/src/simcore_service_webserver/projects/projects_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/projects_api.py @@ -385,7 +385,7 @@ async def _get_default_pricing_and_hardware_info( _MACHINE_TOTAL_RAM_SAFE_MARGIN_RATIO: Final[ float ] = 0.1 # NOTE: machines always have less available RAM than advertised -_SIDECARS_OPS_SAFE_RAM_MARGIN: Final[ByteSize] = ByteSize("1GiB") +_SIDECARS_OPS_SAFE_RAM_MARGIN: Final[ByteSize] = TypeAdapter(ByteSize).validate_python("1GiB") _CPUS_SAFE_MARGIN: Final[float] = 1.4 _MIN_NUM_CPUS: Final[float] = 0.5 From e0db4a94d7f3922778482146e87ba62a8b78fc9e Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 15:46:55 +0200 Subject: [PATCH 022/165] fix validation --- .../tests/unit/isolated/test_studies_dispatcher_models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/server/tests/unit/isolated/test_studies_dispatcher_models.py b/services/web/server/tests/unit/isolated/test_studies_dispatcher_models.py index 06f216362d7..3dbcd062d47 100644 --- a/services/web/server/tests/unit/isolated/test_studies_dispatcher_models.py +++ b/services/web/server/tests/unit/isolated/test_studies_dispatcher_models.py @@ -11,7 +11,7 @@ import pytest from aiohttp.test_utils import make_mocked_request from models_library.utils.pydantic_tools_extension import parse_obj_or_none -from pydantic import ByteSize +from pydantic import ByteSize, TypeAdapter from servicelib.aiohttp.requests_validation import parse_request_query_parameters_as from simcore_service_webserver.studies_dispatcher._models import ( FileParams, @@ -23,7 +23,7 @@ ) from yarl import URL -_SIZEBYTES = ByteSize("3MiB") +_SIZEBYTES = TypeAdapter(ByteSize).validate_python("3MiB") # SEE https://github.com/ITISFoundation/osparc-simcore/issues/3951#issuecomment-1489992645 # AWS download links have query arg From a135e421320de3b3defbca1fe6f084c75da7aa0d Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 15:57:59 +0200 Subject: [PATCH 023/165] continue fixing --- .../with_dbs/03/resource_usage/test_usage_services__list.py | 2 ++ .../web/server/tests/unit/with_dbs/03/test_storage_handlers.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/services/web/server/tests/unit/with_dbs/03/resource_usage/test_usage_services__list.py b/services/web/server/tests/unit/with_dbs/03/resource_usage/test_usage_services__list.py index 33b9d9146f5..e43913ed189 100644 --- a/services/web/server/tests/unit/with_dbs/03/resource_usage/test_usage_services__list.py +++ b/services/web/server/tests/unit/with_dbs/03/resource_usage/test_usage_services__list.py @@ -45,6 +45,8 @@ "started_at": "2023-08-26T14:18:17.600493+00:00", "stopped_at": "2023-08-26T14:18:19.358355+00:00", "service_run_status": "SUCCESS", + "credit_cost": None, + "transaction_status": None } ) ], diff --git a/services/web/server/tests/unit/with_dbs/03/test_storage_handlers.py b/services/web/server/tests/unit/with_dbs/03/test_storage_handlers.py index c7495866c8f..694d49a998b 100644 --- a/services/web/server/tests/unit/with_dbs/03/test_storage_handlers.py +++ b/services/web/server/tests/unit/with_dbs/03/test_storage_handlers.py @@ -69,7 +69,7 @@ def _resolve(*args, **kwargs) -> AnyUrl: MOCK_FILE_UPLOAD_SCHEMA = FileUploadSchema( - chunk_size=ByteSize("5GiB"), + chunk_size=TypeAdapter(ByteSize).validate_python("5GiB"), urls=[TypeAdapter(AnyUrl).validate_python("s3://file_id")], links=FileUploadLinks( abort_upload=TypeAdapter(AnyUrl).validate_python( From 0c639bf67986306d3d843f3d692e221c16adfb74 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 16:13:09 +0200 Subject: [PATCH 024/165] continue fixing --- .../unit/with_dbs/03/test_users__preferences_api.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/services/web/server/tests/unit/with_dbs/03/test_users__preferences_api.py b/services/web/server/tests/unit/with_dbs/03/test_users__preferences_api.py index 3835883af8b..8db8935616d 100644 --- a/services/web/server/tests/unit/with_dbs/03/test_users__preferences_api.py +++ b/services/web/server/tests/unit/with_dbs/03/test_users__preferences_api.py @@ -11,12 +11,13 @@ from aiohttp import web from aiohttp.test_utils import TestClient from faker import Faker +from common_library.pydantic_fields_extension import get_type from models_library.api_schemas_webserver.users_preferences import Preference from models_library.products import ProductName from models_library.user_preferences import FrontendUserPreference from models_library.users import UserID from pydantic import BaseModel -from pydantic.fields import ModelField +from pydantic.fields import FieldInfo from pytest_simcore.helpers.monkeypatch_envs import EnvVarsDict, setenvs_from_dict from pytest_simcore.helpers.webserver_login import NewUser from simcore_postgres_database.models.groups_extra_properties import ( @@ -64,8 +65,8 @@ def product_name() -> ProductName: return "osparc" -def _get_model_field(model_class: type[BaseModel], field_name: str) -> ModelField: - return model_class.__dict__["__fields__"][field_name] +def _get_model_field(model_class: type[BaseModel], field_name: str) -> FieldInfo: + return model_class.model_fields[field_name] def _get_default_field_value(model_class: type[BaseModel]) -> Any: @@ -83,7 +84,7 @@ def _get_non_default_value( """given a default value transforms into something that is different""" model_field = _get_model_field(model_class, "value") - value_type = model_field.type_ + value_type = get_type(model_field) value = _get_default_field_value(model_class) if isinstance(value, bool): From 48b6c26ba62119924038b3b8d2e12716fc41d92f Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 16:26:38 +0200 Subject: [PATCH 025/165] no sort_keys anymore --- .../src/simcore_service_webserver/application_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/application_settings.py b/services/web/server/src/simcore_service_webserver/application_settings.py index dccfe7a019a..bbd92050d94 100644 --- a/services/web/server/src/simcore_service_webserver/application_settings.py +++ b/services/web/server/src/simcore_service_webserver/application_settings.py @@ -437,7 +437,7 @@ def setup_settings(app: web.Application) -> ApplicationSettings: app[APP_SETTINGS_KEY] = settings _logger.debug( "Captured app settings:\n%s", - app[APP_SETTINGS_KEY].model_dump_json(indent=1, sort_keys=True), + app[APP_SETTINGS_KEY].model_dump_json(indent=1), ) return settings From 5a6a7d1b3d3cb30b5033d466d22701aa5cb2540d Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 16:41:46 +0200 Subject: [PATCH 026/165] fix rootmodel --- .../unit/with_dbs/03/version_control/test_version_control.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control.py b/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control.py index 614bdca0209..822eb16951b 100644 --- a/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control.py +++ b/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control.py @@ -4,13 +4,12 @@ from models_library.projects import NodesDict -from pydantic import ConfigDict, BaseModel +from pydantic import ConfigDict, RootModel from simcore_service_webserver.projects.models import ProjectDict from simcore_service_webserver.version_control.db import compute_workbench_checksum -class WorkbenchModel(BaseModel): - __root__: NodesDict +class WorkbenchModel(RootModel[NodesDict]): model_config = ConfigDict(populate_by_name=True) From 1d0e14d0bbfa5e5adf587dab795c67bfb8498a91 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 16:46:16 +0200 Subject: [PATCH 027/165] fix validate_call --- .../version_control/_core.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/version_control/_core.py b/services/web/server/src/simcore_service_webserver/version_control/_core.py index c8d444339ea..860d124ce48 100644 --- a/services/web/server/src/simcore_service_webserver/version_control/_core.py +++ b/services/web/server/src/simcore_service_webserver/version_control/_core.py @@ -12,7 +12,7 @@ from uuid import UUID from aiopg.sa.result import RowProxy -from pydantic import NonNegativeInt, PositiveInt, validate_arguments +from pydantic import NonNegativeInt, PositiveInt, validate_call from .db import VersionControlRepository from .errors import CleanRequiredError @@ -146,10 +146,10 @@ async def get_workbench( _CONFIG = {"arbitrary_types_allowed": True} -list_repos_safe = validate_arguments(list_repos, config=_CONFIG) # type: ignore -list_checkpoints_safe = validate_arguments(list_checkpoints, config=_CONFIG) # type: ignore -create_checkpoint_safe = validate_arguments(create_checkpoint, config=_CONFIG) # type: ignore -get_checkpoint_safe = validate_arguments(get_checkpoint, config=_CONFIG) # type: ignore -update_checkpoint_safe = validate_arguments(update_checkpoint, config=_CONFIG) # type: ignore -checkout_checkpoint_safe = validate_arguments(checkout_checkpoint, config=_CONFIG) # type: ignore -get_workbench_safe = validate_arguments(get_workbench, config=_CONFIG) # type: ignore +list_repos_safe = validate_call(list_repos, config=_CONFIG) # type: ignore +list_checkpoints_safe = validate_call(list_checkpoints, config=_CONFIG) # type: ignore +create_checkpoint_safe = validate_call(create_checkpoint, config=_CONFIG) # type: ignore +get_checkpoint_safe = validate_call(get_checkpoint, config=_CONFIG) # type: ignore +update_checkpoint_safe = validate_call(update_checkpoint, config=_CONFIG) # type: ignore +checkout_checkpoint_safe = validate_call(checkout_checkpoint, config=_CONFIG) # type: ignore +get_workbench_safe = validate_call(get_workbench, config=_CONFIG) # type: ignore From f7cdf055bc4ce614c2bc72379da7d1a9f2db5df2 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 24 Oct 2024 16:48:15 +0200 Subject: [PATCH 028/165] fix mypy --- .../src/simcore_service_webserver/login/handlers_change.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/login/handlers_change.py b/services/web/server/src/simcore_service_webserver/login/handlers_change.py index f8b71ce8763..75c93ff990e 100644 --- a/services/web/server/src/simcore_service_webserver/login/handlers_change.py +++ b/services/web/server/src/simcore_service_webserver/login/handlers_change.py @@ -3,7 +3,7 @@ from aiohttp import web from aiohttp.web import RouteTableDef from models_library.emails import LowerCaseEmailStr -from pydantic import SecretStr, validator +from pydantic import SecretStr, field_validator from servicelib.aiohttp.requests_validation import parse_request_body_as from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from servicelib.request_keys import RQT_USERID_KEY @@ -188,7 +188,7 @@ class ChangePasswordBody(InputSchema): new: SecretStr confirm: SecretStr - _password_confirm_match = validator("confirm", allow_reuse=True)( + _password_confirm_match = field_validator("confirm")( create_password_match_validator(reference_field="new") ) From 39b989831718e26a38b05254173463044b440eb4 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 25 Oct 2024 09:16:43 +0200 Subject: [PATCH 029/165] remove deprecated from_orm --- .../src/simcore_service_webserver/api_keys/_db.py | 4 ++-- .../simcore_service_webserver/folders/_folders_db.py | 10 +++++----- .../payments/_autorecharge_db.py | 4 ++-- .../simcore_service_webserver/payments/_methods_db.py | 2 +- .../simcore_service_webserver/projects/_db_utils.py | 2 +- .../src/simcore_service_webserver/projects/db.py | 8 ++++---- .../src/simcore_service_webserver/scicrunch/db.py | 2 +- .../server/src/simcore_service_webserver/users/_db.py | 2 +- .../simcore_service_webserver/workspaces/_groups_db.py | 8 ++++---- .../workspaces/_workspaces_db.py | 8 ++++---- 10 files changed, 25 insertions(+), 25 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/api_keys/_db.py b/services/web/server/src/simcore_service_webserver/api_keys/_db.py index 4a51464e1a9..009369005f6 100644 --- a/services/web/server/src/simcore_service_webserver/api_keys/_db.py +++ b/services/web/server/src/simcore_service_webserver/api_keys/_db.py @@ -79,7 +79,7 @@ async def get( result: ResultProxy = await conn.execute(stmt) row: RowProxy | None = await result.fetchone() - return ApiKeyInDB.from_orm(row) if row else None + return ApiKeyInDB.model_validate(row) if row else None async def get_or_create( self, @@ -116,7 +116,7 @@ async def get_or_create( result = await conn.execute(insert_stmt) row = await result.fetchone() assert row # nosec - return ApiKeyInDB.from_orm(row) + return ApiKeyInDB.model_validate(row) async def delete_by_name( self, *, display_name: str, user_id: UserID, product_name: ProductName diff --git a/services/web/server/src/simcore_service_webserver/folders/_folders_db.py b/services/web/server/src/simcore_service_webserver/folders/_folders_db.py index 5c1dcf4d47f..d2992ed30d5 100644 --- a/services/web/server/src/simcore_service_webserver/folders/_folders_db.py +++ b/services/web/server/src/simcore_service_webserver/folders/_folders_db.py @@ -70,7 +70,7 @@ async def create( .returning(*_SELECTION_ARGS) ) row = await result.first() - return FolderDB.from_orm(row) + return FolderDB.model_validate(row) async def list_( @@ -123,7 +123,7 @@ async def list_( result = await conn.execute(list_query) rows = await result.fetchall() or [] - results: list[FolderDB] = [FolderDB.from_orm(row) for row in rows] + results: list[FolderDB] = [FolderDB.model_validate(row) for row in rows] return cast(int, total_count), results @@ -149,7 +149,7 @@ async def get( raise FolderAccessForbiddenError( reason=f"Folder {folder_id} does not exist.", ) - return FolderDB.from_orm(row) + return FolderDB.model_validate(row) async def get_for_user_or_workspace( @@ -185,7 +185,7 @@ async def get_for_user_or_workspace( raise FolderAccessForbiddenError( reason=f"User does not have access to the folder {folder_id}. Or folder does not exist.", ) - return FolderDB.from_orm(row) + return FolderDB.model_validate(row) async def update( @@ -213,7 +213,7 @@ async def update( row = await result.first() if row is None: raise FolderNotFoundError(reason=f"Folder {folder_id} not found.") - return FolderDB.from_orm(row) + return FolderDB.model_validate(row) async def delete_recursively( diff --git a/services/web/server/src/simcore_service_webserver/payments/_autorecharge_db.py b/services/web/server/src/simcore_service_webserver/payments/_autorecharge_db.py index f276b31c5ab..813fa6b9eb1 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_autorecharge_db.py +++ b/services/web/server/src/simcore_service_webserver/payments/_autorecharge_db.py @@ -36,7 +36,7 @@ async def get_wallet_autorecharge( stmt = AutoRechargeStmts.get_wallet_autorecharge(wallet_id) result = await conn.execute(stmt) row = await result.first() - return PaymentsAutorechargeDB.from_orm(row) if row else None + return PaymentsAutorechargeDB.model_validate(row) if row else None async def replace_wallet_autorecharge( @@ -73,4 +73,4 @@ async def replace_wallet_autorecharge( result = await conn.execute(stmt) row = await result.first() assert row # nosec - return PaymentsAutorechargeDB.from_orm(row) + return PaymentsAutorechargeDB.model_validate(row) diff --git a/services/web/server/src/simcore_service_webserver/payments/_methods_db.py b/services/web/server/src/simcore_service_webserver/payments/_methods_db.py index 1d47dc19dd0..5c1c638b37b 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_methods_db.py +++ b/services/web/server/src/simcore_service_webserver/payments/_methods_db.py @@ -105,7 +105,7 @@ async def get_successful_payment_method( if row is None: raise PaymentMethodNotFoundError(payment_method_id=payment_method_id) - return PaymentsMethodsDB.from_orm(row) + return PaymentsMethodsDB.model_validate(row) async def get_pending_payment_methods_ids( diff --git a/services/web/server/src/simcore_service_webserver/projects/_db_utils.py b/services/web/server/src/simcore_service_webserver/projects/_db_utils.py index 9173c2b131d..6ef6cd905a2 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_db_utils.py +++ b/services/web/server/src/simcore_service_webserver/projects/_db_utils.py @@ -191,7 +191,7 @@ async def _execute_without_permission_check( assert isinstance(row, RowProxy) # nosec try: await asyncio.get_event_loop().run_in_executor( - None, ProjectAtDB.from_orm, row + None, ProjectAtDB.model_validate, row ) except ProjectInvalidRightsError: diff --git a/services/web/server/src/simcore_service_webserver/projects/db.py b/services/web/server/src/simcore_service_webserver/projects/db.py index 193ddf83f4d..722bf51b5cf 100644 --- a/services/web/server/src/simcore_service_webserver/projects/db.py +++ b/services/web/server/src/simcore_service_webserver/projects/db.py @@ -744,7 +744,7 @@ async def get_project_db(self, project_uuid: ProjectID) -> ProjectDB: row = await result.fetchone() if row is None: raise ProjectNotFoundError(project_uuid=project_uuid) - return ProjectDB.from_orm(row) + return ProjectDB.model_validate(row) async def get_user_specific_project_data_db( self, project_uuid: ProjectID, private_workspace_user_id_or_none: UserID | None @@ -772,7 +772,7 @@ async def get_user_specific_project_data_db( row = await result.fetchone() if row is None: raise ProjectNotFoundError(project_uuid=project_uuid) - return UserSpecificProjectDataDB.from_orm(row) + return UserSpecificProjectDataDB.model_validate(row) async def get_pure_project_access_rights_without_workspace( self, user_id: UserID, project_uuid: ProjectID @@ -818,7 +818,7 @@ async def get_pure_project_access_rights_without_workspace( raise ProjectInvalidRightsError( user_id=user_id, project_uuid=project_uuid ) - return UserProjectAccessRightsDB.from_orm(row) + return UserProjectAccessRightsDB.model_validate(row) async def replace_project( self, @@ -913,7 +913,7 @@ async def patch_project( row = await result.fetchone() if row is None: raise ProjectNotFoundError(project_uuid=project_uuid) - return ProjectDB.from_orm(row) + return ProjectDB.model_validate(row) async def get_project_product(self, project_uuid: ProjectID) -> ProductName: async with self.engine.acquire() as conn: diff --git a/services/web/server/src/simcore_service_webserver/scicrunch/db.py b/services/web/server/src/simcore_service_webserver/scicrunch/db.py index 94f06853d29..57e19bbed35 100644 --- a/services/web/server/src/simcore_service_webserver/scicrunch/db.py +++ b/services/web/server/src/simcore_service_webserver/scicrunch/db.py @@ -39,7 +39,7 @@ async def list_resources(self) -> list[ResearchResource]: ) res: ResultProxy = await conn.execute(stmt) rows: list[RowProxy] = await res.fetchall() - return [ResearchResource.from_orm(row) for row in rows] if rows else [] + return [ResearchResource.model_validate(row) for row in rows] if rows else [] async def get(self, rrid: str) -> ResearchResourceAtdB | None: async with self._engine.acquire() as conn: diff --git a/services/web/server/src/simcore_service_webserver/users/_db.py b/services/web/server/src/simcore_service_webserver/users/_db.py index f7d8769f963..2071034d2e6 100644 --- a/services/web/server/src/simcore_service_webserver/users/_db.py +++ b/services/web/server/src/simcore_service_webserver/users/_db.py @@ -212,4 +212,4 @@ async def get_user_billing_details( user_billing_details = await UsersRepo.get_billing_details(conn, user_id) if not user_billing_details: raise BillingDetailsNotFoundError(user_id=user_id) - return UserBillingDetails.from_orm(user_billing_details) + return UserBillingDetails.model_validate(user_billing_details) diff --git a/services/web/server/src/simcore_service_webserver/workspaces/_groups_db.py b/services/web/server/src/simcore_service_webserver/workspaces/_groups_db.py index 1fd21bc4338..c186786f603 100644 --- a/services/web/server/src/simcore_service_webserver/workspaces/_groups_db.py +++ b/services/web/server/src/simcore_service_webserver/workspaces/_groups_db.py @@ -61,7 +61,7 @@ async def create_workspace_group( .returning(literal_column("*")) ) row = await result.first() - return WorkspaceGroupGetDB.from_orm(row) + return WorkspaceGroupGetDB.model_validate(row) async def list_workspace_groups( @@ -84,7 +84,7 @@ async def list_workspace_groups( async with get_database_engine(app).acquire() as conn: result = await conn.execute(stmt) rows = await result.fetchall() or [] - return [WorkspaceGroupGetDB.from_orm(row) for row in rows] + return [WorkspaceGroupGetDB.model_validate(row) for row in rows] async def get_workspace_group( @@ -115,7 +115,7 @@ async def get_workspace_group( raise WorkspaceGroupNotFoundError( workspace_id=workspace_id, group_id=group_id ) - return WorkspaceGroupGetDB.from_orm(row) + return WorkspaceGroupGetDB.model_validate(row) async def update_workspace_group( @@ -146,7 +146,7 @@ async def update_workspace_group( raise WorkspaceGroupNotFoundError( workspace_id=workspace_id, group_id=group_id ) - return WorkspaceGroupGetDB.from_orm(row) + return WorkspaceGroupGetDB.model_validate(row) async def delete_workspace_group( diff --git a/services/web/server/src/simcore_service_webserver/workspaces/_workspaces_db.py b/services/web/server/src/simcore_service_webserver/workspaces/_workspaces_db.py index 23de15c3b19..4f007bc7552 100644 --- a/services/web/server/src/simcore_service_webserver/workspaces/_workspaces_db.py +++ b/services/web/server/src/simcore_service_webserver/workspaces/_workspaces_db.py @@ -66,7 +66,7 @@ async def create_workspace( .returning(*_SELECTION_ARGS) ) row = await result.first() - return WorkspaceDB.from_orm(row) + return WorkspaceDB.model_validate(row) access_rights_subquery = ( @@ -155,7 +155,7 @@ async def list_workspaces_for_user( result = await conn.execute(list_query) rows = await result.fetchall() or [] results: list[UserWorkspaceAccessRightsDB] = [ - UserWorkspaceAccessRightsDB.from_orm(row) for row in rows + UserWorkspaceAccessRightsDB.model_validate(row) for row in rows ] return cast(int, total_count), results @@ -191,7 +191,7 @@ async def get_workspace_for_user( raise WorkspaceAccessForbiddenError( reason=f"User {user_id} does not have access to the workspace {workspace_id}. Or workspace does not exist.", ) - return UserWorkspaceAccessRightsDB.from_orm(row) + return UserWorkspaceAccessRightsDB.model_validate(row) async def update_workspace( @@ -220,7 +220,7 @@ async def update_workspace( row = await result.first() if row is None: raise WorkspaceNotFoundError(reason=f"Workspace {workspace_id} not found.") - return WorkspaceDB.from_orm(row) + return WorkspaceDB.model_validate(row) async def delete_workspace( From 1cb84d618b31aa890ad78ce0f94afd88f9e69b2d Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 25 Oct 2024 09:45:35 +0200 Subject: [PATCH 030/165] fix frozen settings --- .../src/simcore_service_webserver/application_settings.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/web/server/src/simcore_service_webserver/application_settings.py b/services/web/server/src/simcore_service_webserver/application_settings.py index bbd92050d94..c49aa0b0cd9 100644 --- a/services/web/server/src/simcore_service_webserver/application_settings.py +++ b/services/web/server/src/simcore_service_webserver/application_settings.py @@ -22,6 +22,7 @@ ) from pydantic.fields import Field from pydantic.types import PositiveInt +from pydantic_settings import SettingsConfigDict from settings_library.base import BaseCustomSettings from settings_library.email import SMTPSettings from settings_library.postgres import PostgresSettings @@ -270,6 +271,10 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings): "Currently this is a system plugin and cannot be disabled", ) + model_config = SettingsConfigDict( + frozen=False + ) + @model_validator(mode="after") @classmethod def build_vcs_release_url_if_unset(cls, v): From 6eba301159125a4387912d7fde053a817040c218 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 25 Oct 2024 10:30:48 +0200 Subject: [PATCH 031/165] change thumbnail field type --- .../server/src/simcore_service_webserver/projects/models.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/models.py b/services/web/server/src/simcore_service_webserver/projects/models.py index c0ca8954f9e..6c9a068fc5f 100644 --- a/services/web/server/src/simcore_service_webserver/projects/models.py +++ b/services/web/server/src/simcore_service_webserver/projects/models.py @@ -3,7 +3,6 @@ from typing import Any, TypeAlias from aiopg.sa.result import RowProxy -from models_library.basic_types import HttpUrlWithCustomMinLength from models_library.folders import FolderID from models_library.projects import ClassifierID, ProjectID from models_library.projects_ui import StudyUI @@ -13,7 +12,7 @@ none_to_empty_str_pre_validator, ) from models_library.workspaces import WorkspaceID -from pydantic import ConfigDict, BaseModel, field_validator +from pydantic import ConfigDict, BaseModel, HttpUrl, field_validator from simcore_postgres_database.models.projects import ProjectType, projects ProjectDict: TypeAlias = dict[str, Any] @@ -40,7 +39,7 @@ class ProjectDB(BaseModel): uuid: ProjectID name: str description: str - thumbnail: HttpUrlWithCustomMinLength | None = None + thumbnail: HttpUrl | None = None prj_owner: UserID creation_date: datetime last_change_date: datetime From efefd0a864ef2f0e613e096c65dab56c9f26d610 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 25 Oct 2024 10:37:41 +0200 Subject: [PATCH 032/165] upgrade deprecated --- .../dynamic_scheduler/settings.py | 6 +++--- .../users/_notifications_handlers.py | 6 ++++-- .../users/_preferences_models.py | 2 +- .../src/simcore_service_webserver/users/_schemas.py | 11 ++++------- .../server/tests/unit/isolated/test_login_settings.py | 2 +- .../web/server/tests/unit/with_dbs/03/test_users.py | 4 ++-- 6 files changed, 15 insertions(+), 16 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/dynamic_scheduler/settings.py b/services/web/server/src/simcore_service_webserver/dynamic_scheduler/settings.py index b92a0e2d432..2ebe8bc141c 100644 --- a/services/web/server/src/simcore_service_webserver/dynamic_scheduler/settings.py +++ b/services/web/server/src/simcore_service_webserver/dynamic_scheduler/settings.py @@ -1,7 +1,7 @@ from typing import Final from aiohttp import web -from pydantic import Field, NonNegativeInt +from pydantic import AliasChoices, Field, NonNegativeInt from settings_library.base import BaseCustomSettings from settings_library.utils_service import MixinServiceSettings @@ -23,10 +23,10 @@ class DynamicSchedulerSettings(BaseCustomSettings, MixinServiceSettings): "- director-v* requests save_state and uses a 01:00:00 timeout" "The +10 seconds is used to make sure the director replies" ), - envs=[ + validation_alias=AliasChoices( "DIRECTOR_V2_STOP_SERVICE_TIMEOUT", "DYNAMIC_SCHEDULER_STOP_SERVICE_TIMEOUT", - ], + ), ) diff --git a/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py b/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py index 357ff18b3c7..58fb1a483e5 100644 --- a/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py +++ b/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py @@ -106,7 +106,7 @@ async def mark_notification_as_read(request: web.Request) -> web.Response: # NOTE: only the user's notifications can be patched key = get_notification_key(req_ctx.user_id) all_user_notifications: list[UserNotification] = [ - UserNotification.parse_raw(x) + UserNotification.model_validate_json(x) for x in await handle_redis_returns_union_types(redis_client.lrange(key, 0, -1)) ] for k, user_notification in enumerate(all_user_notifications): @@ -130,7 +130,9 @@ async def list_user_permissions(request: web.Request) -> web.Response: ) return envelope_json_response( [ - PermissionGet.construct(_fields_set=p.__fields_set__, **p.model_dump()) + PermissionGet.model_construct( + _fields_set=p.model_fields_set, **p.model_dump() + ) for p in list_permissions ] ) diff --git a/services/web/server/src/simcore_service_webserver/users/_preferences_models.py b/services/web/server/src/simcore_service_webserver/users/_preferences_models.py index 01b6b87e377..6a871bcfafe 100644 --- a/services/web/server/src/simcore_service_webserver/users/_preferences_models.py +++ b/services/web/server/src/simcore_service_webserver/users/_preferences_models.py @@ -132,7 +132,7 @@ class BillingCenterUsageColumnOrderFrontendUserPreference(FrontendUserPreference ] _PREFERENCE_NAME_TO_IDENTIFIER_MAPPING: dict[PreferenceName, PreferenceIdentifier] = { - p.get_preference_name(): p.__fields__["preference_identifier"].default + p.get_preference_name(): p.model_fields["preference_identifier"].default for p in ALL_FRONTEND_PREFERENCES } _PREFERENCE_IDENTIFIER_TO_NAME_MAPPING: dict[PreferenceIdentifier, PreferenceName] = { diff --git a/services/web/server/src/simcore_service_webserver/users/_schemas.py b/services/web/server/src/simcore_service_webserver/users/_schemas.py index 19aa761a124..2d6f5de033b 100644 --- a/services/web/server/src/simcore_service_webserver/users/_schemas.py +++ b/services/web/server/src/simcore_service_webserver/users/_schemas.py @@ -12,7 +12,7 @@ from models_library.api_schemas_webserver._base import InputSchema, OutputSchema from models_library.emails import LowerCaseEmailStr from models_library.products import ProductName -from pydantic import ConfigDict, field_validator, model_validator, Field, field_validator +from pydantic import ConfigDict, Field, field_validator, model_validator from simcore_postgres_database.models.users import UserStatus @@ -74,10 +74,7 @@ class PreUserProfile(InputSchema): description="Keeps extra information provided in the request form. At most MAX_NUM_EXTRAS fields", ) - model_config = ConfigDict( - str_strip_whitespace=True, - str_max_length=200 - ) + model_config = ConfigDict(str_strip_whitespace=True, str_max_length=200) @model_validator(mode="before") @classmethod @@ -93,8 +90,8 @@ def _preprocess_aliases_and_extras(cls, values): # collect extras extra_fields = {} field_names_and_aliases = ( - set(cls.__fields__.keys()) - | {f.alias for f in cls.__fields__.values() if f.alias} + set(cls.model_fields.keys()) + | {f.alias for f in cls.model_fields.values() if f.alias} | set(alias_by_priority) ) for key, value in values.items(): diff --git a/services/web/server/tests/unit/isolated/test_login_settings.py b/services/web/server/tests/unit/isolated/test_login_settings.py index 3c189e4bff8..0bfff446911 100644 --- a/services/web/server/tests/unit/isolated/test_login_settings.py +++ b/services/web/server/tests/unit/isolated/test_login_settings.py @@ -137,6 +137,6 @@ def test_smtp_settings(mock_env_devel_environment: dict[str, Any]): def test_product_login_settings_in_plugin_settings(): # pylint: disable=no-member customizable_attributes = set(ProductLoginSettingsDict.__annotations__.keys()) - settings_atrributes = set(LoginSettingsForProduct.__fields__.keys()) + settings_atrributes = set(LoginSettingsForProduct.model_fields.keys()) assert customizable_attributes.issubset(settings_atrributes) diff --git a/services/web/server/tests/unit/with_dbs/03/test_users.py b/services/web/server/tests/unit/with_dbs/03/test_users.py index 6fa9a78fd44..055a0ffbb50 100644 --- a/services/web/server/tests/unit/with_dbs/03/test_users.py +++ b/services/web/server/tests/unit/with_dbs/03/test_users.py @@ -149,7 +149,7 @@ async def test_update_profile( data, _ = await assert_status(resp, status.HTTP_200_OK) # This is a PUT! i.e. full replace of profile variable fields! - assert data["first_name"] == ProfileUpdate.__fields__["first_name"].default + assert data["first_name"] == ProfileUpdate.model_fields["first_name"].default assert data["last_name"] == "Foo" assert data["role"] == user_role.name @@ -381,7 +381,7 @@ def test_preuserprofile_parse_model_without_extras( account_request_form: dict[str, Any] ): required = { - f.alias or f.name for f in PreUserProfile.__fields__.values() if f.required + f.alias or f.name for f in PreUserProfile.model_fields.values() if f.required } data = {k: account_request_form[k] for k in required} assert not PreUserProfile(**data).extras From b32370617dc894666060b3fa4035b47161e1eb6d Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 25 Oct 2024 10:42:45 +0200 Subject: [PATCH 033/165] fix auto_default_from_env prop --- .../web/server/src/simcore_service_webserver/login/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/login/settings.py b/services/web/server/src/simcore_service_webserver/login/settings.py index 8efaf2440be..691384cf0f7 100644 --- a/services/web/server/src/simcore_service_webserver/login/settings.py +++ b/services/web/server/src/simcore_service_webserver/login/settings.py @@ -36,7 +36,7 @@ class LoginSettings(BaseCustomSettings): ) LOGIN_TWILIO: TwilioSettings | None = Field( - auto_default_from_env=True, + json_schema_extra={"auto_default_from_env": True}, description="Twilio service settings. Used to send SMS for 2FA", ) From a605f2fe4307f0dc260076e6bcdb7c18957ca191 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 25 Oct 2024 11:40:32 +0200 Subject: [PATCH 034/165] fix envs --- .../server/tests/unit/isolated/conftest.py | 19 +++++++++++++++++++ .../isolated/test_application_settings.py | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/services/web/server/tests/unit/isolated/conftest.py b/services/web/server/tests/unit/isolated/conftest.py index 9cc0948ff88..edce6f736d3 100644 --- a/services/web/server/tests/unit/isolated/conftest.py +++ b/services/web/server/tests/unit/isolated/conftest.py @@ -178,6 +178,7 @@ def mock_webserver_service_environment( mock_env_devel_environment: EnvVarsDict, # pylint: disable=redefined-outer-name mock_env_dockerfile_build: EnvVarsDict, # pylint: disable=redefined-outer-name mock_env_deployer_pipeline: EnvVarsDict, # pylint: disable=redefined-outer-name + faker: Faker ) -> EnvVarsDict: """ Mocks environment produce in the docker compose config with a .env (.env-devel) @@ -219,6 +220,24 @@ def mock_webserver_service_environment( "SWARM_STACK_NAME": os.environ.get("SWARM_STACK_NAME", "simcore"), "WEBSERVER_LOGLEVEL": os.environ.get("LOG_LEVEL", "WARNING"), "SESSION_COOKIE_MAX_AGE": str(7 * 24 * 60 * 60), + "WEBSERVER_EMAIL": json.dumps({ + "SMTP_HOST": faker.url(), + "SMTP_PORT": faker.port_number() + }), + "WEBSERVER_LOGIN": json.dumps({ + "LOGIN_REGISTRATION_INVITATION_REQUIRED": True + }), + "WEBSERVER_PAYMENTS": json.dumps({ + "PAYMENTS_USERNAME": faker.user_name(), + "PAYMENTS_PASSWORD": faker.password() + }), + "WEBSERVER_SCICRUNCH": json.dumps({ + "SCICRUNCH_API_KEY": faker.pystr() + }), + "WEBSERVER_TRACING": json.dumps({ + "TRACING_OPENTELEMETRY_COLLECTOR_ENDPOINT": faker.url(), + "TRACING_OPENTELEMETRY_COLLECTOR_PORT": faker.port_number() + }) }, ) diff --git a/services/web/server/tests/unit/isolated/test_application_settings.py b/services/web/server/tests/unit/isolated/test_application_settings.py index 32487b234ce..42124d848c6 100644 --- a/services/web/server/tests/unit/isolated/test_application_settings.py +++ b/services/web/server/tests/unit/isolated/test_application_settings.py @@ -22,9 +22,10 @@ def app_settings( ) -> ApplicationSettings: app = web.Application() + print("envs\n", json.dumps(mock_webserver_service_environment, indent=1)) + # init and validation happens here settings = setup_settings(app) - print("envs\n", json.dumps(mock_webserver_service_environment, indent=1)) print("settings:\n", settings.model_dump_json(indent=1)) assert APP_SETTINGS_KEY in app From 32043a47b8357c4bb35bcf0b131fe308ce239a60 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 25 Oct 2024 11:53:17 +0200 Subject: [PATCH 035/165] fix sleep --- .../server/tests/unit/isolated/test_diagnostics_healthcheck.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py b/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py index f35d7991539..8ade35a1521 100644 --- a/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py +++ b/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py @@ -119,7 +119,7 @@ async def expected_failure(request: web.Request): @routes.get(r"/slow") async def blocking_slow(request: web.Request): - time.sleep(SLOW_HANDLER_DELAY_SECS * 1.1) + await asyncio.sleep(SLOW_HANDLER_DELAY_SECS * 1.1) return web.json_response({"data": True, "error": None}) @routes.get(r"/cancel") From 2c062e362f1e64a51a8782a91c6979fa535e0059 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 25 Oct 2024 11:53:57 +0200 Subject: [PATCH 036/165] fix first elem get --- .../src/simcore_service_webserver/projects/projects_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/projects_api.py b/services/web/server/src/simcore_service_webserver/projects/projects_api.py index 856b5251639..1249df58b0a 100644 --- a/services/web/server/src/simcore_service_webserver/projects/projects_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/projects_api.py @@ -1425,7 +1425,7 @@ async def _get_project_lock_state( ) return ProjectLocked( value=False, - owner=Owner(user_id=list(set_user_ids)[0], **usernames[0]), # type: ignore[arg-type] + owner=Owner(user_id=next(iter(set_user_ids)), **usernames[0]), status=ProjectStatus.OPENED, ) # the project is opened in another tab or browser, or by another user, both case resolves to the project being locked, and opened @@ -1436,7 +1436,7 @@ async def _get_project_lock_state( ) return ProjectLocked( value=True, - owner=Owner(user_id=list(set_user_ids)[0], **usernames[0]), # type: ignore[arg-type] + owner=Owner(user_id=next(iter(set_user_ids)), **usernames[0]), status=ProjectStatus.OPENED, ) From b9a4f883c3a4219eb8d874ad2a64bc1f2fa64abd Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 25 Oct 2024 11:57:57 +0200 Subject: [PATCH 037/165] fix import --- .../server/tests/unit/isolated/test_diagnostics_healthcheck.py | 1 - 1 file changed, 1 deletion(-) diff --git a/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py b/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py index 8ade35a1521..90a75a0cc4d 100644 --- a/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py +++ b/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py @@ -6,7 +6,6 @@ import asyncio import logging -import time from collections.abc import Callable, Coroutine import pytest From a2e1af67282392da2ea4784befbba11974a9dde7 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 25 Oct 2024 12:23:09 +0200 Subject: [PATCH 038/165] fix envvar --- .../server/tests/unit/isolated/conftest.py | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/services/web/server/tests/unit/isolated/conftest.py b/services/web/server/tests/unit/isolated/conftest.py index edce6f736d3..802076d9313 100644 --- a/services/web/server/tests/unit/isolated/conftest.py +++ b/services/web/server/tests/unit/isolated/conftest.py @@ -96,12 +96,31 @@ def mock_env_deployer_pipeline(monkeypatch: pytest.MonkeyPatch) -> EnvVarsDict: def mock_env_devel_environment( mock_env_devel_environment: EnvVarsDict, # pylint: disable=redefined-outer-name monkeypatch: pytest.MonkeyPatch, + faker: Faker ) -> EnvVarsDict: # Overrides to ensure dev-features are enabled testings return mock_env_devel_environment | setenvs_from_dict( monkeypatch, envs={ "WEBSERVER_DEV_FEATURES_ENABLED": "1", + "WEBSERVER_EMAIL": json.dumps({ + "SMTP_HOST": faker.url(), + "SMTP_PORT": faker.port_number() + }), + "WEBSERVER_LOGIN": json.dumps({ + "LOGIN_REGISTRATION_INVITATION_REQUIRED": True + }), + "WEBSERVER_PAYMENTS": json.dumps({ + "PAYMENTS_USERNAME": faker.user_name(), + "PAYMENTS_PASSWORD": faker.password() + }), + "WEBSERVER_SCICRUNCH": json.dumps({ + "SCICRUNCH_API_KEY": faker.pystr() + }), + "WEBSERVER_TRACING": json.dumps({ + "TRACING_OPENTELEMETRY_COLLECTOR_ENDPOINT": faker.url(), + "TRACING_OPENTELEMETRY_COLLECTOR_PORT": faker.port_number() + }) }, ) @@ -178,7 +197,6 @@ def mock_webserver_service_environment( mock_env_devel_environment: EnvVarsDict, # pylint: disable=redefined-outer-name mock_env_dockerfile_build: EnvVarsDict, # pylint: disable=redefined-outer-name mock_env_deployer_pipeline: EnvVarsDict, # pylint: disable=redefined-outer-name - faker: Faker ) -> EnvVarsDict: """ Mocks environment produce in the docker compose config with a .env (.env-devel) @@ -219,26 +237,8 @@ def mock_webserver_service_environment( "STORAGE_PORT": os.environ.get("STORAGE_PORT", "8080"), "SWARM_STACK_NAME": os.environ.get("SWARM_STACK_NAME", "simcore"), "WEBSERVER_LOGLEVEL": os.environ.get("LOG_LEVEL", "WARNING"), - "SESSION_COOKIE_MAX_AGE": str(7 * 24 * 60 * 60), - "WEBSERVER_EMAIL": json.dumps({ - "SMTP_HOST": faker.url(), - "SMTP_PORT": faker.port_number() - }), - "WEBSERVER_LOGIN": json.dumps({ - "LOGIN_REGISTRATION_INVITATION_REQUIRED": True - }), - "WEBSERVER_PAYMENTS": json.dumps({ - "PAYMENTS_USERNAME": faker.user_name(), - "PAYMENTS_PASSWORD": faker.password() - }), - "WEBSERVER_SCICRUNCH": json.dumps({ - "SCICRUNCH_API_KEY": faker.pystr() - }), - "WEBSERVER_TRACING": json.dumps({ - "TRACING_OPENTELEMETRY_COLLECTOR_ENDPOINT": faker.url(), - "TRACING_OPENTELEMETRY_COLLECTOR_PORT": faker.port_number() - }) - }, + "SESSION_COOKIE_MAX_AGE": str(7 * 24 * 60 * 60) + } ) return ( From 8421102876ce4485229359391cd8d082d8097333 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 25 Oct 2024 13:07:34 +0200 Subject: [PATCH 039/165] fix test --- .../tests/unit/isolated/test_diagnostics_healthcheck.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py b/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py index 90a75a0cc4d..0e625158474 100644 --- a/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py +++ b/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py @@ -5,6 +5,7 @@ # pylint: disable=unused-variable import asyncio +import json import logging from collections.abc import Callable, Coroutine @@ -87,9 +88,11 @@ def mock_environment( { **mock_env_devel_environment, "AIODEBUG_SLOW_DURATION_SECS": f"{SLOW_HANDLER_DELAY_SECS / 10}", - "DIAGNOSTICS_MAX_TASK_DELAY": f"{SLOW_HANDLER_DELAY_SECS}", - "DIAGNOSTICS_MAX_AVG_LATENCY": f"{2.0}", - "DIAGNOSTICS_START_SENSING_DELAY": f"{0}", + "WEBSERVER_DIAGNOSTICS": json.dumps({ + "DIAGNOSTICS_MAX_AVG_LATENCY": "2.0", + "DIAGNOSTICS_MAX_TASK_DELAY": f"{SLOW_HANDLER_DELAY_SECS}", + "DIAGNOSTICS_START_SENSING_DELAY": f"{0}", + }), "SC_HEALTHCHECK_TIMEOUT": "2m", "DIAGNOSTICS_HEALTHCHECK_ENABLED": "1", }, From fe537633f6b683120aeadfb78268c61f677d45f6 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 25 Oct 2024 13:21:10 +0200 Subject: [PATCH 040/165] revert time --- .../server/tests/unit/isolated/test_diagnostics_healthcheck.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py b/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py index 0e625158474..e71c1b83b91 100644 --- a/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py +++ b/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py @@ -8,6 +8,7 @@ import json import logging from collections.abc import Callable, Coroutine +import time import pytest import simcore_service_webserver @@ -121,7 +122,7 @@ async def expected_failure(request: web.Request): @routes.get(r"/slow") async def blocking_slow(request: web.Request): - await asyncio.sleep(SLOW_HANDLER_DELAY_SECS * 1.1) + time.sleep(SLOW_HANDLER_DELAY_SECS * 1.1) return web.json_response({"data": True, "error": None}) @routes.get(r"/cancel") From 00970e33054eeee60688b35ac1b9d08b7f47c5e3 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 25 Oct 2024 14:01:55 +0200 Subject: [PATCH 041/165] fix diagnostic settings mock --- .../server/tests/unit/isolated/test_diagnostics_healthcheck.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py b/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py index e71c1b83b91..b35b2b378f4 100644 --- a/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py +++ b/services/web/server/tests/unit/isolated/test_diagnostics_healthcheck.py @@ -93,9 +93,9 @@ def mock_environment( "DIAGNOSTICS_MAX_AVG_LATENCY": "2.0", "DIAGNOSTICS_MAX_TASK_DELAY": f"{SLOW_HANDLER_DELAY_SECS}", "DIAGNOSTICS_START_SENSING_DELAY": f"{0}", + "DIAGNOSTICS_HEALTHCHECK_ENABLED": "1", }), "SC_HEALTHCHECK_TIMEOUT": "2m", - "DIAGNOSTICS_HEALTHCHECK_ENABLED": "1", }, ) From 2e6fc93930306955ea9265975ef40ae84c5dbc56 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 25 Oct 2024 14:18:46 +0200 Subject: [PATCH 042/165] fix usernotfound error --- .../simcore_service_webserver/users/exceptions.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/users/exceptions.py b/services/web/server/src/simcore_service_webserver/users/exceptions.py index 51fb1cc2b19..39791ea39fe 100644 --- a/services/web/server/src/simcore_service_webserver/users/exceptions.py +++ b/services/web/server/src/simcore_service_webserver/users/exceptions.py @@ -9,12 +9,16 @@ class UsersBaseError(WebServerBaseError): class UserNotFoundError(UsersBaseError): def __init__(self, *, uid: int | None = None, email: str | None = None, **ctx: Any): - super().__init__(**ctx) + super().__init__( + msg_template=( + "User id {uid} not found" + if uid + else f"User with email {email} not found" + ), + **ctx, + ) self.uid = uid self.email = email - self.msg_template = ( - "User id {uid} not found" if uid else f"User with email {email} not found" - ) class TokenNotFoundError(UsersBaseError): From 28f6d1aa9bd8af1d0fdd63cd1c6372d29a00124f Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 25 Oct 2024 15:29:25 +0200 Subject: [PATCH 043/165] continue fixing --- .../src/models_library/api_schemas_webserver/groups.py | 3 ++- .../src/simcore_service_webserver/login/settings.py | 10 +++++----- .../src/simcore_service_webserver/products/_model.py | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/models-library/src/models_library/api_schemas_webserver/groups.py b/packages/models-library/src/models_library/api_schemas_webserver/groups.py index 46e9da3dc52..8ab467a3dfc 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/groups.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/groups.py @@ -1,6 +1,7 @@ from contextlib import suppress from pydantic import ( + AnyHttpUrl, AnyUrl, BaseModel, ConfigDict, @@ -90,7 +91,7 @@ def _sanitize_legacy_data(cls, v): if v: # Enforces null if thumbnail is not valid URL or empty with suppress(ValidationError): - return TypeAdapter(AnyUrl).validate_python(v) + return TypeAdapter(AnyHttpUrl).validate_python(v) return None diff --git a/services/web/server/src/simcore_service_webserver/login/settings.py b/services/web/server/src/simcore_service_webserver/login/settings.py index 691384cf0f7..91ee1041889 100644 --- a/services/web/server/src/simcore_service_webserver/login/settings.py +++ b/services/web/server/src/simcore_service_webserver/login/settings.py @@ -2,7 +2,7 @@ from typing import Final, Literal from aiohttp import web -from pydantic import BaseModel, field_validator +from pydantic import BaseModel, ValidationInfo, field_validator from pydantic.fields import Field from pydantic.types import PositiveFloat, PositiveInt, SecretStr from settings_library.base import BaseCustomSettings @@ -56,17 +56,17 @@ class LoginSettings(BaseCustomSettings): @field_validator("LOGIN_2FA_REQUIRED") @classmethod - def login_2fa_needs_email_registration(cls, v, values): + def _login_2fa_needs_email_registration(cls, v, info: ValidationInfo): # NOTE: this constraint ensures that a phone is registered in current workflow - if v and not values.get("LOGIN_REGISTRATION_CONFIRMATION_REQUIRED", False): + if v and not info.data.get("LOGIN_REGISTRATION_CONFIRMATION_REQUIRED", False): msg = "Cannot enable 2FA w/o email confirmation" raise ValueError(msg) return v @field_validator("LOGIN_2FA_REQUIRED") @classmethod - def login_2fa_needs_sms_service(cls, v, values): - if v and values.get("LOGIN_TWILIO") is None: + def _login_2fa_needs_sms_service(cls, v, info: ValidationInfo): + if v and info.data.get("LOGIN_TWILIO") is None: msg = "Cannot enable 2FA w/o twilio settings which is used to send SMS" raise ValueError(msg) return v diff --git a/services/web/server/src/simcore_service_webserver/products/_model.py b/services/web/server/src/simcore_service_webserver/products/_model.py index a71ca2a201d..d2fbb96b816 100644 --- a/services/web/server/src/simcore_service_webserver/products/_model.py +++ b/services/web/server/src/simcore_service_webserver/products/_model.py @@ -267,7 +267,7 @@ def get_template_name_for(self, filename: str) -> str | None: """Checks for field marked with 'x_template_name' that fits the argument""" template_name = filename.removesuffix(".jinja2") for name, field in self.model_fields.items(): - if field.json_schema_extra.get("x_template_name") == template_name: + if field.json_schema_extra and field.json_schema_extra.get("x_template_name") == template_name: template_name_attribute: str = getattr(self, name) return template_name_attribute return None From d11850d3cce373784d6ea621feacf3198f28f9b1 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 25 Oct 2024 16:18:11 +0200 Subject: [PATCH 044/165] fix Url --- .../web/server/src/simcore_service_webserver/statics/_events.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/statics/_events.py b/services/web/server/src/simcore_service_webserver/statics/_events.py index 49cef6deb64..d5731754f02 100644 --- a/services/web/server/src/simcore_service_webserver/statics/_events.py +++ b/services/web/server/src/simcore_service_webserver/statics/_events.py @@ -64,7 +64,7 @@ async def create_cached_indexes(app: web.Application) -> None: session: ClientSession = get_client_session(app) for frontend_name in FRONTEND_APPS_AVAILABLE: - url = URL(settings.STATIC_WEBSERVER_URL) / frontend_name + url = URL(f"{settings.STATIC_WEBSERVER_URL}") / frontend_name _logger.info("Fetching index from %s", url) try: body = "" From 5b2fd380239a20fa34b1994252c8bb229d1058f0 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 25 Oct 2024 16:33:32 +0200 Subject: [PATCH 045/165] fix mypy --- .../director_v2/_core_computations.py | 30 +++++++------------ .../payments/_onetime_api.py | 9 ++---- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/director_v2/_core_computations.py b/services/web/server/src/simcore_service_webserver/director_v2/_core_computations.py index 3cdefd8426e..de8767c7927 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/_core_computations.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/_core_computations.py @@ -4,12 +4,12 @@ """ -import json import logging from typing import Any from uuid import UUID from aiohttp import web +from common_library.serialization import model_dump_with_secrets from models_library.api_schemas_directorv2.clusters import ( ClusterCreate, ClusterDetails, @@ -30,7 +30,6 @@ from pydantic.types import PositiveInt from servicelib.aiohttp import status from servicelib.logging_utils import log_decorator -from settings_library.utils_encoders import create_json_encoder_wo_secrets from ..products.api import get_product from ._api_utils import get_wallet_info @@ -225,12 +224,8 @@ async def create_cluster( "POST", url=(settings.base_url / "clusters").update_query(user_id=int(user_id)), expected_status=web.HTTPCreated, - data=json.loads( - new_cluster.model_dump_json( - by_alias=True, - exclude_unset=True, - encoder=create_json_encoder_wo_secrets(ClusterCreate), - ) + data=model_dump_with_secrets( + new_cluster, show_secrets=True, by_alias=True, exclude_unset=True ), ) assert isinstance(cluster, dict) # nosec @@ -322,12 +317,8 @@ async def update_cluster( user_id=int(user_id) ), expected_status=web.HTTPOk, - data=json.loads( - cluster_patch.model_dump_json( - by_alias=True, - exclude_unset=True, - encoder=create_json_encoder_wo_secrets(ClusterPatch), - ) + data=model_dump_with_secrets( + cluster_patch, show_secrets=True, by_alias=True, exclude_none=True ), on_error={ status.HTTP_404_NOT_FOUND: ( @@ -377,12 +368,11 @@ async def ping_cluster(app: web.Application, cluster_ping: ClusterPing) -> None: "POST", url=settings.base_url / "clusters:ping", expected_status=web.HTTPNoContent, - data=json.loads( - cluster_ping.model_dump_json( - by_alias=True, - exclude_unset=True, - encoder=create_json_encoder_wo_secrets(ClusterPing), - ) + data=model_dump_with_secrets( + cluster_ping, + show_secrets=True, + by_alias=True, + exclude_unset=True, ), on_error={ status.HTTP_422_UNPROCESSABLE_ENTITY: ( diff --git a/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py b/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py index 55c43e0d6c7..e1b75e8c00c 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py +++ b/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py @@ -1,6 +1,6 @@ import logging from decimal import Decimal -from typing import Any, cast +from typing import Any from uuid import uuid4 import arrow @@ -235,11 +235,8 @@ async def _fake_get_payment_invoice_url( assert user_id # nosec assert wallet_id # nosec - return cast( - HttpUrl, - TypeAdapter(HttpUrl).validate_python( - f"https://fake-invoice.com/?id={payment_id}" - ), + return TypeAdapter(HttpUrl).validate_python( + f"https://fake-invoice.com/?id={payment_id}" ) From 820592c91ece7f566a8a5173642ed4f3f577b259 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 25 Oct 2024 16:38:55 +0200 Subject: [PATCH 046/165] fix url --- .../src/simcore_service_webserver/payments/_onetime_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py b/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py index e1b75e8c00c..25f8ad0e5a1 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py +++ b/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py @@ -81,7 +81,7 @@ async def _fake_init_payment( # get_form_payment_url settings: PaymentsSettings = get_plugin_settings(app) external_form_link = ( - URL(settings.PAYMENTS_FAKE_GATEWAY_URL) + URL(f"{settings.PAYMENTS_FAKE_GATEWAY_URL}") .with_path("/pay") .with_query(id=payment_id) ) From f1d4df50b2e2a077c12a697554a14bc82a9e9c46 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 28 Oct 2024 09:22:21 +0100 Subject: [PATCH 047/165] fix mypy --- .../products/_invitations_handlers.py | 4 ++-- .../projects/_projects_nodes_pricing_unit_handlers.py | 4 ++-- .../web/server/src/simcore_service_webserver/projects/lock.py | 2 +- .../resource_usage/_service_runs_handlers.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/products/_invitations_handlers.py b/services/web/server/src/simcore_service_webserver/products/_invitations_handlers.py index bbb340b5fa6..fa2bb927405 100644 --- a/services/web/server/src/simcore_service_webserver/products/_invitations_handlers.py +++ b/services/web/server/src/simcore_service_webserver/products/_invitations_handlers.py @@ -55,13 +55,13 @@ async def generate_invitation(request: web.Request): assert generated.product == req_ctx.product_name # nosec assert generated.guest == body.guest # nosec - url = URL(generated.invitation_url) + url = URL(f"{generated.invitation_url}") invitation_link = request.url.with_path(url.path).with_fragment(url.raw_fragment) invitation = InvitationGenerated( product_name=generated.product, issuer=generated.issuer, - guest=generated.guest, # type: ignore[arg-type] + guest=generated.guest, trial_account_days=generated.trial_account_days, extra_credits_in_usd=generated.extra_credits_in_usd, created=generated.created, diff --git a/services/web/server/src/simcore_service_webserver/projects/_projects_nodes_pricing_unit_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_projects_nodes_pricing_unit_handlers.py index 208f3fc782d..62e031f92da 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_projects_nodes_pricing_unit_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_projects_nodes_pricing_unit_handlers.py @@ -6,12 +6,12 @@ import logging from aiohttp import web +from common_library.errors_classes import OsparcErrorMixin from models_library.api_schemas_webserver.resource_usage import PricingUnitGet from models_library.projects import ProjectID from models_library.projects_nodes_io import NodeID, NodeIDStr from models_library.resource_tracker import PricingPlanId, PricingUnitId from pydantic import BaseModel, ConfigDict -from pydantic.errors import PydanticErrorMixin from servicelib.aiohttp.requests_validation import parse_request_path_parameters_as from servicelib.aiohttp.typing_extension import Handler @@ -29,7 +29,7 @@ _logger = logging.getLogger(__name__) -class PricingUnitError(PydanticErrorMixin, ValueError): +class PricingUnitError(OsparcErrorMixin, ValueError): ... diff --git a/services/web/server/src/simcore_service_webserver/projects/lock.py b/services/web/server/src/simcore_service_webserver/projects/lock.py index 3141b7bca8d..9dc78fdba40 100644 --- a/services/web/server/src/simcore_service_webserver/projects/lock.py +++ b/services/web/server/src/simcore_service_webserver/projects/lock.py @@ -33,7 +33,7 @@ async def lock_project( PROJECT_REDIS_LOCK_KEY.format(project_uuid), timeout=PROJECT_LOCK_TIMEOUT.total_seconds(), ) - owner = Owner(user_id=user_id, **user_fullname) # type: ignore[arg-type] + owner = Owner(user_id=user_id, **user_fullname) async with common_lock_project( redis_lock, project_uuid=project_uuid, status=status, owner=owner diff --git a/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py b/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py index b760b517fe5..2a6cca6efac 100644 --- a/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py +++ b/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py @@ -165,7 +165,7 @@ async def list_resource_usage_services(request: web.Request): offset=query_params.offset, limit=query_params.limit, order_by=OrderBy.model_validate(query_params.order_by), - filters=TypeAdapter(ServiceResourceUsagesFilters | None).validate_python(query_params.filters), # type: ignore[arg-type] # from pydantic v2 --> https://github.com/pydantic/pydantic/discussions/4950 + filters=TypeAdapter(ServiceResourceUsagesFilters | None).validate_python(query_params.filters), ) page = Page[dict[str, Any]].model_validate( From 893d0dc4aa8d70c0687cfeedcbc1c41d12b89dd1 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 28 Oct 2024 10:34:34 +0100 Subject: [PATCH 048/165] fix test --- .../studies_dispatcher/_projects.py | 2 +- .../unit/isolated/test_studies_dispatcher_settings.py | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py index 5f6029215ff..c2d5b6f7a41 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py @@ -98,7 +98,7 @@ def _create_project( description=description, thumbnail=thumbnail, prjOwner=owner.email, - accessRights={owner.primary_gid: access_rights}, # type: ignore[dict-item] + accessRights={f"{owner.primary_gid}": access_rights}, creationDate=DateTimeStr(now_str()), lastChangeDate=DateTimeStr(now_str()), workbench=workbench, diff --git a/services/web/server/tests/unit/isolated/test_studies_dispatcher_settings.py b/services/web/server/tests/unit/isolated/test_studies_dispatcher_settings.py index e2255bbcf26..bc0b9456f71 100644 --- a/services/web/server/tests/unit/isolated/test_studies_dispatcher_settings.py +++ b/services/web/server/tests/unit/isolated/test_studies_dispatcher_settings.py @@ -51,9 +51,7 @@ def test_studies_dispatcher_settings_invalid_lifetime( StudiesDispatcherSettings.create_from_envs() validation_error: ErrorDict = exc_info.value.errors()[0] + assert validation_error["loc"] == ("STUDIES_GUEST_ACCOUNT_LIFETIME",) assert "-2" in validation_error["msg"] - assert validation_error == { - "loc": ("STUDIES_GUEST_ACCOUNT_LIFETIME",), - "type": "value_error", - "msg": validation_error["msg"], - } + assert validation_error["msg"] == validation_error["msg"] + assert validation_error["type"] == "value_error" From bb36b72c802c3abab2a66890c94b4cea3f932121 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 28 Oct 2024 10:57:57 +0100 Subject: [PATCH 049/165] continue fixing --- .../web/server/src/simcore_service_webserver/email/_core.py | 2 +- .../meta_modeling/_function_nodes.py | 2 +- .../web/server/src/simcore_service_webserver/products/_db.py | 2 +- .../server/src/simcore_service_webserver/products/_events.py | 2 +- .../simcore_service_webserver/studies_dispatcher/_projects.py | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/email/_core.py b/services/web/server/src/simcore_service_webserver/email/_core.py index 39070a4ced7..269687a95a7 100644 --- a/services/web/server/src/simcore_service_webserver/email/_core.py +++ b/services/web/server/src/simcore_service_webserver/email/_core.py @@ -36,7 +36,7 @@ async def _do_send_mail( WARNING: _do_send_mail is mocked so be careful when changing the signature or name !! """ - _logger.debug("Email configuration %s", settings.model_dump_json(indeent=1)) + _logger.debug("Email configuration %s", settings.model_dump_json(indent=1)) if settings.SMTP_PORT == 587: # NOTE: aiosmtplib does not handle port 587 correctly this is a workaround diff --git a/services/web/server/src/simcore_service_webserver/meta_modeling/_function_nodes.py b/services/web/server/src/simcore_service_webserver/meta_modeling/_function_nodes.py index c51f3374510..3e0e3a630f7 100644 --- a/services/web/server/src/simcore_service_webserver/meta_modeling/_function_nodes.py +++ b/services/web/server/src/simcore_service_webserver/meta_modeling/_function_nodes.py @@ -37,7 +37,7 @@ def create_param_node_from_iterator_with_outputs(iterator_node: Node) -> Node: label=iterator_node.label, inputs={}, inputNodes=[], - thumbnail="", # type: ignore[arg-type] # NOTE: hack due to issue in projects json-schema + thumbnail="", # NOTE: hack due to issue in projects json-schema outputs=deepcopy(iterator_node.outputs), ) diff --git a/services/web/server/src/simcore_service_webserver/products/_db.py b/services/web/server/src/simcore_service_webserver/products/_db.py index 37a960bf9a4..d0317fdc61b 100644 --- a/services/web/server/src/simcore_service_webserver/products/_db.py +++ b/services/web/server/src/simcore_service_webserver/products/_db.py @@ -100,7 +100,7 @@ async def get_product(self, product_name: str) -> Product | None: return Product( **dict(row.items()), is_payment_enabled=payments.enabled, - credits_per_usd=payments.credits_per_usd, # type: ignore[arg-type] + credits_per_usd=payments.credits_per_usd, ) return None diff --git a/services/web/server/src/simcore_service_webserver/products/_events.py b/services/web/server/src/simcore_service_webserver/products/_events.py index f1e4601d7c7..836e43a902f 100644 --- a/services/web/server/src/simcore_service_webserver/products/_events.py +++ b/services/web/server/src/simcore_service_webserver/products/_events.py @@ -90,7 +90,7 @@ async def load_products_on_startup(app: web.Application): app_products[name] = Product( **dict(row.items()), is_payment_enabled=payments.enabled, - credits_per_usd=payments.credits_per_usd, # type: ignore[arg-type] + credits_per_usd=payments.credits_per_usd, ) assert name in FRONTEND_APPS_AVAILABLE # nosec diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py index c2d5b6f7a41..dbcc6b812b3 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py @@ -13,7 +13,7 @@ from aiohttp import web from models_library.projects import DateTimeStr, Project, ProjectID -from models_library.projects_access import AccessRights +from models_library.projects_access import AccessRights, GroupIDStr from models_library.projects_nodes import Node from models_library.projects_nodes_io import DownloadLink, NodeID, PortLink from models_library.projects_ui import StudyUI @@ -98,7 +98,7 @@ def _create_project( description=description, thumbnail=thumbnail, prjOwner=owner.email, - accessRights={f"{owner.primary_gid}": access_rights}, + accessRights={GroupIDStr(owner.primary_gid): access_rights}, creationDate=DateTimeStr(now_str()), lastChangeDate=DateTimeStr(now_str()), workbench=workbench, From 2100adcd47b95258f233061391bc8d58d34e359c Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 28 Oct 2024 11:47:44 +0100 Subject: [PATCH 050/165] fix test --- .../isolated/test_studies_dispatcher_projects_permalinks.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/web/server/tests/unit/isolated/test_studies_dispatcher_projects_permalinks.py b/services/web/server/tests/unit/isolated/test_studies_dispatcher_projects_permalinks.py index 03a0eb5920f..6858ac07ad9 100644 --- a/services/web/server/tests/unit/isolated/test_studies_dispatcher_projects_permalinks.py +++ b/services/web/server/tests/unit/isolated/test_studies_dispatcher_projects_permalinks.py @@ -44,13 +44,17 @@ def app_environment( "WEBSERVER_DIAGNOSTICS": "null", "WEBSERVER_DIRECTOR_V2": "null", "WEBSERVER_EXPORTER": "null", + "WEBSERVER_EMAIL": "null", "WEBSERVER_GARBAGE_COLLECTOR": "null", "WEBSERVER_GROUPS": "1", + "WEBSERVER_LOGIN": "null", "WEBSERVER_META_MODELING": "0", + "WEBSERVER_PAYMENTS": "null", "WEBSERVER_PRODUCTS": "1", "WEBSERVER_PUBLICATIONS": "0", "WEBSERVER_RABBITMQ": "null", "WEBSERVER_REMOTE_DEBUG": "0", + "WEBSERVER_SCICRUNCH": "null", "WEBSERVER_STORAGE": "null", "WEBSERVER_SOCKETIO": "0", "WEBSERVER_TAGS": "1", @@ -63,6 +67,7 @@ def app_environment( ) # NOTE: To see logs, use pytest -s --log-cli-level=DEBUG # setup_logging(level=logging.DEBUG) + print(env_vars) return env_vars From b500cd658b38abbe817f0c3751ccc85cc570e1a0 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 28 Oct 2024 12:09:18 +0100 Subject: [PATCH 051/165] continue fixing --- .../src/simcore_service_webserver/director_v2/_models.py | 4 ++-- .../src/simcore_service_webserver/products/_handlers.py | 2 +- .../src/simcore_service_webserver/session/access_policies.py | 4 ++-- .../simcore_service_webserver/studies_dispatcher/_projects.py | 4 ++-- .../web/server/src/simcore_service_webserver/users/schemas.py | 2 +- services/web/server/src/simcore_service_webserver/utils.py | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/director_v2/_models.py b/services/web/server/src/simcore_service_webserver/director_v2/_models.py index d25a4aa23e6..8503d9ce394 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/_models.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/_models.py @@ -45,7 +45,7 @@ def set_default_thumbnail_if_empty(cls, v, values): "examples": [ { "name": "My awesome cluster", - "type": ClusterType.ON_PREMISE, # can use also values from equivalent enum + "type": ClusterType.ON_PREMISE.value, # can use also values from equivalent enum "endpoint": "https://registry.osparc-development.fake.dev", "authentication": { "type": "simple", @@ -56,7 +56,7 @@ def set_default_thumbnail_if_empty(cls, v, values): { "name": "My AWS cluster", "description": "a AWS cluster administered by me", - "type": ClusterType.AWS, + "type": ClusterType.AWS.value, "owner": 154, "endpoint": "https://registry.osparc-development.fake.dev", "authentication": { diff --git a/services/web/server/src/simcore_service_webserver/products/_handlers.py b/services/web/server/src/simcore_service_webserver/products/_handlers.py index bd845e379ec..3f9db2d2cab 100644 --- a/services/web/server/src/simcore_service_webserver/products/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/products/_handlers.py @@ -70,7 +70,7 @@ async def _get_product(request: web.Request): except KeyError as err: raise web.HTTPNotFound(reason=f"{product_name=} not found") from err - assert GetProduct.model_config.extra == "ignore" # nosec + assert GetProduct.model_config["extra"] == "ignore" # nosec data = GetProduct(**product.model_dump(), templates=[]) return envelope_json_response(data) diff --git a/services/web/server/src/simcore_service_webserver/session/access_policies.py b/services/web/server/src/simcore_service_webserver/session/access_policies.py index 05736703563..8fd72f71caf 100644 --- a/services/web/server/src/simcore_service_webserver/session/access_policies.py +++ b/services/web/server/src/simcore_service_webserver/session/access_policies.py @@ -7,7 +7,7 @@ from aiohttp import web from aiohttp_session import Session -from pydantic import PositiveInt, validate_arguments +from pydantic import PositiveInt, validate_call from servicelib.aiohttp import status from servicelib.aiohttp.typing_extension import Handler @@ -64,7 +64,7 @@ def _access_tokens_cleanup_ctx(session: Session) -> Iterator[dict[str, _AccessTo session[_SESSION_GRANTED_ACCESS_TOKENS_KEY] = pruned_access_tokens -@validate_arguments +@validate_call def on_success_grant_session_access_to( name: str, *, diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py index dbcc6b812b3..ce5f7d2dae5 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py @@ -147,7 +147,7 @@ def _create_project_with_filepicker_and_service( viewer_info: ViewerInfo, ) -> Project: file_picker, file_picker_output_id = _create_file_picker( - download_link, output_label=None + f"{download_link}", output_label=None ) viewer_service = Node( @@ -348,7 +348,7 @@ async def get_or_create_project_with_file( ): # nodes file_picker, _ = _create_file_picker( - file_params.download_link, output_label=file_params.file_name + f"{file_params.download_link}", output_label=file_params.file_name ) # project diff --git a/services/web/server/src/simcore_service_webserver/users/schemas.py b/services/web/server/src/simcore_service_webserver/users/schemas.py index d22ac9e3037..0f2f8ee1684 100644 --- a/services/web/server/src/simcore_service_webserver/users/schemas.py +++ b/services/web/server/src/simcore_service_webserver/users/schemas.py @@ -92,7 +92,7 @@ class ProfileGet(BaseModel): { "id": 42, "login": "bla@foo.com", - "role": UserRole.ADMIN, + "role": UserRole.ADMIN.value, "expirationDate": "2022-09-14", "preferences": {}, }, diff --git a/services/web/server/src/simcore_service_webserver/utils.py b/services/web/server/src/simcore_service_webserver/utils.py index 2d5dd6fdd9f..1732106ba25 100644 --- a/services/web/server/src/simcore_service_webserver/utils.py +++ b/services/web/server/src/simcore_service_webserver/utils.py @@ -191,4 +191,4 @@ def compute_sha1_on_small_dataset(d: Any) -> SHA1Str: """ # SEE options in https://github.com/ijl/orjson#option data_bytes = orjson.dumps(d, option=orjson.OPT_NON_STR_KEYS | orjson.OPT_SORT_KEYS) - return cast(SHA1Str, hashlib.sha1(data_bytes).hexdigest()) # nosec + return SHA1Str(hashlib.sha1(data_bytes).hexdigest()) # nosec From f1a2f46572d2df9bd7fa574f910ea1fb774d45fb Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 28 Oct 2024 12:19:48 +0100 Subject: [PATCH 052/165] fix --- .../simcore_service_webserver/studies_dispatcher/_catalog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_catalog.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_catalog.py index 9e9d5921d5f..6e63b905bfc 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_catalog.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_catalog.py @@ -114,7 +114,7 @@ async def iter_latest_product_services( version=row.version, title=row.name, description=row.description, - thumbnail=row.thumbnail or settings.STUDIES_DEFAULT_SERVICE_THUMBNAIL, + thumbnail=row.thumbnail or f"{settings.STUDIES_DEFAULT_SERVICE_THUMBNAIL}", file_extensions=service_filetypes.get(row.key, []), ) From 8a7bc177cd903ffe8710c024a29d92477a055866 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 28 Oct 2024 12:34:29 +0100 Subject: [PATCH 053/165] continue fixing --- .../test_studies_dispatcher_models.py | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/services/web/server/tests/unit/isolated/test_studies_dispatcher_models.py b/services/web/server/tests/unit/isolated/test_studies_dispatcher_models.py index 3dbcd062d47..24570916463 100644 --- a/services/web/server/tests/unit/isolated/test_studies_dispatcher_models.py +++ b/services/web/server/tests/unit/isolated/test_studies_dispatcher_models.py @@ -63,14 +63,14 @@ def test_download_link_validators_1(url_in: str, expected_download_link: str): @pytest.fixture def file_and_service_params() -> dict[str, Any]: - return dict( - file_name="dataset_description.slsx", - file_size=_SIZEBYTES, - file_type="MSExcel", - viewer_key="simcore/services/dynamic/fooo", - viewer_version="1.0.0", - download_link=_DOWNLOAD_LINK, - ) + return { + "file_name": "dataset_description.slsx", + "file_size": _SIZEBYTES, + "file_type": "MSExcel", + "viewer_key": "simcore/services/dynamic/fooo", + "viewer_version": "1.0.0", + "download_link": _DOWNLOAD_LINK, + } def test_download_link_validators_2(file_and_service_params: dict[str, Any]): @@ -78,10 +78,10 @@ def test_download_link_validators_2(file_and_service_params: dict[str, Any]): assert params.download_link - assert params.download_link.host and params.download_link.host.endswith( + assert params.download_link.host + assert params.download_link.host.endswith( "s3.amazonaws.com" ) - assert params.download_link.host_type == "domain" query = parse_qs(params.download_link.query) assert {"AWSAccessKeyId", "Signature", "Expires", "x-amz-request-payer"} == set( @@ -105,12 +105,12 @@ def test_file_and_service_params(file_and_service_params: dict[str, Any]): def test_file_only_params(): - request_params = dict( - file_name="dataset_description.slsx", - file_size=_SIZEBYTES, - file_type="MSExcel", - download_link=_DOWNLOAD_LINK, - ) + request_params = { + "file_name": "dataset_description.slsx", + "file_size": _SIZEBYTES, + "file_type": "MSExcel", + "download_link": _DOWNLOAD_LINK, + } file_params = parse_obj_or_none(FileParams, request_params) assert file_params @@ -125,10 +125,10 @@ def test_file_only_params(): def test_service_only_params(): - request_params = dict( - viewer_key="simcore/services/dynamic/fooo", - viewer_version="1.0.0", - ) + request_params = { + "viewer_key": "simcore/services/dynamic/fooo", + "viewer_version": "1.0.0", + } file_params = parse_obj_or_none(FileParams, request_params) assert not file_params From 9a3b124b4b50e668ec89037db660012fc062be81 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 28 Oct 2024 12:44:18 +0100 Subject: [PATCH 054/165] silent sonar --- services/web/server/src/simcore_service_webserver/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/utils.py b/services/web/server/src/simcore_service_webserver/utils.py index 1732106ba25..0807cee96b1 100644 --- a/services/web/server/src/simcore_service_webserver/utils.py +++ b/services/web/server/src/simcore_service_webserver/utils.py @@ -191,4 +191,4 @@ def compute_sha1_on_small_dataset(d: Any) -> SHA1Str: """ # SEE options in https://github.com/ijl/orjson#option data_bytes = orjson.dumps(d, option=orjson.OPT_NON_STR_KEYS | orjson.OPT_SORT_KEYS) - return SHA1Str(hashlib.sha1(data_bytes).hexdigest()) # nosec + return SHA1Str(hashlib.sha1(data_bytes).hexdigest()) # nosec # NOSONAR From dcf7d83b27ceba977c8a18c50ed1671d1b66c8cc Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 28 Oct 2024 12:53:35 +0100 Subject: [PATCH 055/165] fix test --- .../web/server/tests/unit/isolated/test_users_models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/web/server/tests/unit/isolated/test_users_models.py b/services/web/server/tests/unit/isolated/test_users_models.py index fd0bbabbd90..a5670b5054e 100644 --- a/services/web/server/tests/unit/isolated/test_users_models.py +++ b/services/web/server/tests/unit/isolated/test_users_models.py @@ -1,5 +1,5 @@ from copy import deepcopy -from datetime import datetime +from datetime import UTC, datetime from pprint import pformat from typing import Any @@ -40,13 +40,13 @@ def test_user_models_examples( def test_profile_get_expiration_date(faker: Faker): - fake_expiration = datetime.utcnow() + fake_expiration = datetime.now(UTC) profile = ProfileGet( id=1, login=faker.email(), role=UserRole.ADMIN, - expiration_date=fake_expiration, + expiration_date=fake_expiration.date(), preferences={}, ) From 76e52b30064e254c60a5d95aeadd5b9495f76653 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 28 Oct 2024 13:57:04 +0100 Subject: [PATCH 056/165] fix --- .../src/common_library/pydantic_validators.py | 25 +++++++++++++ .../tests/test_pydantic_validators.py | 36 +++++++++++++++++-- .../studies_dispatcher/settings.py | 20 ++++++----- .../test_studies_dispatcher_settings.py | 5 +-- 4 files changed, 74 insertions(+), 12 deletions(-) diff --git a/packages/common-library/src/common_library/pydantic_validators.py b/packages/common-library/src/common_library/pydantic_validators.py index 60f6219fb13..534a2b11274 100644 --- a/packages/common-library/src/common_library/pydantic_validators.py +++ b/packages/common-library/src/common_library/pydantic_validators.py @@ -1,10 +1,35 @@ import datetime +import re import warnings from datetime import timedelta from pydantic import TypeAdapter, field_validator +def validate_legacy_timedelta_str(time_str: str | timedelta) -> str | timedelta: + if not isinstance(time_str, str): + return time_str + + # Match the format [-][DD ][HH:MM]SS[.ffffff] + match = re.match( + r"^(?P-)?(?:(?P\d+)\s)?(?:(?P\d+):)?(?:(?P\d+):)?(?P\d+)(?P\.\d+)?$", + time_str, + ) + if not match: + return time_str + + # Extract components with defaults if not present + sign = match.group("sign") or "" + days = match.group("days") or "0" + hours = match.group("hours") or "0" + minutes = match.group("minutes") or "0" + seconds = match.group("seconds") + fraction = match.group("fraction") or "" + + # Convert to the format [-][DD]D[,][HH:MM:]SS[.ffffff] + return f"{sign}{int(days)}D,{int(hours):02}:{int(minutes):02}:{seconds}{fraction}" + + def validate_numeric_string_as_timedelta(field: str): """Transforms a float/int number into a valid datetime as it used to work in the past""" diff --git a/packages/common-library/tests/test_pydantic_validators.py b/packages/common-library/tests/test_pydantic_validators.py index da1ccf95adb..89e57bc0999 100644 --- a/packages/common-library/tests/test_pydantic_validators.py +++ b/packages/common-library/tests/test_pydantic_validators.py @@ -1,13 +1,45 @@ from datetime import timedelta +from typing import Annotated import pytest -from common_library.pydantic_validators import validate_numeric_string_as_timedelta +from common_library.pydantic_validators import ( + validate_legacy_timedelta_str, + validate_numeric_string_as_timedelta, +) from faker import Faker -from pydantic import Field +from pydantic import BeforeValidator, Field from pydantic_settings import BaseSettings, SettingsConfigDict from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict +def test_validate_legacy_timedelta(monkeypatch: pytest.MonkeyPatch, faker: Faker): + class Settings(BaseSettings): + APP_NAME: str + REQUEST_TIMEOUT: Annotated[ + timedelta, BeforeValidator(validate_legacy_timedelta_str) + ] = Field(default=timedelta(hours=1)) + + model_config = SettingsConfigDict() + + app_name = faker.pystr() + env_vars: dict[str, str | bool] = {"APP_NAME": app_name} + + # without timedelta + setenvs_from_dict(monkeypatch, env_vars) + settings = Settings() + print(settings.model_dump()) + assert app_name == settings.APP_NAME + assert timedelta(hours=1) == settings.REQUEST_TIMEOUT + + # with timedelta in seconds + env_vars["REQUEST_TIMEOUT"] = "2 1:10:00" + setenvs_from_dict(monkeypatch, env_vars) + settings = Settings() + print(settings.model_dump()) + assert app_name == settings.APP_NAME + assert timedelta(days=2, hours=1, minutes=10) == settings.REQUEST_TIMEOUT + + def test_validate_timedelta_in_legacy_mode( monkeypatch: pytest.MonkeyPatch, faker: Faker ): diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/settings.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/settings.py index 38e3cf20969..31da8b9b8f1 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/settings.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/settings.py @@ -1,8 +1,9 @@ from datetime import timedelta +from typing import Annotated from aiohttp import web -from common_library.pydantic_validators import validate_numeric_string_as_timedelta -from pydantic import TypeAdapter, field_validator, ByteSize, HttpUrl +from common_library.pydantic_validators import validate_legacy_timedelta_str +from pydantic import BeforeValidator, ByteSize, HttpUrl, TypeAdapter, field_validator from pydantic.fields import Field from pydantic_settings import SettingsConfigDict from servicelib.aiohttp.application_keys import APP_SETTINGS_KEY @@ -15,19 +16,25 @@ class StudiesDispatcherSettings(BaseCustomSettings): description="If enabled, the study links are accessible to anonymous users", ) - STUDIES_GUEST_ACCOUNT_LIFETIME: timedelta = Field( + STUDIES_GUEST_ACCOUNT_LIFETIME: Annotated[ + timedelta, BeforeValidator(validate_legacy_timedelta_str) + ] = Field( default=timedelta(minutes=15), description="Sets lifetime of a guest user until it is logged out " " and removed by the GC", ) STUDIES_DEFAULT_SERVICE_THUMBNAIL: HttpUrl = Field( - default=TypeAdapter(HttpUrl).validate_python("https://via.placeholder.com/170x120.png"), + default=TypeAdapter(HttpUrl).validate_python( + "https://via.placeholder.com/170x120.png" + ), description="Default thumbnail for services or dispatch project with a service", ) STUDIES_DEFAULT_FILE_THUMBNAIL: HttpUrl = Field( - default=TypeAdapter(HttpUrl).validate_python("https://via.placeholder.com/170x120.png"), + default=TypeAdapter(HttpUrl).validate_python( + "https://via.placeholder.com/170x120.png" + ), description="Default thumbnail for dispatch projects with only data (i.e. file-picker)", ) @@ -51,9 +58,6 @@ def is_login_required(self): """ return not self.STUDIES_ACCESS_ANONYMOUS_ALLOWED - _validate_studies_guest_account_lifetime = validate_numeric_string_as_timedelta( - "STUDIES_GUEST_ACCOUNT_LIFETIME" - ) model_config = SettingsConfigDict( json_schema_extra={ "example": { diff --git a/services/web/server/tests/unit/isolated/test_studies_dispatcher_settings.py b/services/web/server/tests/unit/isolated/test_studies_dispatcher_settings.py index bc0b9456f71..e8ad20b82c0 100644 --- a/services/web/server/tests/unit/isolated/test_studies_dispatcher_settings.py +++ b/services/web/server/tests/unit/isolated/test_studies_dispatcher_settings.py @@ -37,8 +37,9 @@ def test_studies_dispatcher_settings(environment: EnvVarsDict): assert not settings.is_login_required() # 2 days 1h and 10 mins - assert settings.STUDIES_GUEST_ACCOUNT_LIFETIME == timedelta( - days=2, hours=1, minutes=10 + assert ( + timedelta(days=2, hours=1, minutes=10) + == settings.STUDIES_GUEST_ACCOUNT_LIFETIME ) From 0aae0436d4a37be048ed9a905e6aefb57e1190e0 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 28 Oct 2024 14:29:03 +0100 Subject: [PATCH 057/165] fix test --- .../src/common_library/pydantic_validators.py | 4 ++-- .../studies_dispatcher/settings.py | 12 +++++++----- .../isolated/test_studies_dispatcher_settings.py | 3 +-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/common-library/src/common_library/pydantic_validators.py b/packages/common-library/src/common_library/pydantic_validators.py index 534a2b11274..471ba1a4bf3 100644 --- a/packages/common-library/src/common_library/pydantic_validators.py +++ b/packages/common-library/src/common_library/pydantic_validators.py @@ -6,7 +6,7 @@ from pydantic import TypeAdapter, field_validator -def validate_legacy_timedelta_str(time_str: str | timedelta) -> str | timedelta: +def _validate_legacy_timedelta_str(time_str: str | timedelta) -> str | timedelta: if not isinstance(time_str, str): return time_str @@ -54,7 +54,7 @@ def _numeric_string_as_timedelta( return converted_value except ValueError: # returns format like "1:00:00" - return v + return _validate_legacy_timedelta_str(v) return v return field_validator(field, mode="before")(_numeric_string_as_timedelta) diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/settings.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/settings.py index 31da8b9b8f1..00024ce2d23 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/settings.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/settings.py @@ -2,8 +2,8 @@ from typing import Annotated from aiohttp import web -from common_library.pydantic_validators import validate_legacy_timedelta_str -from pydantic import BeforeValidator, ByteSize, HttpUrl, TypeAdapter, field_validator +from common_library.pydantic_validators import validate_numeric_string_as_timedelta +from pydantic import ByteSize, HttpUrl, TypeAdapter, field_validator from pydantic.fields import Field from pydantic_settings import SettingsConfigDict from servicelib.aiohttp.application_keys import APP_SETTINGS_KEY @@ -16,9 +16,7 @@ class StudiesDispatcherSettings(BaseCustomSettings): description="If enabled, the study links are accessible to anonymous users", ) - STUDIES_GUEST_ACCOUNT_LIFETIME: Annotated[ - timedelta, BeforeValidator(validate_legacy_timedelta_str) - ] = Field( + STUDIES_GUEST_ACCOUNT_LIFETIME: timedelta = Field( default=timedelta(minutes=15), description="Sets lifetime of a guest user until it is logged out " " and removed by the GC", @@ -58,6 +56,10 @@ def is_login_required(self): """ return not self.STUDIES_ACCESS_ANONYMOUS_ALLOWED + _validate_studies_guest_account_lifetime = validate_numeric_string_as_timedelta( + "STUDIES_GUEST_ACCOUNT_LIFETIME" + ) + model_config = SettingsConfigDict( json_schema_extra={ "example": { diff --git a/services/web/server/tests/unit/isolated/test_studies_dispatcher_settings.py b/services/web/server/tests/unit/isolated/test_studies_dispatcher_settings.py index e8ad20b82c0..5c4377d56fd 100644 --- a/services/web/server/tests/unit/isolated/test_studies_dispatcher_settings.py +++ b/services/web/server/tests/unit/isolated/test_studies_dispatcher_settings.py @@ -51,8 +51,7 @@ def test_studies_dispatcher_settings_invalid_lifetime( with pytest.raises(ValidationError) as exc_info: StudiesDispatcherSettings.create_from_envs() - validation_error: ErrorDict = exc_info.value.errors()[0] + validation_error: ErrorDict = next(iter(exc_info.value.errors())) assert validation_error["loc"] == ("STUDIES_GUEST_ACCOUNT_LIFETIME",) assert "-2" in validation_error["msg"] - assert validation_error["msg"] == validation_error["msg"] assert validation_error["type"] == "value_error" From 6b460497f7e561186f840928ea80018bb1150fb5 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 28 Oct 2024 15:16:13 +0100 Subject: [PATCH 058/165] fix test --- .../src/simcore_service_webserver/products/_model.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/products/_model.py b/services/web/server/src/simcore_service_webserver/products/_model.py index d2fbb96b816..14d6b626828 100644 --- a/services/web/server/src/simcore_service_webserver/products/_model.py +++ b/services/web/server/src/simcore_service_webserver/products/_model.py @@ -20,6 +20,7 @@ ConfigDict, Field, PositiveInt, + field_serializer, field_validator, ) from simcore_postgres_database.models.products import ( @@ -133,6 +134,10 @@ def _validate_name(cls, v): raise ValueError(msg) return v + @field_serializer("issues", "vendor") + def _preserve_snake_case(self, v: Any) -> Any: + return v + @property def twilio_alpha_numeric_sender_id(self) -> str: return self.short_name or self.display_name.replace(string.punctuation, "")[:11] @@ -267,7 +272,10 @@ def get_template_name_for(self, filename: str) -> str | None: """Checks for field marked with 'x_template_name' that fits the argument""" template_name = filename.removesuffix(".jinja2") for name, field in self.model_fields.items(): - if field.json_schema_extra and field.json_schema_extra.get("x_template_name") == template_name: + if ( + field.json_schema_extra + and field.json_schema_extra.get("x_template_name") == template_name + ): template_name_attribute: str = getattr(self, name) return template_name_attribute return None From eec18e04f9586371ed3f856b2a6923e999fcfb4d Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 28 Oct 2024 15:36:29 +0100 Subject: [PATCH 059/165] fix typecheck --- .../server/src/simcore_service_webserver/catalog/_api.py | 8 ++++---- .../src/simcore_service_webserver/director_v2/_models.py | 6 +++--- .../src/simcore_service_webserver/socketio/models.py | 4 ++-- .../studies_dispatcher/_errors.py | 8 ++++---- .../studies_dispatcher/_projects.py | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/catalog/_api.py b/services/web/server/src/simcore_service_webserver/catalog/_api.py index 3b02e74f679..f2fc9be73a9 100644 --- a/services/web/server/src/simcore_service_webserver/catalog/_api.py +++ b/services/web/server/src/simcore_service_webserver/catalog/_api.py @@ -285,7 +285,7 @@ async def get_compatible_inputs_given_source_output( ) from_output: ServiceOutput = ServiceOutput.model_construct( - **service_output.model_dump(include=ServiceOutput.model_fields.keys()) + **service_output.model_dump(include=ServiceOutput.model_fields.keys()) # type: ignore[arg-type] ) # N inputs @@ -294,7 +294,7 @@ async def get_compatible_inputs_given_source_output( def iter_service_inputs() -> Iterator[tuple[ServiceInputKey, ServiceInput]]: for service_input in service_inputs: yield service_input.key_id, ServiceInput.model_construct( - **service_input.model_dump(include=ServiceInput.model_fields.keys()) + **service_input.model_dump(include=ServiceInput.model_fields.keys()) # type: ignore[arg-type] ) # check @@ -353,7 +353,7 @@ async def get_compatible_outputs_given_target_input( def iter_service_outputs() -> Iterator[tuple[ServiceOutputKey, ServiceOutput]]: for service_output in service_outputs: yield service_output.key_id, ServiceOutput.model_construct( - **service_output.model_dump(include=ServiceOutput.model_fields.keys()) + **service_output.model_dump(include=ServiceOutput.model_fields.keys()) # type: ignore[arg-type] ) # 1 input @@ -361,7 +361,7 @@ def iter_service_outputs() -> Iterator[tuple[ServiceOutputKey, ServiceOutput]]: to_service_key, to_service_version, to_input_key, ctx ) to_input: ServiceInput = ServiceInput.model_construct( - **service_input.model_dump(include=ServiceInput.model_fields.keys()) + **service_input.model_dump(include=ServiceInput.model_fields.keys()) # type: ignore[arg-type] ) # check diff --git a/services/web/server/src/simcore_service_webserver/director_v2/_models.py b/services/web/server/src/simcore_service_webserver/director_v2/_models.py index 8503d9ce394..5c42feadd77 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/_models.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/_models.py @@ -65,9 +65,9 @@ def set_default_thumbnail_if_empty(cls, v, values): "password": "somepassword", }, "access_rights": { - 154: CLUSTER_ADMIN_RIGHTS, - 12: CLUSTER_MANAGER_RIGHTS, - 7899: CLUSTER_USER_RIGHTS, + 154: CLUSTER_ADMIN_RIGHTS, # type: ignore + 12: CLUSTER_MANAGER_RIGHTS, # type: ignore + 7899: CLUSTER_USER_RIGHTS, # type: ignore }, }, ] diff --git a/services/web/server/src/simcore_service_webserver/socketio/models.py b/services/web/server/src/simcore_service_webserver/socketio/models.py index 5c348dff159..37bb942298b 100644 --- a/services/web/server/src/simcore_service_webserver/socketio/models.py +++ b/services/web/server/src/simcore_service_webserver/socketio/models.py @@ -12,11 +12,11 @@ from models_library.socketio import SocketMessageDict from models_library.users import UserID from models_library.utils.fastapi_encoders import jsonable_encoder -from pydantic import ConfigDict, BaseModel +from pydantic import ConfigDict, BaseModel, Field class WebSocketMessageBase(BaseModel): - event_type: Literal[...] = ... + event_type: str = Field(frozen=True) @classmethod def get_event_type(cls) -> str: diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_errors.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_errors.py index d68cc284190..5cff412f415 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_errors.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_errors.py @@ -6,22 +6,22 @@ class StudyDispatcherError(WebServerBaseError, ValueError): class IncompatibleService(StudyDispatcherError): - code = "studies_dispatcher.incompatible_service" + code = "studies_dispatcher.incompatible_service" # type: ignore msg_template = "None of the registered services can handle '{file_type}'" class FileToLarge(StudyDispatcherError): - code = "studies_dispatcher.file_to_large" + code = "studies_dispatcher.file_to_large" # type: ignore msg_template = "File size {file_size_in_mb} MB is over allowed limit" class ServiceNotFound(StudyDispatcherError): - code = "studies_dispatcher.service_not_found" + code = "studies_dispatcher.service_not_found" # type: ignore msg_template = "Service {service_key}:{service_version} not found" class InvalidRedirectionParams(StudyDispatcherError): - code = "studies_dispatcher.invalid_redirection_params" + code = "studies_dispatcher.invalid_redirection_params" # type: ignore msg_template = ( "The link you provided is invalid because it doesn't contain any information related to data or a service." " Please check the link and make sure it is correct." diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py index ce5f7d2dae5..53f61713a43 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py @@ -59,9 +59,9 @@ def _create_file_picker(download_link: str, output_label: str | None): data = {} data["downloadLink"] = url = TypeAdapter(AnyUrl).validate_python(download_link) if output_label: - data["label"] = Path(output_label).name + data["label"] = Path(output_label).name # type: ignore[assignment] elif url.path: - data["label"] = Path(url.path).name + data["label"] = Path(url.path).name # type: ignore[assignment] output = DownloadLink.model_validate(data) output_id = "outFile" From 4409fa250aead22178028d7028b3f256589db920 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 28 Oct 2024 16:32:10 +0100 Subject: [PATCH 060/165] fix bytesize validation --- services/web/server/tests/unit/with_dbs/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/server/tests/unit/with_dbs/conftest.py b/services/web/server/tests/unit/with_dbs/conftest.py index c5078809d3a..2e364019c3e 100644 --- a/services/web/server/tests/unit/with_dbs/conftest.py +++ b/services/web/server/tests/unit/with_dbs/conftest.py @@ -38,7 +38,7 @@ from models_library.api_schemas_directorv2.dynamic_services import DynamicServiceGet from models_library.products import ProductName from models_library.services_enums import ServiceState -from pydantic import ByteSize +from pydantic import ByteSize, TypeAdapter from pytest_mock import MockerFixture from pytest_simcore.helpers.dict_tools import ConfigDict from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict @@ -369,7 +369,7 @@ async def _mock_result(): mock3 = mocker.patch( "simcore_service_webserver.projects._crud_api_create.get_project_total_size_simcore_s3", autospec=True, - return_value=ByteSize("1Gib"), + return_value=TypeAdapter(ByteSize).validate_python("1Gib"), ) return MockedStorageSubsystem(mock, mock1, mock2, mock3) From dd174740b8944b167db46c2f212d87c8995bd952 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 29 Oct 2024 09:45:28 +0100 Subject: [PATCH 061/165] fix extra --- .../src/models_library/api_schemas_directorv2/clusters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models-library/src/models_library/api_schemas_directorv2/clusters.py b/packages/models-library/src/models_library/api_schemas_directorv2/clusters.py index 1c9892a7201..0e9b5d75f71 100644 --- a/packages/models-library/src/models_library/api_schemas_directorv2/clusters.py +++ b/packages/models-library/src/models_library/api_schemas_directorv2/clusters.py @@ -96,7 +96,7 @@ class ClusterGet(Cluster): alias="accessRights", default_factory=dict ) - model_config = ConfigDict(populate_by_name=True) + model_config = ConfigDict(extra="allow", populate_by_name=True) @model_validator(mode="before") @classmethod From 65d0f7c51863040ddbc257c124c94da48e747656 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 29 Oct 2024 10:20:16 +0100 Subject: [PATCH 062/165] continue fixing --- .../src/models_library/api_schemas_directorv2/clusters.py | 7 ++++--- .../src/simcore_service_webserver/projects/_groups_api.py | 2 +- .../unit/with_dbs/01/clusters/test_clusters_handlers.py | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/models-library/src/models_library/api_schemas_directorv2/clusters.py b/packages/models-library/src/models_library/api_schemas_directorv2/clusters.py index 0e9b5d75f71..3d6eb64c17b 100644 --- a/packages/models-library/src/models_library/api_schemas_directorv2/clusters.py +++ b/packages/models-library/src/models_library/api_schemas_directorv2/clusters.py @@ -7,6 +7,7 @@ Field, HttpUrl, NonNegativeFloat, + ValidationInfo, field_validator, model_validator, ) @@ -112,7 +113,7 @@ class ClusterDetailsGet(ClusterDetails): class ClusterCreate(BaseCluster): - owner: GroupID | None # type: ignore[assignment] + owner: GroupID | None authentication: ExternalClusterAuthentication access_rights: dict[GroupID, ClusterAccessRights] = Field( alias="accessRights", default_factory=dict @@ -154,9 +155,9 @@ class ClusterCreate(BaseCluster): @field_validator("thumbnail", mode="before") @classmethod - def set_default_thumbnail_if_empty(cls, v, values): + def set_default_thumbnail_if_empty(cls, v, info: ValidationInfo): if v is None: - cluster_type = values["type"] + cluster_type = info.data["type"] default_thumbnails = { ClusterTypeInModel.AWS.value: "https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Amazon_Web_Services_Logo.svg/250px-Amazon_Web_Services_Logo.svg.png", ClusterTypeInModel.ON_PREMISE.value: "https://upload.wikimedia.org/wikipedia/commons/thumb/a/ac/Crystal_Clear_app_network_local.png/120px-Crystal_Clear_app_network_local.png", diff --git a/services/web/server/src/simcore_service_webserver/projects/_groups_api.py b/services/web/server/src/simcore_service_webserver/projects/_groups_api.py index 53369af7720..7ae45f0f90c 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_groups_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/_groups_api.py @@ -80,7 +80,7 @@ async def list_project_groups_by_user_and_project( ] = await projects_groups_db.list_project_groups(app=app, project_id=project_id) project_groups_api: list[ProjectGroupGet] = [ - ProjectGroupGet.model_validate(group) for group in project_groups_db + ProjectGroupGet.model_validate(group.model_dump()) for group in project_groups_db ] return project_groups_api diff --git a/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py b/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py index b53ab561736..1e275998aab 100644 --- a/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py +++ b/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py @@ -94,6 +94,7 @@ def cluster_create(faker: Faker) -> ClusterCreate: name=faker.name(), endpoint=faker.uri(), type=random.choice(list(ClusterType)), + owner=faker.pyint(), authentication=SimpleAuthentication( username=faker.user_name(), password=faker.password() ), From 5ab5d99636a123218e0962f8dde76f440888ee6b Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 29 Oct 2024 10:45:54 +0100 Subject: [PATCH 063/165] upgrade new code --- .../src/models_library/api_schemas_webserver/projects.py | 2 +- .../src/simcore_service_webserver/projects/models.py | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/models-library/src/models_library/api_schemas_webserver/projects.py b/packages/models-library/src/models_library/api_schemas_webserver/projects.py index a892df7fe92..037db7204e7 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/projects.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/projects.py @@ -131,7 +131,7 @@ class ProjectPatch(InputSchema): ui: StudyUI | None = FieldNotRequired() quality: dict[str, Any] = FieldNotRequired() - _empty_is_none = field_validator("thumbnail", allow_reuse=True, pre=True)( + _empty_is_none = field_validator("thumbnail", mode="before")( empty_str_to_none_pre_validator ) diff --git a/services/web/server/src/simcore_service_webserver/projects/models.py b/services/web/server/src/simcore_service_webserver/projects/models.py index c3e982fdfa5..0f0f3766fb0 100644 --- a/services/web/server/src/simcore_service_webserver/projects/models.py +++ b/services/web/server/src/simcore_service_webserver/projects/models.py @@ -4,7 +4,6 @@ from aiopg.sa.result import RowProxy from models_library.api_schemas_webserver.projects import ProjectPatch -from models_library.basic_types import HttpUrlWithCustomMinLength from models_library.folders import FolderID from models_library.projects import ClassifierID, ProjectID from models_library.projects_ui import StudyUI @@ -14,7 +13,7 @@ none_to_empty_str_pre_validator, ) from models_library.workspaces import WorkspaceID -from pydantic import ConfigDict, BaseModel, HttpUrl, field_validator +from pydantic import BaseModel, ConfigDict, HttpUrl, field_validator from simcore_postgres_database.models.projects import ProjectType, projects ProjectDict: TypeAlias = dict[str, Any] @@ -96,9 +95,7 @@ class ProjectPatchExtended(ProjectPatch): # Only used internally trashed_at: datetime | None = None - class Config: - allow_population_by_field_name = True - extra = Extra.forbid + model_config = ConfigDict(populate_by_name=True, extra="forbid") __all__: tuple[str, ...] = ( From cc4b2cd0f3ff174a09a6fb7fe8d854104fe8462b Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 29 Oct 2024 11:43:42 +0100 Subject: [PATCH 064/165] continue fixing --- .../models_library/api_schemas_webserver/clusters.py | 2 +- .../models-library/src/models_library/clusters.py | 3 ++- .../with_dbs/01/clusters/test_clusters_handlers.py | 12 ++++++------ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/models-library/src/models_library/api_schemas_webserver/clusters.py b/packages/models-library/src/models_library/api_schemas_webserver/clusters.py index 109e0618b98..17232a8b482 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/clusters.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/clusters.py @@ -8,7 +8,7 @@ class ClusterPathParams(BaseModel): cluster_id: ClusterID model_config = ConfigDict( - populate_by_name=True, + populate_by_name=True, extra="forbid", ) diff --git a/packages/models-library/src/models_library/clusters.py b/packages/models-library/src/models_library/clusters.py index c98ea29757a..09540b8c16e 100644 --- a/packages/models-library/src/models_library/clusters.py +++ b/packages/models-library/src/models_library/clusters.py @@ -159,6 +159,7 @@ class Cluster(BaseCluster): id: ClusterID = Field(..., description="The cluster ID") model_config = ConfigDict( + extra="allow", json_schema_extra={ "examples": [ { @@ -217,7 +218,7 @@ class Cluster(BaseCluster): }, }, ] - } + }, ) @model_validator(mode="before") diff --git a/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py b/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py index 1e275998aab..7d03ed40ef3 100644 --- a/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py +++ b/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py @@ -45,19 +45,19 @@ def mocked_director_v2_api(mocker: MockerFixture): "simcore_service_webserver.clusters._handlers.director_v2_api", autospec=True ) - mocked_director_v2_api.create_cluster.return_value = Cluster.model_validate( - random.choice(Cluster.model_config["json_schema_extra"]["examples"]) + mocked_director_v2_api.create_cluster.return_value = random.choice( + Cluster.model_config["json_schema_extra"]["examples"] ) mocked_director_v2_api.list_clusters.return_value = [] - mocked_director_v2_api.get_cluster.return_value = Cluster.model_validate( - random.choice(Cluster.model_config["json_schema_extra"]["examples"]) + mocked_director_v2_api.get_cluster.return_value = random.choice( + Cluster.model_config["json_schema_extra"]["examples"] ) mocked_director_v2_api.get_cluster_details.return_value = { "scheduler": {"status": "running"}, "dashboardLink": "https://link.to.dashboard", } - mocked_director_v2_api.update_cluster.return_value = Cluster.model_validate( - random.choice(Cluster.model_config["json_schema_extra"]["examples"]) + mocked_director_v2_api.update_cluster.return_value = random.choice( + Cluster.model_config["json_schema_extra"]["examples"] ) mocked_director_v2_api.delete_cluster.return_value = None mocked_director_v2_api.ping_cluster.return_value = None From 3d916b22c8a99970d5bf72fd88c22864c8ca7c81 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 29 Oct 2024 13:37:08 +0100 Subject: [PATCH 065/165] continue fixing --- .../api_schemas_directorv2/clusters.py | 16 +++---- .../01/clusters/test_clusters_handlers.py | 43 +++++++++++++++++-- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/packages/models-library/src/models_library/api_schemas_directorv2/clusters.py b/packages/models-library/src/models_library/api_schemas_directorv2/clusters.py index 3d6eb64c17b..1e7bf14ca44 100644 --- a/packages/models-library/src/models_library/api_schemas_directorv2/clusters.py +++ b/packages/models-library/src/models_library/api_schemas_directorv2/clusters.py @@ -168,15 +168,15 @@ def set_default_thumbnail_if_empty(cls, v, info: ValidationInfo): class ClusterPatch(BaseCluster): - name: str | None # type: ignore[assignment] - description: str | None - type: ClusterTypeInModel | None # type: ignore[assignment] - owner: GroupID | None # type: ignore[assignment] - thumbnail: HttpUrl | None - endpoint: AnyUrl | None # type: ignore[assignment] - authentication: ExternalClusterAuthentication | None # type: ignore[assignment] + name: str | None = None # type: ignore[assignment] + description: str | None = None + type: ClusterTypeInModel | None = None # type: ignore[assignment] + owner: GroupID | None = None # type: ignore[assignment] + thumbnail: HttpUrl | None = None + endpoint: AnyUrl | None = None # type: ignore[assignment] + authentication: ExternalClusterAuthentication | None = None # type: ignore[assignment] access_rights: dict[GroupID, ClusterAccessRights] | None = Field( # type: ignore[assignment] - alias="accessRights" + None, alias="accessRights" ) model_config = ConfigDict( diff --git a/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py b/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py index 7d03ed40ef3..e88d13474fe 100644 --- a/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py +++ b/services/web/server/tests/unit/with_dbs/01/clusters/test_clusters_handlers.py @@ -12,6 +12,7 @@ from typing import Any import hypothesis +import hypothesis.provisional import pytest from aiohttp.test_utils import TestClient from faker import Faker @@ -21,7 +22,14 @@ ClusterPatch, ClusterPing, ) -from models_library.clusters import CLUSTER_ADMIN_RIGHTS, Cluster, SimpleAuthentication +from models_library.clusters import ( + CLUSTER_ADMIN_RIGHTS, + Cluster, + ClusterTypeInModel, + SimpleAuthentication, +) +from pydantic import HttpUrl, TypeAdapter +from pydantic_core import Url from pytest_mock import MockerFixture from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.webserver_parametrizations import ( # nopycln: import @@ -39,6 +47,29 @@ ) +@st.composite +def http_url_strategy(draw): + return TypeAdapter(HttpUrl).validate_python(draw(hypothesis.provisional.urls())) + + +@st.composite +def cluster_patch_strategy(draw): + return ClusterPatch( + name=draw(st.text()), + description=draw(st.text()), + owner=draw(st.integers(min_value=1)), + type=draw(st.sampled_from(ClusterTypeInModel)), + thumbnail=draw(http_url_strategy()), + endpoint=draw(http_url_strategy()), + authentication=None, + accessRights={}, + ) + + +st.register_type_strategy(ClusterPatch, cluster_patch_strategy()) +st.register_type_strategy(Url, http_url_strategy()) + + @pytest.fixture def mocked_director_v2_api(mocker: MockerFixture): mocked_director_v2_api = mocker.patch( @@ -262,7 +293,9 @@ async def test_ping_cluster( print(f"--> pinging {cluster_ping=!r}") assert client.app url = client.app.router["ping_cluster"].url_for() - rsp = await client.post(f"{url}", json=json.loads(cluster_ping.json(by_alias=True))) + rsp = await client.post( + f"{url}", json=json.loads(cluster_ping.model_dump_json(by_alias=True)) + ) data, error = await assert_status(rsp, expected.no_content) if not error: assert data is None @@ -310,7 +343,9 @@ async def test_create_cluster_with_error( url = client.app.router["create_cluster"].url_for() rsp = await client.post( f"{url}", - json=json.loads(cluster_create.json(by_alias=True, exclude_unset=True)), + json=json.loads( + cluster_create.model_dump_json(by_alias=True, exclude_unset=True) + ), ) data, error = await assert_status(rsp, expected_http_error) assert not data @@ -411,7 +446,7 @@ async def test_update_cluster_with_error( url = client.app.router["update_cluster"].url_for(cluster_id=f"{25}") rsp = await client.patch( f"{url}", - json=json.loads(ClusterPatch().json(**_PATCH_EXPORT)), + json=json.loads(ClusterPatch().model_dump_json(**_PATCH_EXPORT)), ) data, error = await assert_status(rsp, expected_http_error) assert not data From 5aa6da7fcb7d1de0a2594761fdcfba9b336bdd96 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 29 Oct 2024 14:05:07 +0100 Subject: [PATCH 066/165] fix --- .../src/models_library/api_schemas_webserver/groups.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/models-library/src/models_library/api_schemas_webserver/groups.py b/packages/models-library/src/models_library/api_schemas_webserver/groups.py index 8ab467a3dfc..d77c04288c9 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/groups.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/groups.py @@ -144,12 +144,14 @@ class AllUsersGroups(BaseModel): class GroupUserGet(BaseModel): - id: str | None = Field(None, description="the user id") + id: str | None = Field(None, description="the user id", coerce_numbers_to_str=True) login: LowerCaseEmailStr | None = Field(None, description="the user login email") first_name: str | None = Field(None, description="the user first name") last_name: str | None = Field(None, description="the user last name") gravatar_id: str | None = Field(None, description="the user gravatar id hash") - gid: str | None = Field(None, description="the user primary gid") + gid: str | None = Field( + None, description="the user primary gid", coerce_numbers_to_str=True + ) access_rights: GroupAccessRights = Field(..., alias="accessRights") model_config = ConfigDict( From 4781498cad02bd80ac5736f5fe3fc9f1a0142849 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 29 Oct 2024 14:20:59 +0100 Subject: [PATCH 067/165] fix --- .../src/pytest_simcore/hypothesis_type_strategies.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/pytest-simcore/src/pytest_simcore/hypothesis_type_strategies.py b/packages/pytest-simcore/src/pytest_simcore/hypothesis_type_strategies.py index d158a0694b5..ad80ab57774 100644 --- a/packages/pytest-simcore/src/pytest_simcore/hypothesis_type_strategies.py +++ b/packages/pytest-simcore/src/pytest_simcore/hypothesis_type_strategies.py @@ -1,5 +1,9 @@ +from hypothesis import provisional +from hypothesis import strategies as st +from pydantic import AnyHttpUrl, AnyUrl, HttpUrl + # FIXME: For now it seems the pydantic hypothesis plugin does not provide strategies for these types. # therefore we currently provide it -# st.register_type_strategy(AnyUrl, provisional.urls()) -# st.register_type_strategy(HttpUrl, provisional.urls()) -# st.register_type_strategy(AnyHttpUrl, provisional.urls()) +st.register_type_strategy(AnyUrl, provisional.urls()) +st.register_type_strategy(HttpUrl, provisional.urls()) +st.register_type_strategy(AnyHttpUrl, provisional.urls()) From 744e3662569aa2b5bce6f717203dee4969f893eb Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 29 Oct 2024 14:30:45 +0100 Subject: [PATCH 068/165] update strategies --- .../pytest_simcore/hypothesis_type_strategies.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/pytest-simcore/src/pytest_simcore/hypothesis_type_strategies.py b/packages/pytest-simcore/src/pytest_simcore/hypothesis_type_strategies.py index ad80ab57774..788a0d36dab 100644 --- a/packages/pytest-simcore/src/pytest_simcore/hypothesis_type_strategies.py +++ b/packages/pytest-simcore/src/pytest_simcore/hypothesis_type_strategies.py @@ -1,9 +1,13 @@ from hypothesis import provisional from hypothesis import strategies as st -from pydantic import AnyHttpUrl, AnyUrl, HttpUrl +from hypothesis.strategies import composite +from pydantic import TypeAdapter +from pydantic_core import Url -# FIXME: For now it seems the pydantic hypothesis plugin does not provide strategies for these types. -# therefore we currently provide it -st.register_type_strategy(AnyUrl, provisional.urls()) -st.register_type_strategy(HttpUrl, provisional.urls()) -st.register_type_strategy(AnyHttpUrl, provisional.urls()) + +@composite +def url_strategy(draw): + return TypeAdapter(Url).validate_python(draw(provisional.urls())) + + +st.register_type_strategy(Url, url_strategy()) From d0e1f4b9c2111fc8c469bea01c69d2dc27a49850 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 29 Oct 2024 14:35:22 +0100 Subject: [PATCH 069/165] fix import --- .../src/simcore_service_webserver/projects/_crud_handlers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py index 31d6fc28c03..3bb8562c3be 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py @@ -8,6 +8,7 @@ import logging from aiohttp import web +from common_library.json_serialization import json_dumps from models_library.api_schemas_webserver.projects import ( EmptyModel, ProjectCopyOverride, From fa50fac04d0e34f80061f36cec4fb5608462891c Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 29 Oct 2024 14:45:01 +0100 Subject: [PATCH 070/165] fix typecheck --- .../src/simcore_service_webserver/payments/_onetime_db.py | 2 +- .../web/server/src/simcore_service_webserver/products/_model.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/payments/_onetime_db.py b/services/web/server/src/simcore_service_webserver/payments/_onetime_db.py index 4cd7e30d328..05f1a84ccad 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_onetime_db.py +++ b/services/web/server/src/simcore_service_webserver/payments/_onetime_db.py @@ -103,7 +103,7 @@ async def complete_payment_transaction( payment_id=payment_id, completion_state=completion_state, state_message=state_message, - **optional_kwargs, + **optional_kwargs, # type: ignore[arg-type] ) if isinstance(row, PaymentNotFound): diff --git a/services/web/server/src/simcore_service_webserver/products/_model.py b/services/web/server/src/simcore_service_webserver/products/_model.py index 14d6b626828..48f9f20fa01 100644 --- a/services/web/server/src/simcore_service_webserver/products/_model.py +++ b/services/web/server/src/simcore_service_webserver/products/_model.py @@ -274,7 +274,7 @@ def get_template_name_for(self, filename: str) -> str | None: for name, field in self.model_fields.items(): if ( field.json_schema_extra - and field.json_schema_extra.get("x_template_name") == template_name + and field.json_schema_extra.get("x_template_name") == template_name # type: ignore[union-attr] ): template_name_attribute: str = getattr(self, name) return template_name_attribute From 60ee4c86472452bb0b978e36dabb501d9bb0b4e3 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 29 Oct 2024 15:20:13 +0100 Subject: [PATCH 071/165] fix urls --- .../02/test_projects_comments_handlers.py | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) 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 index 13fe3a7633a..604ee40308c 100644 --- 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 @@ -42,7 +42,7 @@ async def test_project_comments_user_role_access( base_url = client.app.router["list_project_comments"].url_for( project_uuid=user_project["uuid"] ) - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") assert resp.status == 401 if user_role == UserRole.ANONYMOUS else 200 @@ -65,7 +65,7 @@ async def test_project_comments_full_workflow( base_url = client.app.router["list_project_comments"].url_for( project_uuid=user_project["uuid"] ) - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data, _, meta, links = await assert_status( resp, expected, @@ -78,7 +78,7 @@ async def test_project_comments_full_workflow( # Now we will add first comment body = {"contents": "My first comment"} - resp = await client.post(base_url, json=body) + resp = await client.post(f"{base_url}", json=body) data, _ = await assert_status( resp, status.HTTP_201_CREATED, @@ -86,7 +86,7 @@ async def test_project_comments_full_workflow( first_comment_id = data["comment_id"] # Now we will add second comment - resp = await client.post(base_url, json={"contents": "My second comment"}) + resp = await client.post(f"{base_url}", json={"contents": "My second comment"}) data, _ = await assert_status( resp, status.HTTP_201_CREATED, @@ -94,7 +94,7 @@ async def test_project_comments_full_workflow( second_comment_id = data["comment_id"] # Now we will list all comments for the project - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data, _, meta, links = await assert_status( resp, expected, @@ -108,7 +108,7 @@ async def test_project_comments_full_workflow( # Now we will update the second comment updated_comment = "Updated second comment" resp = await client.put( - base_url / f"{second_comment_id}", + f"{base_url}/{second_comment_id}", json={"contents": updated_comment}, ) data, _ = await assert_status( @@ -117,7 +117,7 @@ async def test_project_comments_full_workflow( ) # Now we will get the second comment - resp = await client.get(base_url / f"{second_comment_id}") + resp = await client.get(f"{base_url}/{second_comment_id}") data, _ = await assert_status( resp, expected, @@ -125,14 +125,14 @@ async def test_project_comments_full_workflow( assert data["contents"] == updated_comment # Now we will delete the second comment - resp = await client.delete(base_url / f"{second_comment_id}") + 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(base_url) + resp = await client.get(f"{base_url}") data, _, meta, links = await assert_status( resp, expected, @@ -146,14 +146,14 @@ async def test_project_comments_full_workflow( # 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(base_url) + resp = await client.get(f"{base_url}") _, errors = await assert_status( resp, status.HTTP_403_FORBIDDEN, ) assert errors - resp = await client.get(base_url / f"{first_comment_id}") + resp = await client.get(f"{base_url}/{first_comment_id}") _, errors = await assert_status( resp, status.HTTP_403_FORBIDDEN, @@ -173,7 +173,7 @@ async def test_project_comments_full_workflow( # Now the user should have access to the project now # New user will add comment resp = await client.post( - base_url, + f"{base_url}", json={"contents": "My first comment as a new user"}, ) data, _ = await assert_status( @@ -185,7 +185,7 @@ async def test_project_comments_full_workflow( # New user will modify the comment updated_comment = "Updated My first comment as a new user" resp = await client.put( - base_url / f"{new_user_comment_id}", + f"{base_url}/{new_user_comment_id}", json={"contents": updated_comment}, ) data, _ = await assert_status( @@ -195,7 +195,7 @@ async def test_project_comments_full_workflow( assert data["contents"] == updated_comment # New user will list all comments - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data, _, meta, links = await assert_status( resp, expected, @@ -209,7 +209,7 @@ async def test_project_comments_full_workflow( # New user will modify comment of the previous user updated_comment = "Updated comment of previous user" resp = await client.put( - base_url / f"{first_comment_id}", + f"{base_url}/{first_comment_id}", json={"contents": updated_comment}, ) data, _ = await assert_status( @@ -219,7 +219,7 @@ async def test_project_comments_full_workflow( assert data["contents"] == updated_comment # New user will delete comment of the previous user - resp = await client.delete(base_url / f"{first_comment_id}") + resp = await client.delete(f"{base_url}/{first_comment_id}") data, _ = await assert_status( resp, status.HTTP_204_NO_CONTENT, From 1e29d7b132a443302744c7da6b8015a187da6031 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 29 Oct 2024 15:35:55 +0100 Subject: [PATCH 072/165] fix test --- .../unit/with_dbs/02/test_projects_nodes_handler.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py b/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py index 778738b052e..be79b2b81fd 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py @@ -75,12 +75,13 @@ async def test_get_node_resources( node_resources = TypeAdapter(ServiceResourcesDict).validate_python(data) assert node_resources assert DEFAULT_SINGLE_SERVICE_NAME in node_resources - assert ( - node_resources - == ServiceResourcesDictHelpers.model_config["json_schema_extra"][ - "examples" - ][0] - ) + assert {k: v.model_dump() for k, v in node_resources.items()} == next( + iter( + ServiceResourcesDictHelpers.model_config["json_schema_extra"][ + "examples" + ] + ) + ) # type: ignore else: assert not data assert error From 7f76165e53794ea5495591fd27a787f91d0e107e Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 29 Oct 2024 15:41:26 +0100 Subject: [PATCH 073/165] fix --- packages/models-library/src/models_library/projects_ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models-library/src/models_library/projects_ui.py b/packages/models-library/src/models_library/projects_ui.py index 93aa68d628b..0e914de9dba 100644 --- a/packages/models-library/src/models_library/projects_ui.py +++ b/packages/models-library/src/models_library/projects_ui.py @@ -68,7 +68,7 @@ class StudyUI(BaseModel): current_node_id: NodeID | None = Field(default=None, alias="currentNodeId") annotations: dict[NodeIDStr, Annotation] | None = None - model_config = ConfigDict(extra="allow") + model_config = ConfigDict(extra="allow", populate_by_name=True) _empty_is_none = field_validator("*", mode="before")( empty_str_to_none_pre_validator From aca5dcd2c8f725c0ff06e21e9154c26d0e4b1307 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 29 Oct 2024 16:23:12 +0100 Subject: [PATCH 074/165] fix --- .../src/models_library/api_schemas_webserver/auth.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/models-library/src/models_library/api_schemas_webserver/auth.py b/packages/models-library/src/models_library/api_schemas_webserver/auth.py index b0b11661cb3..c841056d40c 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/auth.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/auth.py @@ -83,9 +83,10 @@ class ApiKeyGet(BaseModel): api_secret: str model_config = ConfigDict( + from_attributes=True, json_schema_extra={ "examples": [ {"display_name": "myapi", "api_key": "key", "api_secret": "secret"}, ] - } + }, ) From c4784bb75d150c67089bc9f636ade01023957974 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 29 Oct 2024 16:37:50 +0100 Subject: [PATCH 075/165] fix test --- .../server/src/simcore_service_webserver/catalog/_handlers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/catalog/_handlers.py b/services/web/server/src/simcore_service_webserver/catalog/_handlers.py index 61a04130511..a9ba40b5378 100644 --- a/services/web/server/src/simcore_service_webserver/catalog/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/catalog/_handlers.py @@ -26,7 +26,7 @@ ServiceResourcesDict, ServiceResourcesDictHelpers, ) -from pydantic import BaseModel, ConfigDict, Field, TypeAdapter, field_validator +from pydantic import BaseModel, ConfigDict, Field, field_validator from servicelib.aiohttp.requests_validation import ( parse_request_body_as, parse_request_path_parameters_as, @@ -388,5 +388,5 @@ async def get_service_pricing_plan(request: Request): ) return envelope_json_response( - TypeAdapter(PricingPlanGet).validate_python(pricing_plan) + PricingPlanGet.model_validate(pricing_plan.model_dump()) ) From 0402d8208052912e52652581a049b3122d94c7e6 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 29 Oct 2024 16:42:56 +0100 Subject: [PATCH 076/165] fix bytesize --- .../01/studies_dispatcher/test_studies_dispatcher_handlers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_handlers.py b/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_handlers.py index 8acc5c2e7c7..14f673ce5da 100644 --- a/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_handlers.py +++ b/services/web/server/tests/unit/with_dbs/01/studies_dispatcher/test_studies_dispatcher_handlers.py @@ -352,7 +352,7 @@ def redirect_url(redirect_type: str, client: TestClient) -> URL: if redirect_type == "service_and_file": query = { "file_name": "users.csv", - "file_size": ByteSize("100KB"), + "file_size": TypeAdapter(ByteSize).validate_python("100KB"), "file_type": "CSV", "viewer_key": "simcore/services/dynamic/raw-graphs", "viewer_version": "2.11.1", @@ -368,7 +368,7 @@ def redirect_url(redirect_type: str, client: TestClient) -> URL: elif redirect_type == "file_only": query = { "file_name": "users.csv", - "file_size": ByteSize("1MiB"), + "file_size": TypeAdapter(ByteSize).validate_python("1MiB"), "file_type": "CSV", "download_link": URL( "https://raw.githubusercontent.com/ITISFoundation/osparc-simcore/8987c95d0ca0090e14f3a5b52db724fa24114cf5/services/storage/tests/data/users.csv" From 44add09a9d92b8eb835f953f8e9ab0354b5261b2 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 12:08:49 +0100 Subject: [PATCH 077/165] fix env vars --- .../web/server/tests/unit/with_dbs/03/wallets/conftest.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/web/server/tests/unit/with_dbs/03/wallets/conftest.py b/services/web/server/tests/unit/with_dbs/03/wallets/conftest.py index 1686507e13d..05eef543fa0 100644 --- a/services/web/server/tests/unit/with_dbs/03/wallets/conftest.py +++ b/services/web/server/tests/unit/with_dbs/03/wallets/conftest.py @@ -33,6 +33,10 @@ def app_environment( "WEBSERVER_DB_LISTENER": "0", "WEBSERVER_DEV_FEATURES_ENABLED": "1", "WEBSERVER_GARBAGE_COLLECTOR": "null", + "WEBSERVER_EMAIL": '{"SMTP_HOST": "smtp.fake.com", "SMTP_PORT": "25"}', + "WEBSERVER_LOGIN": '{"LOGIN_REGISTRATION_INVITATION_REQUIRED": "True"}', + "WEBSERVER_PAYMENTS": '{"PAYMENTS_USERNAME": "somebody", "PAYMENTS_PASSWORD": "mys3cr3t!!"}', + "WEBSERVER_SCICRUNCH": "null", "PAYMENTS_FAKE_GATEWAY_URL": "https://some-fake-gateway.com", }, ) From f634ec4f88ba6da051742e9a288268e3d544f40a Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 12:10:11 +0100 Subject: [PATCH 078/165] fix env vars --- services/web/server/tests/unit/with_dbs/03/wallets/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/tests/unit/with_dbs/03/wallets/conftest.py b/services/web/server/tests/unit/with_dbs/03/wallets/conftest.py index 05eef543fa0..3487854757a 100644 --- a/services/web/server/tests/unit/with_dbs/03/wallets/conftest.py +++ b/services/web/server/tests/unit/with_dbs/03/wallets/conftest.py @@ -33,7 +33,7 @@ def app_environment( "WEBSERVER_DB_LISTENER": "0", "WEBSERVER_DEV_FEATURES_ENABLED": "1", "WEBSERVER_GARBAGE_COLLECTOR": "null", - "WEBSERVER_EMAIL": '{"SMTP_HOST": "smtp.fake.com", "SMTP_PORT": "25"}', + "WEBSERVER_EMAIL": '{"SMTP_HOST": "smtp.fake.com", "SMTP_PORT": 25}', "WEBSERVER_LOGIN": '{"LOGIN_REGISTRATION_INVITATION_REQUIRED": "True"}', "WEBSERVER_PAYMENTS": '{"PAYMENTS_USERNAME": "somebody", "PAYMENTS_PASSWORD": "mys3cr3t!!"}', "WEBSERVER_SCICRUNCH": "null", From 976acf78a5ca029a320373cd0fb3763e6cb4afc4 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 13:05:18 +0100 Subject: [PATCH 079/165] fix env vars --- .../web/server/tests/unit/with_dbs/03/wallets/conftest.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/services/web/server/tests/unit/with_dbs/03/wallets/conftest.py b/services/web/server/tests/unit/with_dbs/03/wallets/conftest.py index 3487854757a..f52f5abb647 100644 --- a/services/web/server/tests/unit/with_dbs/03/wallets/conftest.py +++ b/services/web/server/tests/unit/with_dbs/03/wallets/conftest.py @@ -7,6 +7,7 @@ from copy import deepcopy from pathlib import Path +from faker import Faker import pytest import sqlalchemy as sa from aioresponses import aioresponses @@ -23,6 +24,7 @@ def app_environment( app_environment: EnvVarsDict, env_devel_dict: EnvVarsDict, monkeypatch: pytest.MonkeyPatch, + faker: Faker ): new_envs = setenvs_from_dict( monkeypatch, @@ -33,9 +35,9 @@ def app_environment( "WEBSERVER_DB_LISTENER": "0", "WEBSERVER_DEV_FEATURES_ENABLED": "1", "WEBSERVER_GARBAGE_COLLECTOR": "null", - "WEBSERVER_EMAIL": '{"SMTP_HOST": "smtp.fake.com", "SMTP_PORT": 25}', - "WEBSERVER_LOGIN": '{"LOGIN_REGISTRATION_INVITATION_REQUIRED": "True"}', - "WEBSERVER_PAYMENTS": '{"PAYMENTS_USERNAME": "somebody", "PAYMENTS_PASSWORD": "mys3cr3t!!"}', + "WEBSERVER_EMAIL": json.dumps({"SMTP_HOST": "smtp.fake.com", "SMTP_PORT": 25}), + "WEBSERVER_LOGIN": json.dumps({"LOGIN_REGISTRATION_INVITATION_REQUIRED": "True"}), + "WEBSERVER_PAYMENTS": json.dumps({"PAYMENTS_USERNAME": "somebody", "PAYMENTS_PASSWORD": faker.password(length=10)}), "WEBSERVER_SCICRUNCH": "null", "PAYMENTS_FAKE_GATEWAY_URL": "https://some-fake-gateway.com", }, From e6d1d7147d0ef61676aed0f794f503f50e1ceadb Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 13:18:24 +0100 Subject: [PATCH 080/165] fix --- .../src/models_library/api_schemas_webserver/wallets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/models-library/src/models_library/api_schemas_webserver/wallets.py b/packages/models-library/src/models_library/api_schemas_webserver/wallets.py index 1a417e9671d..12679df2f37 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/wallets.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/wallets.py @@ -22,6 +22,7 @@ class WalletGet(OutputSchema): modified: datetime model_config = ConfigDict( + from_attributes=True, frozen=False ) From 4432e008d4f28137dcb503d716b44f54f854439b Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 14:12:22 +0100 Subject: [PATCH 081/165] continue fixing --- .../src/simcore_service_webserver/payments/_methods_api.py | 2 +- .../tests/unit/with_dbs/03/wallets/payments/test_payments.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/payments/_methods_api.py b/services/web/server/src/simcore_service_webserver/payments/_methods_api.py index e303a382f35..d19313d5bcc 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_methods_api.py +++ b/services/web/server/src/simcore_service_webserver/payments/_methods_api.py @@ -79,7 +79,7 @@ async def _fake_init_creation_of_wallet_payment_method( await asyncio.sleep(1) payment_method_id = PaymentMethodID(f"{_FAKE_PAYMENT_METHOD_ID_PREFIX}_{uuid4()}") form_link = ( - URL(settings.PAYMENTS_FAKE_GATEWAY_URL) + URL(f"{settings.PAYMENTS_FAKE_GATEWAY_URL}") .with_path("/payment-methods/form") .with_query(id=payment_method_id) ) diff --git a/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments.py b/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments.py index 0963efe0b70..719eb7e6dc8 100644 --- a/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments.py +++ b/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments.py @@ -362,7 +362,7 @@ async def test_payment_not_found( def test_payment_transaction_state_and_literals_are_in_sync(): - state_literals = PaymentTransaction.model_fields["state"].type_ + state_literals = PaymentTransaction.model_fields["state"].annotation assert ( TypeAdapter(list[state_literals]).validate_python( [f"{s}" for s in PaymentTransactionState] From b685088b79b2b4ddc647781f66ff894eb63c52db Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 14:13:12 +0100 Subject: [PATCH 082/165] fix validation field --- .../src/models_library/api_schemas_webserver/wallets.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/models-library/src/models_library/api_schemas_webserver/wallets.py b/packages/models-library/src/models_library/api_schemas_webserver/wallets.py index 12679df2f37..755463ff12f 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/wallets.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/wallets.py @@ -2,7 +2,7 @@ from decimal import Decimal from typing import Annotated, Literal, TypeAlias -from pydantic import ConfigDict, Field, HttpUrl, PlainSerializer, field_validator +from pydantic import ConfigDict, Field, HttpUrl, PlainSerializer, ValidationInfo, field_validator from ..basic_types import AmountDecimal, IDStr, NonNegativeDecimal from ..users import GroupID @@ -204,8 +204,8 @@ class ReplaceWalletAutoRecharge(InputSchema): @field_validator("monthly_limit_in_usd") @classmethod - def _monthly_limit_greater_than_top_up(cls, v, values): - top_up = values["top_up_amount_in_usd"] + def _monthly_limit_greater_than_top_up(cls, v, info: ValidationInfo): + top_up = info.data["top_up_amount_in_usd"] if v is not None and v < top_up: msg = "Monthly limit ({v} USD) should be greater than top up amount ({top_up} USD)" raise ValueError(msg) From f6f5908959925ea647a2fda9386b1d2131ed6a06 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 14:28:59 +0100 Subject: [PATCH 083/165] fix validation --- .../server/src/simcore_service_webserver/login/_models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/login/_models.py b/services/web/server/src/simcore_service_webserver/login/_models.py index 36e73d8922b..23775712301 100644 --- a/services/web/server/src/simcore_service_webserver/login/_models.py +++ b/services/web/server/src/simcore_service_webserver/login/_models.py @@ -1,6 +1,6 @@ from typing import Any, Callable -from pydantic import BaseModel, ConfigDict, SecretStr +from pydantic import BaseModel, ConfigDict, SecretStr, ValidationInfo from ._constants import MSG_PASSWORD_MISMATCH @@ -16,11 +16,11 @@ class InputSchema(BaseModel): def create_password_match_validator( reference_field: str, ) -> Callable[[SecretStr, dict[str, Any]], SecretStr]: - def _check(v: SecretStr, values: dict[str, Any]): + def _check(v: SecretStr, info: ValidationInfo): if ( v is not None - and reference_field in values - and v.get_secret_value() != values[reference_field].get_secret_value() + and reference_field in info.data + and v.get_secret_value() != info.data[reference_field].get_secret_value() ): raise ValueError(MSG_PASSWORD_MISMATCH) return v From ecd48d2f4211e326628a6afdb0ea12ece16133d0 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 14:34:53 +0100 Subject: [PATCH 084/165] fix dump --- .../web/server/tests/unit/with_dbs/03/login/test_login_auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/tests/unit/with_dbs/03/login/test_login_auth.py b/services/web/server/tests/unit/with_dbs/03/login/test_login_auth.py index ec6713d3894..7d16e912414 100644 --- a/services/web/server/tests/unit/with_dbs/03/login/test_login_auth.py +++ b/services/web/server/tests/unit/with_dbs/03/login/test_login_auth.py @@ -30,7 +30,7 @@ def test_login_plugin_setup_succeeded(client: TestClient): assert client.app - print(client.app[APP_SETTINGS_KEY].model_dump_json(indent=1, sort_keys=True)) + print(client.app[APP_SETTINGS_KEY].model_dump_json(indent=1)) # this should raise AssertionError if not succeedd settings = get_plugin_settings(client.app) From f948082b6645e26a9f1f62d8dd661ca66dff3118 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 15:03:20 +0100 Subject: [PATCH 085/165] remove deprecated --- .../src/simcore_service_webserver/announcements/_redis.py | 2 +- .../notifications/_rabbitmq_exclusive_queue_consumers.py | 4 ++-- .../notifications/_rabbitmq_nonexclusive_queue_consumers.py | 2 +- .../web/server/src/simcore_service_webserver/projects/lock.py | 2 +- .../tests/unit/with_dbs/03/test_users__notifications.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/announcements/_redis.py b/services/web/server/src/simcore_service_webserver/announcements/_redis.py index aad45ea8fee..785f954521f 100644 --- a/services/web/server/src/simcore_service_webserver/announcements/_redis.py +++ b/services/web/server/src/simcore_service_webserver/announcements/_redis.py @@ -37,7 +37,7 @@ async def list_announcements( announcements = [] for i, item in enumerate(items): try: - model = Announcement.parse_raw(item) + model = Announcement.model_validate_json(item) # filters if include_product not in model.products: continue diff --git a/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_exclusive_queue_consumers.py b/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_exclusive_queue_consumers.py index f4178305384..67c5d39b65b 100644 --- a/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_exclusive_queue_consumers.py +++ b/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_exclusive_queue_consumers.py @@ -97,7 +97,7 @@ async def _progress_message_parser(app: web.Application, data: bytes) -> bool: async def _log_message_parser(app: web.Application, data: bytes) -> bool: - rabbit_message = LoggerRabbitMessage.parse_raw(data) + rabbit_message = LoggerRabbitMessage.model_validate_json(data) await send_message_to_user( app, rabbit_message.user_id, @@ -111,7 +111,7 @@ async def _log_message_parser(app: web.Application, data: bytes) -> bool: async def _events_message_parser(app: web.Application, data: bytes) -> bool: - rabbit_message = EventRabbitMessage.parse_raw(data) + rabbit_message = EventRabbitMessage.model_validate_json(data) await send_message_to_user( app, rabbit_message.user_id, diff --git a/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_nonexclusive_queue_consumers.py b/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_nonexclusive_queue_consumers.py index cd47a062f2e..b6271be822a 100644 --- a/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_nonexclusive_queue_consumers.py +++ b/services/web/server/src/simcore_service_webserver/notifications/_rabbitmq_nonexclusive_queue_consumers.py @@ -23,7 +23,7 @@ async def _instrumentation_message_parser(app: web.Application, data: bytes) -> bool: - rabbit_message = InstrumentationRabbitMessage.parse_raw(data) + rabbit_message = InstrumentationRabbitMessage.model_validate_json(data) if rabbit_message.metrics == "service_started": service_started( app, diff --git a/services/web/server/src/simcore_service_webserver/projects/lock.py b/services/web/server/src/simcore_service_webserver/projects/lock.py index 9dc78fdba40..84b24c087e7 100644 --- a/services/web/server/src/simcore_service_webserver/projects/lock.py +++ b/services/web/server/src/simcore_service_webserver/projects/lock.py @@ -63,5 +63,5 @@ async def get_project_locked_state( if lock_value := await redis_locks_client.get( PROJECT_REDIS_LOCK_KEY.format(project_uuid) ): - return ProjectLocked.parse_raw(lock_value) + return ProjectLocked.model_validate_json(lock_value) return None diff --git a/services/web/server/tests/unit/with_dbs/03/test_users__notifications.py b/services/web/server/tests/unit/with_dbs/03/test_users__notifications.py index 6a70478ce78..048dc779c51 100644 --- a/services/web/server/tests/unit/with_dbs/03/test_users__notifications.py +++ b/services/web/server/tests/unit/with_dbs/03/test_users__notifications.py @@ -379,7 +379,7 @@ async def test_update_user_notification_at_correct_index( async def _get_stored_notifications() -> list[UserNotification]: return [ - UserNotification.parse_raw(x) + UserNotification.model_validate_json(x) for x in await notification_redis_client.lrange( get_notification_key(user_id), 0, -1 ) From 7604a40e462273b971e3d394da2be66e758f78df Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 15:35:37 +0100 Subject: [PATCH 086/165] continue fixing --- .../src/simcore_service_webserver/projects/_crud_api_read.py | 2 +- .../tests/unit/with_dbs/02/test_projects_nodes_handler.py | 2 +- .../tests/unit/with_dbs/02/test_projects_states_handlers.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py b/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py index 793dd19b083..c893276f08e 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py @@ -53,7 +53,7 @@ async def _append_fields( # replace project access rights (if project is in workspace) if workspace_access_rights: project["accessRights"] = { - gid: access.model_dump() for gid, access in workspace_access_rights.items() + f"{gid}": access.model_dump() for gid, access in workspace_access_rights.items() } # validate diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py b/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py index be79b2b81fd..8945e2c9296 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_handler.py @@ -202,7 +202,7 @@ async def test_replace_node_resources_is_ok_if_explicitly_authorized( assert node_resources assert DEFAULT_SINGLE_SERVICE_NAME in node_resources assert ( - node_resources + {k: v.model_dump() for k, v in node_resources.items()} == ServiceResourcesDictHelpers.model_config["json_schema_extra"][ "examples" ][0] diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py b/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py index c97689e090b..2efbd947a91 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py @@ -9,7 +9,7 @@ import time from collections.abc import Awaitable, Callable, Iterator from copy import deepcopy -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from http import HTTPStatus from typing import Any from unittest import mock @@ -716,7 +716,7 @@ async def test_open_project_with_deprecated_services_ok_but_does_not_start_dynam mocked_notifications_plugin: dict[str, mock.Mock], ): mock_catalog_api["get_service"].return_value["deprecated"] = ( - datetime.utcnow() - timedelta(days=1) + datetime.now(UTC) - timedelta(days=1) ).isoformat() url = client.app.router["open_project"].url_for(project_id=user_project["uuid"]) resp = await client.post(url, json=client_session_id_factory()) From d1cc36bcc58e8670032a9b7ca47b9763373e798a Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 16:06:37 +0100 Subject: [PATCH 087/165] fix date --- .../src/simcore_service_webserver/projects/projects_api.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/projects_api.py b/services/web/server/src/simcore_service_webserver/projects/projects_api.py index d289442c594..12b29ab8153 100644 --- a/services/web/server/src/simcore_service_webserver/projects/projects_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/projects_api.py @@ -1511,11 +1511,8 @@ async def is_service_deprecated( app, user_id, service_key, service_version, product_name ) if deprecation_date := service.get("deprecated"): - deprecation_date = TypeAdapter(datetime.datetime).validate_python( - deprecation_date - ) deprecation_date_bool: bool = ( - datetime.datetime.now(datetime.UTC) > deprecation_date + datetime.datetime.now(datetime.UTC) > datetime.datetime.fromisoformat(deprecation_date) ) return deprecation_date_bool return False From 574a80ae959307236bb7efb4d4584388cdb06718 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 16:29:01 +0100 Subject: [PATCH 088/165] continue fixing --- .../api_schemas_webserver/projects.py | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/packages/models-library/src/models_library/api_schemas_webserver/projects.py b/packages/models-library/src/models_library/api_schemas_webserver/projects.py index 037db7204e7..b1ba1555269 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/projects.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/projects.py @@ -6,11 +6,11 @@ """ from datetime import datetime -from typing import Any, Literal, TypeAlias +from typing import Annotated, Any, Literal, TypeAlias from models_library.folders import FolderID from models_library.workspaces import WorkspaceID -from pydantic import ConfigDict, Field, HttpUrl, field_validator +from pydantic import BeforeValidator, ConfigDict, Field, HttpUrl, field_validator from ..api_schemas_long_running_tasks.tasks import TaskGet from ..basic_types import LongTruncatedStr, ShortTruncatedStr @@ -122,19 +122,15 @@ class ProjectReplace(InputSchema): class ProjectPatch(InputSchema): - name: ShortTruncatedStr = FieldNotRequired() - description: LongTruncatedStr = FieldNotRequired() - thumbnail: HttpUrl = FieldNotRequired() - access_rights: dict[GroupIDStr, AccessRights] = FieldNotRequired() - classifiers: list[ClassifierID] = FieldNotRequired() - dev: dict | None = FieldNotRequired() - ui: StudyUI | None = FieldNotRequired() - quality: dict[str, Any] = FieldNotRequired() - - _empty_is_none = field_validator("thumbnail", mode="before")( - empty_str_to_none_pre_validator - ) - + name: ShortTruncatedStr | None = Field(default=None) + description: LongTruncatedStr | None = Field(default=None) + thumbnail: Annotated[HttpUrl, BeforeValidator(empty_str_to_none_pre_validator)] | None = Field(default=None) + access_rights: dict[GroupIDStr, AccessRights] | None = Field(default=None) + classifiers: list[ClassifierID] | None = Field(default=None) + dev: dict | None = Field(default=None) + ui: StudyUI | None = Field(default=None) + quality: dict[str, Any] | None = Field(default=None) + __all__: tuple[str, ...] = ( "EmptyModel", From b0d44b68cd0a6c692ce4eebee861d6587410f460 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 16:47:37 +0100 Subject: [PATCH 089/165] fix --- .../src/models_library/api_schemas_webserver/folders_v2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models-library/src/models_library/api_schemas_webserver/folders_v2.py b/packages/models-library/src/models_library/api_schemas_webserver/folders_v2.py index ee44a9b18d4..85f3604381a 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/folders_v2.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/folders_v2.py @@ -45,7 +45,7 @@ class CreateFolderBodyParams(InputSchema): class PutFolderBodyParams(InputSchema): name: IDStr - parent_folder_id: FolderID | None + parent_folder_id: FolderID | None = None model_config = ConfigDict(extra="forbid") _null_or_none_str_to_none_validator = field_validator( From a2bf2f3bea830015edd7bcf82ba246df26376560 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 16:49:48 +0100 Subject: [PATCH 090/165] fix --- .../03/invitations/test_products__invitations_handlers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/tests/unit/with_dbs/03/invitations/test_products__invitations_handlers.py b/services/web/server/tests/unit/with_dbs/03/invitations/test_products__invitations_handlers.py index f816a8535b0..71da6536363 100644 --- a/services/web/server/tests/unit/with_dbs/03/invitations/test_products__invitations_handlers.py +++ b/services/web/server/tests/unit/with_dbs/03/invitations/test_products__invitations_handlers.py @@ -107,7 +107,7 @@ async def test_product_owner_generates_invitation( assert got.model_dump(include=set(expected), by_alias=False) == expected product_base_url = f"{client.make_url('/')}" - assert got.invitation_link.startswith(product_base_url) + assert f"{got.invitation_link}".startswith(product_base_url) assert before_dt < got.created assert got.created < datetime.now(tz=timezone.utc) From 723ca369ed4b490764f4ce1401a3afa503530a9c Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 17:04:37 +0100 Subject: [PATCH 091/165] fix field --- .../src/models_library/api_schemas_webserver/resource_usage.py | 2 +- .../resource_usage/_pricing_plans_handlers.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/models-library/src/models_library/api_schemas_webserver/resource_usage.py b/packages/models-library/src/models_library/api_schemas_webserver/resource_usage.py index 8242105f55a..f8f3b11bbe7 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/resource_usage.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/resource_usage.py @@ -48,7 +48,7 @@ class ServiceRunGet( class PricingUnitGet(OutputSchema): pricing_unit_id: PricingUnitId unit_name: str - unit_extra_info: dict + unit_extra_info: UnitExtraInfo current_cost_per_unit: Decimal default: bool diff --git a/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_handlers.py b/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_handlers.py index 9715efae496..41d9be1ce09 100644 --- a/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_handlers.py +++ b/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_handlers.py @@ -75,7 +75,7 @@ async def get_pricing_plan_unit(request: web.Request): webserver_pricing_unit_get = PricingUnitGet( pricing_unit_id=pricing_unit_get.pricing_unit_id, unit_name=pricing_unit_get.unit_name, - unit_extra_info=pricing_unit_get.unit_extra_info, # type: ignore[arg-type] + unit_extra_info=pricing_unit_get.unit_extra_info, current_cost_per_unit=pricing_unit_get.current_cost_per_unit, default=pricing_unit_get.default, ) From b44186a5e81d0f9902acfde3f306996de861f091 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 21:17:13 +0100 Subject: [PATCH 092/165] add env vars --- .../01/test_resource_manager_user_sessions.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/services/web/server/tests/unit/with_dbs/01/test_resource_manager_user_sessions.py b/services/web/server/tests/unit/with_dbs/01/test_resource_manager_user_sessions.py index 9e46f5a27f7..ac3eff4a0c4 100644 --- a/services/web/server/tests/unit/with_dbs/01/test_resource_manager_user_sessions.py +++ b/services/web/server/tests/unit/with_dbs/01/test_resource_manager_user_sessions.py @@ -3,7 +3,7 @@ # pylint: disable=too-many-arguments # pylint: disable=unused-argument # pylint: disable=unused-variable - +import json import time from collections.abc import Callable from random import randint @@ -12,10 +12,12 @@ import pytest import redis.asyncio as aioredis from aiohttp import web +from faker import Faker from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict from servicelib.aiohttp.application import create_safe_application from servicelib.aiohttp.application_setup import is_setup_completed from simcore_service_webserver.application_settings import setup_settings +from simcore_service_webserver.payments._methods_api import Faker from simcore_service_webserver.resource_manager.plugin import setup_resource_manager from simcore_service_webserver.resource_manager.registry import ( _ALIVE_SUFFIX, @@ -33,10 +35,17 @@ @pytest.fixture def mock_env_devel_environment( - mock_env_devel_environment: dict[str, str], monkeypatch: pytest.MonkeyPatch + mock_env_devel_environment: dict[str, str], monkeypatch: pytest.MonkeyPatch, faker: Faker ): return mock_env_devel_environment | setenvs_from_dict( - monkeypatch, {"RESOURCE_MANAGER_RESOURCE_TTL_S": "3"} + monkeypatch, { + "RESOURCE_MANAGER_RESOURCE_TTL_S": "3", + "WEBSERVER_EMAIL": json.dumps({"SMTP_HOST": "smtp.fake.com", "SMTP_PORT": 25}), + "WEBSERVER_LOGIN": json.dumps({"LOGIN_REGISTRATION_INVITATION_REQUIRED": "True"}), + "WEBSERVER_PAYMENTS": json.dumps({"PAYMENTS_USERNAME": "somebody", "PAYMENTS_PASSWORD": faker.password(length=10)}), + "WEBSERVER_SCICRUNCH": "null", + "WEBSERVER_TRACING": "null" + } ) From 8df92e85f6492065ab282f64ed27502e447ce516 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 21:20:41 +0100 Subject: [PATCH 093/165] remove unused ignore --- .../resource_usage/_pricing_plans_admin_handlers.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_admin_handlers.py b/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_admin_handlers.py index 72ed1c10bff..762796afc07 100644 --- a/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_admin_handlers.py +++ b/services/web/server/src/simcore_service_webserver/resource_usage/_pricing_plans_admin_handlers.py @@ -136,7 +136,7 @@ async def get_pricing_plan(request: web.Request): PricingUnitAdminGet( pricing_unit_id=pricing_unit.pricing_unit_id, unit_name=pricing_unit.unit_name, - unit_extra_info=pricing_unit.unit_extra_info, # type: ignore[arg-type] + unit_extra_info=pricing_unit.unit_extra_info, specific_info=pricing_unit.specific_info, current_cost_per_unit=pricing_unit.current_cost_per_unit, default=pricing_unit.default, @@ -185,7 +185,7 @@ async def create_pricing_plan(request: web.Request): PricingUnitAdminGet( pricing_unit_id=pricing_unit.pricing_unit_id, unit_name=pricing_unit.unit_name, - unit_extra_info=pricing_unit.unit_extra_info, # type: ignore[arg-type] + unit_extra_info=pricing_unit.unit_extra_info, specific_info=pricing_unit.specific_info, current_cost_per_unit=pricing_unit.current_cost_per_unit, default=pricing_unit.default, @@ -235,7 +235,7 @@ async def update_pricing_plan(request: web.Request): PricingUnitAdminGet( pricing_unit_id=pricing_unit.pricing_unit_id, unit_name=pricing_unit.unit_name, - unit_extra_info=pricing_unit.unit_extra_info, # type: ignore[arg-type] + unit_extra_info=pricing_unit.unit_extra_info, specific_info=pricing_unit.specific_info, current_cost_per_unit=pricing_unit.current_cost_per_unit, default=pricing_unit.default, @@ -278,7 +278,7 @@ async def get_pricing_unit(request: web.Request): webserver_pricing_unit_get = PricingUnitAdminGet( pricing_unit_id=pricing_unit_get.pricing_unit_id, unit_name=pricing_unit_get.unit_name, - unit_extra_info=pricing_unit_get.unit_extra_info, # type: ignore[arg-type] + unit_extra_info=pricing_unit_get.unit_extra_info, specific_info=pricing_unit_get.specific_info, current_cost_per_unit=pricing_unit_get.current_cost_per_unit, default=pricing_unit_get.default, @@ -317,7 +317,7 @@ async def create_pricing_unit(request: web.Request): webserver_pricing_unit_get = PricingUnitAdminGet( pricing_unit_id=pricing_unit_get.pricing_unit_id, unit_name=pricing_unit_get.unit_name, - unit_extra_info=pricing_unit_get.unit_extra_info, # type: ignore[arg-type] + unit_extra_info=pricing_unit_get.unit_extra_info, specific_info=pricing_unit_get.specific_info, current_cost_per_unit=pricing_unit_get.current_cost_per_unit, default=pricing_unit_get.default, @@ -356,7 +356,7 @@ async def update_pricing_unit(request: web.Request): webserver_pricing_unit_get = PricingUnitAdminGet( pricing_unit_id=pricing_unit_get.pricing_unit_id, unit_name=pricing_unit_get.unit_name, - unit_extra_info=pricing_unit_get.unit_extra_info, # type: ignore[arg-type] + unit_extra_info=pricing_unit_get.unit_extra_info, specific_info=pricing_unit_get.specific_info, current_cost_per_unit=pricing_unit_get.current_cost_per_unit, default=pricing_unit_get.default, From 17a2049d2a531789a846034b339ec1088211e0b6 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 22:03:36 +0100 Subject: [PATCH 094/165] add env vars --- .../web/server/tests/unit/with_dbs/03/login/conftest.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/services/web/server/tests/unit/with_dbs/03/login/conftest.py b/services/web/server/tests/unit/with_dbs/03/login/conftest.py index 167315facb4..da032d8bbd7 100644 --- a/services/web/server/tests/unit/with_dbs/03/login/conftest.py +++ b/services/web/server/tests/unit/with_dbs/03/login/conftest.py @@ -20,7 +20,7 @@ @pytest.fixture -def app_environment(app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatch): +def app_environment(app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatch, faker: Faker): envs_plugins = setenvs_from_dict( monkeypatch, { @@ -41,6 +41,11 @@ def app_environment(app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatc "WEBSERVER_TRACING": "null", "WEBSERVER_VERSION_CONTROL": "0", "WEBSERVER_WALLETS": "1", + "WEBSERVER_EMAIL": json.dumps({"SMTP_HOST": "smtp.fake.com", "SMTP_PORT": 25}), + "WEBSERVER_LOGIN": json.dumps({"LOGIN_REGISTRATION_INVITATION_REQUIRED": "True"}), + "WEBSERVER_PAYMENTS": json.dumps({"PAYMENTS_USERNAME": "somebody", "PAYMENTS_PASSWORD": faker.password(length=10)}), + "WEBSERVER_SCICRUNCH": "null", + "WEBSERVER_TRACING": "null" }, ) From c3d76a2f30d7b978b1f350b995c0733447f7227a Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 22:04:14 +0100 Subject: [PATCH 095/165] fix message --- .../server/src/simcore_service_webserver/login/_constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/login/_constants.py b/services/web/server/src/simcore_service_webserver/login/_constants.py index 99cab2bb95c..6f5310fe46a 100644 --- a/services/web/server/src/simcore_service_webserver/login/_constants.py +++ b/services/web/server/src/simcore_service_webserver/login/_constants.py @@ -31,7 +31,7 @@ "Please retry and if the problem persist contact {support_email}" ) MSG_PASSWORD_CHANGED: Final[str] = "Your password is changed" -MSG_PASSWORD_MISMATCH: Final[str] = "Password and confirmation do not match" +MSG_PASSWORD_MISMATCH: Final[str] = "Value error, Password and confirmation do not match" MSG_PHONE_MISSING: Final[str] = "No phone was registered for this user" MSG_UNAUTHORIZED_CODE_RESEND_2FA: Final[ str From ef0fc2f63d418dd16beda952feea8601333f4782 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 22:21:52 +0100 Subject: [PATCH 096/165] fix msg --- .../tests/unit/with_dbs/03/login/test_login_registration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/server/tests/unit/with_dbs/03/login/test_login_registration.py b/services/web/server/tests/unit/with_dbs/03/login/test_login_registration.py index a3b51fe4d2c..2cf8dde5c85 100644 --- a/services/web/server/tests/unit/with_dbs/03/login/test_login_registration.py +++ b/services/web/server/tests/unit/with_dbs/03/login/test_login_registration.py @@ -95,8 +95,8 @@ async def test_register_body_validation( "status": 422, "errors": [ { - "code": "value_error.email", - "message": "value is not a valid email address", + "code": "value_error", + "message": "value is not a valid email address: An email address must have an @-sign.", "resource": "/v0/auth/register", "field": "email", }, From c7fd8284432b8e8b0d075d8a0d15008e7f6e0e4f Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 22:27:01 +0100 Subject: [PATCH 097/165] fix invitations --- .../server/src/simcore_service_webserver/users/_schemas.py | 6 +++--- .../server/tests/unit/with_dbs/03/invitations/conftest.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/users/_schemas.py b/services/web/server/src/simcore_service_webserver/users/_schemas.py index 2d6f5de033b..3452537166a 100644 --- a/services/web/server/src/simcore_service_webserver/users/_schemas.py +++ b/services/web/server/src/simcore_service_webserver/users/_schemas.py @@ -12,7 +12,7 @@ from models_library.api_schemas_webserver._base import InputSchema, OutputSchema from models_library.emails import LowerCaseEmailStr from models_library.products import ProductName -from pydantic import ConfigDict, Field, field_validator, model_validator +from pydantic import ConfigDict, Field, ValidationInfo, field_validator, model_validator from simcore_postgres_database.models.users import UserStatus @@ -45,8 +45,8 @@ class UserProfile(OutputSchema): @field_validator("status") @classmethod - def _consistency_check(cls, v, values): - registered = values["registered"] + def _consistency_check(cls, v, info: ValidationInfo): + registered = info.data["registered"] status = v if not registered and status is not None: msg = f"{registered=} and {status=} is not allowed" diff --git a/services/web/server/tests/unit/with_dbs/03/invitations/conftest.py b/services/web/server/tests/unit/with_dbs/03/invitations/conftest.py index bbc5e83f8a8..014ed5db536 100644 --- a/services/web/server/tests/unit/with_dbs/03/invitations/conftest.py +++ b/services/web/server/tests/unit/with_dbs/03/invitations/conftest.py @@ -117,7 +117,7 @@ def mock_invitations_service_http_api( assert "/v1/invitations:extract" in oas["paths"] def _extract(url, **kwargs): - fake_code = URL(URL(kwargs["json"]["invitation_url"]).fragment).query[ + fake_code = URL(URL(f'{kwargs["json"]["invitation_url"]}').fragment).query[ "invitation" ] # if nothing is encoded in fake_code, just return fake_osparc_invitation From a83a7a6f209124e51e9ddc5701d6d5a2facdfa82 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 22:43:30 +0100 Subject: [PATCH 098/165] fix sessionsettings --- .../server/src/simcore_service_webserver/session/settings.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/web/server/src/simcore_service_webserver/session/settings.py b/services/web/server/src/simcore_service_webserver/session/settings.py index f71620e7c95..f6fec9a878e 100644 --- a/services/web/server/src/simcore_service_webserver/session/settings.py +++ b/services/web/server/src/simcore_service_webserver/session/settings.py @@ -4,6 +4,7 @@ from pydantic import AliasChoices, PositiveInt, field_validator from pydantic.fields import Field from pydantic.types import SecretStr +from pydantic_settings import SettingsConfigDict from settings_library.base import BaseCustomSettings from settings_library.utils_session import MixinSessionSettings @@ -53,6 +54,10 @@ class SessionSettings(BaseCustomSettings, MixinSessionSettings): default=True, description="This prevents JavaScript from accessing the session cookie", ) + + model_config = SettingsConfigDict( + extra="allow" + ) @field_validator("SESSION_SECRET_KEY") @classmethod From 19f787a048a3bf07e1ab34b38efd27b6e218817b Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 22:46:16 +0100 Subject: [PATCH 099/165] revert msg --- .../server/src/simcore_service_webserver/login/_constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/login/_constants.py b/services/web/server/src/simcore_service_webserver/login/_constants.py index 6f5310fe46a..99cab2bb95c 100644 --- a/services/web/server/src/simcore_service_webserver/login/_constants.py +++ b/services/web/server/src/simcore_service_webserver/login/_constants.py @@ -31,7 +31,7 @@ "Please retry and if the problem persist contact {support_email}" ) MSG_PASSWORD_CHANGED: Final[str] = "Your password is changed" -MSG_PASSWORD_MISMATCH: Final[str] = "Value error, Password and confirmation do not match" +MSG_PASSWORD_MISMATCH: Final[str] = "Password and confirmation do not match" MSG_PHONE_MISSING: Final[str] = "No phone was registered for this user" MSG_UNAUTHORIZED_CODE_RESEND_2FA: Final[ str From 9404441dac24a28d862c8b3a0ce4da91d2f355f9 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 23:31:59 +0100 Subject: [PATCH 100/165] continue fixing --- .../projects/_projects_nodes_pricing_unit_handlers.py | 2 +- .../src/simcore_service_webserver/workspaces/_groups_api.py | 6 +++++- .../web/server/tests/unit/with_dbs/03/login/conftest.py | 4 ---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/_projects_nodes_pricing_unit_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_projects_nodes_pricing_unit_handlers.py index 62e031f92da..05bb2f8e767 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_projects_nodes_pricing_unit_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_projects_nodes_pricing_unit_handlers.py @@ -87,7 +87,7 @@ async def get_project_node_pricing_unit(request: web.Request): webserver_pricing_unit_get = PricingUnitGet( pricing_unit_id=pricing_unit_get.pricing_unit_id, unit_name=pricing_unit_get.unit_name, - unit_extra_info=pricing_unit_get.unit_extra_info, # type: ignore[arg-type] + unit_extra_info=pricing_unit_get.unit_extra_info, current_cost_per_unit=pricing_unit_get.current_cost_per_unit, default=pricing_unit_get.default, ) diff --git a/services/web/server/src/simcore_service_webserver/workspaces/_groups_api.py b/services/web/server/src/simcore_service_webserver/workspaces/_groups_api.py index f3a0ab3f0ff..cca4da82e4e 100644 --- a/services/web/server/src/simcore_service_webserver/workspaces/_groups_api.py +++ b/services/web/server/src/simcore_service_webserver/workspaces/_groups_api.py @@ -5,7 +5,7 @@ from models_library.products import ProductName from models_library.users import GroupID, UserID from models_library.workspaces import UserWorkspaceAccessRightsDB, WorkspaceID -from pydantic import BaseModel +from pydantic import BaseModel, ConfigDict from ..users import api as users_api from . import _groups_db as workspaces_groups_db @@ -24,6 +24,10 @@ class WorkspaceGroupGet(BaseModel): delete: bool created: datetime modified: datetime + + model_config = ConfigDict( + from_attributes=True + ) async def create_workspace_group( diff --git a/services/web/server/tests/unit/with_dbs/03/login/conftest.py b/services/web/server/tests/unit/with_dbs/03/login/conftest.py index da032d8bbd7..b9a458add16 100644 --- a/services/web/server/tests/unit/with_dbs/03/login/conftest.py +++ b/services/web/server/tests/unit/with_dbs/03/login/conftest.py @@ -41,10 +41,6 @@ def app_environment(app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatc "WEBSERVER_TRACING": "null", "WEBSERVER_VERSION_CONTROL": "0", "WEBSERVER_WALLETS": "1", - "WEBSERVER_EMAIL": json.dumps({"SMTP_HOST": "smtp.fake.com", "SMTP_PORT": 25}), - "WEBSERVER_LOGIN": json.dumps({"LOGIN_REGISTRATION_INVITATION_REQUIRED": "True"}), - "WEBSERVER_PAYMENTS": json.dumps({"PAYMENTS_USERNAME": "somebody", "PAYMENTS_PASSWORD": faker.password(length=10)}), - "WEBSERVER_SCICRUNCH": "null", "WEBSERVER_TRACING": "null" }, ) From 7592d97b5920beb80484d1ab5acdc850112b8890 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 23:33:59 +0100 Subject: [PATCH 101/165] fix --- .../src/simcore_service_webserver/projects/projects_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/projects_api.py b/services/web/server/src/simcore_service_webserver/projects/projects_api.py index 12b29ab8153..7126172be0b 100644 --- a/services/web/server/src/simcore_service_webserver/projects/projects_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/projects_api.py @@ -216,7 +216,7 @@ async def get_project_for_user( ) ) project["accessRights"] = { - gid: access.model_dump() + f"{gid}": access.model_dump() for gid, access in workspace_db.access_rights.items() } From b865a7695b18c0c2d31fb2dbcd95fab885a60792 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 30 Oct 2024 23:37:31 +0100 Subject: [PATCH 102/165] fix mypy --- .../web/server/src/simcore_service_webserver/login/_models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/login/_models.py b/services/web/server/src/simcore_service_webserver/login/_models.py index 23775712301..c0aef7a6015 100644 --- a/services/web/server/src/simcore_service_webserver/login/_models.py +++ b/services/web/server/src/simcore_service_webserver/login/_models.py @@ -1,4 +1,4 @@ -from typing import Any, Callable +from typing import Callable from pydantic import BaseModel, ConfigDict, SecretStr, ValidationInfo @@ -15,7 +15,7 @@ class InputSchema(BaseModel): def create_password_match_validator( reference_field: str, -) -> Callable[[SecretStr, dict[str, Any]], SecretStr]: +) -> Callable[[SecretStr, ValidationInfo], SecretStr]: def _check(v: SecretStr, info: ValidationInfo): if ( v is not None From b810b5e83476f1a2d9bc89ff20b1ac84903f2d96 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 31 Oct 2024 00:01:54 +0100 Subject: [PATCH 103/165] fix --- .../src/simcore_service_webserver/wallets/_groups_api.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/wallets/_groups_api.py b/services/web/server/src/simcore_service_webserver/wallets/_groups_api.py index 0990a91b93a..bdace14a9de 100644 --- a/services/web/server/src/simcore_service_webserver/wallets/_groups_api.py +++ b/services/web/server/src/simcore_service_webserver/wallets/_groups_api.py @@ -5,7 +5,7 @@ from models_library.products import ProductName from models_library.users import GroupID, UserID from models_library.wallets import UserWalletDB, WalletID -from pydantic import BaseModel +from pydantic import BaseModel, ConfigDict from ..users import api as users_api from . import _db as wallets_db @@ -23,6 +23,10 @@ class WalletGroupGet(BaseModel): delete: bool created: datetime modified: datetime + + model_config = ConfigDict( + from_attributes=True + ) async def create_wallet_group( From c9ab39f5ae99ac568560f22c759cfd35d81979c9 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 31 Oct 2024 00:04:53 +0100 Subject: [PATCH 104/165] fix --- .../src/models_library/api_schemas_webserver/wallets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/models-library/src/models_library/api_schemas_webserver/wallets.py b/packages/models-library/src/models_library/api_schemas_webserver/wallets.py index aa0bf450467..ae20bee1e62 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/wallets.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/wallets.py @@ -142,6 +142,7 @@ class PaymentMethodGet(OutputSchema): ) model_config = ConfigDict( + frozen=False, json_schema_extra={ "examples": [ { From fc5de1163cd09dbed9f4bf0d32cfbbb338caca24 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 31 Oct 2024 09:17:05 +0100 Subject: [PATCH 105/165] fixing --- .../resource_usage/_service_runs_handlers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py b/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py index 2a6cca6efac..5a9dbf05849 100644 --- a/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py +++ b/services/web/server/src/simcore_service_webserver/resource_usage/_service_runs_handlers.py @@ -213,7 +213,7 @@ async def list_osparc_credits_aggregated_usages(request: web.Request): page = Page[dict[str, Any]].model_validate( paginate_data( - chunk=aggregated_services.items, + chunk=[item.model_dump() for item in aggregated_services.items], request_url=request.url, total=aggregated_services.total, limit=query_params.limit, From 45e9a08df7bfc06105e86127a0d1b74e6f842e84 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 31 Oct 2024 09:57:12 +0100 Subject: [PATCH 106/165] fix fields --- .../src/models_library/api_schemas_webserver/groups.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/models-library/src/models_library/api_schemas_webserver/groups.py b/packages/models-library/src/models_library/api_schemas_webserver/groups.py index d77c04288c9..ab99d891a25 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/groups.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/groups.py @@ -144,13 +144,13 @@ class AllUsersGroups(BaseModel): class GroupUserGet(BaseModel): - id: str | None = Field(None, description="the user id", coerce_numbers_to_str=True) + id: str | None = Field(None, description="the user id") login: LowerCaseEmailStr | None = Field(None, description="the user login email") first_name: str | None = Field(None, description="the user first name") last_name: str | None = Field(None, description="the user last name") gravatar_id: str | None = Field(None, description="the user gravatar id hash") gid: str | None = Field( - None, description="the user primary gid", coerce_numbers_to_str=True + None, description="the user primary gid" ) access_rights: GroupAccessRights = Field(..., alias="accessRights") From 35e8c9ddcf3ecec0c189cdf7313372e2eb7caff2 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 31 Oct 2024 11:50:20 +0100 Subject: [PATCH 107/165] fix tests --- .../src/simcore_service_webserver/payments/_onetime_api.py | 2 +- .../server/tests/unit/with_dbs/01/wallets/payments/conftest.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py b/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py index 25f8ad0e5a1..903e14ad002 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py +++ b/services/web/server/src/simcore_service_webserver/payments/_onetime_api.py @@ -49,7 +49,7 @@ def _to_api_model( "osparc_credits": transaction.osparc_credits, "wallet_id": transaction.wallet_id, "created_at": transaction.initiated_at, - "state": transaction.state, + "state": f"{transaction.state}", "completed_at": transaction.completed_at, } diff --git a/services/web/server/tests/unit/with_dbs/01/wallets/payments/conftest.py b/services/web/server/tests/unit/with_dbs/01/wallets/payments/conftest.py index c84023dfe9a..5fce5fad9cb 100644 --- a/services/web/server/tests/unit/with_dbs/01/wallets/payments/conftest.py +++ b/services/web/server/tests/unit/with_dbs/01/wallets/payments/conftest.py @@ -10,6 +10,7 @@ from typing import Any, TypeAlias, cast from unittest.mock import MagicMock +import pycountry import pytest import sqlalchemy as sa from aiohttp import web @@ -334,7 +335,7 @@ def setup_user_pre_registration_details_db( address=faker.address().replace("\n", ", "), city=faker.city(), state=faker.state(), - country=faker.country(), + country=faker.random_element([c.name for c in pycountry.countries]), postal_code=faker.postcode(), created_by=None, ) From 83635d202489ae862dfd46da1c2dd1fc5678f54a Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 31 Oct 2024 13:25:00 +0100 Subject: [PATCH 108/165] fix dynamic scheduler settings --- .../dynamic_scheduler/settings.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/dynamic_scheduler/settings.py b/services/web/server/src/simcore_service_webserver/dynamic_scheduler/settings.py index 2ebe8bc141c..91dac1317b6 100644 --- a/services/web/server/src/simcore_service_webserver/dynamic_scheduler/settings.py +++ b/services/web/server/src/simcore_service_webserver/dynamic_scheduler/settings.py @@ -1,21 +1,18 @@ -from typing import Final +import datetime from aiohttp import web -from pydantic import AliasChoices, Field, NonNegativeInt +from pydantic import AliasChoices, Field from settings_library.base import BaseCustomSettings from settings_library.utils_service import MixinServiceSettings from .._constants import APP_SETTINGS_KEY -_MINUTE: Final[NonNegativeInt] = 60 -_HOUR: Final[NonNegativeInt] = 60 * _MINUTE - class DynamicSchedulerSettings(BaseCustomSettings, MixinServiceSettings): - DYNAMIC_SCHEDULER_STOP_SERVICE_TIMEOUT: NonNegativeInt = Field( - _HOUR + 10, + DYNAMIC_SCHEDULER_STOP_SERVICE_TIMEOUT: datetime.timedelta = Field( + datetime.timedelta(hours=1, seconds=10), description=( - "Timeout on stop service request (seconds)" + "Timeout on stop service request" "ANE: The below will try to help explaining what is happening: " "webserver -(stop_service)-> dynamic-scheduler -(relays the stop)-> " "director-v* -(save_state)-> service_x" From af87df5d10f7e2e15e489dab4b506b6799f8f02c Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 31 Oct 2024 14:43:02 +0100 Subject: [PATCH 109/165] fix import --- .../simcore_service_webserver/version_control/_handlers_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/version_control/_handlers_base.py b/services/web/server/src/simcore_service_webserver/version_control/_handlers_base.py index c40b27946f2..3424788fafa 100644 --- a/services/web/server/src/simcore_service_webserver/version_control/_handlers_base.py +++ b/services/web/server/src/simcore_service_webserver/version_control/_handlers_base.py @@ -4,7 +4,7 @@ from aiohttp import web from common_library.json_serialization import json_dumps -from pydantic.error_wrappers import ValidationError +from pydantic import ValidationError from servicelib.aiohttp.typing_extension import Handler from ..projects.exceptions import ProjectNotFoundError From d3d9b71dc486501788b66b238416130a665293ab Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 31 Oct 2024 14:47:25 +0100 Subject: [PATCH 110/165] remove env vars --- .../unit/with_dbs/01/test_resource_manager_user_sessions.py | 5 ----- .../web/server/tests/unit/with_dbs/01/wallets/conftest.py | 4 ---- 2 files changed, 9 deletions(-) diff --git a/services/web/server/tests/unit/with_dbs/01/test_resource_manager_user_sessions.py b/services/web/server/tests/unit/with_dbs/01/test_resource_manager_user_sessions.py index ac3eff4a0c4..00f3ce6d576 100644 --- a/services/web/server/tests/unit/with_dbs/01/test_resource_manager_user_sessions.py +++ b/services/web/server/tests/unit/with_dbs/01/test_resource_manager_user_sessions.py @@ -40,11 +40,6 @@ def mock_env_devel_environment( return mock_env_devel_environment | setenvs_from_dict( monkeypatch, { "RESOURCE_MANAGER_RESOURCE_TTL_S": "3", - "WEBSERVER_EMAIL": json.dumps({"SMTP_HOST": "smtp.fake.com", "SMTP_PORT": 25}), - "WEBSERVER_LOGIN": json.dumps({"LOGIN_REGISTRATION_INVITATION_REQUIRED": "True"}), - "WEBSERVER_PAYMENTS": json.dumps({"PAYMENTS_USERNAME": "somebody", "PAYMENTS_PASSWORD": faker.password(length=10)}), - "WEBSERVER_SCICRUNCH": "null", - "WEBSERVER_TRACING": "null" } ) diff --git a/services/web/server/tests/unit/with_dbs/01/wallets/conftest.py b/services/web/server/tests/unit/with_dbs/01/wallets/conftest.py index f52f5abb647..36164328005 100644 --- a/services/web/server/tests/unit/with_dbs/01/wallets/conftest.py +++ b/services/web/server/tests/unit/with_dbs/01/wallets/conftest.py @@ -35,10 +35,6 @@ def app_environment( "WEBSERVER_DB_LISTENER": "0", "WEBSERVER_DEV_FEATURES_ENABLED": "1", "WEBSERVER_GARBAGE_COLLECTOR": "null", - "WEBSERVER_EMAIL": json.dumps({"SMTP_HOST": "smtp.fake.com", "SMTP_PORT": 25}), - "WEBSERVER_LOGIN": json.dumps({"LOGIN_REGISTRATION_INVITATION_REQUIRED": "True"}), - "WEBSERVER_PAYMENTS": json.dumps({"PAYMENTS_USERNAME": "somebody", "PAYMENTS_PASSWORD": faker.password(length=10)}), - "WEBSERVER_SCICRUNCH": "null", "PAYMENTS_FAKE_GATEWAY_URL": "https://some-fake-gateway.com", }, ) From dba3ce258a7da0546b55c6ec0369aa924d7c68de Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 31 Oct 2024 14:48:30 +0100 Subject: [PATCH 111/165] remove env vars --- .../web/server/tests/unit/isolated/conftest.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/services/web/server/tests/unit/isolated/conftest.py b/services/web/server/tests/unit/isolated/conftest.py index 802076d9313..31f0aa33f98 100644 --- a/services/web/server/tests/unit/isolated/conftest.py +++ b/services/web/server/tests/unit/isolated/conftest.py @@ -103,24 +103,6 @@ def mock_env_devel_environment( monkeypatch, envs={ "WEBSERVER_DEV_FEATURES_ENABLED": "1", - "WEBSERVER_EMAIL": json.dumps({ - "SMTP_HOST": faker.url(), - "SMTP_PORT": faker.port_number() - }), - "WEBSERVER_LOGIN": json.dumps({ - "LOGIN_REGISTRATION_INVITATION_REQUIRED": True - }), - "WEBSERVER_PAYMENTS": json.dumps({ - "PAYMENTS_USERNAME": faker.user_name(), - "PAYMENTS_PASSWORD": faker.password() - }), - "WEBSERVER_SCICRUNCH": json.dumps({ - "SCICRUNCH_API_KEY": faker.pystr() - }), - "WEBSERVER_TRACING": json.dumps({ - "TRACING_OPENTELEMETRY_COLLECTOR_ENDPOINT": faker.url(), - "TRACING_OPENTELEMETRY_COLLECTOR_PORT": faker.port_number() - }) }, ) From 9001bd5af162ebb2207b08a51074cc395d7192e2 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 31 Oct 2024 16:14:35 +0100 Subject: [PATCH 112/165] fix urls --- .../unit/with_dbs/01/folders/test_folders.py | 24 +++++++-------- .../with_dbs/01/workspaces/test_workspaces.py | 2 +- ...t_workspaces__folders_and_projects_crud.py | 23 +++++++------- ...t_workspaces__list_projects_full_search.py | 16 +++++----- ...ces__moving_projects_between_workspaces.py | 24 +++++++-------- .../server/tests/unit/with_dbs/02/conftest.py | 2 +- ...handlers__clone_in_workspace_and_folder.py | 4 +-- .../02/test_projects_crud_handlers__list.py | 2 +- ...s_crud_handlers__list_with_query_params.py | 30 +++++++++---------- ...st_projects_nodes_pricing_unit_handlers.py | 6 ++-- .../02/test_projects_wallet_handlers.py | 16 +++++----- .../unit/with_dbs/03/login/test_login_2fa.py | 2 +- 12 files changed, 76 insertions(+), 75 deletions(-) diff --git a/services/web/server/tests/unit/with_dbs/01/folders/test_folders.py b/services/web/server/tests/unit/with_dbs/01/folders/test_folders.py index 46f2f2b559e..bb0c830ef27 100644 --- a/services/web/server/tests/unit/with_dbs/01/folders/test_folders.py +++ b/services/web/server/tests/unit/with_dbs/01/folders/test_folders.py @@ -76,7 +76,7 @@ async def test_folders_full_workflow( url = client.app.router["get_folder"].url_for( folder_id=f"{added_folder['folderId']}" ) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert FolderGet.model_validate(data) assert data["folderId"] == added_folder["folderId"] @@ -157,7 +157,7 @@ async def test_sub_folders_full_workflow( # list user specific folder base_url = client.app.router["list_folders"].url_for() url = base_url.with_query({"folder_id": f"{subfolder_folder['folderId']}"}) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 0 @@ -175,7 +175,7 @@ async def test_sub_folders_full_workflow( # list user subfolder folders base_url = client.app.router["list_folders"].url_for() url = base_url.with_query({"folder_id": f"{subfolder_folder['folderId']}"}) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 1 assert data[0]["name"] == "My sub sub folder" @@ -211,7 +211,7 @@ async def test_sub_folders_full_workflow( # list user root folders base_url = client.app.router["list_folders"].url_for() url = base_url.with_query({"folder_id": "null"}) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 2 @@ -298,7 +298,7 @@ async def test_project_listing_inside_of_private_folder( # list project in user private folder base_url = client.app.router["list_projects"].url_for() url = base_url.with_query({"folder_id": f"{original_user_folder['folderId']}"}) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 1 assert data[0]["uuid"] == user_project["uuid"] @@ -310,7 +310,7 @@ async def test_project_listing_inside_of_private_folder( # Try to list folder that user doesn't have access to base_url = client.app.router["list_projects"].url_for() url = base_url.with_query({"folder_id": f"{original_user_folder['folderId']}"}) - resp = await client.get(url) + resp = await client.get(f"{url}") _, errors = await assert_status( resp, status.HTTP_403_FORBIDDEN, @@ -330,7 +330,7 @@ async def test_project_listing_inside_of_private_folder( # list new user root folder base_url = client.app.router["list_projects"].url_for() url = base_url.with_query({"folder_id": "null"}) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 1 assert data[0]["uuid"] == user_project["uuid"] @@ -353,7 +353,7 @@ async def test_project_listing_inside_of_private_folder( # list new user specific folder base_url = client.app.router["list_projects"].url_for() url = base_url.with_query({"folder_id": f"{new_user_folder['folderId']}"}) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 1 assert data[0]["uuid"] == user_project["uuid"] @@ -448,14 +448,14 @@ async def test_folders_deletion( # list subfolder projects base_url = client.app.router["list_projects"].url_for() url = base_url.with_query({"folder_id": f"{subfolder_2['folderId']}"}) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 1 assert data[0]["uuid"] == user_project["uuid"] # list root projects base_url = client.app.router["list_projects"].url_for() - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 0 @@ -474,13 +474,13 @@ async def test_folders_deletion( await assert_status(resp, status.HTTP_204_NO_CONTENT) fire_and_forget_tasks = client.app[APP_FIRE_AND_FORGET_TASKS_KEY] - t: asyncio.Task = list(fire_and_forget_tasks)[0] + t: asyncio.Task = next(iter(fire_and_forget_tasks)) assert t.get_name().startswith("fire_and_forget_task_delete_project_task_") await t assert len(client.app[APP_FIRE_AND_FORGET_TASKS_KEY]) == 0 # list root projects (The project should have been deleted) base_url = client.app.router["list_projects"].url_for() - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 0 diff --git a/services/web/server/tests/unit/with_dbs/01/workspaces/test_workspaces.py b/services/web/server/tests/unit/with_dbs/01/workspaces/test_workspaces.py index 9365094d679..25de918f878 100644 --- a/services/web/server/tests/unit/with_dbs/01/workspaces/test_workspaces.py +++ b/services/web/server/tests/unit/with_dbs/01/workspaces/test_workspaces.py @@ -78,7 +78,7 @@ async def test_workspaces_workflow( url = client.app.router["get_workspace"].url_for( workspace_id=f"{added_workspace['workspaceId']}" ) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert data["workspaceId"] == added_workspace["workspaceId"] assert data["name"] == "My first workspace" diff --git a/services/web/server/tests/unit/with_dbs/01/workspaces/test_workspaces__folders_and_projects_crud.py b/services/web/server/tests/unit/with_dbs/01/workspaces/test_workspaces__folders_and_projects_crud.py index c95aebe6fdd..0ebb5518b28 100644 --- a/services/web/server/tests/unit/with_dbs/01/workspaces/test_workspaces__folders_and_projects_crud.py +++ b/services/web/server/tests/unit/with_dbs/01/workspaces/test_workspaces__folders_and_projects_crud.py @@ -79,7 +79,7 @@ async def test_workspaces_full_workflow_with_folders_and_projects( # List project in workspace base_url = client.app.router["list_projects"].url_for() url = base_url.with_query({"workspace_id": f"{added_workspace['workspaceId']}"}) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 1 assert data[0]["uuid"] == project["uuid"] @@ -88,7 +88,7 @@ async def test_workspaces_full_workflow_with_folders_and_projects( # Get project in workspace base_url = client.app.router["get_project"].url_for(project_id=project["uuid"]) - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert data["uuid"] == project["uuid"] assert data["workspaceId"] == added_workspace["workspaceId"] @@ -110,7 +110,7 @@ async def test_workspaces_full_workflow_with_folders_and_projects( url = base_url.with_query( {"workspace_id": f"{added_workspace['workspaceId']}", "folder_id": "null"} ) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 1 assert data[0]["folderId"] == first_folder["folderId"] @@ -131,7 +131,7 @@ async def test_workspaces_full_workflow_with_folders_and_projects( "folder_id": f"{first_folder['folderId']}", } ) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 1 assert data[0]["uuid"] == project["uuid"] @@ -142,7 +142,7 @@ async def test_workspaces_full_workflow_with_folders_and_projects( # Try to list folder that user doesn't have access to base_url = client.app.router["list_projects"].url_for() url = base_url.with_query({"workspace_id": f"{added_workspace['workspaceId']}"}) - resp = await client.get(url) + resp = await client.get(f"{url}") _, errors = await assert_status( resp, status.HTTP_403_FORBIDDEN, @@ -164,7 +164,7 @@ async def test_workspaces_full_workflow_with_folders_and_projects( url = base_url.with_query( {"workspace_id": f"{added_workspace['workspaceId']}", "folder_id": "null"} ) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 1 @@ -176,7 +176,7 @@ async def test_workspaces_full_workflow_with_folders_and_projects( "folder_id": "none", } ) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 0 @@ -188,7 +188,7 @@ async def test_workspaces_full_workflow_with_folders_and_projects( "folder_id": f"{first_folder['folderId']}", } ) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 1 assert data[0]["uuid"] == project["uuid"] @@ -230,7 +230,7 @@ async def test_workspaces_full_workflow_with_folders_and_projects( url = base_url.with_query( {"workspace_id": f"{added_workspace['workspaceId']}", "folder_id": "null"} ) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 2 @@ -299,7 +299,7 @@ async def test_workspaces_delete_folders( # List project in workspace base_url = client.app.router["list_projects"].url_for() url = base_url.with_query({"workspace_id": f"{added_workspace['workspaceId']}"}) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 2 @@ -362,6 +362,7 @@ async def test_workspaces_delete_folders( # List project in workspace (The projects should have been deleted) base_url = client.app.router["list_projects"].url_for() url = base_url.with_query({"workspace_id": f"{added_workspace['workspaceId']}"}) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 0 + diff --git a/services/web/server/tests/unit/with_dbs/01/workspaces/test_workspaces__list_projects_full_search.py b/services/web/server/tests/unit/with_dbs/01/workspaces/test_workspaces__list_projects_full_search.py index a7efd64b485..6c146bb5a1f 100644 --- a/services/web/server/tests/unit/with_dbs/01/workspaces/test_workspaces__list_projects_full_search.py +++ b/services/web/server/tests/unit/with_dbs/01/workspaces/test_workspaces__list_projects_full_search.py @@ -82,7 +82,7 @@ async def test_workspaces__list_projects_full_search( # List project with full search base_url = client.app.router["list_projects_full_search"].url_for() url = base_url.with_query({"text": "solution"}) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 1 assert data[0]["uuid"] == project_1["uuid"] @@ -104,7 +104,7 @@ async def test_workspaces__list_projects_full_search( # List project with full search base_url = client.app.router["list_projects_full_search"].url_for() url = base_url.with_query({"text": "Orion"}) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 1 assert data[0]["uuid"] == project_2["uuid"] @@ -137,7 +137,7 @@ async def test_workspaces__list_projects_full_search( # List project with full search base_url = client.app.router["list_projects_full_search"].url_for() url = base_url.with_query({"text": "Skyline"}) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 1 assert data[0]["uuid"] == project_3["uuid"] @@ -147,7 +147,7 @@ async def test_workspaces__list_projects_full_search( # List project with full search (it should return data across all workspaces/folders) base_url = client.app.router["list_projects_full_search"].url_for() url = base_url.with_query({"text": "solution"}) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) sorted_data = sorted(data, key=lambda x: x["uuid"]) assert len(sorted_data) == 3 @@ -190,7 +190,7 @@ async def test__list_projects_full_search_with_query_parameters( # Full search with text base_url = client.app.router["list_projects_full_search"].url_for() url = base_url.with_query({"text": "Orion"}) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 1 assert data[0]["uuid"] == project["uuid"] @@ -203,7 +203,7 @@ async def test__list_projects_full_search_with_query_parameters( "order_by": json.dumps({"field": "uuid", "direction": "desc"}), } ) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 1 assert data[0]["uuid"] == project["uuid"] @@ -211,7 +211,7 @@ async def test__list_projects_full_search_with_query_parameters( # Full search with tag_ids base_url = client.app.router["list_projects_full_search"].url_for() url = base_url.with_query({"text": "Orion", "tag_ids": "1,2"}) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 0 @@ -232,7 +232,7 @@ async def test__list_projects_full_search_with_query_parameters( # Full search with tag_ids base_url = client.app.router["list_projects_full_search"].url_for() url = base_url.with_query({"text": "Orion", "tag_ids": f"{added_tag['id']}"}) - resp = await client.get(url) + resp = await client.get(f"{url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert len(data) == 1 assert data[0]["uuid"] == project["uuid"] diff --git a/services/web/server/tests/unit/with_dbs/01/workspaces/test_workspaces__moving_projects_between_workspaces.py b/services/web/server/tests/unit/with_dbs/01/workspaces/test_workspaces__moving_projects_between_workspaces.py index f583b9e3ecf..21b16ea9738 100644 --- a/services/web/server/tests/unit/with_dbs/01/workspaces/test_workspaces__moving_projects_between_workspaces.py +++ b/services/web/server/tests/unit/with_dbs/01/workspaces/test_workspaces__moving_projects_between_workspaces.py @@ -58,7 +58,7 @@ async def test_moving_between_workspaces_user_role_permissions( base_url = client.app.router["replace_project_workspace"].url_for( project_id=fake_project["uuid"], workspace_id="null" ) - resp = await client.put(base_url) + resp = await client.put(f"{base_url}") await assert_status(resp, expected.no_content) @@ -98,7 +98,7 @@ async def test_moving_between_private_and_shared_workspaces( # Get project in workspace base_url = client.app.router["get_project"].url_for(project_id=project["uuid"]) - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert data["workspaceId"] == added_workspace["workspaceId"] # <-- Workspace ID @@ -106,12 +106,12 @@ async def test_moving_between_private_and_shared_workspaces( base_url = client.app.router["replace_project_workspace"].url_for( project_id=project["uuid"], workspace_id="null" ) - resp = await client.put(base_url) + resp = await client.put(f"{base_url}") await assert_status(resp, status.HTTP_204_NO_CONTENT) # Get project in workspace base_url = client.app.router["get_project"].url_for(project_id=project["uuid"]) - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert data["workspaceId"] is None # <-- Workspace ID is None @@ -119,12 +119,12 @@ async def test_moving_between_private_and_shared_workspaces( base_url = client.app.router["replace_project_workspace"].url_for( project_id=project["uuid"], workspace_id=f"{added_workspace['workspaceId']}" ) - resp = await client.put(base_url) + resp = await client.put(f"{base_url}") await assert_status(resp, status.HTTP_204_NO_CONTENT) # Get project in workspace base_url = client.app.router["get_project"].url_for(project_id=project["uuid"]) - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert data["workspaceId"] == added_workspace["workspaceId"] # <-- Workspace ID @@ -177,7 +177,7 @@ async def test_moving_between_shared_and_shared_workspaces( # Get project in workspace base_url = client.app.router["get_project"].url_for(project_id=project["uuid"]) - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert data["workspaceId"] == added_workspace["workspaceId"] # <-- Workspace ID @@ -185,12 +185,12 @@ async def test_moving_between_shared_and_shared_workspaces( base_url = client.app.router["replace_project_workspace"].url_for( project_id=project["uuid"], workspace_id=f"{second_workspace['workspaceId']}" ) - resp = await client.put(base_url) + resp = await client.put(f"{base_url}") await assert_status(resp, status.HTTP_204_NO_CONTENT) # Get project in workspace base_url = client.app.router["get_project"].url_for(project_id=project["uuid"]) - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert data["workspaceId"] == second_workspace["workspaceId"] # <-- Workspace ID @@ -257,7 +257,7 @@ async def test_moving_between_workspaces_check_removed_from_folder( # Get project in workspace base_url = client.app.router["get_project"].url_for(project_id=project["uuid"]) - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert data["workspaceId"] == added_workspace["workspaceId"] # <-- Workspace ID @@ -265,12 +265,12 @@ async def test_moving_between_workspaces_check_removed_from_folder( base_url = client.app.router["replace_project_workspace"].url_for( project_id=project["uuid"], workspace_id="none" ) - resp = await client.put(base_url) + resp = await client.put(f"{base_url}") await assert_status(resp, status.HTTP_204_NO_CONTENT) # Get project in workspace base_url = client.app.router["get_project"].url_for(project_id=project["uuid"]) - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data, _ = await assert_status(resp, status.HTTP_200_OK) assert data["workspaceId"] is None # <-- Workspace ID is None diff --git a/services/web/server/tests/unit/with_dbs/02/conftest.py b/services/web/server/tests/unit/with_dbs/02/conftest.py index 33257ef2026..7e765694d3d 100644 --- a/services/web/server/tests/unit/with_dbs/02/conftest.py +++ b/services/web/server/tests/unit/with_dbs/02/conftest.py @@ -254,7 +254,7 @@ async def _assert_it( ) -> dict: # GET /v0/projects/{project_id} with a project owned by user url = client.app.router["get_project"].url_for(project_id=project["uuid"]) - resp = await client.get(url) + resp = await client.get(f"{url}") data, error = await assert_status(resp, expected) if not error: diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone_in_workspace_and_folder.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone_in_workspace_and_folder.py index 755a1081fcb..18ba745eaee 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone_in_workspace_and_folder.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__clone_in_workspace_and_folder.py @@ -114,7 +114,7 @@ async def test_clone_project( "folder_id": f"{create_workspace_and_folder[1]}", } url = base_url.with_query(**query_parameters) - resp = await client.get(url) + resp = await client.get(f"{url}") data = await resp.json() assert resp.status == 200 assert len(data["data"]) == 1 @@ -135,7 +135,7 @@ async def test_clone_project( "folder_id": f"{create_workspace_and_folder[1]}", } url = base_url.with_query(**query_parameters) - resp = await client.get(url) + resp = await client.get(f"{url}") data = await resp.json() assert resp.status == 200 assert len(data["data"]) == 2 diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list.py index dae689d1974..e5f580cd785 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list.py @@ -55,7 +55,7 @@ async def _list_projects( if query_parameters: url = url.with_query(**query_parameters) - resp = await client.get(url) + resp = await client.get(f"{url}") data, errors, meta, links = await assert_status( resp, expected, diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py index 89b7fed1544..abb26a3f3e3 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py @@ -153,7 +153,7 @@ async def test_list_projects_with_search_parameter( base_url = client.app.router["list_projects"].url_for() assert f"{base_url}" == f"/{api_version_prefix}/projects" - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data = await resp.json() assert resp.status == 200 @@ -164,7 +164,7 @@ async def test_list_projects_with_search_parameter( url = base_url.with_query(**query_parameters) assert f"{url}" == f"/{api_version_prefix}/projects?search=" - resp = await client.get(url) + resp = await client.get(f"{url}") data = await resp.json() assert resp.status == 200 @@ -175,7 +175,7 @@ async def test_list_projects_with_search_parameter( url = base_url.with_query(**query_parameters) assert f"{url}" == f"/{api_version_prefix}/projects?search=nAmE+5" - resp = await client.get(url) + resp = await client.get(f"{url}") data = await resp.json() assert resp.status == 200 @@ -188,7 +188,7 @@ async def test_list_projects_with_search_parameter( url = base_url.with_query(**query_parameters) assert f"{url}" == f"/{api_version_prefix}/projects?search=2-fe1b-11ed-b038-cdb1" - resp = await client.get(url) + resp = await client.get(f"{url}") data = await resp.json() assert resp.status == 200 @@ -207,7 +207,7 @@ async def test_list_projects_with_search_parameter( == f"/{api_version_prefix}/projects?search={user_name_substring_query_parsed}" ) - resp = await client.get(url) + resp = await client.get(f"{url}") data = await resp.json() assert resp.status == 200 @@ -225,7 +225,7 @@ async def test_list_projects_with_search_parameter( url = base_url.with_query(**query_parameters) assert f"{url}" == f"/{api_version_prefix}/projects?search=oda" - resp = await client.get(url) + resp = await client.get(f"{url}") data = await resp.json() assert resp.status == 200 @@ -236,7 +236,7 @@ async def test_list_projects_with_search_parameter( url = base_url.with_query(**query_parameters) assert f"{url}" == f"/{api_version_prefix}/projects?search=does+not+exists" - resp = await client.get(url) + resp = await client.get(f"{url}") data = await resp.json() assert resp.status == 200 @@ -249,7 +249,7 @@ async def test_list_projects_with_search_parameter( url = base_url.with_query(**query_parameters) assert f"{url}" == f"/{api_version_prefix}/projects?search=oda&offset=0&limit=1" - resp = await client.get(url) + resp = await client.get(f"{url}") data = await resp.json() assert resp.status == 200 @@ -327,7 +327,7 @@ async def test_list_projects_with_order_by_parameter( f"{url}" == f"/{api_version_prefix}/projects?order_by=%7B%22field%22:+%22uuid%22,+%22direction%22:+%22asc%22%7D" ) - resp = await client.get(url) + resp = await client.get(f"{url}") data = await resp.json() assert resp.status == 200 assert [item["uuid"][0] for item in data["data"]] == _alphabetically_ordered_list @@ -337,7 +337,7 @@ async def test_list_projects_with_order_by_parameter( url = base_url.with_query( order_by=json.dumps({"field": "uuid", "direction": "desc"}) ) - resp = await client.get(url) + resp = await client.get(f"{url}") data = await resp.json() assert resp.status == 200 assert [item["uuid"][0] for item in data["data"]] == _alphabetically_ordered_list[ @@ -349,7 +349,7 @@ async def test_list_projects_with_order_by_parameter( url = base_url.with_query( order_by=json.dumps({"field": "name", "direction": "asc"}) ) - resp = await client.get(url) + resp = await client.get(f"{url}") data = await resp.json() assert resp.status == 200 assert [item["name"][0] for item in data["data"]] == _alphabetically_ordered_list @@ -359,7 +359,7 @@ async def test_list_projects_with_order_by_parameter( url = base_url.with_query( order_by=json.dumps({"field": "description", "direction": "asc"}) ) - resp = await client.get(url) + resp = await client.get(f"{url}") data = await resp.json() assert resp.status == 200 assert [ @@ -453,7 +453,7 @@ async def test_list_projects_for_specific_folder_id( base_url = client.app.router["list_projects"].url_for() assert f"{base_url}" == f"/{api_version_prefix}/projects" - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data = await resp.json() assert resp.status == 200 @@ -463,7 +463,7 @@ async def test_list_projects_for_specific_folder_id( query_parameters = {"folder_id": "null"} url = base_url.with_query(**query_parameters) - resp = await client.get(url) + resp = await client.get(f"{url}") data = await resp.json() assert resp.status == 200 @@ -476,7 +476,7 @@ async def test_list_projects_for_specific_folder_id( url = base_url.with_query(**query_parameters) assert f"{url}" == f"/{api_version_prefix}/projects?folder_id={setup_folders_db}" - resp = await client.get(url) + resp = await client.get(f"{url}") data = await resp.json() assert resp.status == 200 diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_pricing_unit_handlers.py b/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_pricing_unit_handlers.py index 10367ac9155..a867d2e3c40 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_pricing_unit_handlers.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_nodes_pricing_unit_handlers.py @@ -52,7 +52,7 @@ async def test_project_node_pricing_unit_user_role_access( base_url = client.app.router["get_project_node_pricing_unit"].url_for( project_id=user_project["uuid"], node_id=node_id ) - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") assert ( resp.status == status.HTTP_401_UNAUTHORIZED if user_role == UserRole.ANONYMOUS @@ -71,7 +71,7 @@ async def test_project_node_pricing_unit_user_project_access( base_url = client.app.router["get_project_node_pricing_unit"].url_for( project_id=user_project["uuid"], node_id=node_id ) - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data, _ = await assert_status(resp, expected) assert data == None @@ -80,7 +80,7 @@ async def test_project_node_pricing_unit_user_project_access( base_url = client.app.router["get_project_node_pricing_unit"].url_for( project_id=user_project["uuid"], node_id=node_id ) - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") _, errors = await assert_status(resp, status.HTTP_403_FORBIDDEN) assert errors diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_wallet_handlers.py b/services/web/server/tests/unit/with_dbs/02/test_projects_wallet_handlers.py index 269358a4a9f..30aaa89abbc 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_wallet_handlers.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_wallet_handlers.py @@ -42,7 +42,7 @@ async def test_project_wallets_user_role_access( base_url = client.app.router["get_project_wallet"].url_for( project_id=user_project["uuid"] ) - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") assert ( resp.status == status.HTTP_401_UNAUTHORIZED if user_role == UserRole.ANONYMOUS @@ -61,7 +61,7 @@ async def test_project_wallets_user_project_access( base_url = client.app.router["get_project_wallet"].url_for( project_id=user_project["uuid"] ) - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data, _ = await assert_status(resp, expected) assert data == None @@ -70,7 +70,7 @@ async def test_project_wallets_user_project_access( base_url = client.app.router["get_project_wallet"].url_for( project_id=user_project["uuid"] ) - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") _, errors = await assert_status(resp, status.HTTP_403_FORBIDDEN) assert errors @@ -110,7 +110,7 @@ async def test_project_wallets_full_workflow( base_url = client.app.router["get_project_wallet"].url_for( project_id=user_project["uuid"] ) - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data, _ = await assert_status(resp, expected) assert data == None @@ -118,14 +118,14 @@ async def test_project_wallets_full_workflow( base_url = client.app.router["connect_wallet_to_project"].url_for( project_id=user_project["uuid"], wallet_id=f"{setup_wallets_db[0].wallet_id}" ) - resp = await client.put(base_url) + resp = await client.put(f"{base_url}") data, _ = await assert_status(resp, expected) assert data["walletId"] == setup_wallets_db[0].wallet_id base_url = client.app.router["get_project_wallet"].url_for( project_id=user_project["uuid"] ) - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data, _ = await assert_status(resp, expected) assert data["walletId"] == setup_wallets_db[0].wallet_id @@ -133,13 +133,13 @@ async def test_project_wallets_full_workflow( base_url = client.app.router["connect_wallet_to_project"].url_for( project_id=user_project["uuid"], wallet_id=f"{setup_wallets_db[1].wallet_id}" ) - resp = await client.put(base_url) + resp = await client.put(f"{base_url}") data, _ = await assert_status(resp, expected) assert data["walletId"] == setup_wallets_db[1].wallet_id base_url = client.app.router["get_project_wallet"].url_for( project_id=user_project["uuid"] ) - resp = await client.get(base_url) + resp = await client.get(f"{base_url}") data, _ = await assert_status(resp, expected) assert data["walletId"] == setup_wallets_db[1].wallet_id diff --git a/services/web/server/tests/unit/with_dbs/03/login/test_login_2fa.py b/services/web/server/tests/unit/with_dbs/03/login/test_login_2fa.py index 5cb85785419..29324b2af23 100644 --- a/services/web/server/tests/unit/with_dbs/03/login/test_login_2fa.py +++ b/services/web/server/tests/unit/with_dbs/03/login/test_login_2fa.py @@ -148,7 +148,7 @@ def _get_confirmation_link_from_email(): url = _get_confirmation_link_from_email() # 2. confirmation - response = await client.get(url) + response = await client.get(f"{url}") assert response.status == status.HTTP_200_OK # check email+password registered From 42bea1e5966d3548c949e63745fbda367cebfd09 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 31 Oct 2024 20:30:17 +0100 Subject: [PATCH 113/165] fix optional --- .../src/models_library/api_schemas_webserver/projects_nodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models-library/src/models_library/api_schemas_webserver/projects_nodes.py b/packages/models-library/src/models_library/api_schemas_webserver/projects_nodes.py index 7b65b84cb5f..9f70b1b46f8 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/projects_nodes.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/projects_nodes.py @@ -19,7 +19,7 @@ class NodeCreate(InputSchemaWithoutCamelCase): service_key: ServiceKey service_version: ServiceVersion - service_id: str | None + service_id: str | None = Field(default=None) BootOptions: TypeAlias = dict From 51191594ed52cd6a1bb68be065bceb1805b6de25 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 31 Oct 2024 20:53:05 +0100 Subject: [PATCH 114/165] fix projects --- .../src/models_library/api_schemas_webserver/projects.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/models-library/src/models_library/api_schemas_webserver/projects.py b/packages/models-library/src/models_library/api_schemas_webserver/projects.py index 07e65b52c8b..0ae2bdee311 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/projects.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/projects.py @@ -107,7 +107,7 @@ class ProjectReplace(InputSchema): uuid: ProjectID name: ShortTruncatedStr description: LongTruncatedStr - thumbnail: HttpUrl | None + thumbnail: Annotated[HttpUrl | None, BeforeValidator(empty_str_to_none_pre_validator)] = Field(default=None) creation_date: DateTimeStr last_change_date: DateTimeStr workbench: NodesDict @@ -123,15 +123,11 @@ class ProjectReplace(InputSchema): default_factory=dict, json_schema_extra={"default": {}} ) - _empty_is_none = field_validator("thumbnail", mode="before")( - empty_str_to_none_pre_validator - ) - class ProjectPatch(InputSchema): name: ShortTruncatedStr | None = Field(default=None) description: LongTruncatedStr | None = Field(default=None) - thumbnail: Annotated[HttpUrl, BeforeValidator(empty_str_to_none_pre_validator)] | None = Field(default=None) + thumbnail: Annotated[HttpUrl | None, BeforeValidator(empty_str_to_none_pre_validator)] = Field(default=None) access_rights: dict[GroupIDStr, AccessRights] | None = Field(default=None) classifiers: list[ClassifierID] | None = Field(default=None) dev: dict | None = Field(default=None) From e1e182b3a06cca1831c5a634df49595c096b972a Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 31 Oct 2024 20:54:30 +0100 Subject: [PATCH 115/165] continue fixing --- .../tests/unit/with_dbs/02/test_projects_states_handlers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py b/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py index 2efbd947a91..693372aab27 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py @@ -285,8 +285,8 @@ async def test_share_project( ) if new_project: assert new_project["accessRights"] == { - str(primary_group["gid"]): {"read": True, "write": True, "delete": True}, - str(all_group["gid"]): share_rights, + f'{primary_group["gid"]}': {"read": True, "write": True, "delete": True}, + f'{(all_group["gid"])}': share_rights, } # user 1 can always get to his project From b5b8495fc0037ea5038327d8f81631c79ec3d771 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Thu, 31 Oct 2024 21:24:09 +0100 Subject: [PATCH 116/165] coerce num to str --- .../src/models_library/api_schemas_webserver/groups.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/models-library/src/models_library/api_schemas_webserver/groups.py b/packages/models-library/src/models_library/api_schemas_webserver/groups.py index ab99d891a25..d77c04288c9 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/groups.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/groups.py @@ -144,13 +144,13 @@ class AllUsersGroups(BaseModel): class GroupUserGet(BaseModel): - id: str | None = Field(None, description="the user id") + id: str | None = Field(None, description="the user id", coerce_numbers_to_str=True) login: LowerCaseEmailStr | None = Field(None, description="the user login email") first_name: str | None = Field(None, description="the user first name") last_name: str | None = Field(None, description="the user last name") gravatar_id: str | None = Field(None, description="the user gravatar id hash") gid: str | None = Field( - None, description="the user primary gid" + None, description="the user primary gid", coerce_numbers_to_str=True ) access_rights: GroupAccessRights = Field(..., alias="accessRights") From c63bb666a3e49753417c304b13a902d69ddc6fce Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 09:12:23 +0100 Subject: [PATCH 117/165] revert for now --- .../src/simcore_service_webserver/studies_dispatcher/_users.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_users.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_users.py index 811628a719b..568a534832a 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_users.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_users.py @@ -84,7 +84,7 @@ async def create_temporary_guest_user(request: web.Request): f"{random_user_name}@guest-at-osparc.io" ) password = generate_password(length=12) - expires_at = datetime.now(UTC) + settings.STUDIES_GUEST_ACCOUNT_LIFETIME + expires_at = datetime.utcnow() + settings.STUDIES_GUEST_ACCOUNT_LIFETIME # GUEST_USER_RC_LOCK: # From 5298ad9116cb44f75fa5115013ad43816c09a865 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 09:58:06 +0100 Subject: [PATCH 118/165] fix test --- services/web/server/tests/unit/with_dbs/03/test_users.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/server/tests/unit/with_dbs/03/test_users.py b/services/web/server/tests/unit/with_dbs/03/test_users.py index 055a0ffbb50..99a1ab326ef 100644 --- a/services/web/server/tests/unit/with_dbs/03/test_users.py +++ b/services/web/server/tests/unit/with_dbs/03/test_users.py @@ -85,7 +85,7 @@ async def test_get_profile( e = Envelope[ProfileGet].model_validate(await resp.json()) assert e.error == error assert ( - e.data.model_dump(**RESPONSE_MODEL_POLICY) == data if e.data else e.data == data + e.data.model_dump(**RESPONSE_MODEL_POLICY, mode="json") == data if e.data else e.data == data ) if not error: @@ -107,7 +107,7 @@ async def test_get_profile( assert profile.role == user_role.name assert profile.groups - got_profile_groups = profile.groups.model_dump(**RESPONSE_MODEL_POLICY) + got_profile_groups = profile.groups.model_dump(**RESPONSE_MODEL_POLICY, mode="json") assert got_profile_groups["me"] == primary_group assert got_profile_groups["all"] == all_group From 34be411ef248dc30c84ceca285c78d8237989f34 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 10:14:40 +0100 Subject: [PATCH 119/165] fix nullable --- .../src/simcore_service_webserver/version_control/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/version_control/models.py b/services/web/server/src/simcore_service_webserver/version_control/models.py index de0e74bc637..2869d4b37ce 100644 --- a/services/web/server/src/simcore_service_webserver/version_control/models.py +++ b/services/web/server/src/simcore_service_webserver/version_control/models.py @@ -35,7 +35,7 @@ class Checkpoint(BaseModel): created_at: datetime tags: tuple[str, ...] message: str | None = None - parents_ids: tuple[PositiveInt, ...] = Field(default=None) + parents_ids: tuple[PositiveInt, ...] | None = Field(default=None) @classmethod def from_commit_log(cls, commit: RowProxy, tags: list[RowProxy]) -> "Checkpoint": From 10ed6a0d2bfe3dd8a139a73621eeb4029c48ec7f Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 11:02:07 +0100 Subject: [PATCH 120/165] fix nullable --- .../server/src/simcore_service_webserver/storage/_handlers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/storage/_handlers.py b/services/web/server/src/simcore_service_webserver/storage/_handlers.py index 5f6844d52fa..83372296dd2 100644 --- a/services/web/server/src/simcore_service_webserver/storage/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/storage/_handlers.py @@ -229,7 +229,7 @@ class _PathParams(BaseModel): parse_request_path_parameters_as(_PathParams, request) class _QueryParams(BaseModel): - file_size: ByteSize | None + file_size: ByteSize | None = None link_type: LinkType = LinkType.PRESIGNED is_directory: bool = False From fb633f244ed728e177148c2b2b2c628bae3ba609 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 11:04:10 +0100 Subject: [PATCH 121/165] fix url --- .../03/version_control/test_version_control_handlers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_handlers.py b/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_handlers.py index e7f2a44a0dd..5270a17c04c 100644 --- a/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_handlers.py +++ b/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control_handlers.py @@ -108,7 +108,7 @@ async def test_workflow( resp = await client.get( f"/{VX}/repos/projects/{project_uuid}/checkpoints/{checkpoint1.id}" ) - assert str(resp.url) == checkpoint1.url + assert f"{resp.url}" == f"{checkpoint1.url}" assert CheckpointApiModel.model_validate(data) == checkpoint1 # LIST checkpoints From 83a34e5fdd9057406499129ad1719e9d70de6d27 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 11:09:36 +0100 Subject: [PATCH 122/165] fix nullable --- .../server/src/simcore_service_webserver/users/_schemas.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/users/_schemas.py b/services/web/server/src/simcore_service_webserver/users/_schemas.py index 3452537166a..b7b3b2a21ad 100644 --- a/services/web/server/src/simcore_service_webserver/users/_schemas.py +++ b/services/web/server/src/simcore_service_webserver/users/_schemas.py @@ -61,12 +61,12 @@ class PreUserProfile(InputSchema): first_name: str last_name: str email: LowerCaseEmailStr - institution: str | None = Field(None, description="company, university, ...") + institution: str | None = Field(default=None, description="company, university, ...") phone: str | None # billing details address: str city: str - state: str | None + state: str | None = Field(default=None) postal_code: str country: str extras: dict[str, Any] = Field( From cf0a1ca854852aefbb5a5c2563b4dde8771243b4 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 11:10:58 +0100 Subject: [PATCH 123/165] fix rootmodel --- .../tests/integration/02/scicrunch/test_scicrunch__rest.py | 4 ++-- .../unit/with_dbs/03/version_control/test_version_control.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/services/web/server/tests/integration/02/scicrunch/test_scicrunch__rest.py b/services/web/server/tests/integration/02/scicrunch/test_scicrunch__rest.py index a6671355826..4ae3ca6a3e1 100644 --- a/services/web/server/tests/integration/02/scicrunch/test_scicrunch__rest.py +++ b/services/web/server/tests/integration/02/scicrunch/test_scicrunch__rest.py @@ -159,7 +159,7 @@ async def test_scicrunch_service_autocomplete_by_name(settings: SciCrunchSetting }, {"rid": "SCR_014398", "original_id": "SCR_014398", "name": "GNU Octave"}, ] - ).model_dump()["__root__"] + ).model_dump()["root"] async with ClientSession() as client: @@ -167,6 +167,6 @@ async def test_scicrunch_service_autocomplete_by_name(settings: SciCrunchSetting resource_hits = await autocomplete_by_name("octave", client, settings) - hits = resource_hits.model_dump()["__root__"] + hits = resource_hits.model_dump()["root"] assert expected == hits, f"for {guess_name}" diff --git a/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control.py b/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control.py index 822eb16951b..ae95f95f9f9 100644 --- a/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control.py +++ b/services/web/server/tests/unit/with_dbs/03/version_control/test_version_control.py @@ -24,6 +24,6 @@ def test_compute_workbench_checksum(fake_project: ProjectDict): # # e.g. order after parse maps order in BaseModel but not in dict # - sha1_w_model = compute_workbench_checksum(workbench.__root__) + sha1_w_model = compute_workbench_checksum(workbench.root) assert sha1_w_model == sha1_w_dict From 8b9f93fe6af08aa09d04e609d9b66052c618118a Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 11:31:36 +0100 Subject: [PATCH 124/165] pin pydantic-settins version --- services/web/server/requirements/_base.txt | 44 ++++++++++++++++++- services/web/server/requirements/_test.txt | 4 ++ .../web/server/requirements/constraints.txt | 1 + 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/services/web/server/requirements/_base.txt b/services/web/server/requirements/_base.txt index a327d06768b..5806bbb7f7d 100644 --- a/services/web/server/requirements/_base.txt +++ b/services/web/server/requirements/_base.txt @@ -26,6 +26,7 @@ aiofiles==0.8.0 # -r requirements/_base.in aiohttp==3.8.5 # via + # -c requirements/../../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -120,6 +121,7 @@ captcha==0.5.0 # via -r requirements/_base.in certifi==2023.7.22 # via + # -c requirements/../../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -158,6 +160,7 @@ click==8.1.3 # via typer cryptography==41.0.7 # via + # -c requirements/../../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -240,6 +243,7 @@ jinja-app-loader==1.0.2 # via -r requirements/_base.in jinja2==3.1.2 # via + # -c requirements/../../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -286,6 +290,7 @@ lazy-object-proxy==1.7.1 # via openapi-core mako==1.2.2 # via + # -c requirements/../../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -408,6 +413,7 @@ opentelemetry-util-http==0.47b0 # opentelemetry-instrumentation-requests orjson==3.10.0 # via + # -c requirements/../../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -435,10 +441,24 @@ orjson==3.10.0 # -c requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/simcore-sdk/requirements/../../../requirements/constraints.txt # -c requirements/../../../../requirements/constraints.txt + # -r requirements/../../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/models-library/requirements/_base.in + # -r requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/service-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/_base.in packaging==24.1 # via @@ -475,6 +495,7 @@ pycparser==2.21 # via cffi pydantic==2.9.2 # via + # -c requirements/../../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -503,6 +524,7 @@ pydantic==2.9.2 # -c requirements/../../../../packages/simcore-sdk/requirements/../../../requirements/constraints.txt # -c requirements/../../../../requirements/constraints.txt # -c requirements/./constraints.txt + # -r requirements/../../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/models-library/requirements/_base.in # -r requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/_base.in @@ -537,12 +559,27 @@ pydantic-core==2.23.4 # via pydantic pydantic-extra-types==2.9.0 # via + # -r requirements/../../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/models-library/requirements/_base.in + # -r requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/service-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in -pydantic-settings==2.6.0 + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../../packages/simcore-sdk/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in +pydantic-settings==2.5.2 # via + # -c requirements/./constraints.txt # -r requirements/../../../../packages/models-library/requirements/_base.in # -r requirements/../../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in # -r requirements/../../../../packages/service-library/requirements/../../../packages/settings-library/requirements/_base.in @@ -577,6 +614,7 @@ pytz==2022.1 # via twilio pyyaml==6.0.1 # via + # -c requirements/../../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -610,6 +648,7 @@ pyyaml==6.0.1 # openapi-spec-validator redis==5.0.4 # via + # -c requirements/../../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -674,6 +713,7 @@ sniffio==1.3.1 # via anyio sqlalchemy==1.4.47 # via + # -c requirements/../../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -744,6 +784,7 @@ typing-extensions==4.12.0 # typer ujson==5.5.0 # via + # -c requirements/../../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -774,6 +815,7 @@ ujson==5.5.0 # aiohttp-swagger urllib3==1.26.11 # via + # -c requirements/../../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/models-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt diff --git a/services/web/server/requirements/_test.txt b/services/web/server/requirements/_test.txt index 5a977c27bea..d787c281d34 100644 --- a/services/web/server/requirements/_test.txt +++ b/services/web/server/requirements/_test.txt @@ -65,6 +65,10 @@ frozenlist==1.4.1 # -c requirements/_base.txt # aiohttp # aiosignal +greenlet==2.0.2 + # via + # -c requirements/_base.txt + # sqlalchemy hypothesis==6.91.0 # via -r requirements/_test.in icdiff==2.0.7 diff --git a/services/web/server/requirements/constraints.txt b/services/web/server/requirements/constraints.txt index 71c712593cb..9592eb8b76c 100644 --- a/services/web/server/requirements/constraints.txt +++ b/services/web/server/requirements/constraints.txt @@ -10,3 +10,4 @@ frozenlist>=1.3.1 # See: https://github.com/pydantic/pydantic/issues/4011 pydantic>=1.10 +pydantic-settings<2.6 # GCR: remove me \ No newline at end of file From 871ecead9602309dd30f1e2f51d6c47a854c3006 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 11:57:58 +0100 Subject: [PATCH 125/165] continue fixing --- .../login/handlers_registration.py | 2 +- .../users/_schemas.py | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/login/handlers_registration.py b/services/web/server/src/simcore_service_webserver/login/handlers_registration.py index 7983f0dfd61..3d00ab57c03 100644 --- a/services/web/server/src/simcore_service_webserver/login/handlers_registration.py +++ b/services/web/server/src/simcore_service_webserver/login/handlers_registration.py @@ -249,7 +249,7 @@ async def register(request: web.Request): if settings.LOGIN_REGISTRATION_CONFIRMATION_REQUIRED: # Confirmation required: send confirmation email _confirmation: ConfirmationTokenDict = await db.create_confirmation( - user["id"], REGISTRATION, data=invitation.json() if invitation else None + user["id"], REGISTRATION, data=invitation.model_dump_json() if invitation else None ) try: diff --git a/services/web/server/src/simcore_service_webserver/users/_schemas.py b/services/web/server/src/simcore_service_webserver/users/_schemas.py index b7b3b2a21ad..d9eac4c12c3 100644 --- a/services/web/server/src/simcore_service_webserver/users/_schemas.py +++ b/services/web/server/src/simcore_service_webserver/users/_schemas.py @@ -17,27 +17,27 @@ class UserProfile(OutputSchema): - first_name: str | None - last_name: str | None + first_name: str | None = Field(default=None) + last_name: str | None = Field(default=None) email: LowerCaseEmailStr - institution: str | None - phone: str | None - address: str | None - city: str | None - state: str | None = Field(description="State, province, canton, ...") - postal_code: str | None - country: str | None + institution: str | None = Field(default=None) + phone: str | None = Field(default=None) + address: str | None = Field(default=None) + city: str | None = Field(default=None) + state: str | None = Field(default=None, description="State, province, canton, ...") + postal_code: str | None = Field(default=None) + country: str | None = Field(default=None) extras: dict[str, Any] = Field( default_factory=dict, description="Keeps extra information provided in the request form", ) # authorization - invited_by: str | None = None + invited_by: str | None = Field(default=None) # user status registered: bool - status: UserStatus | None + status: UserStatus | None = Field(default=None) products: list[ProductName] | None = Field( default=None, description="List of products this users is included or None if fields is unset", From 7e00d51186de206df01d77d4882bbec3c57fbfac Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 12:21:42 +0100 Subject: [PATCH 126/165] fix error_msg --- .../unit/with_dbs/02/test_projects_crud_handlers__list.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list.py index e5f580cd785..93272d0b6a7 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list.py @@ -121,9 +121,9 @@ def standard_user_role() -> tuple[str, tuple[UserRole, ExpectedResponse]]: @pytest.mark.parametrize( "limit, offset, expected_error_msg", [ - (-7, 0, "ensure this value is greater than or equal to 1"), - (0, 0, "ensure this value is greater than or equal to 1"), - (43, -2, "ensure this value is greater than or equal to 0"), + (-7, 0, "Input should be greater than or equal to 1"), + (0, 0, "Input should be greater than or equal to 1"), + (43, -2, "Input should be greater than or equal to 0"), ], ) @pytest.mark.parametrize(*standard_user_role()) From e377829be73e66e5ec49510b0b3f4f92d577e35f Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 12:23:19 +0100 Subject: [PATCH 127/165] fix error_code --- .../tests/unit/with_dbs/02/test_projects_crud_handlers__list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list.py index 93272d0b6a7..a9f111e9c4a 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list.py @@ -145,7 +145,7 @@ async def test_list_projects_with_invalid_pagination_parameters( status.HTTP_422_UNPROCESSABLE_ENTITY, query_parameters={"limit": limit, "offset": offset}, expected_error_msg=expected_error_msg, - expected_error_code="value_error.number.not_ge", + expected_error_code="greater_than_equal", ) From 3c1dee52395007cac0d0e36ee20b1cc264d69a4e Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 12:42:56 +0100 Subject: [PATCH 128/165] fix datetime --- .../src/simcore_service_webserver/projects/projects_api.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/projects_api.py b/services/web/server/src/simcore_service_webserver/projects/projects_api.py index 7126172be0b..b7dd753ae56 100644 --- a/services/web/server/src/simcore_service_webserver/projects/projects_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/projects_api.py @@ -1511,9 +1511,8 @@ async def is_service_deprecated( app, user_id, service_key, service_version, product_name ) if deprecation_date := service.get("deprecated"): - deprecation_date_bool: bool = ( - datetime.datetime.now(datetime.UTC) > datetime.datetime.fromisoformat(deprecation_date) - ) + deprecation_date_bool: bool = datetime.datetime.utcnow() > datetime.datetime.fromisoformat(deprecation_date) + return deprecation_date_bool return False From 9641b16e81d7070b129b1b3ded427a03ff28de7c Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 13:00:05 +0100 Subject: [PATCH 129/165] fix --- .../src/servicelib/aiohttp/requests_validation.py | 2 +- .../unit/with_dbs/03/login/test_login_registration_handlers.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/service-library/src/servicelib/aiohttp/requests_validation.py b/packages/service-library/src/servicelib/aiohttp/requests_validation.py index 5a588041682..0dd40949ba8 100644 --- a/packages/service-library/src/servicelib/aiohttp/requests_validation.py +++ b/packages/service-library/src/servicelib/aiohttp/requests_validation.py @@ -224,7 +224,7 @@ async def parse_request_body_as( except json.decoder.JSONDecodeError as err: raise web.HTTPBadRequest(reason=f"Invalid json in body: {err}") from err - if hasattr(model_schema_cls, "parse_obj"): + if hasattr(model_schema_cls, "model_validate"): # NOTE: model_schema can be 'list[T]' or 'dict[T]' which raise TypeError # with issubclass(model_schema, BaseModel) assert issubclass(model_schema_cls, BaseModel) # nosec diff --git a/services/web/server/tests/unit/with_dbs/03/login/test_login_registration_handlers.py b/services/web/server/tests/unit/with_dbs/03/login/test_login_registration_handlers.py index 906350725e5..655afde42b7 100644 --- a/services/web/server/tests/unit/with_dbs/03/login/test_login_registration_handlers.py +++ b/services/web/server/tests/unit/with_dbs/03/login/test_login_registration_handlers.py @@ -197,7 +197,7 @@ async def test_request_an_account( response = await client.post( "/v0/auth/request-account", - json={"form": user_data, "captcha": 123456}, + json={"form": user_data, "captcha": "123456"}, ) await assert_status(response, status.HTTP_204_NO_CONTENT) From 22ea2f7ea5a06afded9e19b55c50add96023d8de Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 13:04:02 +0100 Subject: [PATCH 130/165] fix --- .../models_library/api_schemas_webserver/resource_usage.py | 4 ++-- .../models-library/src/models_library/resource_tracker.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/models-library/src/models_library/api_schemas_webserver/resource_usage.py b/packages/models-library/src/models_library/api_schemas_webserver/resource_usage.py index f8f3b11bbe7..3eea55c6d67 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/resource_usage.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/resource_usage.py @@ -1,7 +1,7 @@ from datetime import datetime from decimal import Decimal -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel, ConfigDict, Field from ..projects import ProjectID from ..projects_nodes_io import NodeID @@ -131,7 +131,7 @@ class UpdatePricingUnitBodyParams(InputSchema): unit_extra_info: UnitExtraInfo default: bool specific_info: SpecificInfo - pricing_unit_cost_update: PricingUnitCostUpdate | None + pricing_unit_cost_update: PricingUnitCostUpdate | None = Field(default=None) model_config = ConfigDict( str_strip_whitespace=True, diff --git a/packages/models-library/src/models_library/resource_tracker.py b/packages/models-library/src/models_library/resource_tracker.py index c3b42a08795..6bac22798d8 100644 --- a/packages/models-library/src/models_library/resource_tracker.py +++ b/packages/models-library/src/models_library/resource_tracker.py @@ -259,7 +259,7 @@ class PricingUnitWithCostUpdate(BaseModel): unit_extra_info: UnitExtraInfo default: bool specific_info: SpecificInfo - pricing_unit_cost_update: None | PricingUnitCostUpdate + pricing_unit_cost_update: None | PricingUnitCostUpdate = Field(default=None) model_config = ConfigDict( json_schema_extra={ From 99127a4f8389adefe9c14d392ba9557403f70c6a Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 13:16:39 +0100 Subject: [PATCH 131/165] fix typecheck --- .../src/simcore_service_webserver/version_control/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/version_control/models.py b/services/web/server/src/simcore_service_webserver/version_control/models.py index 2869d4b37ce..451d302d90f 100644 --- a/services/web/server/src/simcore_service_webserver/version_control/models.py +++ b/services/web/server/src/simcore_service_webserver/version_control/models.py @@ -24,7 +24,7 @@ CommitID: TypeAlias = int BranchID: TypeAlias = int -RefID: TypeAlias = Union[CommitID, str] +RefID: TypeAlias = CommitID | str CheckpointID: TypeAlias = PositiveInt @@ -44,7 +44,7 @@ def from_commit_log(cls, commit: RowProxy, tags: list[RowProxy]) -> "Checkpoint" checksum=commit.snapshot_checksum, tags=tuple(tag.name for tag in tags), message=commit.message, - parents_ids=(commit.parent_commit_id,) if commit.parent_commit_id else None, # type: ignore[arg-type] + parents_ids=(commit.parent_commit_id,) if commit.parent_commit_id else None, created_at=commit.created, ) From ddf210ec5c1007d3203b6678b7a8488f4c1d141f Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 13:25:39 +0100 Subject: [PATCH 132/165] fix message --- .../tests/unit/with_dbs/03/login/test_login_change_password.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/tests/unit/with_dbs/03/login/test_login_change_password.py b/services/web/server/tests/unit/with_dbs/03/login/test_login_change_password.py index 41f90807925..a171ec63ae2 100644 --- a/services/web/server/tests/unit/with_dbs/03/login/test_login_change_password.py +++ b/services/web/server/tests/unit/with_dbs/03/login/test_login_change_password.py @@ -85,7 +85,7 @@ async def test_wrong_confirm_pass(client: TestClient, new_password: str): "errors": [ { "code": "value_error", - "message": MSG_PASSWORD_MISMATCH, + "message": f"Value error, {MSG_PASSWORD_MISMATCH}", "resource": "/v0/auth/change-password", "field": "confirm", } From 958d4ddf34fe407f955e529f6e0ba67ef8965f26 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 13:37:31 +0100 Subject: [PATCH 133/165] fix typecheck --- .../src/simcore_service_webserver/application_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/application_settings.py b/services/web/server/src/simcore_service_webserver/application_settings.py index a176d4e9cf4..387dc6d7c02 100644 --- a/services/web/server/src/simcore_service_webserver/application_settings.py +++ b/services/web/server/src/simcore_service_webserver/application_settings.py @@ -129,7 +129,7 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings): ) WEBSERVER_LOG_FILTER_MAPPING: dict[LoggerName, list[MessageSubstring]] = Field( default_factory=dict, - env=["WEBSERVER_LOG_FILTER_MAPPING", "LOG_FILTER_MAPPING"], + validation_alias=AliasChoices("WEBSERVER_LOG_FILTER_MAPPING", "LOG_FILTER_MAPPING"), description="is a dictionary that maps specific loggers (such as 'uvicorn.access' or 'gunicorn.access') to a list of log message patterns that should be filtered out.", ) # TODO: find a better name!? From d6c71c67d545a8b473040cf215f40d0ecee4d292 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 13:48:52 +0100 Subject: [PATCH 134/165] continue fixing --- services/web/server/tests/unit/with_dbs/03/test_users.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/tests/unit/with_dbs/03/test_users.py b/services/web/server/tests/unit/with_dbs/03/test_users.py index 99a1ab326ef..432759f1670 100644 --- a/services/web/server/tests/unit/with_dbs/03/test_users.py +++ b/services/web/server/tests/unit/with_dbs/03/test_users.py @@ -381,7 +381,7 @@ def test_preuserprofile_parse_model_without_extras( account_request_form: dict[str, Any] ): required = { - f.alias or f.name for f in PreUserProfile.model_fields.values() if f.required + f.alias or f_name for f_name, f in PreUserProfile.model_fields.items() if f.is_required } data = {k: account_request_form[k] for k in required} assert not PreUserProfile(**data).extras From ce882a5151c04cadb86d8e841b97ede4d51e0756 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 13:50:35 +0100 Subject: [PATCH 135/165] continue fixing --- .../tests/unit/with_dbs/03/login/test_login_registration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/server/tests/unit/with_dbs/03/login/test_login_registration.py b/services/web/server/tests/unit/with_dbs/03/login/test_login_registration.py index 2cf8dde5c85..3fb1ca9ed57 100644 --- a/services/web/server/tests/unit/with_dbs/03/login/test_login_registration.py +++ b/services/web/server/tests/unit/with_dbs/03/login/test_login_registration.py @@ -96,13 +96,13 @@ async def test_register_body_validation( "errors": [ { "code": "value_error", - "message": "value is not a valid email address: An email address must have an @-sign.", + "message": "value is not a valid email address: An email address must have an @-sign.", "resource": "/v0/auth/register", "field": "email", }, { "code": "value_error", - "message": MSG_PASSWORD_MISMATCH, + "message": f"Value error, {MSG_PASSWORD_MISMATCH}", "resource": "/v0/auth/register", "field": "confirm", }, From 1b3128f85c9ae62175b02c880730f94bcc539692 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 14:06:28 +0100 Subject: [PATCH 136/165] fix error --- .../tests/unit/with_dbs/03/login/test_login_registration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/tests/unit/with_dbs/03/login/test_login_registration.py b/services/web/server/tests/unit/with_dbs/03/login/test_login_registration.py index 3fb1ca9ed57..d99f8f1f297 100644 --- a/services/web/server/tests/unit/with_dbs/03/login/test_login_registration.py +++ b/services/web/server/tests/unit/with_dbs/03/login/test_login_registration.py @@ -96,7 +96,7 @@ async def test_register_body_validation( "errors": [ { "code": "value_error", - "message": "value is not a valid email address: An email address must have an @-sign.", + "message": "value is not a valid email address: An email address must have an @-sign.", "resource": "/v0/auth/register", "field": "email", }, From d672a7740d22af1aac98e49c78ec2e75b235c1ab Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 14:46:08 +0100 Subject: [PATCH 137/165] fix pagination --- .../src/models_library/rest_pagination_utils.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/models-library/src/models_library/rest_pagination_utils.py b/packages/models-library/src/models_library/rest_pagination_utils.py index 41899acd8cf..d4027583ae7 100644 --- a/packages/models-library/src/models_library/rest_pagination_utils.py +++ b/packages/models-library/src/models_library/rest_pagination_utils.py @@ -1,5 +1,6 @@ from math import ceil -from typing import Any, Protocol, TypedDict, Union, runtime_checkable +from typing import Any, Protocol, runtime_checkable +from typing_extensions import TypedDict from common_library.pydantic_networks_extension import AnyHttpUrlLegacy from pydantic import TypeAdapter @@ -29,7 +30,7 @@ def replace_query_params(self, **kwargs: Any) -> "_StarletteURL": ... -_URLType = Union[_YarlURL, _StarletteURL] +_URLType = _YarlURL | _StarletteURL def _replace_query(url: _URLType, query: dict[str, Any]) -> str: @@ -69,9 +70,11 @@ def paginate_data( """ last_page = ceil(total / limit) - 1 + data = [item.model_dump() if hasattr(item, "model_dump") else item for item in chunk] + return PageDict( _meta=PageMetaInfoLimitOffset( - total=total, count=len(chunk), limit=limit, offset=offset + total=total, count=len(data), limit=limit, offset=offset ), _links=PageLinks( self=_replace_query(request_url, {"offset": offset, "limit": limit}), @@ -91,5 +94,5 @@ def paginate_data( request_url, {"offset": last_page * limit, "limit": limit} ), ), - data=chunk, + data=data, ) From 0332decddb15082057bdea146cc359faf2873752 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 15:00:46 +0100 Subject: [PATCH 138/165] fix error msg --- .../with_dbs/03/resource_usage/test_usage_services__list.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/web/server/tests/unit/with_dbs/03/resource_usage/test_usage_services__list.py b/services/web/server/tests/unit/with_dbs/03/resource_usage/test_usage_services__list.py index e43913ed189..6e3a12bfe84 100644 --- a/services/web/server/tests/unit/with_dbs/03/resource_usage/test_usage_services__list.py +++ b/services/web/server/tests/unit/with_dbs/03/resource_usage/test_usage_services__list.py @@ -201,7 +201,7 @@ async def test_list_service_usage_with_order_by_query_param( assert mock_list_usage_services.called assert error["status"] == status.HTTP_422_UNPROCESSABLE_ENTITY assert error["errors"][0]["message"].startswith( - "We do not support ordering by provided field" + "Value error, We do not support ordering by provided field" ) # with non-parsable field in order by query parameter @@ -239,7 +239,7 @@ async def test_list_service_usage_with_order_by_query_param( assert mock_list_usage_services.called assert error["status"] == status.HTTP_422_UNPROCESSABLE_ENTITY assert error["errors"][0]["message"].startswith( - "value is not a valid enumeration member" + "Input should be 'asc' or 'desc'" ) # without field @@ -253,7 +253,7 @@ async def test_list_service_usage_with_order_by_query_param( _, error = await assert_status(resp, status.HTTP_422_UNPROCESSABLE_ENTITY) assert mock_list_usage_services.called assert error["status"] == status.HTTP_422_UNPROCESSABLE_ENTITY - assert error["errors"][0]["message"].startswith("field required") + assert error["errors"][0]["message"].startswith("Field required") @pytest.mark.parametrize("user_role", [(UserRole.USER)]) From e695d8eaa291bd3eb69db96af06ab9e42f0578f9 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 1 Nov 2024 16:48:17 +0100 Subject: [PATCH 139/165] fix sql --- .../simcore_service_webserver/studies_dispatcher/_catalog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_catalog.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_catalog.py index 6e63b905bfc..2cc3eed3663 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_catalog.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_catalog.py @@ -97,7 +97,7 @@ async def iter_latest_product_services( ) & (services_meta_data.c.deprecated.is_(None)) & (services_access_rights.c.gid == EVERYONE_GROUP_ID) - & (services_access_rights.c.execute_access is True) + & (services_access_rights.c.execute_access == sa.true()) & (services_access_rights.c.product_name == product_name) ) ) From 05d515c182da0f86b38fe35d315d7118d6424b97 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 4 Nov 2024 09:25:03 +0100 Subject: [PATCH 140/165] fix datetime --- .../src/simcore_service_webserver/projects/projects_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/projects_api.py b/services/web/server/src/simcore_service_webserver/projects/projects_api.py index b7dd753ae56..8ba0c6358dc 100644 --- a/services/web/server/src/simcore_service_webserver/projects/projects_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/projects_api.py @@ -1511,7 +1511,7 @@ async def is_service_deprecated( app, user_id, service_key, service_version, product_name ) if deprecation_date := service.get("deprecated"): - deprecation_date_bool: bool = datetime.datetime.utcnow() > datetime.datetime.fromisoformat(deprecation_date) + deprecation_date_bool: bool = datetime.datetime.now(datetime.UTC) > datetime.datetime.fromisoformat(deprecation_date) return deprecation_date_bool return False From a66f145a5fcac2c821a249240330a56cf13f5faf Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 4 Nov 2024 10:25:49 +0100 Subject: [PATCH 141/165] fix gid --- .../src/simcore_service_webserver/projects/_crud_api_create.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py b/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py index 1d721af164b..d26a63a9cf8 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py @@ -417,7 +417,7 @@ async def create_project( # pylint: disable=too-many-arguments,too-many-branche ) ) new_project["accessRights"] = { - gid: access.model_dump() + f"{gid}": access.model_dump() for gid, access in workspace_db.access_rights.items() } From f934a6a2634c0cc2e3359463977ae06929ee4009 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 4 Nov 2024 11:08:23 +0100 Subject: [PATCH 142/165] fix validation --- .../server/src/simcore_service_webserver/projects/_ports_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/_ports_api.py b/services/web/server/src/simcore_service_webserver/projects/_ports_api.py index 637543aa41a..9ae42c397c8 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_ports_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/_ports_api.py @@ -163,7 +163,7 @@ def set_inputs_in_project( class _NonStrictPortLink(PortLink): - model_config = ConfigDict(populate_by_name=True) + model_config = ConfigDict(populate_by_name=True, from_attributes=True) class _OutputPortInfo(NamedTuple): From f1ba05fa36c8422b7bc5fbaef94fef3641e6b7d9 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 4 Nov 2024 12:43:21 +0100 Subject: [PATCH 143/165] fix date timezone --- .../src/simcore_service_webserver/projects/projects_api.py | 2 +- services/web/server/tests/conftest.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/projects_api.py b/services/web/server/src/simcore_service_webserver/projects/projects_api.py index 8ba0c6358dc..d86be96058d 100644 --- a/services/web/server/src/simcore_service_webserver/projects/projects_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/projects_api.py @@ -1511,7 +1511,7 @@ async def is_service_deprecated( app, user_id, service_key, service_version, product_name ) if deprecation_date := service.get("deprecated"): - deprecation_date_bool: bool = datetime.datetime.now(datetime.UTC) > datetime.datetime.fromisoformat(deprecation_date) + deprecation_date_bool: bool = datetime.datetime.now(datetime.UTC) > datetime.datetime.fromisoformat(deprecation_date).replace(tzinfo=datetime.UTC) return deprecation_date_bool return False diff --git a/services/web/server/tests/conftest.py b/services/web/server/tests/conftest.py index 2c0f7aba764..97a96fa2847 100644 --- a/services/web/server/tests/conftest.py +++ b/services/web/server/tests/conftest.py @@ -403,7 +403,7 @@ async def _creator( # the access rights are set to use the logged user primary group + whatever was inside the project expected_data["accessRights"].update( { - str(primary_group["gid"]): { + f"{primary_group['gid']}": { "read": True, "write": True, "delete": True, From 59b423e0e6481e3aff77970b00ca5b94e8ed59f3 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 4 Nov 2024 14:06:35 +0100 Subject: [PATCH 144/165] fix required --- services/web/server/tests/unit/with_dbs/03/test_users.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/tests/unit/with_dbs/03/test_users.py b/services/web/server/tests/unit/with_dbs/03/test_users.py index 432759f1670..33d22b4e815 100644 --- a/services/web/server/tests/unit/with_dbs/03/test_users.py +++ b/services/web/server/tests/unit/with_dbs/03/test_users.py @@ -381,7 +381,7 @@ def test_preuserprofile_parse_model_without_extras( account_request_form: dict[str, Any] ): required = { - f.alias or f_name for f_name, f in PreUserProfile.model_fields.items() if f.is_required + f.alias or f_name for f_name, f in PreUserProfile.model_fields.items() if f.is_required() } data = {k: account_request_form[k] for k in required} assert not PreUserProfile(**data).extras From 4905f4c01ce32ebc2d351131fd14ae84250a06f1 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 4 Nov 2024 14:32:40 +0100 Subject: [PATCH 145/165] fix user prefs --- .../src/models_library/user_preferences.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/models-library/src/models_library/user_preferences.py b/packages/models-library/src/models_library/user_preferences.py index 2680c10223d..f16c934b2da 100644 --- a/packages/models-library/src/models_library/user_preferences.py +++ b/packages/models-library/src/models_library/user_preferences.py @@ -1,6 +1,7 @@ from enum import auto -from typing import Annotated, Any, ClassVar, Literal, TypeAlias, get_args +from typing import Annotated, Any, ClassVar, Literal, TypeAlias +from common_library.pydantic_fields_extension import get_type from pydantic import BaseModel, Field from pydantic._internal._model_construction import ModelMetaclass @@ -94,11 +95,7 @@ def to_db(self) -> dict: @classmethod def update_preference_default_value(cls, new_default: Any) -> None: - expected_type = ( - t[0] - if (t := get_args(cls.model_fields["value"].annotation)) - else cls.model_fields["value"].annotation - ) + expected_type = get_type(cls.model_fields["value"]) detected_type = type(new_default) if expected_type != detected_type: msg = ( @@ -110,6 +107,9 @@ def update_preference_default_value(cls, new_default: Any) -> None: cls.model_fields["value"].default_factory = lambda: new_default else: cls.model_fields["value"].default = new_default + cls.model_fields["value"].default_factory = None + + cls.model_rebuild(force=True) class UserServiceUserPreference(_BaseUserPreferenceModel): From 3c6cc75f13af73a9f3a0abaf102ac222c0a8d027 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 4 Nov 2024 15:20:16 +0100 Subject: [PATCH 146/165] fix query --- .../simcore_service_webserver/studies_dispatcher/_catalog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_catalog.py b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_catalog.py index 2cc3eed3663..e9250b18a01 100644 --- a/services/web/server/src/simcore_service_webserver/studies_dispatcher/_catalog.py +++ b/services/web/server/src/simcore_service_webserver/studies_dispatcher/_catalog.py @@ -161,7 +161,7 @@ async def validate_requested_service( sa.select(services_consume_filetypes.c.is_guest_allowed) .where( (services_consume_filetypes.c.service_key == service_key) - & (services_consume_filetypes.c.is_guest_allowed is True) + & (services_consume_filetypes.c.is_guest_allowed == sa.true()) ) .limit(1) ) From cf63244cf3c498b4650bdd72d72cb0e469269484 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 4 Nov 2024 15:25:47 +0100 Subject: [PATCH 147/165] fix column check --- .../web/server/src/simcore_service_webserver/api_keys/_db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/api_keys/_db.py b/services/web/server/src/simcore_service_webserver/api_keys/_db.py index 009369005f6..ec08ce5dd67 100644 --- a/services/web/server/src/simcore_service_webserver/api_keys/_db.py +++ b/services/web/server/src/simcore_service_webserver/api_keys/_db.py @@ -145,7 +145,7 @@ async def prune_expired(self) -> list[str]: stmt = ( api_keys.delete() .where( - (api_keys.c.expires_at != None) # noqa: E711 + (api_keys.c.expires_at.is_not(None)) & (api_keys.c.expires_at < sa.func.now()) ) .returning(api_keys.c.display_name) From ded3b252c6012574739ddc30f480147e1f3468f0 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 5 Nov 2024 09:41:55 +0100 Subject: [PATCH 148/165] Update packages/models-library/src/models_library/api_schemas_directorv2/clusters.py Co-authored-by: Sylvain <35365065+sanderegg@users.noreply.github.com> --- .../src/models_library/api_schemas_directorv2/clusters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models-library/src/models_library/api_schemas_directorv2/clusters.py b/packages/models-library/src/models_library/api_schemas_directorv2/clusters.py index 1e7bf14ca44..9c4698a5dc3 100644 --- a/packages/models-library/src/models_library/api_schemas_directorv2/clusters.py +++ b/packages/models-library/src/models_library/api_schemas_directorv2/clusters.py @@ -176,7 +176,7 @@ class ClusterPatch(BaseCluster): endpoint: AnyUrl | None = None # type: ignore[assignment] authentication: ExternalClusterAuthentication | None = None # type: ignore[assignment] access_rights: dict[GroupID, ClusterAccessRights] | None = Field( # type: ignore[assignment] - None, alias="accessRights" + default=None, alias="accessRights" ) model_config = ConfigDict( From bbe8ea2d0932ef98f1d2cef66ae5f854dbc4e065 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 5 Nov 2024 10:26:00 +0100 Subject: [PATCH 149/165] remove pattern --- .../src/simcore_service_webserver/payments/_onetime_db.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/payments/_onetime_db.py b/services/web/server/src/simcore_service_webserver/payments/_onetime_db.py index 05f1a84ccad..d6146cd0f81 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_onetime_db.py +++ b/services/web/server/src/simcore_service_webserver/payments/_onetime_db.py @@ -27,8 +27,6 @@ _logger = logging.getLogger(__name__) -PaymentIDTypeAdapter: TypeAdapter[PaymentID] = TypeAdapter(PaymentID) - # # NOTE: this will be moved to the payments service # NOTE: with https://sqlmodel.tiangolo.com/ we would only define this once! @@ -76,7 +74,7 @@ async def get_pending_payment_transactions_ids(app: web.Application) -> list[Pay .order_by(payments_transactions.c.initiated_at.asc()) # oldest first ) rows = await result.fetchall() or [] - return [PaymentIDTypeAdapter.validate_python(row.payment_id) for row in rows] + return [TypeAdapter(PaymentID).validate_python(row.payment_id) for row in rows] async def complete_payment_transaction( From 509a7ac1ead05d54b6cdd311d73a211485444976 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 5 Nov 2024 10:56:47 +0100 Subject: [PATCH 150/165] revert nullable --- .../src/simcore_service_webserver/projects/models.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/models.py b/services/web/server/src/simcore_service_webserver/projects/models.py index 0f0f3766fb0..97aca0807d3 100644 --- a/services/web/server/src/simcore_service_webserver/projects/models.py +++ b/services/web/server/src/simcore_service_webserver/projects/models.py @@ -40,17 +40,17 @@ class ProjectDB(BaseModel): uuid: ProjectID name: str description: str - thumbnail: HttpUrl | None = None + thumbnail: HttpUrl | None prj_owner: UserID creation_date: datetime last_change_date: datetime - ui: StudyUI | None = None + ui: StudyUI | None classifiers: list[ClassifierID] - dev: dict | None = None + dev: dict | None quality: dict[str, Any] published: bool hidden: bool - workspace_id: WorkspaceID | None = None + workspace_id: WorkspaceID | None trashed_at: datetime | None model_config = ConfigDict(from_attributes=True, arbitrary_types_allowed=True) @@ -65,7 +65,7 @@ class ProjectDB(BaseModel): class UserSpecificProjectDataDB(ProjectDB): - folder_id: FolderID | None = None + folder_id: FolderID | None model_config = ConfigDict(from_attributes=True) From f6e21281c1aed7f7fa5a67066c6e38107d431d44 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 5 Nov 2024 11:17:43 +0100 Subject: [PATCH 151/165] remove constraint --- services/web/server/requirements/constraints.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/services/web/server/requirements/constraints.txt b/services/web/server/requirements/constraints.txt index 9592eb8b76c..71c712593cb 100644 --- a/services/web/server/requirements/constraints.txt +++ b/services/web/server/requirements/constraints.txt @@ -10,4 +10,3 @@ frozenlist>=1.3.1 # See: https://github.com/pydantic/pydantic/issues/4011 pydantic>=1.10 -pydantic-settings<2.6 # GCR: remove me \ No newline at end of file From 003495288026ee016d9db9b63383bac9f19a5a12 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 5 Nov 2024 11:27:45 +0100 Subject: [PATCH 152/165] fix model_dump_json option --- services/web/server/src/simcore_service_webserver/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/cli.py b/services/web/server/src/simcore_service_webserver/cli.py index daae79ce9bf..f5a82427683 100644 --- a/services/web/server/src/simcore_service_webserver/cli.py +++ b/services/web/server/src/simcore_service_webserver/cli.py @@ -69,7 +69,7 @@ async def app_factory() -> web.Application: _logger.info( "Application settings: %s", - app_settings.model_dump_json(indent=2, sort_keys=True), + app_settings.model_dump_json(indent=2), ) app, _ = _setup_app_from_settings(app_settings) From 9db58f7c6bc69c497fb2acbfd4c1bc613d394841 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 5 Nov 2024 13:27:59 +0100 Subject: [PATCH 153/165] fix fields --- .../api_schemas_webserver/projects_nodes.py | 2 +- .../projects/_workspaces_handlers.py | 11 ++++------- .../src/simcore_service_webserver/projects/models.py | 3 ++- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/models-library/src/models_library/api_schemas_webserver/projects_nodes.py b/packages/models-library/src/models_library/api_schemas_webserver/projects_nodes.py index 9f70b1b46f8..7b65b84cb5f 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/projects_nodes.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/projects_nodes.py @@ -19,7 +19,7 @@ class NodeCreate(InputSchemaWithoutCamelCase): service_key: ServiceKey service_version: ServiceVersion - service_id: str | None = Field(default=None) + service_id: str | None BootOptions: TypeAlias = dict diff --git a/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py index 61e6c011603..ff881b418af 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py @@ -1,11 +1,12 @@ import functools import logging +from typing import Annotated from aiohttp import web from models_library.projects import ProjectID from models_library.utils.common_validators import null_or_none_str_to_none_validator from models_library.workspaces import WorkspaceID -from pydantic import BaseModel, ConfigDict, field_validator +from pydantic import BaseModel, BeforeValidator, ConfigDict, Field from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import parse_request_path_parameters_as from servicelib.aiohttp.typing_extension import Handler @@ -50,13 +51,9 @@ async def wrapper(request: web.Request) -> web.StreamResponse: class _ProjectWorkspacesPathParams(BaseModel): project_id: ProjectID - workspace_id: WorkspaceID | None = None - model_config = ConfigDict(extra="forbid") + workspace_id: Annotated[WorkspaceID | None, BeforeValidator(null_or_none_str_to_none_validator)] = Field(default=None) - # validators - _null_or_none_str_to_none_validator = field_validator( - "workspace_id", mode="before" - )(null_or_none_str_to_none_validator) + model_config = ConfigDict(extra="forbid") @routes.put( diff --git a/services/web/server/src/simcore_service_webserver/projects/models.py b/services/web/server/src/simcore_service_webserver/projects/models.py index 97aca0807d3..5b3e900b531 100644 --- a/services/web/server/src/simcore_service_webserver/projects/models.py +++ b/services/web/server/src/simcore_service_webserver/projects/models.py @@ -66,6 +66,7 @@ class ProjectDB(BaseModel): class UserSpecificProjectDataDB(ProjectDB): folder_id: FolderID | None + model_config = ConfigDict(from_attributes=True) @@ -84,7 +85,7 @@ class UserProjectAccessRightsDB(BaseModel): class UserProjectAccessRightsWithWorkspace(BaseModel): uid: UserID - workspace_id: WorkspaceID | None = None # None if it's a private workspace + workspace_id: WorkspaceID | None # None if it's a private workspace read: bool write: bool delete: bool From d29278844b6e9a1136ea98f55c0493e0cd8a3437 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 5 Nov 2024 13:32:49 +0100 Subject: [PATCH 154/165] fix --- .../src/simcore_service_webserver/payments/_methods_db.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/payments/_methods_db.py b/services/web/server/src/simcore_service_webserver/payments/_methods_db.py index 5c1c638b37b..3b2bcf8ede8 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_methods_db.py +++ b/services/web/server/src/simcore_service_webserver/payments/_methods_db.py @@ -26,9 +26,6 @@ _logger = logging.getLogger(__name__) -PaymentMethodIDTypeAdapter: TypeAdapter[PaymentMethodID] = TypeAdapter(PaymentMethodID) - - class PaymentsMethodsDB(BaseModel): payment_method_id: PaymentMethodID user_id: UserID @@ -114,12 +111,12 @@ async def get_pending_payment_methods_ids( async with get_database_engine(app).acquire() as conn: result = await conn.execute( sa.select(payments_methods.c.payment_method_id) - .where(payments_methods.c.completed_at == None) # noqa: E711 + .where(payments_methods.c.completed_at.is_(None)) .order_by(payments_methods.c.initiated_at.asc()) # oldest first ) rows = await result.fetchall() or [] return [ - PaymentMethodIDTypeAdapter.validate_python(row.payment_method_id) + TypeAdapter(PaymentMethodID).validate_python(row.payment_method_id) for row in rows ] From 59a64dcb624fbb731e742c4a6b8515539da423bb Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 5 Nov 2024 13:58:21 +0100 Subject: [PATCH 155/165] remove frozen --- .../src/simcore_service_webserver/application_settings.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/application_settings.py b/services/web/server/src/simcore_service_webserver/application_settings.py index 7734a688f34..94d1be5358d 100644 --- a/services/web/server/src/simcore_service_webserver/application_settings.py +++ b/services/web/server/src/simcore_service_webserver/application_settings.py @@ -279,10 +279,6 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings): "Currently this is a system plugin and cannot be disabled", ) - model_config = SettingsConfigDict( - frozen=False - ) - @model_validator(mode="after") @classmethod def build_vcs_release_url_if_unset(cls, v): From 9a1bc009030d2aed53a31871f3577d2404601220 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 5 Nov 2024 13:59:46 +0100 Subject: [PATCH 156/165] fix jsondump --- services/web/server/src/simcore_service_webserver/cli.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/cli.py b/services/web/server/src/simcore_service_webserver/cli.py index f5a82427683..b09f4eca0c4 100644 --- a/services/web/server/src/simcore_service_webserver/cli.py +++ b/services/web/server/src/simcore_service_webserver/cli.py @@ -19,6 +19,7 @@ import typer from aiohttp import web +from common_library.json_serialization import json_dumps from settings_library.utils_cli import create_settings_command from typing_extensions import Annotated @@ -69,7 +70,7 @@ async def app_factory() -> web.Application: _logger.info( "Application settings: %s", - app_settings.model_dump_json(indent=2), + json_dumps(app_settings, indent=2, sort_keys=True), ) app, _ = _setup_app_from_settings(app_settings) From f24cd1974f4b365c4fc9a95cfacf5c6fb68b5c73 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 5 Nov 2024 14:01:20 +0100 Subject: [PATCH 157/165] minor fix --- .../src/simcore_service_webserver/announcements/_models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/announcements/_models.py b/services/web/server/src/simcore_service_webserver/announcements/_models.py index febeeadb175..7b48d86e2b2 100644 --- a/services/web/server/src/simcore_service_webserver/announcements/_models.py +++ b/services/web/server/src/simcore_service_webserver/announcements/_models.py @@ -9,7 +9,7 @@ # - parse+validate from redis # - schema in the response class Announcement(BaseModel): - id: str # noqa: A003 + id: str products: list[str] start: datetime end: datetime @@ -20,7 +20,7 @@ class Announcement(BaseModel): @field_validator("end") @classmethod - def check_start_before_end(cls, v, info: ValidationInfo): + def _check_start_before_end(cls, v, info: ValidationInfo): if start := info.data.get("start"): end = v if end <= start: From 4634acb71f19beedf6d1d5bc662da7930ab7a925 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 5 Nov 2024 16:21:12 +0100 Subject: [PATCH 158/165] fix comments --- .../projects/_crud_handlers.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py index 3bb8562c3be..a6a32fbb0bc 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py @@ -109,16 +109,14 @@ async def _wrapper(request: web.Request) -> web.StreamResponse: routes = web.RouteTableDef() -# -# - Create https://google.aip.dev/133 -# - - @routes.post(f"/{VTAG}/projects", name="create_project") @login_required @permission_required("project.create") @permission_required("services.pipeline.*") # due to update_pipeline_db async def create_project(request: web.Request): + # + # - Create https://google.aip.dev/133 + # req_ctx = RequestContext.model_validate(request) query_params: ProjectCreateParams = parse_request_query_parameters_as( ProjectCreateParams, request @@ -171,8 +169,6 @@ async def create_project(request: web.Request): ) -# - List https://google.aip.dev/132 -# @routes.get(f"/{VTAG}/projects", name="list_projects") @@ -180,6 +176,9 @@ async def create_project(request: web.Request): @permission_required("project.read") @_handle_projects_exceptions async def list_projects(request: web.Request): + # + # - List https://google.aip.dev/132 + # """ Raises: @@ -265,16 +264,14 @@ async def list_projects_full_search(request: web.Request): ) -# -# - Get https://google.aip.dev/131 -# - Get active project: Singleton per-session resources https://google.aip.dev/156 -# - - @routes.get(f"/{VTAG}/projects/active", name="get_active_project") @login_required @permission_required("project.read") async def get_active_project(request: web.Request) -> web.Response: + # + # - Get https://google.aip.dev/131 + # - Get active project: Singleton per-session resources https://google.aip.dev/156 + # """ Raises: @@ -395,6 +392,9 @@ async def get_project_inactivity(request: web.Request): @permission_required("services.pipeline.*") @_handle_projects_exceptions async def patch_project(request: web.Request): + # + # Update https://google.aip.dev/134 + # req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) project_patch = await parse_request_body_as(ProjectPatch, request) @@ -411,7 +411,7 @@ async def patch_project(request: web.Request): # -# - Delete https://google.aip.dev/135 + # @@ -419,6 +419,7 @@ async def patch_project(request: web.Request): @login_required @permission_required("project.delete") async def delete_project(request: web.Request): + # Delete https://google.aip.dev/135 """ Raises: @@ -431,7 +432,6 @@ async def delete_project(request: web.Request): web.HTTPConflict: Somethine went wrong while deleting web.HTTPNoContent: Sucess """ - req_ctx = RequestContext.model_validate(request) path_params = parse_request_path_parameters_as(ProjectPathParams, request) From c74b2b234f6f5639c2d6dd4e719e73b4baf99e8c Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 5 Nov 2024 16:38:04 +0100 Subject: [PATCH 159/165] fix nullable --- packages/models-library/src/models_library/resource_tracker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models-library/src/models_library/resource_tracker.py b/packages/models-library/src/models_library/resource_tracker.py index 6bac22798d8..53e370913a9 100644 --- a/packages/models-library/src/models_library/resource_tracker.py +++ b/packages/models-library/src/models_library/resource_tracker.py @@ -259,7 +259,7 @@ class PricingUnitWithCostUpdate(BaseModel): unit_extra_info: UnitExtraInfo default: bool specific_info: SpecificInfo - pricing_unit_cost_update: None | PricingUnitCostUpdate = Field(default=None) + pricing_unit_cost_update: PricingUnitCostUpdate | None model_config = ConfigDict( json_schema_extra={ From 7b7d020728e2af82067c41946eab3e6b823a4acb Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Tue, 5 Nov 2024 16:56:10 +0100 Subject: [PATCH 160/165] fix enum --- .../src/simcore_service_webserver/director_v2/_models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/director_v2/_models.py b/services/web/server/src/simcore_service_webserver/director_v2/_models.py index 5c42feadd77..06f9b3b1c2c 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/_models.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/_models.py @@ -45,7 +45,7 @@ def set_default_thumbnail_if_empty(cls, v, values): "examples": [ { "name": "My awesome cluster", - "type": ClusterType.ON_PREMISE.value, # can use also values from equivalent enum + "type": f"{ClusterType.ON_PREMISE}", # can use also values from equivalent enum "endpoint": "https://registry.osparc-development.fake.dev", "authentication": { "type": "simple", @@ -56,7 +56,7 @@ def set_default_thumbnail_if_empty(cls, v, values): { "name": "My AWS cluster", "description": "a AWS cluster administered by me", - "type": ClusterType.AWS.value, + "type": f"{ClusterType.AWS}", "owner": 154, "endpoint": "https://registry.osparc-development.fake.dev", "authentication": { From 274e2cb7421af6fc24bd4cdf2580081e84b25e51 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 6 Nov 2024 08:40:51 +0100 Subject: [PATCH 161/165] fix schema --- .../web/server/src/simcore_service_webserver/storage/schemas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/storage/schemas.py b/services/web/server/src/simcore_service_webserver/storage/schemas.py index e1e97392274..26381218c0e 100644 --- a/services/web/server/src/simcore_service_webserver/storage/schemas.py +++ b/services/web/server/src/simcore_service_webserver/storage/schemas.py @@ -96,7 +96,7 @@ class FileUploadCompleteEnveloped(BaseModel): class FileUploadCompleteFutureEnveloped(BaseModel): data: FileUploadCompleteFuture - error: Any = None + error: Any class DatasetMetaEnvelope(BaseModel): From 8aac73dc9978bbcff7f58ddc4ce1c67bcfc280b2 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 6 Nov 2024 08:43:25 +0100 Subject: [PATCH 162/165] revert none --- .../users/_schemas.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/users/_schemas.py b/services/web/server/src/simcore_service_webserver/users/_schemas.py index d9eac4c12c3..8df6c890a8b 100644 --- a/services/web/server/src/simcore_service_webserver/users/_schemas.py +++ b/services/web/server/src/simcore_service_webserver/users/_schemas.py @@ -17,16 +17,16 @@ class UserProfile(OutputSchema): - first_name: str | None = Field(default=None) - last_name: str | None = Field(default=None) + first_name: str | None + last_name: str | None email: LowerCaseEmailStr - institution: str | None = Field(default=None) - phone: str | None = Field(default=None) - address: str | None = Field(default=None) - city: str | None = Field(default=None) - state: str | None = Field(default=None, description="State, province, canton, ...") - postal_code: str | None = Field(default=None) - country: str | None = Field(default=None) + institution: str | None + phone: str | None + address: str | None + city: str | None + state: str | None = Field(description="State, province, canton, ...") + postal_code: str | None + country: str | None extras: dict[str, Any] = Field( default_factory=dict, description="Keeps extra information provided in the request form", @@ -37,7 +37,7 @@ class UserProfile(OutputSchema): # user status registered: bool - status: UserStatus | None = Field(default=None) + status: UserStatus | None products: list[ProductName] | None = Field( default=None, description="List of products this users is included or None if fields is unset", From 399abc8e3ad33e1f563d6eaeb92d9b4862bc2023 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 6 Nov 2024 08:51:01 +0100 Subject: [PATCH 163/165] remove ignore --- .../src/simcore_service_webserver/director_v2/_models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/director_v2/_models.py b/services/web/server/src/simcore_service_webserver/director_v2/_models.py index 06f9b3b1c2c..6d14d4b709d 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/_models.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/_models.py @@ -65,9 +65,9 @@ def set_default_thumbnail_if_empty(cls, v, values): "password": "somepassword", }, "access_rights": { - 154: CLUSTER_ADMIN_RIGHTS, # type: ignore - 12: CLUSTER_MANAGER_RIGHTS, # type: ignore - 7899: CLUSTER_USER_RIGHTS, # type: ignore + 154: CLUSTER_ADMIN_RIGHTS, + 12: CLUSTER_MANAGER_RIGHTS, + 7899: CLUSTER_USER_RIGHTS, }, }, ] From e1b8401a3dcce163bc1bea733afc36f1857f8c25 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 6 Nov 2024 09:00:02 +0100 Subject: [PATCH 164/165] revert gid cast --- .../src/simcore_service_webserver/projects/_crud_api_read.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py b/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py index c893276f08e..793dd19b083 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py @@ -53,7 +53,7 @@ async def _append_fields( # replace project access rights (if project is in workspace) if workspace_access_rights: project["accessRights"] = { - f"{gid}": access.model_dump() for gid, access in workspace_access_rights.items() + gid: access.model_dump() for gid, access in workspace_access_rights.items() } # validate From c7d5f0c25c7a39db1f6bc65090f26eff8d371dfa Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Wed, 6 Nov 2024 09:01:18 +0100 Subject: [PATCH 165/165] fix comment --- .../src/simcore_service_webserver/security/_authz_db.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/security/_authz_db.py b/services/web/server/src/simcore_service_webserver/security/_authz_db.py index b62baf946f4..300130b6f82 100644 --- a/services/web/server/src/simcore_service_webserver/security/_authz_db.py +++ b/services/web/server/src/simcore_service_webserver/security/_authz_db.py @@ -36,11 +36,11 @@ async def get_active_user_or_none(engine: Engine, email: str) -> AuthInfoDict | ) row = await result.fetchone() assert ( - row is None or TypeAdapter(IdInt).validate_python(row.id) is not None - ) # nosec + row is None or TypeAdapter(IdInt).validate_python(row.id) is not None # nosec + ) assert ( - row is None or TypeAdapter(UserRole).validate_python(row.role) is not None - ) # nosec + row is None or TypeAdapter(UserRole).validate_python(row.role) is not None # nosec + ) return AuthInfoDict(id=row.id, role=row.role) if row else None