diff --git a/src/routers/resource_routers/case_study_router.py b/src/routers/resource_routers/case_study_router.py index 7b429692..8e70917d 100644 --- a/src/routers/resource_routers/case_study_router.py +++ b/src/routers/resource_routers/case_study_router.py @@ -1,5 +1,13 @@ +from fastapi import Query, Depends, APIRouter + +from authentication import KeycloakUser, get_user_or_none from database.model.case_study.case_study import CaseStudy, case_study_versions +from dependencies.filtering import ResourceFiltersParams +from dependencies.pagination import PaginationParams +from dependencies.sorting import SortingParams +from routers.resource_routers.organisation_router import add_custom_routes from routers.resource_ai_asset_router import ResourceAIAssetRouter +from versioning import Version class CaseStudyRouter(ResourceAIAssetRouter): @@ -19,6 +27,49 @@ def resource_name_plural(self) -> str: def resource_class(self) -> type[CaseStudy]: return CaseStudy + def get_resources_func(self): + def get_resources( + pagination: PaginationParams, + sorting: SortingParams, + resource_filters: ResourceFiltersParams, + schema: self._possible_schemas_type = "aiod", # type:ignore + get_image: bool = Query(False, description="Include image bytes in response?"), + user: KeycloakUser | None = Depends(get_user_or_none), + ): + return self.get_resources( + schema=schema, + pagination=pagination, + sorting=sorting, + resource_filters=resource_filters, + user=user, + get_image=get_image, + ) + + return get_resources + + def get_resource_func(self): + def get_resource( + identifier: str, + schema: self._possible_schemas_type = "aiod", # type: ignore + get_image: bool = Query(False, description="Include image bytes in response?"), + user: KeycloakUser | None = Depends(get_user_or_none), + ): + resource = self.get_resource( + identifier=identifier, schema=schema, user=user, platform=None, get_image=get_image + ) + + return resource + + return get_resource + + def create(self, url_prefix: str, version: Version = Version.LATEST) -> APIRouter: + router = super().create(url_prefix, version) + + path = f"/{self.resource_name_plural}/{{identifier}}/image" + add_custom_routes(self, router, path) + + return router + case_study_routers = { version: CaseStudyRouter(versioned_resource) diff --git a/src/routers/resource_routers/event_router.py b/src/routers/resource_routers/event_router.py index 88d5a30a..409387d6 100644 --- a/src/routers/resource_routers/event_router.py +++ b/src/routers/resource_routers/event_router.py @@ -1,5 +1,13 @@ +from fastapi import Query, Depends, APIRouter + +from authentication import KeycloakUser, get_user_or_none from database.model.event.event import Event, event_versions +from dependencies.filtering import ResourceFiltersParams +from dependencies.pagination import PaginationParams +from dependencies.sorting import SortingParams +from routers.resource_routers.organisation_router import add_custom_routes from routers.resource_router import ResourceRouter +from versioning import Version class EventRouter(ResourceRouter): @@ -19,6 +27,49 @@ def resource_name_plural(self) -> str: def resource_class(self) -> type[Event]: return Event + def get_resources_func(self): + def get_resources( + pagination: PaginationParams, + sorting: SortingParams, + resource_filters: ResourceFiltersParams, + schema: self._possible_schemas_type = "aiod", # type:ignore + get_image: bool = Query(False, description="Include image bytes in response?"), + user: KeycloakUser | None = Depends(get_user_or_none), + ): + return self.get_resources( + schema=schema, + pagination=pagination, + sorting=sorting, + resource_filters=resource_filters, + user=user, + get_image=get_image, + ) + + return get_resources + + def get_resource_func(self): + def get_resource( + identifier: str, + schema: self._possible_schemas_type = "aiod", # type: ignore + get_image: bool = Query(False, description="Include image bytes in response?"), + user: KeycloakUser | None = Depends(get_user_or_none), + ): + resource = self.get_resource( + identifier=identifier, schema=schema, user=user, platform=None, get_image=get_image + ) + + return resource + + return get_resource + + def create(self, url_prefix: str, version: Version = Version.LATEST) -> APIRouter: + router = super().create(url_prefix, version) + + path = f"/{self.resource_name_plural}/{{identifier}}/image" + add_custom_routes(self, router, path) + + return router + event_routers = { version: EventRouter(versioned_resource) diff --git a/src/routers/resource_routers/news_router.py b/src/routers/resource_routers/news_router.py index c535a236..1b19d13d 100644 --- a/src/routers/resource_routers/news_router.py +++ b/src/routers/resource_routers/news_router.py @@ -1,5 +1,13 @@ +from fastapi import Query, Depends, APIRouter + +from authentication import KeycloakUser, get_user_or_none from database.model.news.news import News, news_versions +from dependencies.filtering import ResourceFiltersParams +from dependencies.pagination import PaginationParams +from dependencies.sorting import SortingParams +from routers.resource_routers.organisation_router import add_custom_routes from routers.resource_router import ResourceRouter +from versioning import Version class NewsRouter(ResourceRouter): @@ -19,6 +27,49 @@ def resource_name_plural(self) -> str: def resource_class(self) -> type[News]: return News + def get_resources_func(self): + def get_resources( + pagination: PaginationParams, + sorting: SortingParams, + resource_filters: ResourceFiltersParams, + schema: self._possible_schemas_type = "aiod", # type:ignore + get_image: bool = Query(False, description="Include image bytes in response?"), + user: KeycloakUser | None = Depends(get_user_or_none), + ): + return self.get_resources( + schema=schema, + pagination=pagination, + sorting=sorting, + resource_filters=resource_filters, + user=user, + get_image=get_image, + ) + + return get_resources + + def get_resource_func(self): + def get_resource( + identifier: str, + schema: self._possible_schemas_type = "aiod", # type: ignore + get_image: bool = Query(False, description="Include image bytes in response?"), + user: KeycloakUser | None = Depends(get_user_or_none), + ): + resource = self.get_resource( + identifier=identifier, schema=schema, user=user, platform=None, get_image=get_image + ) + + return resource + + return get_resource + + def create(self, url_prefix: str, version: Version = Version.LATEST) -> APIRouter: + router = super().create(url_prefix, version) + + path = f"/{self.resource_name_plural}/{{identifier}}/image" + add_custom_routes(self, router, path) + + return router + news_routers = { version: NewsRouter(versioned_resource) for version, versioned_resource in news_versions.items() diff --git a/src/tests/routers/resource_routers/test_image_subrouters.py b/src/tests/routers/resource_routers/test_image_subrouters.py index 0b9aa9fc..8636a17b 100644 --- a/src/tests/routers/resource_routers/test_image_subrouters.py +++ b/src/tests/routers/resource_routers/test_image_subrouters.py @@ -10,12 +10,13 @@ @pytest.fixture(params=[ - "organisation", "project", + "organisation", "project", "case_study", "event", "news", ]) def resource_pair(request): resource_name = request.param resource = request.getfixturevalue(request.param) - return resource_name, resource + plural_name = "case_studies" if resource_name == "case_study" else ("news" if resource_name == "news" else f"{resource_name}s") + return plural_name, resource def test_post_image( @@ -30,7 +31,7 @@ def test_post_image( with logged_in_user(): response = client.post( - f"/{name}s/{identifier}/image", + f"/{name}/{identifier}/image", params={"name": "logo"}, files={"file": ("logo.png", fake_image, "image/png")}, headers={"Authorization": "Fake token"}, @@ -50,7 +51,7 @@ def test_post_image_too_large( with logged_in_user(): response = client.post( - f"/{name}s/{identifier}/image", + f"/{name}/{identifier}/image", params={"name": "big_logo"}, files={"file": ("big_logo.png", large_image, "image/png")}, headers={"Authorization": "Fake token"}, @@ -67,7 +68,7 @@ def test_post_image_incorrect_type(client: TestClient, resource_pair): with logged_in_user(): response = client.post( - f"/{name}s/{identifier}/image", + f"/{name}/{identifier}/image", params={"name": "wrong_logo_type"}, files={"file": ("wrong_logo_type.pdf", pdf_data, "application/pdf")}, headers={"Authorization": "Fake token"}, @@ -89,14 +90,14 @@ def test_put_image( with logged_in_user(): response = client.post( - f"/{name}s/{identifier}/image", + f"/{name}/{identifier}/image", params={"name": "logo"}, files={"file": ("logo.png", fake_image, "image/png")}, headers={"Authorization": "Fake token"}, ) response = client.put( - f"/{name}s/{identifier}/image", + f"/{name}/{identifier}/image", params={"name": "logo"}, files={"file": ("logo.png", fake_image, "image/png")}, headers={"Authorization": "Fake token"}, @@ -117,7 +118,7 @@ def test_put_image_non_existent( with logged_in_user(): response = client.put( - f"/{name}s/{identifier}/image", + f"/{name}/{identifier}/image", params={"name": "LOGO"}, files={"file": ("logo.png", fake_image, "image/png")}, headers={"Authorization": "Fake token"}, @@ -136,7 +137,7 @@ def test_get_with_and_without_image(client: TestClient, resource_pair, get_image with logged_in_user(): response = client.post( - f"/{name}s/{identifier}/image", + f"/{name}/{identifier}/image", params={"name": "logo"}, files={"file": ("logo.png", fake_image, "image/png")}, headers={"Authorization": "Fake token"}, @@ -144,7 +145,7 @@ def test_get_with_and_without_image(client: TestClient, resource_pair, get_image assert response.status_code == HTTPStatus.OK, response.json() - response = client.get(f"/{name}s/{identifier}?get_image={str(get_image).lower()}") + response = client.get(f"/{name}/{identifier}?get_image={str(get_image).lower()}") assert response.status_code == HTTPStatus.OK response = response.json() @@ -169,7 +170,7 @@ def test_get_image( with logged_in_user(): response = client.post( - f"/{name}s/{identifier}/image", + f"/{name}/{identifier}/image", params={"name": "logo"}, files={"file": ("logo.png", fake_image, "image/png")}, headers={"Authorization": "Fake token"}, @@ -177,7 +178,7 @@ def test_get_image( response = client.get( - f"/{name}s/{identifier}/image" + f"/{name}/{identifier}/image" ) assert response.status_code == HTTPStatus.OK response = response.json() @@ -193,7 +194,7 @@ def test_get_image_non_existent( name, resource = resource_pair identifier = register_asset(resource) response = client.get( - f"/{name}s/{identifier}/image" + f"/{name}/{identifier}/image" ) assert response.status_code == HTTPStatus.OK assert response.json() == [] @@ -209,21 +210,21 @@ def test_delete_image( with logged_in_user(): response = client.post( - f"/{name}s/{identifier}/image", + f"/{name}/{identifier}/image", params={"name": "logo"}, files={"file": ("logo.png", fake_image, "image/png")}, headers={"Authorization": "Fake token"}, ) response = client.delete( - f"/{name}s/{identifier}/image", + f"/{name}/{identifier}/image", params={"name": "logo"}, headers={"Authorization": "Fake token"}, ) assert response.status_code == HTTPStatus.OK second_delete_response = client.delete( - f"/{name}s/{identifier}/image", + f"/{name}/{identifier}/image", params={"name": "logo"}, headers={"Authorization": "Fake token"}, ) @@ -244,7 +245,7 @@ def test_put_without_media_keeps_media( with logged_in_user(): response = client.post( - f"/{name}s/{identifier}/image", + f"/{name}/{identifier}/image", params={"name": "logo"}, files={"file": ("logo.png", fake_image, "image/png")}, headers={"Authorization": "Fake token"}, @@ -253,13 +254,13 @@ def test_put_without_media_keeps_media( resource.name = "new name" response = client.put( - f"/{name}s/{identifier}", + f"/{name}/{identifier}", json=jsonable_encoder(resource.dict()), headers={"Authorization": "Fake token"}, ) assert response.status_code == HTTPStatus.OK, response.json() response = client.get( - f"/{name}s/{identifier}", + f"/{name}/{identifier}", headers={"Authorization": "Fake token"}, ) assert response.status_code == HTTPStatus.OK, response.json() @@ -280,7 +281,7 @@ def test_put_with_media_keeps_media_if_no_new_binary( with logged_in_user(): response = client.post( - f"/{name}s/{identifier}/image", + f"/{name}/{identifier}/image", params={"name": "logo"}, files={"file": ("logo.png", fake_image, "image/png")}, headers={"Authorization": "Fake token"}, @@ -288,7 +289,7 @@ def test_put_with_media_keeps_media_if_no_new_binary( assert response.status_code == HTTPStatus.OK, response.json() response = client.get( - f"/{name}s/{identifier}?get_image=true", + f"/{name}/{identifier}?get_image=true", headers={"Authorization": "Fake token"}, ) org = response.json() @@ -297,7 +298,7 @@ def test_put_with_media_keeps_media_if_no_new_binary( {"name": "foo", "binary_blob": "bar="}, ) response = client.put( - f"/{name}s/{identifier}", + f"/{name}/{identifier}", json=org, headers={"Authorization": "Fake token"}, ) @@ -305,7 +306,7 @@ def test_put_with_media_keeps_media_if_no_new_binary( org["media"].pop() response = client.put( - f"/{name}s/{identifier}", + f"/{name}/{identifier}", json=org, headers={"Authorization": "Fake token"}, ) diff --git a/src/tests/testutils/default_instances.py b/src/tests/testutils/default_instances.py index 350334a5..731cf1a2 100644 --- a/src/tests/testutils/default_instances.py +++ b/src/tests/testutils/default_instances.py @@ -15,9 +15,12 @@ from database.model.agent.contact import Contact from database.model.agent.organisation import Organisation from database.model.agent.person import Person +from database.model.case_study.case_study import CaseStudy from database.model.dataset.dataset import Dataset +from database.model.event.event import Event from database.model.knowledge_asset.publication import Publication from database.model.models_and_experiments.experiment import Experiment +from database.model.news.news import News from database.model.platform.platform import Platform from database.model.resource_read_and_create import resource_create from database.model.serializers import deserialize_resource_relationships @@ -144,6 +147,21 @@ def experiment(body_asset: dict) -> Experiment: def project(body_asset: dict) -> Project: return _create_class_with_body(Project, body_asset) +@pytest.fixture +def case_study(body_asset: dict) -> CaseStudy: + return _create_class_with_body(CaseStudy, body_asset) + +@pytest.fixture +def event(body_asset: dict) -> Event: + body = copy.deepcopy(body_asset) + body["start_date"] = "2021-02-03T15:15:00" + return _create_class_with_body(Event, body) + +@pytest.fixture +def news(body_asset: dict) -> News: + return _create_class_with_body(News, body_asset) + + @pytest.fixture def platform() -> Platform: