Skip to content

Commit 883919c

Browse files
committed
models
1 parent c6727e3 commit 883919c

File tree

3 files changed

+84
-81
lines changed

3 files changed

+84
-81
lines changed

packages/models-library/src/models_library/api_schemas_webserver/computations.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,37 @@
77
ComputationGet as _ComputationGetDirectorV2,
88
)
99
from ..projects import CommitID, ProjectID
10-
from ._base import InputSchema, OutputSchema
10+
from ._base import InputSchemaWithoutCamelCase, OutputSchemaWithoutCamelCase
1111

1212

1313
class ComputationPathParams(BaseModel):
1414
project_id: ProjectID
1515

1616

17-
class ComputationGet(_ComputationGetDirectorV2, OutputSchema):
18-
# FIXME: camelcase!!
17+
class ComputationGet(_ComputationGetDirectorV2, OutputSchemaWithoutCamelCase):
1918
# NOTE: this is a copy of the same class in models_library.api_schemas_directorv2
2019
# but it is used in a different context (webserver)
2120
# and we need to add the `OutputSchema` mixin
2221
# so that it can be used as a response model in FastAPI
2322
pass
2423

2524

26-
class ComputationStart(InputSchema):
27-
# FIXME: camelcase!!
25+
class ComputationStart(InputSchemaWithoutCamelCase):
2826
force_restart: bool = False
29-
subgraph: Annotated[set[str], Field(default_factory=set)]
27+
subgraph: Annotated[
28+
set[str], Field(default_factory=set, json_schema_extra={"default": []})
29+
] = DEFAULT_FACTORY
3030

3131

32-
class ComputationStarted(OutputSchema):
33-
# FIXME: camelcase!!
32+
class ComputationStarted(OutputSchemaWithoutCamelCase):
3433
pipeline_id: Annotated[
3534
ProjectID, Field(description="ID for created pipeline (=project identifier)")
3635
]
3736
ref_ids: Annotated[
3837
list[CommitID],
39-
Field(default_factory=list, description="Checkpoints IDs for created pipeline"),
38+
Field(
39+
default_factory=list,
40+
description="Checkpoints IDs for created pipeline",
41+
json_schema_extra={"default": []},
42+
),
4043
] = DEFAULT_FACTORY

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

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2505,7 +2505,7 @@ paths:
25052505
content:
25062506
application/json:
25072507
schema:
2508-
$ref: '#/components/schemas/Envelope__ComputationStarted_'
2508+
$ref: '#/components/schemas/Envelope_ComputationStarted_'
25092509
'402':
25102510
description: Insufficient credits to run computation
25112511
'404':
@@ -8582,9 +8582,25 @@ components:
85828582
type: array
85838583
uniqueItems: true
85848584
title: Subgraph
8585-
default: []
85868585
type: object
85878586
title: ComputationStart
8587+
ComputationStarted:
8588+
properties:
8589+
pipeline_id:
8590+
type: string
8591+
format: uuid
8592+
title: Pipeline Id
8593+
description: ID for created pipeline (=project identifier)
8594+
ref_ids:
8595+
items:
8596+
type: integer
8597+
type: array
8598+
title: Ref Ids
8599+
description: Checkpoints IDs for created pipeline
8600+
type: object
8601+
required:
8602+
- pipeline_id
8603+
title: ComputationStarted
85888604
ConnectServiceToPricingPlanBodyParams:
85898605
properties:
85908606
serviceKey:
@@ -8986,6 +9002,19 @@ components:
89869002
title: Error
89879003
type: object
89889004
title: Envelope[ComputationGet]
9005+
Envelope_ComputationStarted_:
9006+
properties:
9007+
data:
9008+
anyOf:
9009+
- $ref: '#/components/schemas/ComputationStarted'
9010+
- type: 'null'
9011+
error:
9012+
anyOf:
9013+
- {}
9014+
- type: 'null'
9015+
title: Error
9016+
type: object
9017+
title: Envelope[ComputationStarted]
89899018
Envelope_CreditPriceGet_:
89909019
properties:
89919020
data:
@@ -9747,19 +9776,6 @@ components:
97479776
title: Error
97489777
type: object
97499778
title: Envelope[WorkspaceGroupGet]
9750-
Envelope__ComputationStarted_:
9751-
properties:
9752-
data:
9753-
anyOf:
9754-
- $ref: '#/components/schemas/_ComputationStarted'
9755-
- type: 'null'
9756-
error:
9757-
anyOf:
9758-
- {}
9759-
- type: 'null'
9760-
title: Error
9761-
type: object
9762-
title: Envelope[_ComputationStarted]
97639779
Envelope__ProjectGroupAccess_:
97649780
properties:
97659781
data:
@@ -16251,23 +16267,6 @@ components:
1625116267
- write
1625216268
- delete
1625316269
title: WorkspacesGroupsBodyParams
16254-
_ComputationStarted:
16255-
properties:
16256-
pipeline_id:
16257-
type: string
16258-
format: uuid
16259-
title: Pipeline Id
16260-
description: ID for created pipeline (=project identifier)
16261-
ref_ids:
16262-
items:
16263-
type: integer
16264-
type: array
16265-
title: Ref Ids
16266-
description: Checkpoints IDs for created pipeline
16267-
type: object
16268-
required:
16269-
- pipeline_id
16270-
title: _ComputationStarted
1627116270
_ItisVipResourceRestData:
1627216271
properties:
1627316272
source:

