Skip to content

Commit 32c8a6e

Browse files
authored
Merge branch 'master' into fix/set-folder-after-delete
2 parents 16ba64c + 116a6c0 commit 32c8a6e

File tree

27 files changed

+681
-85
lines changed

27 files changed

+681
-85
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
""" Helper script to generate OAS automatically
2+
"""
3+
4+
# pylint: disable=redefined-outer-name
5+
# pylint: disable=unused-argument
6+
# pylint: disable=unused-variable
7+
# pylint: disable=too-many-arguments
8+
9+
from typing import Annotated
10+
11+
from _common import as_query
12+
from fastapi import APIRouter, Depends
13+
from models_library.api_schemas_webserver.licensed_items_purchases import (
14+
LicensedItemPurchaseGet,
15+
)
16+
from models_library.generics import Envelope
17+
from models_library.rest_error import EnvelopedError
18+
from models_library.rest_pagination import Page
19+
from simcore_service_webserver._meta import API_VTAG
20+
from simcore_service_webserver.licenses._exceptions_handlers import _TO_HTTP_ERROR_MAP
21+
from simcore_service_webserver.licenses._licensed_items_checkouts_models import (
22+
LicensedItemCheckoutPathParams,
23+
LicensedItemsCheckoutsListQueryParams,
24+
)
25+
from simcore_service_webserver.wallets._handlers import WalletsPathParams
26+
27+
router = APIRouter(
28+
prefix=f"/{API_VTAG}",
29+
tags=[
30+
"licenses",
31+
],
32+
responses={
33+
i.status_code: {"model": EnvelopedError} for i in _TO_HTTP_ERROR_MAP.values()
34+
},
35+
)
36+
37+
38+
@router.get(
39+
"/wallets/{wallet_id}/licensed-items-checkouts",
40+
response_model=Page[LicensedItemPurchaseGet],
41+
tags=["wallets"],
42+
)
43+
async def list_licensed_item_checkouts_for_wallet(
44+
_path: Annotated[WalletsPathParams, Depends()],
45+
_query: Annotated[as_query(LicensedItemsCheckoutsListQueryParams), Depends()],
46+
):
47+
...
48+
49+
50+
@router.get(
51+
"/licensed-items-checkouts/{licensed_item_checkout_id}",
52+
response_model=Envelope[LicensedItemPurchaseGet],
53+
)
54+
async def get_licensed_item_checkout(
55+
_path: Annotated[LicensedItemCheckoutPathParams, Depends()],
56+
):
57+
...

api/specs/web-server/openapi.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"_long_running_tasks",
3939
"_licensed_items",
4040
"_licensed_items_purchases",
41+
"_licensed_items_checkouts",
4142
"_metamodeling",
4243
"_nih_sparc",
4344
"_nih_sparc_redirections",

packages/common-library/src/common_library/unset.py renamed to packages/common-library/src/common_library/exclude.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@ class UnSet:
1010

1111
def as_dict_exclude_unset(**params) -> dict[str, Any]:
1212
return {k: v for k, v in params.items() if not isinstance(v, UnSet)}
13+
14+
15+
def as_dict_exclude_none(**params) -> dict[str, Any]:
16+
return {k: v for k, v in params.items() if v is not None}

packages/common-library/tests/test_unset.py renamed to packages/common-library/tests/test_exclude.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Any
22

3-
from common_library.unset import UnSet, as_dict_exclude_unset
3+
from common_library.exclude import UnSet, as_dict_exclude_none, as_dict_exclude_unset
44

55

66
def test_as_dict_exclude_unset():
@@ -13,3 +13,10 @@ def f(
1313
assert f(par1="hi") == {"par1": "hi"}
1414
assert f(par2=4) == {"par2": 4}
1515
assert f(par1="hi", par2=4) == {"par1": "hi", "par2": 4}
16+
17+
# still expected behavior
18+
assert as_dict_exclude_unset(par1=None) == {"par1": None}
19+
20+
21+
def test_as_dict_exclude_none():
22+
assert as_dict_exclude_none(par1=None) == {}
Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from datetime import datetime
22
from typing import NamedTuple
33

4-
from pydantic import PositiveInt
4+
from pydantic import BaseModel, PositiveInt
55

66
from ..licensed_items import LicensedItemID
77
from ..products import ProductName
@@ -10,8 +10,29 @@
1010
from ..wallets import WalletID
1111
from ._base import OutputSchema
1212

13+
# RPC
1314

14-
class LicensedItemCheckoutGet(OutputSchema):
15+
16+
class LicensedItemCheckoutRpcGet(BaseModel):
17+
licensed_item_checkout_id: LicensedItemCheckoutID
18+
licensed_item_id: LicensedItemID
19+
wallet_id: WalletID
20+
user_id: UserID
21+
product_name: ProductName
22+
started_at: datetime
23+
stopped_at: datetime | None
24+
num_of_seats: int
25+
26+
27+
class LicensedItemCheckoutRpcGetPage(NamedTuple):
28+
items: list[LicensedItemCheckoutRpcGet]
29+
total: PositiveInt
30+
31+
32+
# Rest
33+
34+
35+
class LicensedItemCheckoutRestGet(OutputSchema):
1536
licensed_item_checkout_id: LicensedItemCheckoutID
1637
licensed_item_id: LicensedItemID
1738
wallet_id: WalletID
@@ -22,6 +43,6 @@ class LicensedItemCheckoutGet(OutputSchema):
2243
num_of_seats: int
2344

2445

25-
class LicensedItemUsageGetPage(NamedTuple):
26-
items: list[LicensedItemCheckoutGet]
46+
class LicensedItemCheckoutRestGetPage(NamedTuple):
47+
items: list[LicensedItemCheckoutRestGet]
2748
total: PositiveInt

packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/webserver/licenses/licensed_items.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
LicensedItemGetPage,
77
)
88
from models_library.api_schemas_webserver.licensed_items_checkouts import (
9-
LicensedItemCheckoutGet,
9+
LicensedItemCheckoutRpcGet,
1010
)
1111
from models_library.licensed_items import LicensedItemID
1212
from models_library.products import ProductName
@@ -78,7 +78,7 @@ async def checkout_licensed_item_for_wallet(
7878
licensed_item_id: LicensedItemID,
7979
num_of_seats: int,
8080
service_run_id: ServiceRunID,
81-
) -> LicensedItemCheckoutGet:
81+
) -> LicensedItemCheckoutRpcGet:
8282
result = await rabbitmq_rpc_client.request(
8383
WEBSERVER_RPC_NAMESPACE,
8484
TypeAdapter(RPCMethodName).validate_python("checkout_licensed_item_for_wallet"),
@@ -89,7 +89,7 @@ async def checkout_licensed_item_for_wallet(
8989
num_of_seats=num_of_seats,
9090
service_run_id=service_run_id,
9191
)
92-
assert isinstance(result, LicensedItemCheckoutGet) # nosec
92+
assert isinstance(result, LicensedItemCheckoutRpcGet) # nosec
9393
return result
9494

9595

@@ -100,13 +100,13 @@ async def release_licensed_item_for_wallet(
100100
product_name: ProductName,
101101
user_id: UserID,
102102
licensed_item_checkout_id: LicensedItemCheckoutID,
103-
) -> LicensedItemCheckoutGet:
103+
) -> LicensedItemCheckoutRpcGet:
104104
result = await rabbitmq_rpc_client.request(
105105
WEBSERVER_RPC_NAMESPACE,
106106
TypeAdapter(RPCMethodName).validate_python("release_licensed_item_for_wallet"),
107107
product_name=product_name,
108108
user_id=user_id,
109109
licensed_item_checkout_id=licensed_item_checkout_id,
110110
)
111-
assert isinstance(result, LicensedItemCheckoutGet) # nosec
111+
assert isinstance(result, LicensedItemCheckoutRpcGet) # nosec
112112
return result

