diff --git a/services/api-server/openapi.json b/services/api-server/openapi.json index dae0d71da28..e04d4b41d32 100644 --- a/services/api-server/openapi.json +++ b/services/api-server/openapi.json @@ -1931,7 +1931,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ServicePricingPlanGet" + "$ref": "#/components/schemas/ServicePricingPlanGetLegacy" } } } @@ -3790,7 +3790,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WalletGetWithAvailableCredits" + "$ref": "#/components/schemas/WalletGetWithAvailableCreditsLegacy" } } } @@ -3929,7 +3929,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PricingUnitGet" + "$ref": "#/components/schemas/PricingUnitGetLegacy" } } } @@ -5121,7 +5121,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WalletGetWithAvailableCredits" + "$ref": "#/components/schemas/WalletGetWithAvailableCreditsLegacy" } } } @@ -5234,7 +5234,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WalletGetWithAvailableCredits" + "$ref": "#/components/schemas/WalletGetWithAvailableCreditsLegacy" } } } @@ -5336,7 +5336,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetCreditPrice" + "$ref": "#/components/schemas/GetCreditPriceLegacy" } } } @@ -5565,7 +5565,7 @@ ], "title": "FileUploadData" }, - "GetCreditPrice": { + "GetCreditPriceLegacy": { "properties": { "productName": { "type": "string", @@ -5604,7 +5604,7 @@ "usdPerCredit", "minPaymentAmountUsd" ], - "title": "GetCreditPrice" + "title": "GetCreditPriceLegacy" }, "Groups": { "properties": { @@ -6473,7 +6473,7 @@ ], "title": "PricingPlanClassification" }, - "PricingUnitGet": { + "PricingUnitGetLegacy": { "properties": { "pricingUnitId": { "type": "integer", @@ -6506,7 +6506,7 @@ "currentCostPerUnit", "default" ], - "title": "PricingUnitGet" + "title": "PricingUnitGetLegacy" }, "Profile": { "properties": { @@ -6648,7 +6648,7 @@ "title": "RunningState", "description": "State of execution of a project's computational workflow\n\nSEE StateType for task state" }, - "ServicePricingPlanGet": { + "ServicePricingPlanGetLegacy": { "properties": { "pricingPlanId": { "type": "integer", @@ -6678,7 +6678,7 @@ }, "pricingUnits": { "items": { - "$ref": "#/components/schemas/PricingUnitGet" + "$ref": "#/components/schemas/PricingUnitGetLegacy" }, "type": "array", "title": "Pricingunits" @@ -6694,7 +6694,7 @@ "pricingPlanKey", "pricingUnits" ], - "title": "ServicePricingPlanGet" + "title": "ServicePricingPlanGetLegacy" }, "Solver": { "properties": { @@ -7035,7 +7035,7 @@ ], "title": "ValidationError" }, - "WalletGetWithAvailableCredits": { + "WalletGetWithAvailableCreditsLegacy": { "properties": { "walletId": { "type": "integer", @@ -7106,7 +7106,7 @@ "modified", "availableCredits" ], - "title": "WalletGetWithAvailableCredits" + "title": "WalletGetWithAvailableCreditsLegacy" }, "WalletStatus": { "type": "string", diff --git a/services/api-server/src/simcore_service_api_server/api/routes/credits.py b/services/api-server/src/simcore_service_api_server/api/routes/credits.py index 1f89440ab2a..5b5258cfb01 100644 --- a/services/api-server/src/simcore_service_api_server/api/routes/credits.py +++ b/services/api-server/src/simcore_service_api_server/api/routes/credits.py @@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends, status -from ...models.schemas.model_adapter import GetCreditPrice +from ...models.schemas.model_adapter import GetCreditPriceLegacy from ..dependencies.webserver import AuthSession, get_webserver_session from ._constants import FMSG_CHANGELOG_NEW_IN_VERSION @@ -12,7 +12,7 @@ @router.get( "/price", status_code=status.HTTP_200_OK, - response_model=GetCreditPrice, + response_model=GetCreditPriceLegacy, description=FMSG_CHANGELOG_NEW_IN_VERSION.format("0.6.0"), ) async def get_credits_price( diff --git a/services/api-server/src/simcore_service_api_server/api/routes/solvers.py b/services/api-server/src/simcore_service_api_server/api/routes/solvers.py index bb7b0b9d414..c85b8b39baf 100644 --- a/services/api-server/src/simcore_service_api_server/api/routes/solvers.py +++ b/services/api-server/src/simcore_service_api_server/api/routes/solvers.py @@ -11,7 +11,7 @@ from ...models.basic_types import VersionStr from ...models.pagination import OnePage, Page, PaginationParams from ...models.schemas.errors import ErrorGet -from ...models.schemas.model_adapter import ServicePricingPlanGet +from ...models.schemas.model_adapter import ServicePricingPlanGetLegacy from ...models.schemas.solvers import Solver, SolverKeyId, SolverPort from ...services.catalog import CatalogApi from ..dependencies.application import get_reverse_url_mapper @@ -262,7 +262,7 @@ async def list_solver_ports( @router.get( "/{solver_key:path}/releases/{version}/pricing_plan", - response_model=ServicePricingPlanGet, + response_model=ServicePricingPlanGetLegacy, description="Gets solver pricing plan\n\n" + FMSG_CHANGELOG_NEW_IN_VERSION.format("0.7"), responses=_SOLVER_STATUS_CODES, diff --git a/services/api-server/src/simcore_service_api_server/api/routes/solvers_jobs_getters.py b/services/api-server/src/simcore_service_api_server/api/routes/solvers_jobs_getters.py index 5902998ada0..4903d1cb815 100644 --- a/services/api-server/src/simcore_service_api_server/api/routes/solvers_jobs_getters.py +++ b/services/api-server/src/simcore_service_api_server/api/routes/solvers_jobs_getters.py @@ -34,8 +34,8 @@ JobOutputs, ) from ...models.schemas.model_adapter import ( - PricingUnitGet, - WalletGetWithAvailableCredits, + PricingUnitGetLegacy, + WalletGetWithAvailableCreditsLegacy, ) from ...models.schemas.solvers import SolverKeyId from ...services.catalog import CatalogApi @@ -378,7 +378,7 @@ async def get_job_custom_metadata( @router.get( "/{solver_key:path}/releases/{version}/jobs/{job_id:uuid}/wallet", - response_model=WalletGetWithAvailableCredits, + response_model=WalletGetWithAvailableCreditsLegacy, responses=WALLET_STATUS_CODES, description=("Get job wallet\n\n" + FMSG_CHANGELOG_NEW_IN_VERSION.format("0.7")), ) @@ -387,7 +387,7 @@ async def get_job_wallet( version: VersionStr, job_id: JobID, webserver_api: Annotated[AuthSession, Depends(get_webserver_session)], -) -> WalletGetWithAvailableCredits: +) -> WalletGetWithAvailableCreditsLegacy: job_name = _compose_job_resource_name(solver_key, version, job_id) _logger.debug("Getting wallet for job '%s'", job_name) @@ -398,7 +398,7 @@ async def get_job_wallet( @router.get( "/{solver_key:path}/releases/{version}/jobs/{job_id:uuid}/pricing_unit", - response_model=PricingUnitGet, + response_model=PricingUnitGetLegacy, responses=_PRICING_UNITS_STATUS_CODES, description=( "Get job pricing unit\n\n" + FMSG_CHANGELOG_NEW_IN_VERSION.format("0.7") diff --git a/services/api-server/src/simcore_service_api_server/api/routes/wallets.py b/services/api-server/src/simcore_service_api_server/api/routes/wallets.py index b10f8d4382f..40e2233fce0 100644 --- a/services/api-server/src/simcore_service_api_server/api/routes/wallets.py +++ b/services/api-server/src/simcore_service_api_server/api/routes/wallets.py @@ -5,7 +5,7 @@ from ...exceptions.service_errors_utils import DEFAULT_BACKEND_SERVICE_STATUS_CODES from ...models.schemas.errors import ErrorGet -from ...models.schemas.model_adapter import WalletGetWithAvailableCredits +from ...models.schemas.model_adapter import WalletGetWithAvailableCreditsLegacy from ..dependencies.webserver import AuthSession, get_webserver_session from ._constants import FMSG_CHANGELOG_NEW_IN_VERSION @@ -29,7 +29,7 @@ @router.get( "/default", description="Get default wallet\n\n" + FMSG_CHANGELOG_NEW_IN_VERSION.format("0.7"), - response_model=WalletGetWithAvailableCredits, + response_model=WalletGetWithAvailableCreditsLegacy, responses=WALLET_STATUS_CODES, ) async def get_default_wallet( @@ -40,7 +40,7 @@ async def get_default_wallet( @router.get( "/{wallet_id}", - response_model=WalletGetWithAvailableCredits, + response_model=WalletGetWithAvailableCreditsLegacy, responses=WALLET_STATUS_CODES, description="Get wallet\n\n" + FMSG_CHANGELOG_NEW_IN_VERSION.format("0.7"), ) diff --git a/services/api-server/src/simcore_service_api_server/models/schemas/model_adapter.py b/services/api-server/src/simcore_service_api_server/models/schemas/model_adapter.py index bc52b3f3880..d769d90e041 100644 --- a/services/api-server/src/simcore_service_api_server/models/schemas/model_adapter.py +++ b/services/api-server/src/simcore_service_api_server/models/schemas/model_adapter.py @@ -7,85 +7,98 @@ from models_library.api_schemas_api_server.pricing_plans import ( ServicePricingPlanGet as _ServicePricingPlanGet, ) -from models_library.api_schemas_webserver._base import OutputSchema from models_library.api_schemas_webserver.product import ( GetCreditPrice as _GetCreditPrice, ) from models_library.api_schemas_webserver.resource_usage import ( PricingUnitGet as _PricingUnitGet, ) -from models_library.api_schemas_webserver.wallets import WalletGet from models_library.api_schemas_webserver.wallets import ( WalletGetWithAvailableCredits as _WalletGetWithAvailableCredits, ) -from models_library.basic_types import NonNegativeDecimal +from models_library.basic_types import IDStr, NonNegativeDecimal from models_library.resource_tracker import ( PricingPlanClassification, PricingPlanId, PricingUnitId, UnitExtraInfo, ) -from pydantic import Field, NonNegativeFloat, NonNegativeInt, PlainSerializer - - -class GetCreditPrice(OutputSchema): - product_name: str - usd_per_credit: Annotated[ - NonNegativeDecimal, - PlainSerializer(float, return_type=NonNegativeFloat, when_used="json"), - ] | None = Field( +from models_library.users import GroupID +from models_library.wallets import WalletID, WalletStatus +from pydantic import BaseModel, Field, NonNegativeFloat, NonNegativeInt, PlainSerializer + + +class GetCreditPriceLegacy(BaseModel): + product_name: str = Field(alias="productName") + usd_per_credit: ( + Annotated[ + NonNegativeDecimal, + PlainSerializer(float, return_type=NonNegativeFloat, when_used="json"), + ] + | None + ) = Field( ..., description="Price of a credit in USD. " "If None, then this product's price is UNDEFINED", + alias="usdPerCredit", ) min_payment_amount_usd: NonNegativeInt | None = Field( ..., description="Minimum amount (included) in USD that can be paid for this product" "Can be None if this product's price is UNDEFINED", + alias="minPaymentAmountUsd", ) -assert set(GetCreditPrice.model_fields.keys()) == set( +assert set(GetCreditPriceLegacy.model_fields.keys()) == set( _GetCreditPrice.model_fields.keys() ) -class PricingUnitGet(OutputSchema): - pricing_unit_id: PricingUnitId - unit_name: str - unit_extra_info: UnitExtraInfo +class PricingUnitGetLegacy(BaseModel): + pricing_unit_id: PricingUnitId = Field(alias="pricingUnitId") + unit_name: str = Field(alias="unitName") + unit_extra_info: UnitExtraInfo = Field(alias="unitExtraInfo") current_cost_per_unit: Annotated[ Decimal, PlainSerializer(float, return_type=NonNegativeFloat, when_used="json") - ] + ] = Field(alias="currentCostPerUnit") default: bool -assert set(PricingUnitGet.model_fields.keys()) == set( +assert set(PricingUnitGetLegacy.model_fields.keys()) == set( _PricingUnitGet.model_fields.keys() ) -class WalletGetWithAvailableCredits(WalletGet): +class WalletGetWithAvailableCreditsLegacy(BaseModel): + wallet_id: WalletID = Field(alias="walletId") + name: IDStr + description: str | None = None + owner: GroupID + thumbnail: str | None = None + status: WalletStatus + created: datetime + modified: datetime available_credits: Annotated[ Decimal, PlainSerializer(float, return_type=NonNegativeFloat, when_used="json") - ] + ] = Field(alias="availableCredits") -assert set(WalletGetWithAvailableCredits.model_fields.keys()) == set( +assert set(WalletGetWithAvailableCreditsLegacy.model_fields.keys()) == set( _WalletGetWithAvailableCredits.model_fields.keys() ) -class ServicePricingPlanGet(OutputSchema): - pricing_plan_id: PricingPlanId - display_name: str +class ServicePricingPlanGetLegacy(BaseModel): + pricing_plan_id: PricingPlanId = Field(alias="pricingPlanId") + display_name: str = Field(alias="displayName") description: str classification: PricingPlanClassification - created_at: datetime - pricing_plan_key: str - pricing_units: list[PricingUnitGet] + created_at: datetime = Field(alias="createdAt") + pricing_plan_key: str = Field(alias="pricingPlanKey") + pricing_units: list[PricingUnitGetLegacy] = Field(alias="pricingUnits") -assert set(ServicePricingPlanGet.model_fields.keys()) == set( +assert set(ServicePricingPlanGetLegacy.model_fields.keys()) == set( _ServicePricingPlanGet.model_fields.keys() ) diff --git a/services/api-server/src/simcore_service_api_server/services/webserver.py b/services/api-server/src/simcore_service_api_server/services/webserver.py index 1f5c6289708..cba31689654 100644 --- a/services/api-server/src/simcore_service_api_server/services/webserver.py +++ b/services/api-server/src/simcore_service_api_server/services/webserver.py @@ -57,7 +57,7 @@ SolverOutputNotFoundError, WalletNotFoundError, ) -from simcore_service_api_server.models.schemas.model_adapter import GetCreditPrice +from simcore_service_api_server.models.schemas.model_adapter import GetCreditPriceLegacy from tenacity import TryAgain from tenacity.asyncio import AsyncRetrying from tenacity.before_sleep import before_sleep_log @@ -73,7 +73,10 @@ from ..models.basic_types import VersionStr from ..models.pagination import MAXIMUM_NUMBER_OF_ITEMS_PER_PAGE from ..models.schemas.jobs import MetaValueType -from ..models.schemas.model_adapter import PricingUnitGet, WalletGetWithAvailableCredits +from ..models.schemas.model_adapter import ( + PricingUnitGetLegacy, + WalletGetWithAvailableCreditsLegacy, +) from ..models.schemas.profiles import Profile, ProfileUpdate from ..models.schemas.solvers import SolverKeyId from ..models.schemas.studies import StudyPort @@ -404,14 +407,14 @@ async def update_project_metadata( @_exception_mapper({status.HTTP_404_NOT_FOUND: PricingUnitNotFoundError}) async def get_project_node_pricing_unit( self, *, project_id: UUID, node_id: UUID - ) -> PricingUnitGet: + ) -> PricingUnitGetLegacy: response = await self.client.get( f"/projects/{project_id}/nodes/{node_id}/pricing-unit", cookies=self.session_cookies, ) response.raise_for_status() - data = Envelope[PricingUnitGet].model_validate_json(response.text).data + data = Envelope[PricingUnitGetLegacy].model_validate_json(response.text).data assert data is not None # nosec return data @@ -526,14 +529,14 @@ async def update_node_outputs( # WALLETS ------------------------------------------------- @_exception_mapper(_WALLET_STATUS_MAP) - async def get_default_wallet(self) -> WalletGetWithAvailableCredits: + async def get_default_wallet(self) -> WalletGetWithAvailableCreditsLegacy: response = await self.client.get( "/wallets/default", cookies=self.session_cookies, ) response.raise_for_status() data = ( - Envelope[WalletGetWithAvailableCredits] + Envelope[WalletGetWithAvailableCreditsLegacy] .model_validate_json(response.text) .data ) @@ -541,14 +544,16 @@ async def get_default_wallet(self) -> WalletGetWithAvailableCredits: return data @_exception_mapper(_WALLET_STATUS_MAP) - async def get_wallet(self, *, wallet_id: int) -> WalletGetWithAvailableCredits: + async def get_wallet( + self, *, wallet_id: int + ) -> WalletGetWithAvailableCreditsLegacy: response = await self.client.get( f"/wallets/{wallet_id}", cookies=self.session_cookies, ) response.raise_for_status() data = ( - Envelope[WalletGetWithAvailableCredits] + Envelope[WalletGetWithAvailableCreditsLegacy] .model_validate_json(response.text) .data ) @@ -569,13 +574,13 @@ async def get_project_wallet(self, *, project_id: ProjectID) -> WalletGet: # PRODUCTS ------------------------------------------------- @_exception_mapper({status.HTTP_404_NOT_FOUND: ProductPriceNotFoundError}) - async def get_product_price(self) -> GetCreditPrice: + async def get_product_price(self) -> GetCreditPriceLegacy: response = await self.client.get( "/credits-price", cookies=self.session_cookies, ) response.raise_for_status() - data = Envelope[GetCreditPrice].model_validate_json(response.text).data + data = Envelope[GetCreditPriceLegacy].model_validate_json(response.text).data assert data is not None # nosec return data diff --git a/services/api-server/tests/unit/_with_db/test_product.py b/services/api-server/tests/unit/_with_db/test_product.py index 60df845989a..274869d094a 100644 --- a/services/api-server/tests/unit/_with_db/test_product.py +++ b/services/api-server/tests/unit/_with_db/test_product.py @@ -20,7 +20,7 @@ from pydantic import PositiveInt from simcore_service_api_server._meta import API_VTAG from simcore_service_api_server.models.schemas.model_adapter import ( - WalletGetWithAvailableCredits, + WalletGetWithAvailableCreditsLegacy, ) @@ -48,8 +48,8 @@ def _check_key_product_compatibility(request: httpx.Request, **kwargs): return httpx.Response( status.HTTP_200_OK, json=jsonable_encoder( - Envelope[WalletGetWithAvailableCredits]( - data=WalletGetWithAvailableCredits( + Envelope[WalletGetWithAvailableCreditsLegacy]( + data=WalletGetWithAvailableCreditsLegacy( wallet_id=wallet_id, name="my_wallet", description="this is my wallet", diff --git a/services/api-server/tests/unit/test_api_solver_jobs.py b/services/api-server/tests/unit/test_api_solver_jobs.py index c1c2fe3334c..4ed52877cfa 100644 --- a/services/api-server/tests/unit/test_api_solver_jobs.py +++ b/services/api-server/tests/unit/test_api_solver_jobs.py @@ -24,8 +24,8 @@ from simcore_service_api_server._meta import API_VTAG from simcore_service_api_server.models.schemas.jobs import Job, JobStatus from simcore_service_api_server.models.schemas.model_adapter import ( - PricingUnitGet, - WalletGetWithAvailableCredits, + PricingUnitGetLegacy, + WalletGetWithAvailableCreditsLegacy, ) from simcore_service_api_server.models.schemas.solvers import Solver from simcore_service_api_server.services.director_v2 import ComputationTaskGet @@ -184,7 +184,7 @@ def _get_pricing_unit_side_effect( ) if capture_file == "get_job_pricing_unit_success.json": assert response.status_code == status.HTTP_200_OK - _ = TypeAdapter(PricingUnitGet).validate_python(response.json()) + _ = TypeAdapter(PricingUnitGetLegacy).validate_python(response.json()) elif capture_file == "get_job_pricing_unit_invalid_job.json": assert response.status_code == status.HTTP_404_NOT_FOUND elif capture_file == "get_job_pricing_unit_invalid_solver.json": @@ -419,7 +419,7 @@ def _wallet_side_effect( capture: HttpApiCallCaptureModel, ): wallet = ( - TypeAdapter(Envelope[WalletGetWithAvailableCredits]) + TypeAdapter(Envelope[WalletGetWithAvailableCreditsLegacy]) .validate_python(capture.response_body) .data ) @@ -427,7 +427,7 @@ def _wallet_side_effect( wallet.available_credits = ( Decimal(10.0) if sufficient_credits else Decimal(-10.0) ) - envelope = Envelope[WalletGetWithAvailableCredits]() + envelope = Envelope[WalletGetWithAvailableCreditsLegacy]() envelope.data = wallet return jsonable_encoder(envelope) diff --git a/services/api-server/tests/unit/test_api_wallets.py b/services/api-server/tests/unit/test_api_wallets.py index 9ef1592fdf3..bf6f7167f23 100644 --- a/services/api-server/tests/unit/test_api_wallets.py +++ b/services/api-server/tests/unit/test_api_wallets.py @@ -16,7 +16,7 @@ ) from simcore_service_api_server._meta import API_VTAG from simcore_service_api_server.models.schemas.model_adapter import ( - WalletGetWithAvailableCredits, + WalletGetWithAvailableCreditsLegacy, ) @@ -54,8 +54,8 @@ def _get_wallet_side_effect( response = await client.get(f"{API_VTAG}/wallets/{wallet_id}", auth=auth) if "success" in capture: assert response.status_code == 200 - wallet: WalletGetWithAvailableCredits = ( - WalletGetWithAvailableCredits.model_validate(response.json()) + wallet: WalletGetWithAvailableCreditsLegacy = ( + WalletGetWithAvailableCreditsLegacy.model_validate(response.json()) ) assert wallet.wallet_id == wallet_id elif "failure" in capture: @@ -79,4 +79,4 @@ async def test_get_default_wallet( response = await client.get(f"{API_VTAG}/wallets/default", auth=auth) assert response.status_code == status.HTTP_200_OK - _ = WalletGetWithAvailableCredits.model_validate(response.json()) + _ = WalletGetWithAvailableCreditsLegacy.model_validate(response.json())