From 9d9c696e331cdd3d58fdde574393d2040d83cf2a Mon Sep 17 00:00:00 2001 From: Pedro Crespo <32402063+pcrespov@users.noreply.github.com> Date: Tue, 29 Nov 2022 17:01:45 +0100 Subject: [PATCH 01/10] renames models --- .../webserver/openapi-projects-ports.yaml | 53 +++++++++++++++---- .../scripts/openapi_projects_ports.py | 13 ++--- .../api/v0/openapi.yaml | 34 ++++++++++-- .../projects/projects_ports_handlers.py | 27 ++++++---- 4 files changed, 97 insertions(+), 30 deletions(-) diff --git a/api/specs/webserver/openapi-projects-ports.yaml b/api/specs/webserver/openapi-projects-ports.yaml index 06267415555..b3cd3c7fac7 100644 --- a/api/specs/webserver/openapi-projects-ports.yaml +++ b/api/specs/webserver/openapi-projects-ports.yaml @@ -20,7 +20,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Envelope_dict_uuid.UUID__simcore_service_webserver.projects.projects_ports_handlers.ProjectPortGet__' + $ref: '#/components/schemas/Envelope_dict_uuid.UUID__simcore_service_webserver.projects.projects_ports_handlers.ProjectInputGet__' patch: tags: - project @@ -42,7 +42,7 @@ paths: title: Updates type: array items: - $ref: '#/components/schemas/ProjectPort' + $ref: '#/components/schemas/ProjectIOBase' required: true responses: '200': @@ -50,7 +50,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Envelope_dict_uuid.UUID__simcore_service_webserver.projects.projects_ports_handlers.ProjectPortGet__' + $ref: '#/components/schemas/Envelope_dict_uuid.UUID__simcore_service_webserver.projects.projects_ports_handlers.ProjectInputGet__' /projects/{project_id}/outputs: get: tags: @@ -72,18 +72,29 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Envelope_dict_uuid.UUID__simcore_service_webserver.projects.projects_ports_handlers.ProjectPortGet__' + $ref: '#/components/schemas/Envelope_dict_uuid.UUID__simcore_service_webserver.projects.projects_ports_handlers.ProjectOutputGet__' components: schemas: - Envelope_dict_uuid.UUID__simcore_service_webserver.projects.projects_ports_handlers.ProjectPortGet__: - title: Envelope[dict[uuid.UUID, simcore_service_webserver.projects.projects_ports_handlers.ProjectPortGet]] + Envelope_dict_uuid.UUID__simcore_service_webserver.projects.projects_ports_handlers.ProjectInputGet__: + title: Envelope[dict[uuid.UUID, simcore_service_webserver.projects.projects_ports_handlers.ProjectInputGet]] type: object properties: data: title: Data type: object additionalProperties: - $ref: '#/components/schemas/ProjectPortGet' + $ref: '#/components/schemas/ProjectInputGet' + error: + title: Error + Envelope_dict_uuid.UUID__simcore_service_webserver.projects.projects_ports_handlers.ProjectOutputGet__: + title: Envelope[dict[uuid.UUID, simcore_service_webserver.projects.projects_ports_handlers.ProjectOutputGet]] + type: object + properties: + data: + title: Data + type: object + additionalProperties: + $ref: '#/components/schemas/ProjectOutputGet' error: title: Error HTTPValidationError: @@ -95,11 +106,28 @@ components: type: array items: $ref: '#/components/schemas/ValidationError' - ProjectPort: - title: ProjectPort + ProjectIOBase: + title: ProjectIOBase + required: + - key + - value + type: object + properties: + key: + title: Key + type: string + description: Project port's unique identifer. Same as the UUID of the associated + port node + format: uuid + value: + title: Value + description: Value assigned to this i/o port + ProjectInputGet: + title: ProjectInputGet required: - key - value + - label type: object properties: key: @@ -111,8 +139,11 @@ components: value: title: Value description: Value assigned to this i/o port - ProjectPortGet: - title: ProjectPortGet + label: + title: Label + type: string + ProjectOutputGet: + title: ProjectOutputGet required: - key - value diff --git a/api/specs/webserver/scripts/openapi_projects_ports.py b/api/specs/webserver/scripts/openapi_projects_ports.py index b084cc47a1c..e9d43d5e8cd 100644 --- a/api/specs/webserver/scripts/openapi_projects_ports.py +++ b/api/specs/webserver/scripts/openapi_projects_ports.py @@ -15,8 +15,9 @@ from models_library.projects import ProjectID from models_library.projects_nodes import NodeID from simcore_service_webserver.projects.projects_ports_handlers import ( - ProjectPort, - ProjectPortGet, + ProjectInputGet, + ProjectIOBase, + ProjectOutputGet, ) # TODO: how to ensure this is in sync with projects_ports_handlers.routes ?? @@ -31,7 +32,7 @@ @app.get( "/projects/{project_id}/inputs", - response_model=Envelope[dict[NodeID, ProjectPortGet]], + response_model=Envelope[dict[NodeID, ProjectInputGet]], tags=TAGS, operation_id="get_project_inputs", ) @@ -41,17 +42,17 @@ async def get_project_inputs(project_id: ProjectID): @app.patch( "/projects/{project_id}/inputs", - response_model=Envelope[dict[NodeID, ProjectPortGet]], + response_model=Envelope[dict[NodeID, ProjectInputGet]], tags=TAGS, operation_id="update_project_inputs", ) -async def update_project_inputs(project_id: ProjectID, updates: list[ProjectPort]): +async def update_project_inputs(project_id: ProjectID, updates: list[ProjectIOBase]): """New in version *0.10*""" @app.get( "/projects/{project_id}/outputs", - response_model=Envelope[dict[NodeID, ProjectPortGet]], + response_model=Envelope[dict[NodeID, ProjectOutputGet]], tags=TAGS, operation_id="get_project_outputs", ) diff --git a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml index e314a6cbada..8fd6ca27e52 100644 --- a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml +++ b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml @@ -3124,14 +3124,14 @@ paths: content: application/json: schema: - title: 'Envelope[dict[uuid.UUID, simcore_service_webserver.projects.projects_ports_handlers.ProjectPortGet]]' + title: 'Envelope[dict[uuid.UUID, simcore_service_webserver.projects.projects_ports_handlers.ProjectInputGet]]' type: object properties: data: title: Data type: object additionalProperties: - title: ProjectPortGet + title: ProjectInputGet required: - key - value @@ -3172,7 +3172,7 @@ paths: title: Updates type: array items: - title: ProjectPort + title: ProjectIOBase required: - key - value @@ -3215,7 +3215,33 @@ paths: content: application/json: schema: - $ref: '#/paths/~1projects~1%7Bproject_id%7D~1inputs/get/responses/200/content/application~1json/schema' + title: 'Envelope[dict[uuid.UUID, simcore_service_webserver.projects.projects_ports_handlers.ProjectOutputGet]]' + type: object + properties: + data: + title: Data + type: object + additionalProperties: + title: ProjectOutputGet + required: + - key + - value + - label + type: object + properties: + key: + title: Key + type: string + description: Project port's unique identifer. Same as the UUID of the associated port node + format: uuid + value: + title: Value + description: Value assigned to this i/o port + label: + title: Label + type: string + error: + title: Error '/nodes/{nodeInstanceUUID}/outputUi/{outputKey}': get: tags: diff --git a/services/web/server/src/simcore_service_webserver/projects/projects_ports_handlers.py b/services/web/server/src/simcore_service_webserver/projects/projects_ports_handlers.py index e47db8b9463..bb79dc4e61e 100644 --- a/services/web/server/src/simcore_service_webserver/projects/projects_ports_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/projects_ports_handlers.py @@ -83,13 +83,11 @@ async def _get_validated_workbench_model( # -# projects/*/inputs COLLECTION ------------------------- +# Models # -routes = web.RouteTableDef() - -class ProjectPort(BaseModel): +class ProjectIOBase(BaseModel): key: NodeID = Field( ..., description="Project port's unique identifer. Same as the UUID of the associated port node", @@ -97,10 +95,21 @@ class ProjectPort(BaseModel): value: Any = Field(..., description="Value assigned to this i/o port") -class ProjectPortGet(ProjectPort): +class ProjectInputGet(ProjectIOBase): label: str +class ProjectOutputGet(ProjectInputGet): + pass + + +# +# projects/*/inputs COLLECTION ------------------------- +# + +routes = web.RouteTableDef() + + @routes.get(f"/{VTAG}/projects/{{project_id}}/inputs", name="get_project_inputs") @login_required @permission_required("project.read") @@ -118,7 +127,7 @@ async def get_project_inputs(request: web.Request) -> web.Response: return _web_json_response_enveloped( data={ - node_id: ProjectPortGet( + node_id: ProjectInputGet( key=node_id, label=workbench[node_id].label, value=value ) for node_id, value in inputs.items() @@ -134,7 +143,7 @@ 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) path_params = parse_request_path_parameters_as(ProjectPathParams, request) - inputs_updates = await parse_request_body_as(list[ProjectPort], request) + inputs_updates = await parse_request_body_as(list[ProjectIOBase], request) assert request.app # nosec @@ -168,7 +177,7 @@ async def update_project_inputs(request: web.Request) -> web.Response: return _web_json_response_enveloped( data={ - node_id: ProjectPortGet( + node_id: ProjectInputGet( key=node_id, label=workbench[node_id].label, value=value ) for node_id, value in inputs.items() @@ -198,7 +207,7 @@ async def get_project_outputs(request: web.Request) -> web.Response: return _web_json_response_enveloped( data={ - node_id: ProjectPortGet( + node_id: ProjectOutputGet( key=node_id, label=workbench[node_id].label, value=value ) for node_id, value in outputs.items() From b4445eb34a896e17e0144c617c557579beb2c318 Mon Sep 17 00:00:00 2001 From: Pedro Crespo <32402063+pcrespov@users.noreply.github.com> Date: Tue, 29 Nov 2022 19:51:49 +0100 Subject: [PATCH 02/10] minor --- .../models-library/src/models_library/utils/services_io.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/models-library/src/models_library/utils/services_io.py b/packages/models-library/src/models_library/utils/services_io.py index f75d5d73732..67225495e49 100644 --- a/packages/models-library/src/models_library/utils/services_io.py +++ b/packages/models-library/src/models_library/utils/services_io.py @@ -8,6 +8,7 @@ from ..services_constants import PROPERTY_TYPE_TO_PYTHON_TYPE_MAP PortKindStr = Literal["input", "output"] +JsonSchemaDict = dict[str, Any] _PROPERTY_TYPE_TO_SCHEMAS = { property_type: schema_of(python_type, title=property_type.capitalize()) @@ -36,7 +37,7 @@ def update_schema_doc(schema: dict[str, Any], port: Union[ServiceInput, ServiceO def get_service_io_json_schema( port: Union[ServiceInput, ServiceOutput] -) -> Optional[dict[str, Any]]: +) -> Optional[JsonSchemaDict]: """Get json-schema for a i/o service For legacy metadata with property_type = integer, etc ... , it applies a conversion From 2c25092be8857599c0b9d9375ddd5dd2115701e7 Mon Sep 17 00:00:00 2001 From: Pedro Crespo <32402063+pcrespov@users.noreply.github.com> Date: Tue, 29 Nov 2022 20:15:01 +0100 Subject: [PATCH 03/10] renames --- .../projects/_ports.py | 20 +++++++++---------- .../unit/with_dbs/02/test_projects__ports.py | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/_ports.py b/services/web/server/src/simcore_service_webserver/projects/_ports.py index 0f483eb0eed..2f1b977f34d 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_ports.py +++ b/services/web/server/src/simcore_service_webserver/projects/_ports.py @@ -8,12 +8,12 @@ JsonSchemaValidationError, jsonschema_validate_data, ) -from models_library.utils.services_io import get_service_io_json_schema +from models_library.utils.services_io import JsonSchemaDict, get_service_io_json_schema from pydantic import ValidationError @dataclass(frozen=True) -class _ProjectPort: +class ProjectPortData: kind: Literal["input", "output"] node_id: NodeID io_key: str @@ -23,7 +23,7 @@ class _ProjectPort: def key(self): return f"{self.node_id}.{self.io_key}" - def get_schema(self) -> Optional[dict[str, Any]]: + def get_schema(self) -> Optional[JsonSchemaDict]: node_meta = catalog.get_metadata(self.node.key, self.node.version) if self.kind == "input" and node_meta.outputs: if input_meta := node_meta.outputs[self.io_key]: @@ -34,10 +34,10 @@ def get_schema(self) -> Optional[dict[str, Any]]: return None -def _iter_project_ports( +def iter_project_ports( workbench: dict[NodeID, Node], filter_kind: Optional[Literal["input", "output"]] = None, -) -> Iterator[_ProjectPort]: +) -> Iterator[ProjectPortData]: """Iterates the nodes in a workbench that define the input/output ports of a project - A node in general has inputs and outputs: @@ -63,7 +63,7 @@ def _iter_project_ports( assert list(node.outputs.keys()) == ["out_1"] # nosec for output_key in node.outputs.keys(): - yield _ProjectPort( + yield ProjectPortData( kind="input", node_id=node_id, io_key=output_key, node=node ) @@ -79,7 +79,7 @@ def _iter_project_ports( assert list(node.inputs.keys()) == ["in_1"] # nosec for inputs_key in node.inputs.keys(): - yield _ProjectPort( + yield ProjectPortData( kind="output", node_id=node_id, io_key=inputs_key, node=node ) @@ -87,7 +87,7 @@ def _iter_project_ports( def get_project_inputs(workbench: dict[NodeID, Node]) -> dict[NodeID, Any]: """Returns the values assigned to each input node""" input_to_value = {} - for port in _iter_project_ports(workbench, "input"): + for port in iter_project_ports(workbench, "input"): input_to_value[port.node_id] = ( port.node.outputs["out_1"] if port.node.outputs else None ) @@ -113,7 +113,7 @@ def set_project_inputs( if (node := workbench[node_id]) and node.outputs != output: # validates value against jsonschema try: - port = _ProjectPort( + port = ProjectPortData( kind="input", node_id=node_id, io_key="out_1", node=node ) if schema := port.get_schema(): @@ -136,7 +136,7 @@ class Config(PortLink.Config): def get_project_outputs(workbench: dict[NodeID, Node]) -> dict[NodeID, Any]: """Returns values assigned to each output node""" output_to_value = {} - for port in _iter_project_ports(workbench, "output"): + for port in iter_project_ports(workbench, "output"): if port.node.inputs: try: # Is link? diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects__ports.py b/services/web/server/tests/unit/with_dbs/02/test_projects__ports.py index c713bbba1ea..726e7c30ba2 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects__ports.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects__ports.py @@ -12,9 +12,9 @@ from pydantic import parse_obj_as from simcore_service_webserver.projects._ports import ( InvalidInputValue, - _iter_project_ports, get_project_inputs, get_project_outputs, + iter_project_ports, set_project_inputs, ) @@ -210,7 +210,7 @@ def test_get_project_outputs(workbench: dict[NodeID, Node]): def test_project_port_get_schema(workbench): - for port in _iter_project_ports(workbench): + for port in iter_project_ports(workbench): # eval json-schema schema = port.get_schema() assert schema From a06d53a5b0263f4e2004a4c82df6af8471e9eb89 Mon Sep 17 00:00:00 2001 From: Pedro Crespo <32402063+pcrespov@users.noreply.github.com> Date: Tue, 29 Nov 2022 20:15:48 +0100 Subject: [PATCH 04/10] adds new handler: list_projects_metadata_ports --- .../scripts/openapi_projects_ports.py | 24 +++++-- .../projects/projects_ports_handlers.py | 68 ++++++++++++++++--- .../02/test_projects_ports_handlers.py | 53 +++++++++++++++ 3 files changed, 128 insertions(+), 17 deletions(-) diff --git a/api/specs/webserver/scripts/openapi_projects_ports.py b/api/specs/webserver/scripts/openapi_projects_ports.py index e9d43d5e8cd..0b9cccb766c 100644 --- a/api/specs/webserver/scripts/openapi_projects_ports.py +++ b/api/specs/webserver/scripts/openapi_projects_ports.py @@ -1,4 +1,6 @@ -""" Helper script to generate OAS automatically +""" Helper script to automatically generate OAS + +This OAS are the source of truth """ # pylint: disable=redefined-outer-name @@ -16,13 +18,11 @@ from models_library.projects_nodes import NodeID from simcore_service_webserver.projects.projects_ports_handlers import ( ProjectInputGet, - ProjectIOBase, + ProjectInputUpdate, + ProjectMetadataPortGet, ProjectOutputGet, ) -# TODO: how to ensure this is in sync with projects_ports_handlers.routes ?? -# this is the source of truth. - app = FastAPI(redoc_url=None) TAGS: list[Union[str, Enum]] = [ @@ -46,7 +46,9 @@ async def get_project_inputs(project_id: ProjectID): tags=TAGS, operation_id="update_project_inputs", ) -async def update_project_inputs(project_id: ProjectID, updates: list[ProjectIOBase]): +async def update_project_inputs( + project_id: ProjectID, updates: list[ProjectInputUpdate] +): """New in version *0.10*""" @@ -60,6 +62,16 @@ async def get_project_outputs(project_id: ProjectID): """New in version *0.10*""" +@app.get( + "/projects/{project_id}/metadata/ports", + response_model=Envelope[list[ProjectMetadataPortGet]], + tags=TAGS, + operation_id="list_project_metadata_ports", +) +async def list_project_metadata_ports(project_id: ProjectID): + """New in version *0.11*""" + + if __name__ == "__main__": from _common import CURRENT_DIR, create_openapi_specs diff --git a/services/web/server/src/simcore_service_webserver/projects/projects_ports_handlers.py b/services/web/server/src/simcore_service_webserver/projects/projects_ports_handlers.py index bb79dc4e61e..95577a5a3dd 100644 --- a/services/web/server/src/simcore_service_webserver/projects/projects_ports_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/projects_ports_handlers.py @@ -5,13 +5,14 @@ import functools import logging -from typing import Any +from typing import Any, Literal, Optional from aiohttp import web from models_library.projects import ProjectID from models_library.projects_nodes import Node, NodeID from models_library.users import UserID from models_library.utils.fastapi_encoders import jsonable_encoder +from models_library.utils.services_io import JsonSchemaDict from pydantic import BaseModel, Field, parse_obj_as from servicelib.aiohttp.requests_validation import ( parse_request_body_as, @@ -82,8 +83,10 @@ async def _get_validated_workbench_model( return workbench +routes = web.RouteTableDef() + # -# Models +# projects/*/inputs COLLECTION ------------------------- # @@ -95,21 +98,18 @@ class ProjectIOBase(BaseModel): value: Any = Field(..., description="Value assigned to this i/o port") +class ProjectInputUpdate(ProjectIOBase): + pass + + class ProjectInputGet(ProjectIOBase): label: str -class ProjectOutputGet(ProjectInputGet): +class ProjectOutputGet(ProjectIOBase): pass -# -# projects/*/inputs COLLECTION ------------------------- -# - -routes = web.RouteTableDef() - - @routes.get(f"/{VTAG}/projects/{{project_id}}/inputs", name="get_project_inputs") @login_required @permission_required("project.read") @@ -143,7 +143,7 @@ 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) path_params = parse_request_path_parameters_as(ProjectPathParams, request) - inputs_updates = await parse_request_body_as(list[ProjectIOBase], request) + inputs_updates = await parse_request_body_as(list[ProjectInputUpdate], request) assert request.app # nosec @@ -213,3 +213,49 @@ async def get_project_outputs(request: web.Request) -> web.Response: for node_id, value in outputs.items() } ) + + +# +# projects/*/metadata/ports sub-collection ------------------------- +# + + +class ProjectMetadataPortGet(BaseModel): + key: NodeID = Field( + ..., + description="Project port's unique identifer. Same as the UUID of the associated port node", + ) + kind: Literal["input", "output"] + content_schema: Optional[JsonSchemaDict] = Field( + None, + description="jsonschema for the port's value. SEE https://json-schema.org/understanding-json-schema/", + ) + + +@routes.get( + f"/{VTAG}/projects/{{project_id}}/metadata/ports", + name="list_project_metadata_ports", +) +@login_required +@permission_required("project.read") +@_handle_project_exceptions +async def list_project_metadata_ports(request: web.Request) -> web.Response: + req_ctx = RequestContext.parse_obj(request) + path_params = parse_request_path_parameters_as(ProjectPathParams, request) + + assert request.app # nosec + + workbench = await _get_validated_workbench_model( + app=request.app, project_id=path_params.project_id, user_id=req_ctx.user_id + ) + + return _web_json_response_enveloped( + data=[ + ProjectMetadataPortGet( + key=port.node_id, + kind=port.kind, + content_schema=port.get_schema(), + ) + for port in _ports.iter_project_ports(workbench) + ] + ) 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 1e5ca3da3fa..2825f3ff2e9 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 @@ -208,6 +208,59 @@ async def test_io_workflow( project_id = user_project["uuid"] + # list_project_metadata_ports + expected_url = client.app.router["list_project_metadata_ports"].url_for( + project_id=project_id + ) + assert URL(f"/v0/projects/{project_id}/metadata/ports") == expected_url + + resp = await client.get(f"/v0/projects/{project_id}/metadata/ports") + ports_meta, error = await assert_status(resp, expected_cls=expected) + + if not error: + assert ports_meta == [ + { + "key": "38a0d401-af4b-4ea7-ab4c-5005c712a546", + "kind": "input", + "content_schema": { + "title": "X", + "type": "integer", + }, + }, + { + "key": "fc48252a-9dbb-4e07-bf9a-7af65a18f612", + "kind": "input", + "content_schema": { + "title": "Z", + "type": "integer", + }, + }, + { + "key": "7bf0741f-bae4-410b-b662-fc34b47c27c9", + "kind": "input", + "content_schema": { + "title": "on", + "type": "boolean", + }, + }, + { + "key": "09fd512e-0768-44ca-81fa-0cecab74ec1a", + "kind": "output", + "content_schema": { + "title": "Random sleep interval_2", + "type": "integer", + }, + }, + { + "key": "76f607b4-8761-4f96-824d-cab670bc45f5", + "kind": "output", + "content-schema": { + "title": "Random sleep interval", + "type": "integer", + }, + }, + ] + # get_project_inputs expected_url = client.app.router["get_project_inputs"].url_for( project_id=project_id From 037737dede555b9b90fb0737bffbc398da61a354 Mon Sep 17 00:00:00 2001 From: Pedro Crespo <32402063+pcrespov@users.noreply.github.com> Date: Tue, 29 Nov 2022 20:22:29 +0100 Subject: [PATCH 05/10] updates OAS --- .../webserver/openapi-projects-ports.yaml | 76 ++++++++++++++++--- api/specs/webserver/openapi.yaml | 3 + .../api/v0/openapi.yaml | 55 +++++++++++++- 3 files changed, 118 insertions(+), 16 deletions(-) diff --git a/api/specs/webserver/openapi-projects-ports.yaml b/api/specs/webserver/openapi-projects-ports.yaml index b3cd3c7fac7..11fb2d7e909 100644 --- a/api/specs/webserver/openapi-projects-ports.yaml +++ b/api/specs/webserver/openapi-projects-ports.yaml @@ -42,7 +42,7 @@ paths: title: Updates type: array items: - $ref: '#/components/schemas/ProjectIOBase' + $ref: '#/components/schemas/ProjectInputUpdate' required: true responses: '200': @@ -73,6 +73,28 @@ paths: application/json: schema: $ref: '#/components/schemas/Envelope_dict_uuid.UUID__simcore_service_webserver.projects.projects_ports_handlers.ProjectOutputGet__' + /projects/{project_id}/metadata/ports: + get: + tags: + - project + summary: List Project Metadata Ports + description: New in version *0.11* + operationId: list_project_metadata_ports + parameters: + - required: true + schema: + title: Project Id + type: string + format: uuid + name: project_id + in: path + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Envelope_list_simcore_service_webserver.projects.projects_ports_handlers.ProjectMetadataPortGet__' components: schemas: Envelope_dict_uuid.UUID__simcore_service_webserver.projects.projects_ports_handlers.ProjectInputGet__: @@ -97,6 +119,17 @@ components: $ref: '#/components/schemas/ProjectOutputGet' error: title: Error + Envelope_list_simcore_service_webserver.projects.projects_ports_handlers.ProjectMetadataPortGet__: + title: Envelope[list[simcore_service_webserver.projects.projects_ports_handlers.ProjectMetadataPortGet]] + type: object + properties: + data: + title: Data + type: array + items: + $ref: '#/components/schemas/ProjectMetadataPortGet' + error: + title: Error HTTPValidationError: title: HTTPValidationError type: object @@ -106,11 +139,12 @@ components: type: array items: $ref: '#/components/schemas/ValidationError' - ProjectIOBase: - title: ProjectIOBase + ProjectInputGet: + title: ProjectInputGet required: - key - value + - label type: object properties: key: @@ -122,12 +156,14 @@ components: value: title: Value description: Value assigned to this i/o port - ProjectInputGet: - title: ProjectInputGet + label: + title: Label + type: string + ProjectInputUpdate: + title: ProjectInputUpdate required: - key - value - - label type: object properties: key: @@ -139,15 +175,34 @@ components: value: title: Value description: Value assigned to this i/o port - label: - title: Label + ProjectMetadataPortGet: + title: ProjectMetadataPortGet + required: + - key + - kind + type: object + properties: + key: + title: Key + type: string + description: Project port's unique identifer. Same as the UUID of the associated + port node + format: uuid + kind: + title: Kind + enum: + - input + - output type: string + content_schema: + title: Content Schema + type: object + description: jsonschema for the port's value. SEE https://json-schema.org/understanding-json-schema/ ProjectOutputGet: title: ProjectOutputGet required: - key - value - - label type: object properties: key: @@ -159,9 +214,6 @@ components: value: title: Value description: Value assigned to this i/o port - label: - title: Label - type: string ValidationError: title: ValidationError required: diff --git a/api/specs/webserver/openapi.yaml b/api/specs/webserver/openapi.yaml index bbc7ac4902e..b95e718d621 100644 --- a/api/specs/webserver/openapi.yaml +++ b/api/specs/webserver/openapi.yaml @@ -240,6 +240,9 @@ paths: /projects/{project_id}/outputs: $ref: "./openapi-projects-ports.yaml#/paths/~1projects~1{project_id}~1outputs" + /projects/{project_id}/metadata/ports: + $ref: "./openapi-projects-ports.yaml#/paths/~1projects~1{project_id}~1metadata~1ports" + /nodes/{nodeInstanceUUID}/outputUi/{outputKey}: $ref: "./openapi-node-v0.0.1.yaml#/paths/~1nodes~1{nodeInstanceUUID}~1outputUi~1{outputKey}" diff --git a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml index 8fd6ca27e52..03b42108b88 100644 --- a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml +++ b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml @@ -3172,7 +3172,7 @@ paths: title: Updates type: array items: - title: ProjectIOBase + title: ProjectInputUpdate required: - key - value @@ -3226,7 +3226,6 @@ paths: required: - key - value - - label type: object properties: key: @@ -3237,9 +3236,57 @@ paths: value: title: Value description: Value assigned to this i/o port - label: - title: Label + error: + title: Error + '/projects/{project_id}/metadata/ports': + get: + tags: + - project + summary: List Project Metadata Ports + description: New in version *0.11* + operationId: list_project_metadata_ports + parameters: + - required: true + schema: + title: Project Id + type: string + format: uuid + name: project_id + in: path + responses: + '200': + description: Successful Response + content: + application/json: + schema: + title: 'Envelope[list[simcore_service_webserver.projects.projects_ports_handlers.ProjectMetadataPortGet]]' + type: object + properties: + data: + title: Data + type: array + items: + title: ProjectMetadataPortGet + required: + - key + - kind + type: object + properties: + key: + title: Key type: string + description: Project port's unique identifer. Same as the UUID of the associated port node + format: uuid + kind: + title: Kind + enum: + - input + - output + type: string + content_schema: + title: Content Schema + type: object + description: 'jsonschema for the port''s value. SEE https://json-schema.org/understanding-json-schema/' error: title: Error '/nodes/{nodeInstanceUUID}/outputUi/{outputKey}': From 82140e48b326282c67e13742075b438bc7e635ca Mon Sep 17 00:00:00 2001 From: Pedro Crespo <32402063+pcrespov@users.noreply.github.com> Date: Tue, 29 Nov 2022 20:22:42 +0100 Subject: [PATCH 06/10] =?UTF-8?q?services/webserver=20api=20version:=200.1?= =?UTF-8?q?1.0=20=E2=86=92=200.12.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/specs/webserver/openapi.yaml | 2 +- services/web/server/VERSION | 2 +- services/web/server/setup.cfg | 2 +- .../server/src/simcore_service_webserver/api/v0/openapi.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/specs/webserver/openapi.yaml b/api/specs/webserver/openapi.yaml index b95e718d621..1f672e860ed 100644 --- a/api/specs/webserver/openapi.yaml +++ b/api/specs/webserver/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: "osparc-simcore web API" - version: 0.11.0 + version: 0.12.0 description: "API designed for the front-end app" contact: name: IT'IS Foundation diff --git a/services/web/server/VERSION b/services/web/server/VERSION index d9df1bbc0c7..ac454c6a1fc 100644 --- a/services/web/server/VERSION +++ b/services/web/server/VERSION @@ -1 +1 @@ -0.11.0 +0.12.0 diff --git a/services/web/server/setup.cfg b/services/web/server/setup.cfg index d735446b988..88242a31019 100644 --- a/services/web/server/setup.cfg +++ b/services/web/server/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.11.0 +current_version = 0.12.0 commit = True message = services/webserver api version: {current_version} → {new_version} tag = False diff --git a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml index 03b42108b88..26cfe51f831 100644 --- a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml +++ b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: osparc-simcore web API - version: 0.11.0 + version: 0.12.0 description: API designed for the front-end app contact: name: IT'IS Foundation From aa8688de7e60ac0698bd7a8a386a4a1f37f347ea Mon Sep 17 00:00:00 2001 From: Pedro Crespo <32402063+pcrespov@users.noreply.github.com> Date: Tue, 29 Nov 2022 20:34:30 +0100 Subject: [PATCH 07/10] fixes version doc --- api/specs/webserver/openapi-projects-ports.yaml | 2 +- api/specs/webserver/scripts/openapi_projects_ports.py | 2 +- .../server/src/simcore_service_webserver/api/v0/openapi.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/specs/webserver/openapi-projects-ports.yaml b/api/specs/webserver/openapi-projects-ports.yaml index 11fb2d7e909..af208f66c0e 100644 --- a/api/specs/webserver/openapi-projects-ports.yaml +++ b/api/specs/webserver/openapi-projects-ports.yaml @@ -78,7 +78,7 @@ paths: tags: - project summary: List Project Metadata Ports - description: New in version *0.11* + description: New in version *0.12* operationId: list_project_metadata_ports parameters: - required: true diff --git a/api/specs/webserver/scripts/openapi_projects_ports.py b/api/specs/webserver/scripts/openapi_projects_ports.py index 0b9cccb766c..67389d0432f 100644 --- a/api/specs/webserver/scripts/openapi_projects_ports.py +++ b/api/specs/webserver/scripts/openapi_projects_ports.py @@ -69,7 +69,7 @@ async def get_project_outputs(project_id: ProjectID): operation_id="list_project_metadata_ports", ) async def list_project_metadata_ports(project_id: ProjectID): - """New in version *0.11*""" + """New in version *0.12*""" if __name__ == "__main__": diff --git a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml index 26cfe51f831..9dc0df44bdd 100644 --- a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml +++ b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml @@ -3243,7 +3243,7 @@ paths: tags: - project summary: List Project Metadata Ports - description: New in version *0.11* + description: New in version *0.12* operationId: list_project_metadata_ports parameters: - required: true From 21f7fce30a7fe6ac21c4fbd8109d494699197972 Mon Sep 17 00:00:00 2001 From: Pedro Crespo <32402063+pcrespov@users.noreply.github.com> Date: Wed, 30 Nov 2022 11:39:12 +0100 Subject: [PATCH 08/10] fixes test --- .../simcore_service_webserver/projects/_ports.py | 13 +++++++++++-- .../with_dbs/02/test_projects_ports_handlers.py | 9 ++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/_ports.py b/services/web/server/src/simcore_service_webserver/projects/_ports.py index 2f1b977f34d..a4de97926f8 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_ports.py +++ b/services/web/server/src/simcore_service_webserver/projects/_ports.py @@ -27,12 +27,21 @@ def get_schema(self) -> Optional[JsonSchemaDict]: node_meta = catalog.get_metadata(self.node.key, self.node.version) if self.kind == "input" and node_meta.outputs: if input_meta := node_meta.outputs[self.io_key]: - return get_service_io_json_schema(input_meta) + return self._get_port_schema(input_meta) elif self.kind == "output" and node_meta.inputs: if output_meta := node_meta.inputs[self.io_key]: - return get_service_io_json_schema(output_meta) + return self._get_port_schema(output_meta) return None + def _get_port_schema(self, io_meta): + schema = get_service_io_json_schema(io_meta) + if schema: + # uses node label instead of service title + # This way it will contain the label the user + # gave to the port + schema["title"] = self.node.label + return schema + def iter_project_ports( workbench: dict[NodeID, Node], 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 2825f3ff2e9..5e52a7cf3e8 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 @@ -223,6 +223,7 @@ async def test_io_workflow( "key": "38a0d401-af4b-4ea7-ab4c-5005c712a546", "kind": "input", "content_schema": { + "description": "Parameter of type integer", "title": "X", "type": "integer", }, @@ -231,6 +232,7 @@ async def test_io_workflow( "key": "fc48252a-9dbb-4e07-bf9a-7af65a18f612", "kind": "input", "content_schema": { + "description": "Parameter of type integer", "title": "Z", "type": "integer", }, @@ -239,6 +241,7 @@ async def test_io_workflow( "key": "7bf0741f-bae4-410b-b662-fc34b47c27c9", "kind": "input", "content_schema": { + "description": "Parameter of type boolean", "title": "on", "type": "boolean", }, @@ -247,6 +250,8 @@ async def test_io_workflow( "key": "09fd512e-0768-44ca-81fa-0cecab74ec1a", "kind": "output", "content_schema": { + "default": 0, + "description": "Captures integer values attached to it", "title": "Random sleep interval_2", "type": "integer", }, @@ -254,7 +259,9 @@ async def test_io_workflow( { "key": "76f607b4-8761-4f96-824d-cab670bc45f5", "kind": "output", - "content-schema": { + "content_schema": { + "default": 0, + "description": "Captures integer values attached to it", "title": "Random sleep interval", "type": "integer", }, From c4a4139efd9df3b86d35ca5913960b54ef44e5e6 Mon Sep 17 00:00:00 2001 From: Pedro Crespo <32402063+pcrespov@users.noreply.github.com> Date: Wed, 30 Nov 2022 14:28:29 +0100 Subject: [PATCH 09/10] adds i/o schema policies --- .../projects/projects_ports_handlers.py | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/projects/projects_ports_handlers.py b/services/web/server/src/simcore_service_webserver/projects/projects_ports_handlers.py index 95577a5a3dd..539b9c99459 100644 --- a/services/web/server/src/simcore_service_webserver/projects/projects_ports_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/projects_ports_handlers.py @@ -13,7 +13,7 @@ from models_library.users import UserID from models_library.utils.fastapi_encoders import jsonable_encoder from models_library.utils.services_io import JsonSchemaDict -from pydantic import BaseModel, Field, parse_obj_as +from pydantic import BaseModel, Extra, Field, parse_obj_as from servicelib.aiohttp.requests_validation import ( parse_request_body_as, parse_request_path_parameters_as, @@ -85,12 +85,28 @@ async def _get_validated_workbench_model( routes = web.RouteTableDef() -# + +class _InputSchema: + class Config: + allow_population_by_field_name = False + extra = Extra.forbid + allow_mutations = False + + +class _OutputSchema: + class Config: + allow_population_by_field_name = True + extra = Extra.ignore + allow_mutations = False + + # + + # projects/*/inputs COLLECTION ------------------------- # -class ProjectIOBase(BaseModel): +class _ProjectIOBase(BaseModel): key: NodeID = Field( ..., description="Project port's unique identifer. Same as the UUID of the associated port node", @@ -98,16 +114,16 @@ class ProjectIOBase(BaseModel): value: Any = Field(..., description="Value assigned to this i/o port") -class ProjectInputUpdate(ProjectIOBase): - pass +class ProjectInputUpdate(_InputSchema, _ProjectIOBase): + ... -class ProjectInputGet(ProjectIOBase): +class ProjectInputGet(_OutputSchema, _ProjectIOBase): label: str -class ProjectOutputGet(ProjectIOBase): - pass +class ProjectOutputGet(_OutputSchema, _ProjectIOBase): + label: str @routes.get(f"/{VTAG}/projects/{{project_id}}/inputs", name="get_project_inputs") From a7bd450846fc7c770067ac424cc020e40412aa94 Mon Sep 17 00:00:00 2001 From: Pedro Crespo <32402063+pcrespov@users.noreply.github.com> Date: Wed, 30 Nov 2022 14:29:50 +0100 Subject: [PATCH 10/10] fixes OAS --- api/specs/webserver/openapi-projects-ports.yaml | 4 ++++ .../server/src/simcore_service_webserver/api/v0/openapi.yaml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/api/specs/webserver/openapi-projects-ports.yaml b/api/specs/webserver/openapi-projects-ports.yaml index af208f66c0e..af24df97b13 100644 --- a/api/specs/webserver/openapi-projects-ports.yaml +++ b/api/specs/webserver/openapi-projects-ports.yaml @@ -203,6 +203,7 @@ components: required: - key - value + - label type: object properties: key: @@ -214,6 +215,9 @@ components: value: title: Value description: Value assigned to this i/o port + label: + title: Label + type: string ValidationError: title: ValidationError required: diff --git a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml index 9dc0df44bdd..f7c700f8d4d 100644 --- a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml +++ b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml @@ -3226,6 +3226,7 @@ paths: required: - key - value + - label type: object properties: key: @@ -3236,6 +3237,9 @@ paths: value: title: Value description: Value assigned to this i/o port + label: + title: Label + type: string error: title: Error '/projects/{project_id}/metadata/ports':