-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
api-server: first impl of rio (#960)
* first impl of rio Signed-off-by: Teo Koon Peng <[email protected]> * fix lint Signed-off-by: Teo Koon Peng <[email protected]> * cleanup Signed-off-by: Teo Koon Peng <[email protected]> * add option to reset app before each test Signed-off-by: Teo Koon Peng <[email protected]> * fix lint Signed-off-by: Teo Koon Peng <[email protected]> --------- Signed-off-by: Teo Koon Peng <[email protected]>
- Loading branch information
Showing
11 changed files
with
184 additions
and
6 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
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,11 @@ | ||
from typing import Any | ||
|
||
from pydantic import BaseModel, ConfigDict | ||
|
||
|
||
class Rio(BaseModel): | ||
model_config = ConfigDict(from_attributes=True) | ||
|
||
id: str | ||
type: str | ||
data: dict[str, Any] |
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,8 @@ | ||
import tortoise | ||
from tortoise.fields import CharField, JSONField | ||
|
||
|
||
class Rio(tortoise.Model): | ||
id = CharField(max_length=255, pk=True) | ||
type = CharField(max_length=255, index=True) | ||
data = JSONField() |
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 |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
alert_events, | ||
beacon_events, | ||
fleet_events, | ||
rio_events, | ||
rmf_events, | ||
task_events, | ||
) | ||
|
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,44 @@ | ||
from typing import Annotated | ||
|
||
from fastapi import Query, Response | ||
|
||
from api_server.fast_io import FastIORouter, SubscriptionRequest | ||
from api_server.models import Rio | ||
from api_server.models.tortoise_models import Rio as DbRio | ||
from api_server.rmf_io import rio_events | ||
|
||
router = FastIORouter(tags=["RIOs"]) | ||
|
||
|
||
@router.get("", response_model=list[Rio]) | ||
async def query_rios( | ||
id_: Annotated[ | ||
str | None, Query(alias="id", description="comma separated list of ids") | ||
] = None, | ||
type_: Annotated[ | ||
str | None, Query(alias="type", description="comma separated list of types") | ||
] = None, | ||
): | ||
filters = {} | ||
if id_: | ||
filters["id__in"] = id_.split(",") | ||
if type_: | ||
filters["type__in"] = type_.split(",") | ||
|
||
rios = await DbRio.filter(**filters) | ||
return [Rio.model_validate(x) for x in rios] | ||
|
||
|
||
@router.sub("", response_model=Rio) | ||
async def sub_rio(_req: SubscriptionRequest): | ||
return rio_events.rios | ||
|
||
|
||
@router.put("", response_model=None) | ||
async def put_rio(rio: Rio, resp: Response): | ||
rio_dict = rio.model_dump() | ||
del rio_dict["id"] | ||
_, created = await DbRio.update_or_create(rio_dict, id=rio.id) | ||
if created: | ||
resp.status_code = 201 | ||
rio_events.rios.on_next(rio) |
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,68 @@ | ||
import pydantic | ||
|
||
from api_server.models import Rio | ||
from api_server.models.tortoise_models import Rio as DbRio | ||
from api_server.rmf_io import rio_events | ||
from api_server.test import AppFixture | ||
|
||
|
||
@AppFixture.reset_app_before_test | ||
class TestRiosRoute(AppFixture): | ||
def test_get_rios(self): | ||
self.portal.call( | ||
DbRio(id="test_rio", type="test_type", data={"battery": 1}).save | ||
) | ||
self.portal.call( | ||
DbRio(id="test_rio2", type="test_type", data={"battery": 0.5}).save | ||
) | ||
self.portal.call( | ||
DbRio(id="test_rio3", type="test_type3", data={"battery": 0}).save | ||
) | ||
|
||
test_cases = [ | ||
("id=test_rio,test_rio2", 2), | ||
("id=test_rio,test_rio4", 1), | ||
("type=test_type,test_type3", 3), | ||
("type=test_type,test_rio", 2), | ||
("id=test_rio,test_rio3&type=test_type3", 1), | ||
] | ||
|
||
for tc in test_cases: | ||
resp = self.client.get(f"/rios?{tc[0]}") | ||
self.assertEqual(200, resp.status_code, tc) | ||
rios = pydantic.TypeAdapter(list[Rio]).validate_json(resp.content) | ||
self.assertEqual(tc[1], len(rios)) | ||
|
||
def test_sub_rios(self): | ||
with self.subscribe_sio("/rios") as sub: | ||
rio_events.rios.on_next( | ||
Rio(id="test_rio", type="test_type", data={"battery": 1}) | ||
) | ||
rio = Rio(**next(sub)) | ||
self.assertEqual("test_rio", rio.id) | ||
|
||
def test_put_rios(self): | ||
resp = self.client.put( | ||
"/rios", | ||
content=Rio( | ||
id="test_rio", type="test_type", data={"battery": 1} | ||
).model_dump_json(), | ||
) | ||
self.assertEqual(201, resp.status_code) | ||
|
||
rios = self.portal.call(DbRio.all) | ||
self.assertEqual(1, len(rios)) | ||
|
||
resp = self.client.put( | ||
"/rios", | ||
content=Rio( | ||
id="test_rio", type="test_type", data={"battery": 0.5} | ||
).model_dump_json(), | ||
) | ||
# should return 200 if an existing resource is updated | ||
self.assertEqual(200, resp.status_code) | ||
rios = self.portal.call(DbRio.all) | ||
self.assertEqual(1, len(rios)) | ||
if not isinstance(rios[0].data, dict): | ||
self.fail("data should be a dict") | ||
self.assertEqual(0.5, rios[0].data["battery"]) |
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