services/web/server/src/simcore_service_webserver/director_v2/_handlers.py

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,22 @@
33
from typing import Any
44

55
from aiohttp import web
6-
from common_library.json_serialization import json_dumps
7-
from models_library.api_schemas_directorv2.computations import ComputationGet
8-
from models_library.api_schemas_webserver.computations import ComputationStart
6+
from models_library.api_schemas_directorv2.computations import (
7+
ComputationGet as _ComputationGetDirectorV2,
8+
)
9+
from models_library.api_schemas_webserver.computations import (
10+
ComputationGet,
11+
ComputationPathParams,
12+
ComputationStart,
13+
ComputationStarted,
14+
)
915
from models_library.projects import ProjectID
10-
from pydantic import BaseModel, Field, TypeAdapter, ValidationError
16+
from pydantic import TypeAdapter, ValidationError
1117
from servicelib.aiohttp import status
18+
from servicelib.aiohttp.requests_validation import (
19+
parse_request_body_as,
20+
parse_request_path_parameters_as,
21+
)
1222
from servicelib.aiohttp.rest_responses import create_http_error, exception_to_response
1323
from servicelib.aiohttp.web_exceptions_extension import get_http_error_class_or_none
1424
from servicelib.common_headers import (
@@ -40,45 +50,25 @@
4050
routes = web.RouteTableDef()
4151

4252

43-
class _ComputationStarted(BaseModel):
44-
pipeline_id: ProjectID = Field(
45-
..., description="ID for created pipeline (=project identifier)"
46-
)
47-
ref_ids: list[CommitID] = Field(
48-
default_factory=list, description="Checkpoints IDs for created pipeline"
49-
)
50-
51-
5253
@routes.post(f"/{VTAG}/computations/{{project_id}}:start", name="start_computation")
5354
@login_required
5455
@permission_required("services.pipeline.*")
5556
@permission_required("project.read")
5657
async def start_computation(request: web.Request) -> web.Response:
5758
# pylint: disable=too-many-statements
5859
try:
60+
simcore_user_agent = request.headers.get(
61+
X_SIMCORE_USER_AGENT, UNDEFINED_DEFAULT_SIMCORE_USER_AGENT_VALUE
62+
)
5963
req_ctx = RequestContext.model_validate(request)
60-
computations = ComputationsApi(request.app)
61-
62-
run_policy = get_project_run_policy(request.app)
63-
assert run_policy # nosec
64-
65-
project_id = ProjectID(request.match_info["project_id"])
64+
path_params = parse_request_path_parameters_as(ComputationPathParams, request)
6665

6766
subgraph: set[str] = set()
6867
force_restart: bool = False # NOTE: deprecate this entry
69-
7068
if request.can_read_body:
71-
body = await request.json()
72-
assert (
73-
TypeAdapter(ComputationStart).validate_python(body) is not None
74-
) # nosec
75-
76-
subgraph = body.get("subgraph", [])
77-
force_restart = bool(body.get("force_restart", force_restart))
78-
79-
simcore_user_agent = request.headers.get(
80-
X_SIMCORE_USER_AGENT, UNDEFINED_DEFAULT_SIMCORE_USER_AGENT_VALUE
81-
)
69+
body_params = await parse_request_body_as(ComputationStart, request)
70+
subgraph = body_params.subgraph
71+
force_restart = body_params.force_restart
8272

8373
async with get_database_engine(request.app).acquire() as conn:
8474
group_properties = (
@@ -93,7 +83,7 @@ async def start_computation(request: web.Request) -> web.Response:
9383
request.app,
9484
product=product,
9585
user_id=req_ctx.user_id,
96-
project_id=project_id,
86+
project_id=path_params.project_id,
9787
product_name=req_ctx.product_name,
9888
)
9989

@@ -106,16 +96,21 @@ async def start_computation(request: web.Request) -> web.Response:
10696
"wallet_info": wallet_info,
10797
}
10898

99+
run_policy = get_project_run_policy(request.app)
100+
assert run_policy # nosec
101+
109102
running_project_ids: list[ProjectID]
110103
project_vc_commits: list[CommitID]
111104

112105
(
113106
running_project_ids,
114107
project_vc_commits,
115-
) = await run_policy.get_or_create_runnable_projects(request, project_id)
108+
) = await run_policy.get_or_create_runnable_projects(
109+
request, path_params.project_id
110+
)
116111
_logger.debug(
117112
"Project %s will start %d variants: %s",
118-
f"{project_id=}",
113+
f"{path_params.project_id=}",
119114
len(running_project_ids),
120115
f"{running_project_ids=}",
121116
)
@@ -127,6 +122,7 @@ async def start_computation(request: web.Request) -> web.Response:
127122
else True
128123
)
129124

125+
computations = ComputationsApi(request.app)
130126
_started_pipelines_ids: list[str] = await asyncio.gather(
131127
*[
132128
computations.start(
@@ -141,14 +137,14 @@ async def start_computation(request: web.Request) -> web.Response:
141137
) # nosec
142138

143139
data: dict[str, Any] = {
144-
"pipeline_id": project_id,
140+
"pipeline_id": path_params.project_id,
145141
}
146142
# Optional
147143
if project_vc_commits:
148144
data["ref_ids"] = project_vc_commits
149145

150146
assert (
151-
TypeAdapter(_ComputationStarted).validate_python(data) is not None
147+
TypeAdapter(ComputationStarted).validate_python(data) is not None
152148
) # nosec
153149

154150
return envelope_json_response(data, status_cls=web.HTTPCreated)
@@ -221,7 +217,9 @@ async def get_computation(request: web.Request) -> web.Response:
221217
request, project_id
222218
)
223219
_logger.debug("Project %s will get %d variants", project_id, len(project_ids))
224-
list_computation_tasks = TypeAdapter(list[ComputationGet]).validate_python(
220+
list_computation_tasks = TypeAdapter(
221+
list[_ComputationGetDirectorV2]
222+
).validate_python(
225223
await asyncio.gather(
226224
*[
227225
computations.get(project_id=pid, user_id=user_id)
@@ -231,10 +229,13 @@ async def get_computation(request: web.Request) -> web.Response:
231229
)
232230
assert len(list_computation_tasks) == len(project_ids) # nosec
233231

234-
return web.json_response(
235-
data={"data": list_computation_tasks[0].model_dump(by_alias=True)},
236-
dumps=json_dumps,
232+
return envelope_json_response(
233+
[
234+
ComputationGet.model_construct(**m.model_dump(exclude_unset=True))
235+
for m in list_computation_tasks
236+
],
237237
)
238+
238239
except DirectorServiceError as exc:
239240
return create_http_error(
240241
exc,

0 commit comments

Comments
 (0)