-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Keep 'ci/dependabot-updates' up-to-date with 'master'
- Loading branch information
Showing
12 changed files
with
280 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
"""Function-specific pydantic response models.""" | ||
from uuid import uuid4 | ||
|
||
from pydantic import Field | ||
|
||
from app.models.response import CreateResponse, GetResponse, InitializeResponse | ||
|
||
IDPREFIX = "function" | ||
|
||
|
||
class CreateFunctionResponse(CreateResponse): | ||
"""### Create a function | ||
Router: `POST /function` | ||
""" | ||
|
||
function_id: str = Field( | ||
default_factory=lambda: f"{IDPREFIX}-{uuid4()}", | ||
description="The function id.", | ||
regex=( | ||
rf"^{IDPREFIX}-[0-9a-f]{{8}}-[0-9a-f]{{4}}-[0-9a-f]{{4}}-[0-9a-f]{{4}}-" | ||
r"[0-9a-f]{12}$" | ||
), | ||
) | ||
|
||
|
||
class GetFunctionResponse(GetResponse): | ||
"""### Get a function | ||
Router: `GET /function/{function_id}` | ||
""" | ||
|
||
|
||
class InitializeFunctionResponse(InitializeResponse): | ||
"""### Initialize a function | ||
Router: `POST /function/{function_id}/initialize` | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
"""Function.""" | ||
import json | ||
from typing import TYPE_CHECKING, Optional | ||
|
||
from aioredis import Redis | ||
from fastapi import APIRouter, Depends, status | ||
from fastapi_plugins import depends_redis | ||
from oteapi.models import FunctionConfig | ||
from oteapi.plugins import create_strategy | ||
|
||
from app.models.error import HTTPNotFoundError, httpexception_404_item_id_does_not_exist | ||
from app.models.function import ( | ||
IDPREFIX, | ||
CreateFunctionResponse, | ||
GetFunctionResponse, | ||
InitializeFunctionResponse, | ||
) | ||
from app.routers.session import _update_session, _update_session_list_item | ||
|
||
if TYPE_CHECKING: # pragma: no cover | ||
from typing import Any, Dict | ||
|
||
ROUTER = APIRouter(prefix=f"/{IDPREFIX}") | ||
|
||
|
||
@ROUTER.post( | ||
"/", | ||
response_model=CreateFunctionResponse, | ||
responses={status.HTTP_404_NOT_FOUND: {"model": HTTPNotFoundError}}, | ||
) | ||
async def create_function( | ||
config: FunctionConfig, | ||
session_id: Optional[str] = None, | ||
cache: Redis = Depends(depends_redis), | ||
) -> CreateFunctionResponse: | ||
"""Create a new function configuration.""" | ||
new_function = CreateFunctionResponse() | ||
|
||
await cache.set(new_function.function_id, config.json()) | ||
|
||
if session_id: | ||
if not await cache.exists(session_id): | ||
raise httpexception_404_item_id_does_not_exist(session_id, "session_id") | ||
await _update_session_list_item( | ||
session_id=session_id, | ||
list_key="function_info", | ||
list_items=[new_function.function_id], | ||
redis=cache, | ||
) | ||
|
||
return new_function | ||
|
||
|
||
@ROUTER.get( | ||
"/{function_id}", | ||
response_model=GetFunctionResponse, | ||
responses={ | ||
status.HTTP_404_NOT_FOUND: {"model": HTTPNotFoundError}, | ||
}, | ||
) | ||
async def get_function( | ||
function_id: str, | ||
session_id: Optional[str] = None, | ||
cache: Redis = Depends(depends_redis), | ||
) -> GetFunctionResponse: | ||
"""Get (execute) function.""" | ||
if not await cache.exists(function_id): | ||
raise httpexception_404_item_id_does_not_exist(function_id, "function_id") | ||
if session_id and not await cache.exists(session_id): | ||
raise httpexception_404_item_id_does_not_exist(session_id, "session_id") | ||
|
||
config = FunctionConfig(**json.loads(await cache.get(function_id))) | ||
|
||
function_strategy = create_strategy("function", config) | ||
session_data: "Optional[Dict[str, Any]]" = ( | ||
None if not session_id else json.loads(await cache.get(session_id)) | ||
) | ||
session_update = function_strategy.get(session=session_data) | ||
|
||
if session_update and session_id: | ||
await _update_session( | ||
session_id=session_id, updated_session=session_update, redis=cache | ||
) | ||
|
||
return GetFunctionResponse(**session_update) | ||
|
||
|
||
@ROUTER.post( | ||
"/{function_id}/initialize", | ||
response_model=InitializeFunctionResponse, | ||
responses={ | ||
status.HTTP_404_NOT_FOUND: {"model": HTTPNotFoundError}, | ||
}, | ||
) | ||
async def initialize_function( | ||
function_id: str, | ||
session_id: Optional[str] = None, | ||
cache: Redis = Depends(depends_redis), | ||
) -> InitializeFunctionResponse: | ||
"""Initialize and update function.""" | ||
if not await cache.exists(function_id): | ||
raise httpexception_404_item_id_does_not_exist(function_id, "function_id") | ||
if session_id and not await cache.exists(session_id): | ||
raise httpexception_404_item_id_does_not_exist(session_id, "session_id") | ||
|
||
config = FunctionConfig(**json.loads(await cache.get(function_id))) | ||
|
||
function_strategy = create_strategy("function", config) | ||
session_data: "Optional[Dict[str, Any]]" = ( | ||
None if not session_id else json.loads(await cache.get(session_id)) | ||
) | ||
session_update = function_strategy.initialize(session=session_data) | ||
|
||
if session_update and session_id: | ||
await _update_session( | ||
session_id=session_id, | ||
updated_session=session_update, | ||
redis=cache, | ||
) | ||
|
||
return InitializeFunctionResponse(**session_update) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
"""Test function.""" | ||
from typing import TYPE_CHECKING | ||
|
||
if TYPE_CHECKING: | ||
from fastapi.testclient import TestClient | ||
|
||
|
||
def test_create_function(client: "TestClient") -> None: | ||
"""Test creating a function.""" | ||
response = client.post( | ||
"/function/", | ||
json={ | ||
"functionType": "function/demo", | ||
"configuration": {}, | ||
}, | ||
) | ||
assert "function_id" in response.json() | ||
assert response.status_code == 200 | ||
|
||
|
||
def test_get_function(client: "TestClient") -> None: | ||
"""Test getting a function.""" | ||
response = client.get("/function/function-a647012a-7ab9-4f2c-9c13-2564aa6d95a1") | ||
assert response.status_code == 200 | ||
|
||
|
||
def test_initialize_function(client: "TestClient") -> None: | ||
"""Test initializing a function.""" | ||
response = client.post( | ||
"/function/function-a647012a-7ab9-4f2c-9c13-2564aa6d95a1/initialize", json={} | ||
) | ||
assert response.status_code == 200 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
"""Demo function strategy class.""" | ||
# pylint: disable=no-self-use,unused-argument | ||
from typing import TYPE_CHECKING | ||
|
||
from oteapi.models import FunctionConfig, SessionUpdate | ||
from pydantic.dataclasses import dataclass | ||
|
||
if TYPE_CHECKING: | ||
from typing import Any, Dict, Optional | ||
|
||
|
||
@dataclass | ||
class DemoFunctionStrategy: | ||
"""Function Strategy. | ||
**Registers strategies**: | ||
- `("functionType", "function/DEMO")` | ||
""" | ||
|
||
function_config: FunctionConfig | ||
|
||
def initialize(self, session: "Optional[Dict[str, Any]]" = None) -> SessionUpdate: | ||
"""Initialize strategy. | ||
This method will be called through the `/initialize` endpoint of the OTEAPI | ||
Services. | ||
Parameters: | ||
session: A session-specific dictionary context. | ||
Returns: | ||
An update model of key/value-pairs to be stored in the | ||
session-specific context from services. | ||
""" | ||
return SessionUpdate() | ||
|
||
def get(self, session: "Optional[Dict[str, Any]]" = None) -> SessionUpdate: | ||
"""Execute the strategy. | ||
This method will be called through the strategy-specific endpoint of the | ||
OTEAPI Services. | ||
Parameters: | ||
session: A session-specific dictionary context. | ||
Returns: | ||
An update model of key/value-pairs to be stored in the | ||
session-specific context from services. | ||
""" | ||
return SessionUpdate() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.