services/catalog/src/simcore_service_catalog/api/rest/_services.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from fastapi import APIRouter, Depends, Header, HTTPException, status
1010
from models_library.api_schemas_catalog.services import ServiceGet, ServiceUpdate
1111
from models_library.services import ServiceKey, ServiceType, ServiceVersion
12+
from models_library.services_authoring import Author
1213
from models_library.services_metadata_published import ServiceMetaDataPublished
1314
from pydantic import ValidationError
1415
from pydantic.types import PositiveInt
@@ -127,7 +128,11 @@ async def list_services(
127128
name="nodetails",
128129
description="nodetails",
129130
type=ServiceType.COMPUTATIONAL,
130-
authors=[{"name": "nodetails", "email": "[email protected]"}],
131+
authors=[
132+
Author.model_construct(
133+
name="nodetails", email="[email protected]"
134+
)
135+
],
131136
contact="[email protected]",
132137
inputs={},
133138
outputs={},

services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/services/director_v2/_thin_client.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import datetime
22
from typing import cast
33

4+
from common_library.exclude import as_dict_exclude_none
45
from common_library.json_serialization import json_dumps
5-
from common_library.unset import UnSet, as_dict_exclude_unset
66
from fastapi import FastAPI, status
77
from httpx import Response, Timeout
88
from models_library.api_schemas_dynamic_scheduler.dynamic_services import (
@@ -133,14 +133,11 @@ async def dynamic_service_retrieve(
133133
@retry_on_errors()
134134
@expect_status(status.HTTP_200_OK)
135135
async def get_dynamic_services(
136-
self,
137-
*,
138-
user_id: UserID | None | UnSet = UnSet.VALUE,
139-
project_id: ProjectID | None | UnSet = UnSet.VALUE,
136+
self, *, user_id: UserID | None = None, project_id: ProjectID | None = None
140137
) -> Response:
141138
return await self.client.get(
142139
"/dynamic_services",
143-
params=as_dict_exclude_unset(user_id=user_id, project_id=project_id),
140+
params=as_dict_exclude_none(user_id=user_id, project_id=project_id),
144141
)
145142

146143
@retry_on_errors()

services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3278,6 +3278,121 @@ paths:
32783278
schema:
32793279
$ref: '#/components/schemas/EnvelopedError'
32803280
description: Bad Request
3281+
/v0/wallets/{wallet_id}/licensed-items-checkouts:
3282+
get:
3283+
tags:
3284+
- licenses
3285+
- wallets
3286+
summary: List Licensed Item Checkouts For Wallet
3287+
operationId: list_licensed_item_checkouts_for_wallet
3288+
parameters:
3289+
- name: wallet_id
3290+
in: path
3291+
required: true
3292+
schema:
3293+
type: integer
3294+
exclusiveMinimum: true
3295+
title: Wallet Id
3296+
minimum: 0
3297+
- name: order_by
3298+
in: query
3299+
required: false
3300+
schema:
3301+
type: string
3302+
contentMediaType: application/json
3303+
contentSchema: {}
3304+
default: '{"field":"started_at","direction":"desc"}'
3305+
title: Order By
3306+
- name: limit
3307+
in: query
3308+
required: false
3309+
schema:
3310+
type: integer
3311+
default: 20
3312+
title: Limit
3313+
- name: offset
3314+
in: query
3315+
required: false
3316+
schema:
3317+
type: integer
3318+
default: 0
3319+
title: Offset
3320+
responses:
3321+
'200':
3322+
description: Successful Response
3323+
content:
3324+
application/json:
3325+
schema:
3326+
$ref: '#/components/schemas/Page_LicensedItemPurchaseGet_'
3327+
'404':
3328+
content:
3329+
application/json:
3330+
schema:
3331+
$ref: '#/components/schemas/EnvelopedError'
3332+
description: Not Found
3333+
'403':
3334+
content:
3335+
application/json:
3336+
schema:
3337+
$ref: '#/components/schemas/EnvelopedError'
3338+
description: Forbidden
3339+
'402':
3340+
content:
3341+
application/json:
3342+
schema:
3343+
$ref: '#/components/schemas/EnvelopedError'
3344+
description: Payment Required
3345+
'400':
3346+
content:
3347+
application/json:
3348+
schema:
3349+
$ref: '#/components/schemas/EnvelopedError'
3350+
description: Bad Request
3351+
/v0/licensed-items-checkouts/{licensed_item_checkout_id}:
3352+
get:
3353+
tags:
3354+
- licenses
3355+
summary: Get Licensed Item Checkout
3356+
operationId: get_licensed_item_checkout
3357+
parameters:
3358+
- name: licensed_item_checkout_id
3359+
in: path
3360+
required: true
3361+
schema:
3362+
type: string
3363+
format: uuid
3364+
title: Licensed Item Checkout Id
3365+
responses:
3366+
'200':
3367+
description: Successful Response
3368+
content:
3369+
application/json:
3370+
schema:
3371+
$ref: '#/components/schemas/Envelope_LicensedItemPurchaseGet_'
3372+
'404':
3373+
content:
3374+
application/json:
3375+
schema:
3376+
$ref: '#/components/schemas/EnvelopedError'
3377+
description: Not Found
3378+
'403':
3379+
content:
3380+
application/json:
3381+
schema:
3382+
$ref: '#/components/schemas/EnvelopedError'
3383+
description: Forbidden
3384+
'402':
3385+
content:
3386+
application/json:
3387+
schema:
3388+
$ref: '#/components/schemas/EnvelopedError'
3389+
description: Payment Required
3390+
'400':
3391+
content:
3392+
application/json:
3393+
schema:
3394+
$ref: '#/components/schemas/EnvelopedError'
3395+
description: Bad Request
32813396
/v0/projects/{project_uuid}/checkpoint/{ref_id}/iterations:
32823397
get:
32833398
tags:

services/web/server/src/simcore_service_webserver/folders/_folders_db.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
import sqlalchemy as sa
1212
from aiohttp import web
13-
from common_library.unset import UnSet, as_dict_exclude_unset
13+
from common_library.exclude import UnSet, as_dict_exclude_unset
1414
from models_library.folders import (
1515
FolderDB,
1616
FolderID,

0 commit comments

Comments
 (0)