Skip to content

✨ api-server: listing of all solver jobs #7566

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
a828f71
✨ [Models] Update project and node models with improved field annotat…
pcrespov Apr 24, 2025
5a967e1
fixes tests
pcrespov Apr 24, 2025
901b9dc
fixes tests
pcrespov Apr 24, 2025
0b536a9
mypy
pcrespov Apr 24, 2025
090e64e
✨ Add endpoint to list all jobs for released solvers (paginated)
pcrespov Apr 23, 2025
f392f5f
✨ Add method to list all solver jobs with pagination in SolverService
pcrespov Apr 23, 2025
5551ea3
rename
pcrespov Apr 23, 2025
82ddf4b
✨ Refactor imports in solver job modules and add new model converters
pcrespov Apr 23, 2025
2b0358d
draft
pcrespov Apr 23, 2025
7b6b759
✨ Update get_solver function signature to improve clarity
pcrespov Apr 23, 2025
84ff3db
drafting tests
pcrespov Apr 23, 2025
ca46e7f
✨ Rename mocked_rpc_catalog_service_api to mocked_catalog_rpc_api for…
pcrespov Apr 23, 2025
2782d39
Extends fake
pcrespov Apr 23, 2025
44f558b
drafting tests
pcrespov Apr 23, 2025
2bd5461
✨ Refactor project-related models and RPCs for consistency and clarity
pcrespov Apr 23, 2025
f4e7731
reverted location
pcrespov Apr 24, 2025
281eeec
✨ Rename ProjectJobRpcGet to ProjectAsJobRpcGet for consistency acros…
pcrespov Apr 24, 2025
e609355
✨ Update ProjectJobRpcGet and ProjectJobDBGet models to include workb…
pcrespov Apr 24, 2025
c577b21
✨ Fix timestamp field name from 'creation_at' to 'created_at' in Proj…
pcrespov Apr 24, 2025
4fdff6e
✨ Add string trimming validator
pcrespov Apr 24, 2025
b3fffd9
✨ Enhance job listing functionality by adding solver filters and upda…
pcrespov Apr 24, 2025
0cc3872
doc
pcrespov Apr 25, 2025
ba4e1eb
✨ Rename job_parent_resource_name_filter to job_parent_resource_name_…
pcrespov Apr 25, 2025
ca09f43
✨ Refactor job parent resource name handling in list_jobs method for …
pcrespov Apr 25, 2025
f50c33f
✨ Refactor service dependency injection to use get_solver_service for…
pcrespov Apr 25, 2025
c22afb9
✨ Refactor test fixtures for improved clarity and consistency in mock…
pcrespov Apr 25, 2025
6e61354
✨ Refactor dependency injection in SolverService and add mocked_app_d…
pcrespov Apr 25, 2025
af8ebf8
✨ Refactor mocked_app_dependencies fixture and replace mocked_webserv…
pcrespov Apr 28, 2025
f2555f3
✨ Add trim_string_before pre-validator and corresponding tests for im…
pcrespov Apr 28, 2025
6cf1f8a
cleanup tests
pcrespov Apr 28, 2025
1c62cdb
drafting tests
pcrespov Apr 28, 2025
1f88d50
adds first tests
pcrespov Apr 28, 2025
f2e646a
drafting doc
pcrespov Apr 28, 2025
cfb0fc4
updates OAS
pcrespov Apr 28, 2025
dddec2d
updates tests
pcrespov Apr 28, 2025
0886a56
tests
pcrespov Apr 28, 2025
accb75c
@bisgaard-itis review: solver arguments
pcrespov Apr 29, 2025
79c08cb
@bisgaard-itis review: dev feature
pcrespov Apr 29, 2025
b31b233
@bisgaard-itis review: tuples
pcrespov Apr 29, 2025
3b9300d
@sanderegg review: back to 2024 :-)
pcrespov Apr 29, 2025
233c53e
@sanderegg review: doc
pcrespov Apr 29, 2025
069615f
Merge branch 'master' into is7399/list-projects-rpc-api-server
pcrespov Apr 29, 2025
ab80092
@matusdrobuliak66 review: pricate workspace
pcrespov Apr 29, 2025
47fae42
@matusdrobuliak66 review: pricate workspace
pcrespov Apr 29, 2025
618a2c7
Merge branch 'master' into is7399/list-projects-rpc-api-server
pcrespov Apr 29, 2025
45b8767
@bisgaard-itis review: url jobs moved
pcrespov Apr 29, 2025
e8b8996
@GitHK review: exception
pcrespov Apr 29, 2025
ebe4b87
@sanderegg review:doc
pcrespov Apr 29, 2025
55fd2a0
Merge branch 'master' into is7399/list-projects-rpc-api-server
pcrespov Apr 29, 2025
bd95422
fixes dependencies and release notes
pcrespov Apr 29, 2025
42a344e
reverted OAS
pcrespov Apr 29, 2025
14be4d5
Merge branch 'master' into is7399/list-projects-rpc-api-server
mergify[bot] Apr 29, 2025
1b9f75e
Merge branch 'master' into is7399/list-projects-rpc-api-server
pcrespov Apr 29, 2025
35e758f
Merge branch 'master' into is7399/list-projects-rpc-api-server
pcrespov Apr 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class ProjectJobRpcGet(BaseModel):
workbench: NodesDict

# timestamps
creation_at: datetime
created_at: datetime
modified_at: datetime

# Specific to jobs
Expand All @@ -43,12 +43,30 @@ def _update_json_schema_extra(schema: JsonDict) -> None:
"examples": [
{
"uuid": "12345678-1234-5678-1234-123456789012",
"name": "My project",
"description": "My project description",
"name": "A solver job",
"description": "A description of a solver job with a single node",
"workbench": {f"{uuid4()}": n for n in nodes_examples[2:3]},
"creation_at": "2023-01-01T00:00:00Z",
"created_at": "2023-01-01T00:00:00Z",
"modified_at": "2023-01-01T00:00:00Z",
"job_parent_resource_name": "solvers/foo/release/1.2.3",
"job_parent_resource_name": "solvers/simcore%2Fservices%2Fcomp%2Fitis%2Fsleeper/releases/2.0.2",
},
{
"uuid": "00000000-1234-5678-1234-123456789012",
"name": "A study job",
"description": "A description of a study job with many node",
"workbench": {f"{uuid4()}": n for n in nodes_examples},
"created_at": "2023-02-01T00:00:00Z",
"modified_at": "2023-02-01T00:00:00Z",
"job_parent_resource_name": "studies/96642f2a-a72c-11ef-8776-02420a00087d",
},
{
"uuid": "00000000-0000-5678-1234-123456789012",
"name": "A program job",
"description": "A program of a solver job with a single node",
"workbench": {f"{uuid4()}": n for n in nodes_examples[2:3]},
"created_at": "2023-03-01T00:00:00Z",
"modified_at": "2023-03-01T00:00:00Z",
"job_parent_resource_name": "program/simcore%2Fservices%2Fdynamic%2Fjupyter/releases/5.0.2",
},
]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
""" Reusable validators
"""Reusable validators

Example:

Expand All @@ -22,10 +22,19 @@ class MyModel(BaseModel):

from common_library.json_serialization import json_loads
from orjson import JSONDecodeError
from pydantic import BaseModel
from pydantic import BaseModel, BeforeValidator
from pydantic.alias_generators import to_camel


def trim_string_before(max_length: int) -> BeforeValidator:
def _trim(value: str):
if isinstance(value, str):
return value[:max_length]
return value

return BeforeValidator(_trim)


def empty_str_to_none_pre_validator(value: Any):
if isinstance(value, str) and value.strip() == "":
return None
Expand Down
68 changes: 67 additions & 1 deletion packages/models-library/tests/test_utils_common_validators.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
from enum import Enum
from typing import Annotated

import pytest
from models_library.utils.common_validators import (
create_enums_pre_validator,
empty_str_to_none_pre_validator,
none_to_empty_str_pre_validator,
null_or_none_str_to_none_validator,
trim_string_before,
)
from pydantic import BaseModel, ValidationError, field_validator
from pydantic import BaseModel, StringConstraints, ValidationError, field_validator


def test_enums_pre_validator():
Expand Down Expand Up @@ -89,3 +91,67 @@ class Model(BaseModel):

model = Model.model_validate({"message": ""})
assert model == Model.model_validate({"message": ""})


def test_trim_string_before():
max_length = 10

class ModelWithTrim(BaseModel):
text: Annotated[str, trim_string_before(max_length=max_length)]

# Test with string shorter than max_length
short_text = "Short"
model = ModelWithTrim(text=short_text)
assert model.text == short_text

# Test with string equal to max_length
exact_text = "1234567890" # 10 characters
model = ModelWithTrim(text=exact_text)
assert model.text == exact_text

# Test with string longer than max_length
long_text = "This is a very long text that should be trimmed"
model = ModelWithTrim(text=long_text)
assert model.text == long_text[:max_length]
assert len(model.text) == max_length

# Test with non-string value (should be left unchanged)
class ModelWithTrimOptional(BaseModel):
text: Annotated[str | None, trim_string_before(max_length=max_length)]

model = ModelWithTrimOptional(text=None)
assert model.text is None


def test_trim_string_before_with_string_constraints():
max_length = 10

class ModelWithTrimAndConstraints(BaseModel):
text: Annotated[
str | None,
StringConstraints(
max_length=max_length
), # NOTE: order does not matter for validation but has an effect in the openapi schema
trim_string_before(max_length=max_length),
]

# Check that the OpenAPI schema contains the string constraint
schema = ModelWithTrimAndConstraints.model_json_schema()
assert schema["properties"]["text"] == {
"anyOf": [{"maxLength": max_length, "type": "string"}, {"type": "null"}],
"title": "Text",
}

# Test with string longer than max_length
# This should pass because trim_string_before runs first and trims the input
# before StringConstraints validation happens
long_text = "This is a very long text that should be trimmed"
model = ModelWithTrimAndConstraints(text=long_text)
assert model.text is not None
assert model.text == long_text[:max_length]
assert len(model.text) == max_length

# Test with string exactly at max_length
exact_text = "1234567890" # 10 characters
model = ModelWithTrimAndConstraints(text=exact_text)
assert model.text == exact_text
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
from models_library.products import ProductName
from models_library.projects import ProjectID
from models_library.rest_pagination import PageOffsetInt
from models_library.rpc.webserver.projects import PageRpcProjectJobRpcGet
from models_library.rpc.webserver.projects import (
PageRpcProjectJobRpcGet,
ProjectJobRpcGet,
)
from models_library.rpc_pagination import (
DEFAULT_NUMBER_OF_ITEMS_PER_PAGE,
PageLimitInt,
Expand Down Expand Up @@ -54,19 +57,28 @@ async def list_projects_marked_as_jobs(
offset: PageOffsetInt = 0,
limit: PageLimitInt = DEFAULT_NUMBER_OF_ITEMS_PER_PAGE,
# filters
job_parent_resource_name_filter: str | None = None,
job_parent_resource_name_prefix: str | None = None,
) -> PageRpcProjectJobRpcGet:
assert rpc_client
assert product_name
assert user_id

if job_parent_resource_name_filter:
assert not job_parent_resource_name_filter.startswith("/")
if job_parent_resource_name_prefix:
assert not job_parent_resource_name_prefix.startswith("/")
assert not job_parent_resource_name_prefix.endswith("%")
assert not job_parent_resource_name_prefix.startswith("%")

items = PageRpcProjectJobRpcGet.model_json_schema()["examples"]
items = [
item
for item in ProjectJobRpcGet.model_json_schema()["examples"]
if job_parent_resource_name_prefix is None
or item.get("job_parent_resource_name").startswith(
job_parent_resource_name_prefix
)
]

return PageRpcProjectJobRpcGet.create(
items[offset, : offset + limit],
items[offset : offset + limit],
total=len(items),
limit=limit,
offset=offset,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ async def list_projects_marked_as_jobs(
offset: PageOffsetInt = 0,
limit: PageLimitInt = DEFAULT_NUMBER_OF_ITEMS_PER_PAGE,
# filters
job_parent_resource_name_filter: str | None = None,
job_parent_resource_name_prefix: str | None = None,
) -> PageRpcProjectJobRpcGet:
result = await rpc_client.request(
WEBSERVER_RPC_NAMESPACE,
Expand All @@ -61,7 +61,7 @@ async def list_projects_marked_as_jobs(
user_id=user_id,
offset=offset,
limit=limit,
job_parent_resource_name_filter=job_parent_resource_name_filter,
job_parent_resource_name_prefix=job_parent_resource_name_prefix,
)
assert TypeAdapter(PageRpcProjectJobRpcGet).validate_python(result) # nosec
return cast(PageRpcProjectJobRpcGet, result)
745 changes: 745 additions & 0 deletions services/api-server/docs/api-server.drawio.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading