Repository: safe-global/safe-config-service

Issue:
Allow querying `ChainInfo` by `shortName`
@rmeissner add use case


Discussion:
@rmeissner we see two options:

1. as a query parameter: we return empty list if not found
2. as a path variable: we respond with a 404 if not found

Which option do you prefer? 
We can do the query parameter path with the config service, and then expose it as a path variable in the CGW, like we do for safe apps. I think this would be the most flexible solution.

Resolving patch:
diff --git a/src/chains/urls.py b/src/chains/urls.py
--- a/src/chains/urls.py
+++ b/src/chains/urls.py
@@ -1,10 +1,15 @@
 from django.urls import path
 
-from chains.views import ChainsDetailView, ChainsListView
+from chains.views import ChainsDetailView, ChainsDetailViewByShortName, ChainsListView
 
 app_name = "chains"
 
 urlpatterns = [
     path("", ChainsListView.as_view(), name="list"),
-    path("<pk>/", ChainsDetailView.as_view(), name="detail"),
+    path("<int:pk>/", ChainsDetailView.as_view(), name="detail"),
+    path(
+        "<str:short_name>/",
+        ChainsDetailViewByShortName.as_view(),
+        name="detail_by_short_name",
+    ),
 ]
diff --git a/src/chains/views.py b/src/chains/views.py
--- a/src/chains/views.py
+++ b/src/chains/views.py
@@ -1,6 +1,11 @@
+from typing import Any
+
+from drf_yasg.utils import swagger_auto_schema
 from rest_framework import filters
 from rest_framework.generics import ListAPIView, RetrieveAPIView
 from rest_framework.pagination import LimitOffsetPagination
+from rest_framework.request import Request
+from rest_framework.response import Response
 
 from .models import Chain
 from .serializers import ChainSerializer
@@ -23,3 +28,22 @@ class ChainsListView(ListAPIView):
 class ChainsDetailView(RetrieveAPIView):
     serializer_class = ChainSerializer
     queryset = Chain.objects.all()
+
+    @swagger_auto_schema(
+        operation_id="Get chain by id"
+    )  # type: ignore[misc] # Untyped decorator makes function "get" untyped
+    def get(self, request: Request, *args: Any, **kwargs: Any) -> Response:
+        return super().get(request, *args, **kwargs)
+
+
+class ChainsDetailViewByShortName(RetrieveAPIView):
+    lookup_field = "short_name"
+    serializer_class = ChainSerializer
+    queryset = Chain.objects.all()
+
+    @swagger_auto_schema(
+        operation_id="Get chain by shortName",
+        operation_description="Warning: `shortNames` may contain characters that need to be URL encoded (i.e.: whitespaces)",  # noqa E501
+    )  # type: ignore[misc] # Untyped decorator makes function "get" untyped
+    def get(self, request: Request, *args: Any, **kwargs: Any) -> Response:
+        return super().get(request, *args, **kwargs)


Test patch:
diff --git a/src/chains/tests/test_views.py b/src/chains/tests/test_views.py
--- a/src/chains/tests/test_views.py
+++ b/src/chains/tests/test_views.py
@@ -252,6 +252,20 @@ def test_match(self) -> None:
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.json(), json_response)
 
+    def test_by_short_name(self) -> None:
+        ChainFactory.create(id=1, short_name="eth")
+        url_by_id = reverse("v1:chains:detail", args=[1])
+        url_by_short_name = reverse("v1:chains:detail_by_short_name", args=["eth"])
+
+        response_by_id = self.client.get(path=url_by_id, data=None, format="json")
+        response_by_short_name = self.client.get(
+            path=url_by_short_name, data=None, format="json"
+        )
+
+        self.assertEqual(response_by_id.status_code, 200)
+        self.assertEqual(response_by_short_name.status_code, 200)
+        self.assertEqual(response_by_id.json(), response_by_short_name.json())
+
 
 class ChainsListViewRelevanceTests(APITestCase):
     def test_relevance_sorting(self) -> None: