Skip to content

Commit c361c2f

Browse files
authored
✨ webserver-catalog rpc connection (#6003)
1 parent 817d81c commit c361c2f

File tree

61 files changed

+1578
-650
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1578
-650
lines changed

api/specs/web-server/_catalog.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from fastapi import APIRouter, Depends
44
from models_library.api_schemas_api_server.pricing_plans import ServicePricingPlanGet
55
from models_library.api_schemas_webserver.catalog import (
6-
DEVServiceGet,
6+
CatalogServiceGet,
77
ServiceGet,
88
ServiceInputGet,
99
ServiceInputKey,
@@ -13,6 +13,7 @@
1313
ServiceUpdate,
1414
)
1515
from models_library.generics import Envelope
16+
from models_library.rest_pagination import Page
1617
from simcore_service_webserver._meta import API_VTAG
1718
from simcore_service_webserver.catalog._handlers import (
1819
ListServiceParams,
@@ -38,23 +39,23 @@
3839

3940
@router.get(
4041
"/dev/catalog/services/-/latest",
41-
response_model=Envelope[list[DEVServiceGet]],
42+
response_model=Page[CatalogServiceGet],
4243
)
4344
def dev_list_services_latest(_query_params: Annotated[ListServiceParams, Depends()]):
4445
pass
4546

4647

4748
@router.get(
4849
"/dev/catalog/services/{service_key}/{service_version}",
49-
response_model=Envelope[DEVServiceGet],
50+
response_model=Envelope[CatalogServiceGet],
5051
)
5152
def dev_get_service(_path_params: Annotated[ServicePathParams, Depends()]):
5253
...
5354

5455

5556
@router.patch(
5657
"/dev/catalog/services/{service_key}/{service_version}",
57-
response_model=Envelope[DEVServiceGet],
58+
response_model=Envelope[CatalogServiceGet],
5859
)
5960
def dev_update_service(
6061
_path_params: Annotated[ServicePathParams, Depends()],
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from typing import Final
2+
3+
from pydantic import parse_obj_as
4+
5+
from ..rabbitmq_basic_types import RPCNamespace
6+
7+
CATALOG_RPC_NAMESPACE: Final[RPCNamespace] = parse_obj_as(RPCNamespace, "catalog")

packages/models-library/src/models_library/api_schemas_catalog/services.py

Lines changed: 231 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
from typing import Any, ClassVar
22

3-
from pydantic import Extra
3+
from models_library.rpc_pagination import PageRpc
4+
from pydantic import BaseModel, Extra, Field, HttpUrl, NonNegativeInt
45

6+
from ..boot_options import BootOptions
57
from ..emails import LowerCaseEmailStr
6-
from ..services import ServiceMetaDataPublished
7-
from ..services_access import ServiceAccessRights
8+
from ..services_access import ServiceAccessRights, ServiceGroupAccessRightsApi
9+
from ..services_authoring import Author, Badge
10+
from ..services_enums import ServiceType
11+
from ..services_history import ServiceRelease
812
from ..services_metadata_editable import ServiceMetaDataEditable
13+
from ..services_metadata_published import (
14+
ServiceInputsDict,
15+
ServiceMetaDataPublished,
16+
ServiceOutputsDict,
17+
)
918
from ..services_resources import ServiceResourcesDict
19+
from ..services_types import ServiceKey, ServiceVersion
20+
from ..users import GroupID
21+
from ..utils.change_case import snake_to_camel
1022

1123

1224
class ServiceUpdate(ServiceMetaDataEditable, ServiceAccessRights):
@@ -61,6 +73,125 @@ class Config:
6173
}
6274

6375

76+
_EXAMPLE_FILEPICKER: dict[str, Any] = {
77+
"name": "File Picker",
78+
"thumbnail": None,
79+
"description": "description",
80+
"classifiers": [],
81+
"quality": {},
82+
"accessRights": {
83+
"1": {"execute_access": True, "write_access": False},
84+
"4": {"execute_access": True, "write_access": True},
85+
},
86+
"key": "simcore/services/frontend/file-picker",
87+
"version": "1.0.0",
88+
"type": "dynamic",
89+
"badges": None,
90+
"authors": [
91+
{
92+
"name": "Red Pandas",
93+
"email": "[email protected]",
94+
"affiliation": None,
95+
}
96+
],
97+
"contact": "[email protected]",
98+
"inputs": {},
99+
"outputs": {
100+
"outFile": {
101+
"displayOrder": 0,
102+
"label": "File",
103+
"description": "Chosen File",
104+
"type": "data:*/*",
105+
"fileToKeyMap": None,
106+
"widget": None,
107+
}
108+
},
109+
"owner": "[email protected]",
110+
}
111+
112+
113+
_EXAMPLE_SLEEPER: dict[str, Any] = {
114+
"name": "sleeper",
115+
"thumbnail": None,
116+
"description": "A service which awaits for time to pass, two times.",
117+
"classifiers": [],
118+
"quality": {},
119+
"accessRights": {"1": {"execute_access": True, "write_access": False}},
120+
"key": "simcore/services/comp/itis/sleeper",
121+
"version": "2.2.1",
122+
"version_display": "2 Xtreme",
123+
"type": "computational",
124+
"authors": [
125+
{
126+
"name": "Author Bar",
127+
"email": "[email protected]",
128+
"affiliation": "ACME",
129+
},
130+
],
131+
"contact": "[email protected]",
132+
"inputs": {
133+
"input_1": {
134+
"displayOrder": 1,
135+
"label": "File with int number",
136+
"description": "Pick a file containing only one integer",
137+
"type": "data:text/plain",
138+
"fileToKeyMap": {"single_number.txt": "input_1"},
139+
},
140+
"input_2": {
141+
"label": "Sleep interval",
142+
"description": "Choose an amount of time to sleep in range [0:]",
143+
"displayOrder": 2,
144+
"type": "integer",
145+
"defaultValue": 2,
146+
},
147+
"input_3": {
148+
"displayOrder": 3,
149+
"label": "Fail after sleep",
150+
"description": "If set to true will cause service to fail after it sleeps",
151+
"type": "boolean",
152+
"defaultValue": False,
153+
},
154+
"input_4": {
155+
"label": "Distance to bed",
156+
"description": "It will first walk the distance to bed",
157+
"displayOrder": 4,
158+
"type": "integer",
159+
"defaultValue": 0,
160+
},
161+
"input_5": {
162+
"label": "Dream (or nightmare) of the night",
163+
"description": "Defines the size of the dream that will be generated [0:]",
164+
"displayOrder": 5,
165+
"type": "integer",
166+
"defaultValue": 0,
167+
},
168+
},
169+
"outputs": {
170+
"output_1": {
171+
"displayOrder": 1,
172+
"label": "File containing one random integer",
173+
"description": "Integer is generated in range [1-9]",
174+
"type": "data:text/plain",
175+
"fileToKeyMap": {"single_number.txt": "output_1"},
176+
},
177+
"output_2": {
178+
"label": "Random sleep interval",
179+
"description": "Interval is generated in range [1-9]",
180+
"displayOrder": 2,
181+
"type": "integer",
182+
},
183+
"output_3": {
184+
"displayOrder": 3,
185+
"label": "Dream output",
186+
"description": "Contains some random data representing a dream",
187+
"type": "data:text/plain",
188+
"fileToKeyMap": {"dream.txt": "output_3"},
189+
},
190+
},
191+
"owner": "[email protected]",
192+
}
193+
194+
64195
class ServiceGet(
65196
ServiceMetaDataPublished, ServiceAccessRights, ServiceMetaDataEditable
66197
): # pylint: disable=too-many-ancestors
@@ -70,43 +201,106 @@ class Config:
70201
allow_population_by_field_name = True
71202
extra = Extra.ignore
72203
schema_extra: ClassVar[dict[str, Any]] = {
73-
"example": {
74-
"name": "File Picker",
75-
"thumbnail": None,
76-
"description": "description",
77-
"classifiers": [],
78-
"quality": {},
79-
"accessRights": {
80-
"1": {"execute_access": True, "write_access": False},
81-
"4": {"execute_access": True, "write_access": True},
204+
"examples": [_EXAMPLE_FILEPICKER, _EXAMPLE_SLEEPER]
205+
}
206+
207+
208+
class ServiceGetV2(BaseModel):
209+
key: ServiceKey
210+
version: ServiceVersion
211+
212+
name: str
213+
thumbnail: HttpUrl | None = None
214+
description: str
215+
216+
version_display: str | None = None
217+
218+
service_type: ServiceType = Field(default=..., alias="type")
219+
220+
badges: list[Badge] | None = None
221+
222+
contact: LowerCaseEmailStr | None
223+
authors: list[Author] = Field(..., min_items=1)
224+
owner: LowerCaseEmailStr | None
225+
226+
inputs: ServiceInputsDict
227+
outputs: ServiceOutputsDict
228+
229+
boot_options: BootOptions | None = None
230+
min_visible_inputs: NonNegativeInt | None = None
231+
232+
access_rights: dict[GroupID, ServiceGroupAccessRightsApi] | None
233+
234+
classifiers: list[str] | None
235+
quality: dict[str, Any] = {}
236+
237+
history: list[ServiceRelease] = Field(
238+
default=[],
239+
description="history of releases for this service at this point in time, starting from the newest to the oldest."
240+
" It includes current release.",
241+
)
242+
243+
class Config:
244+
extra = Extra.forbid
245+
alias_generator = snake_to_camel
246+
allow_population_by_field_name = True
247+
schema_extra: ClassVar[dict[str, Any]] = {
248+
"examples": [
249+
{
250+
**_EXAMPLE_SLEEPER, # v2.2.1 (latest)
251+
"history": [
252+
{
253+
"version": _EXAMPLE_SLEEPER["version"],
254+
"version_display": "Summer Release",
255+
"released": "2024-07-20T15:00:00",
256+
},
257+
{
258+
"version": "2.0.0",
259+
"compatibility": {
260+
"canUpdateTo": _EXAMPLE_SLEEPER["version"],
261+
},
262+
},
263+
{"version": "0.9.11"},
264+
{"version": "0.9.10"},
265+
{
266+
"version": "0.9.8",
267+
"compatibility": {
268+
"canUpdateTo": "0.9.11",
269+
},
270+
},
271+
{
272+
"version": "0.9.1",
273+
"versionDisplay": "Matterhorn",
274+
"released": "2024-01-20T18:49:17",
275+
"compatibility": {
276+
"can_update_to": "0.9.11",
277+
},
278+
},
279+
{
280+
"version": "0.9.0",
281+
"retired": "2024-07-20T15:00:00",
282+
},
283+
{"version": "0.8.0"},
284+
{"version": "0.1.0"},
285+
],
82286
},
83-
"key": "simcore/services/frontend/file-picker",
84-
"version": "1.0.0",
85-
"integration-version": None,
86-
"type": "dynamic",
87-
"badges": None,
88-
"authors": [
89-
{
90-
"name": "Red Pandas",
91-
"email": "[email protected]",
92-
"affiliation": None,
93-
}
94-
],
95-
"contact": "[email protected]",
96-
"inputs": {},
97-
"outputs": {
98-
"outFile": {
99-
"displayOrder": 0,
100-
"label": "File",
101-
"description": "Chosen File",
102-
"type": "data:*/*",
103-
"fileToKeyMap": None,
104-
"widget": None,
105-
}
287+
{
288+
**_EXAMPLE_FILEPICKER,
289+
"history": [
290+
{
291+
"version": _EXAMPLE_FILEPICKER["version"],
292+
"version_display": "Odei Release",
293+
"released": "2025-03-25T00:00:00",
294+
}
295+
],
106296
},
107-
"owner": "[email protected]",
108-
},
297+
]
109298
}
110299

111300

301+
PageRpcServicesGetV2 = PageRpc[
302+
# WARNING: keep this definition in models_library and not in the RPC interface
303+
ServiceGetV2
304+
]
305+
112306
ServiceResourcesGet = ServiceResourcesDict

packages/models-library/src/models_library/api_schemas_invitations/invitations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from typing import Any, ClassVar
22

3-
from models_library.products import ProductName
43
from pydantic import BaseModel, Field, HttpUrl
54

65
from ..invitations import InvitationContent, InvitationInputs
6+
from ..products import ProductName
77

88
_INPUTS_EXAMPLE: dict[str, Any] = {
99
"issuer": "issuerid",

packages/models-library/src/models_library/api_schemas_resource_usage_tracker/pricing_plans.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from decimal import Decimal
33
from typing import Any, ClassVar
44

5-
from models_library.services import ServiceKey, ServiceVersion
65
from pydantic import BaseModel
76

87
from ..resource_tracker import (
@@ -13,6 +12,7 @@
1312
PricingUnitId,
1413
UnitExtraInfo,
1514
)
15+
from ..services_types import ServiceKey, ServiceVersion
1616

1717

1818
class PricingUnitGet(BaseModel):

0 commit comments

Comments
 (0)