diff --git a/src/safe_apps/models.py b/src/safe_apps/models.py index 3ad211ea..ab3e3d24 100644 --- a/src/safe_apps/models.py +++ b/src/safe_apps/models.py @@ -58,6 +58,7 @@ class AccessControlPolicy(str, Enum): DOMAIN_ALLOWLIST = "DOMAIN_ALLOWLIST" app_id = models.BigAutoField(primary_key=True) + # TODO: rename "visible" to "listed" across the service visible = models.BooleanField( default=True ) # True if this safe-app should be visible from the view. False otherwise diff --git a/src/safe_apps/tests/test_views.py b/src/safe_apps/tests/test_views.py index 084d6d16..f13dec3f 100644 --- a/src/safe_apps/tests/test_views.py +++ b/src/safe_apps/tests/test_views.py @@ -656,10 +656,54 @@ def test_visible_safe_app_is_shown(self) -> None: self.assertEqual(response.status_code, 200) self.assertCountEqual(response.json(), json_response) - def test_not_visible_safe_app_is_not_shown(self) -> None: + def test_not_visible_safe_app_is_shown_if_only_listed_is_not_set(self) -> None: + not_visible_safe_app = SafeAppFactory.create(visible=False) + visible_safe_app = SafeAppFactory.create(visible=True) + json_response = [ + { + "id": not_visible_safe_app.app_id, + "url": not_visible_safe_app.url, + "name": not_visible_safe_app.name, + "iconUrl": f"http://testserver{not_visible_safe_app.icon_url.url}", + "description": not_visible_safe_app.description, + "chainIds": not_visible_safe_app.chain_ids, + "provider": None, + "accessControl": { + "type": "NO_RESTRICTIONS", + }, + "tags": [], + "features": [], + "developerWebsite": not_visible_safe_app.developer_website, + "socialProfiles": [], + }, + { + "id": visible_safe_app.app_id, + "url": visible_safe_app.url, + "name": visible_safe_app.name, + "iconUrl": f"http://testserver{visible_safe_app.icon_url.url}", + "description": visible_safe_app.description, + "chainIds": visible_safe_app.chain_ids, + "provider": None, + "accessControl": { + "type": "NO_RESTRICTIONS", + }, + "tags": [], + "features": [], + "developerWebsite": visible_safe_app.developer_website, + "socialProfiles": [], + }, + ] + url = reverse("v1:safe-apps:list") + + response = self.client.get(path=url, data=None, format="json") + + self.assertEqual(response.status_code, 200) + self.assertCountEqual(response.json(), json_response) + + def test_not_visible_safe_app_is_not_shown_if_only_listed_is_set(self) -> None: SafeAppFactory.create(visible=False) json_response: List[Dict[str, Any]] = [] - url = reverse("v1:safe-apps:list") + url = reverse("v1:safe-apps:list") + f'{"?onlyListed=true"}' response = self.client.get(path=url, data=None, format="json") diff --git a/src/safe_apps/views.py b/src/safe_apps/views.py index 117bc9d5..9db2dd9d 100644 --- a/src/safe_apps/views.py +++ b/src/safe_apps/views.py @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, Union from django.db.models import Q, QuerySet from django.utils.decorators import method_decorator @@ -13,6 +13,10 @@ from .serializers import SafeAppsResponseSerializer +def parse_boolean_query_param(value: Union[bool, str, int]) -> bool: + return value in (True, "True", "true", "1", 1) + + class SafeAppsListView(ListAPIView): # type: ignore[type-arg] serializer_class = SafeAppsResponseSerializer pagination_class = None @@ -36,12 +40,21 @@ class SafeAppsListView(ListAPIView): # type: ignore[type-arg] type=openapi.TYPE_STRING, ) + _swagger_only_listed_param = openapi.Parameter( + "onlyListed", + openapi.IN_QUERY, + description="If true, only listed/visible Safe Apps will be included. Else, all Safe Apps will be included", + type=openapi.TYPE_BOOLEAN, + default=False, + ) + @method_decorator(cache_page(60 * 10, cache="safe-apps")) # Cache 10 minutes @swagger_auto_schema( manual_parameters=[ _swagger_chain_id_param, _swagger_client_url_param, _swagger_url_param, + _swagger_only_listed_param, ] ) # type: ignore[misc] def get(self, request: Request, *args: Any, **kwargs: Any) -> Response: @@ -52,7 +65,13 @@ def get(self, request: Request, *args: Any, **kwargs: Any) -> Response: return super().get(request, *args, **kwargs) def get_queryset(self) -> QuerySet[SafeApp]: - queryset = SafeApp.objects.filter(visible=True) + only_listed = parse_boolean_query_param( + self.request.query_params.get("onlyListed", False) + ) + if only_listed: + queryset = SafeApp.objects.filter(visible=True) + else: + queryset = SafeApp.objects.all() chain_id = self.request.query_params.get("chainId") if chain_id is not None and chain_id.isdigit():