Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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 @@ -15,6 +15,7 @@
FunctionJobCollection,
FunctionJobCollectionID,
FunctionJobCollectionIDNotFoundError,
FunctionJobCollectionsListFilters,
FunctionJobCollectionStatus,
FunctionJobID,
FunctionJobIDNotFoundError,
Expand Down Expand Up @@ -53,6 +54,7 @@
"FunctionJobCollectionID",
"FunctionJobCollectionIDNotFoundError",
"FunctionJobCollectionStatus",
"FunctionJobCollectionsListFilters",
"FunctionJobID",
"FunctionJobIDNotFoundError",
"FunctionJobStatus",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
FunctionJobCollection,
FunctionJobCollectionID,
FunctionJobCollectionIDNotFoundError,
FunctionJobCollectionsListFilters,
FunctionJobCollectionStatus,
FunctionJobID,
FunctionJobIDNotFoundError,
Expand Down Expand Up @@ -70,6 +71,7 @@
"FunctionJobCollectionIDNotFoundError",
"FunctionJobCollectionStatus",
"FunctionJobCollectionStatus",
"FunctionJobCollectionsListFilters",
"FunctionJobID",
"FunctionJobID",
"FunctionJobIDNotFoundError",
Expand Down
11 changes: 11 additions & 0 deletions packages/models-library/src/models_library/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,3 +274,14 @@ class FunctionJobCollectionDB(BaseModel):

class RegisteredFunctionJobCollectionDB(FunctionJobCollectionDB):
uuid: FunctionJobCollectionID


class FunctionJobCollectionsListFilters(BaseModel):
"""Filters for listing function job collections"""

has_function_id: Annotated[
str | None,
Field(
description="Filter by having a function ID in the collection",
),
] = None
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
FunctionJob,
FunctionJobCollection,
FunctionJobCollectionID,
FunctionJobCollectionsListFilters,
FunctionJobID,
FunctionOutputSchema,
RegisteredFunction,
Expand Down Expand Up @@ -146,12 +147,14 @@ async def list_function_job_collections(
*,
pagination_limit: int,
pagination_offset: int,
filters: FunctionJobCollectionsListFilters | None = None,
) -> tuple[list[RegisteredFunctionJobCollection], PageMetaInfoLimitOffset]:
result = await rabbitmq_rpc_client.request(
WEBSERVER_RPC_NAMESPACE,
TypeAdapter(RPCMethodName).validate_python("list_function_job_collections"),
pagination_offset=pagination_offset,
pagination_limit=pagination_limit,
filters=filters,
)
return TypeAdapter(
tuple[list[RegisteredFunctionJobCollection], PageMetaInfoLimitOffset]
Expand Down
18 changes: 18 additions & 0 deletions services/api-server/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -5695,6 +5695,24 @@
"default": 0,
"title": "Offset"
}
},
{
"name": "has_function_id",
"in": "query",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Filter by having a function ID in the collection",
"title": "Has Function Id"
},
"description": "Filter by having a function ID in the collection"
}
],
"responses": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from typing import Annotated, Any

from fastapi import Query
from models_library.functions import FunctionJobCollectionsListFilters
from pydantic.fields import FieldInfo


def _get_query_params(field: FieldInfo) -> dict[str, Any]:
params = {}

if field.description:
params["description"] = field.description
if field.examples:
params["example"] = next(
(example for example in field.examples if "*" in example), field.examples[0]
)
return params


def get_function_job_collections_filters(
# pylint: disable=unsubscriptable-object
has_function_id: Annotated[
str | None,
Query(
**_get_query_params(
FunctionJobCollectionsListFilters.model_fields["has_function_id"]
)
),
] = None,
) -> FunctionJobCollectionsListFilters:
return FunctionJobCollectionsListFilters(
has_function_id=has_function_id,
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from models_library.api_schemas_webserver.functions import (
FunctionJobCollection,
FunctionJobCollectionID,
FunctionJobCollectionsListFilters,
FunctionJobCollectionStatus,
RegisteredFunctionJob,
RegisteredFunctionJobCollection,
Expand All @@ -17,6 +18,9 @@
from ...services_http.director_v2 import DirectorV2Api
from ...services_rpc.wb_api_server import WbApiRpcClient
from ..dependencies.authentication import get_current_user_id
from ..dependencies.models_schemas_function_filters import (
get_function_job_collections_filters,
)
from ..dependencies.services import get_api_client
from ..dependencies.webserver_rpc import get_wb_api_rpc_client
from ._constants import FMSG_CHANGELOG_NEW_IN_VERSION, create_route_description
Expand Down Expand Up @@ -48,10 +52,14 @@
async def list_function_job_collections(
wb_api_rpc: Annotated[WbApiRpcClient, Depends(get_wb_api_rpc_client)],
page_params: Annotated[PaginationParams, Depends()],
filters: Annotated[
FunctionJobCollectionsListFilters, Depends(get_function_job_collections_filters)
],
):
function_job_collection_list, meta = await wb_api_rpc.list_function_job_collections(
pagination_offset=page_params.offset,
pagination_limit=page_params.limit,
filters=filters,
)
return create_page(
function_job_collection_list,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
FunctionJob,
FunctionJobCollection,
FunctionJobCollectionID,
FunctionJobCollectionsListFilters,
FunctionJobID,
FunctionOutputSchema,
RegisteredFunction,
Expand Down Expand Up @@ -324,11 +325,13 @@ async def list_function_job_collections(
*,
pagination_offset: PageOffsetInt = 0,
pagination_limit: PageLimitInt = DEFAULT_NUMBER_OF_ITEMS_PER_PAGE,
filters: FunctionJobCollectionsListFilters | None = None,
) -> tuple[list[RegisteredFunctionJobCollection], PageMetaInfoLimitOffset]:
return await functions_rpc_interface.list_function_job_collections(
self._client,
pagination_offset=pagination_offset,
pagination_limit=pagination_limit,
filters=filters,
)

async def run_function(
Expand Down
42 changes: 34 additions & 8 deletions services/api-server/tests/unit/api_functions/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
FunctionClass,
FunctionIDNotFoundError,
FunctionJob,
FunctionJobCollection,
JSONFunctionInputSchema,
JSONFunctionOutputSchema,
ProjectFunction,
Expand All @@ -25,6 +26,7 @@
RegisteredProjectFunction,
RegisteredProjectFunctionJob,
)
from models_library.functions import RegisteredFunctionJobCollection
from models_library.projects import ProjectID
from pytest_mock import MockerFixture
from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict
Expand Down Expand Up @@ -98,7 +100,7 @@ def raise_function_id_not_found() -> FunctionIDNotFoundError:


@pytest.fixture
def sample_function(
def mock_function(
project_id: ProjectID,
sample_input_schema: JSONFunctionInputSchema,
sample_output_schema: JSONFunctionOutputSchema,
Expand All @@ -116,14 +118,14 @@ def sample_function(


@pytest.fixture
def sample_registered_function(sample_function: Function) -> RegisteredFunction:
return RegisteredProjectFunction(**{**sample_function.dict(), "uid": str(uuid4())})
def mock_registered_function(mock_function: Function) -> RegisteredFunction:
return RegisteredProjectFunction(**{**mock_function.dict(), "uid": str(uuid4())})


@pytest.fixture
def sample_function_job(sample_registered_function: RegisteredFunction) -> FunctionJob:
def mock_function_job(mock_registered_function: RegisteredFunction) -> FunctionJob:
mock_function_job = {
"function_uid": sample_registered_function.uid,
"function_uid": mock_registered_function.uid,
"title": "Test Function Job",
"description": "A test function job",
"inputs": {"key": "value"},
Expand All @@ -135,11 +137,35 @@ def sample_function_job(sample_registered_function: RegisteredFunction) -> Funct


@pytest.fixture
def sample_registered_function_job(
sample_function_job: FunctionJob,
def mock_registered_function_job(
mock_function_job: FunctionJob,
) -> RegisteredFunctionJob:
return RegisteredProjectFunctionJob(
**{**sample_function_job.dict(), "uid": str(uuid4())}
**{**mock_function_job.dict(), "uid": str(uuid4())}
)


@pytest.fixture
def mock_function_job_collection(
mock_registered_function_job: RegisteredFunctionJob,
) -> FunctionJobCollection:
mock_function_job_collection = {
"title": "Test Function Job Collection",
"description": "A test function job collection",
"function_uid": mock_registered_function_job.function_uid,
"function_class": FunctionClass.PROJECT,
"project_id": str(uuid4()),
"function_job_ids": [mock_registered_function_job.uid for _ in range(5)],
}
return FunctionJobCollection(**mock_function_job_collection)


@pytest.fixture
def mock_registered_function_job_collection(
mock_function_job_collection: FunctionJobCollection,
) -> RegisteredFunctionJobCollection:
return RegisteredFunctionJobCollection(
**{**mock_function_job_collection.model_dump(), "uid": str(uuid4())}
)


Expand Down
Loading
Loading