Repository: safe-global/safe-config-service

Issue:
Include provider info in the serialized response of `GET /safe-apps/`
The `/safe-apps` endpoint should include data about the provider if any


Discussion:

Resolving patch:
diff --git a/src/safe_apps/serializers.py b/src/safe_apps/serializers.py
--- a/src/safe_apps/serializers.py
+++ b/src/safe_apps/serializers.py
@@ -1,9 +1,17 @@
 from rest_framework import serializers
 
-from .models import SafeApp
+from .models import SafeApp, Provider
+
+
+class ProviderSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = Provider
+        fields = ['url', 'name']
 
 
 class SafeAppsResponseSerializer(serializers.ModelSerializer):
+    provider = ProviderSerializer()
+
     class Meta:
         model = SafeApp
-        fields = ['url', 'name', 'icon_url', 'description', 'networks']
+        fields = ['url', 'name', 'icon_url', 'description', 'networks', 'provider']


Test patch:
diff --git a/src/safe_apps/tests/test_views.py b/src/safe_apps/tests/test_views.py
--- a/src/safe_apps/tests/test_views.py
+++ b/src/safe_apps/tests/test_views.py
@@ -3,7 +3,7 @@
 from django.urls import reverse
 from rest_framework.test import APITestCase
 
-from ..models import SafeApp
+from ..models import SafeApp, Provider
 
 
 class EmptySafeAppsListViewTests(APITestCase):
@@ -33,7 +33,8 @@ def test_safe_app_returned(self):
             'name': 'test_safe_app',
             'icon_url': 'https://example.com/icon',
             'description': 'safe_app_description',
-            'networks': [1]
+            'networks': [1],
+            'provider': None
         }]
         url = reverse('v1:safe-apps')
 
@@ -51,7 +52,8 @@ def setUp(self) -> None:
             name="test_safe_app_1",
             icon_url="https://example.com/icon",
             description="safe_app_description_1",
-            networks=[1]
+            networks=[1],
+            provider=None
         )
 
         SafeApp.objects.create(
@@ -59,7 +61,8 @@ def setUp(self) -> None:
             name="test_safe_app_2",
             icon_url="https://example.com/icon",
             description="safe_app_description_2",
-            networks=[1]
+            networks=[1],
+            provider=None
         )
 
         SafeApp.objects.create(
@@ -67,7 +70,8 @@ def setUp(self) -> None:
             name="test_safe_app_3",
             icon_url="https://example.com/icon",
             description="safe_app_description_3",
-            networks=[2]
+            networks=[2],
+            provider=None
         )
 
     def test_all_safes_returned(self):
@@ -77,21 +81,24 @@ def test_all_safes_returned(self):
                 'name': 'test_safe_app_1',
                 'icon_url': 'https://example.com/icon',
                 'description': 'safe_app_description_1',
-                'networks': [1]
+                'networks': [1],
+                'provider': None
             },
             {
                 'url': 'https://example.com/2',
                 'name': 'test_safe_app_2',
                 'icon_url': 'https://example.com/icon',
                 'description': 'safe_app_description_2',
-                'networks': [1]
+                'networks': [1],
+                'provider': None
             },
             {
                 'url': 'https://example.com/3',
                 'name': 'test_safe_app_3',
                 'icon_url': 'https://example.com/icon',
                 'description': 'safe_app_description_3',
-                'networks': [2]
+                'networks': [2],
+                'provider': None
             },
         ]
         url = reverse('v1:safe-apps')
@@ -108,21 +115,24 @@ def test_all_apps_returned_on_empty_network_value(self):
                 'name': 'test_safe_app_1',
                 'icon_url': 'https://example.com/icon',
                 'description': 'safe_app_description_1',
-                'networks': [1]
+                'networks': [1],
+                'provider': None
             },
             {
                 'url': 'https://example.com/2',
                 'name': 'test_safe_app_2',
                 'icon_url': 'https://example.com/icon',
                 'description': 'safe_app_description_2',
-                'networks': [1]
+                'networks': [1],
+                'provider': None
             },
             {
                 'url': 'https://example.com/3',
                 'name': 'test_safe_app_3',
                 'icon_url': 'https://example.com/icon',
                 'description': 'safe_app_description_3',
-                'networks': [2]
+                'networks': [2],
+                'provider': None
             },
         ]
         url = reverse('v1:safe-apps') + f'{"?network_id="}'
@@ -139,14 +149,16 @@ def test_apps_returned_on_filtered_network(self):
                 'name': 'test_safe_app_1',
                 'icon_url': 'https://example.com/icon',
                 'description': 'safe_app_description_1',
-                'networks': [1]
+                'networks': [1],
+                'provider': None
             },
             {
                 'url': 'https://example.com/2',
                 'name': 'test_safe_app_2',
                 'icon_url': 'https://example.com/icon',
                 'description': 'safe_app_description_2',
-                'networks': [1]
+                'networks': [1],
+                'provider': None
             }
         ]
         url = reverse('v1:safe-apps') + f'{"?network_id=1"}'
@@ -172,14 +184,16 @@ def test_apps_returned_on_same_key_pair(self):
                 'name': 'test_safe_app_1',
                 'icon_url': 'https://example.com/icon',
                 'description': 'safe_app_description_1',
-                'networks': [1]
+                'networks': [1],
+                'provider': None
             },
             {
                 'url': 'https://example.com/2',
                 'name': 'test_safe_app_2',
                 'icon_url': 'https://example.com/icon',
                 'description': 'safe_app_description_2',
-                'networks': [1]
+                'networks': [1],
+                'provider': None
             }
         ]
         url = reverse('v1:safe-apps') + f'{"?network_id=2&network_id=1"}'
@@ -188,3 +202,67 @@ def test_apps_returned_on_same_key_pair(self):
 
         self.assertEqual(response.status_code, 200)
         self.assertEqual(json.loads(response.content), json_response)
+
+
+class ProviderInfoTests(APITestCase):
+    def setUp(self) -> None:
+        provider = Provider.objects.create(
+            url="https://provider1.com",
+            name="Provider 1"
+        )
+
+        SafeApp.objects.create(
+            url="https://example.com/1",
+            name="test_safe_app_1",
+            icon_url="https://example.com/icon",
+            description="safe_app_description_1",
+            networks=[1],
+            provider=provider
+        )
+
+        SafeApp.objects.create(
+            url="https://example.com/2",
+            name="test_safe_app_2",
+            icon_url="https://example.com/icon",
+            description="safe_app_description_2",
+            networks=[2]
+        )
+
+    def test_provider_returned_in_response(self):
+        json_response = [
+            {
+                'url': 'https://example.com/1',
+                'name': 'test_safe_app_1',
+                'icon_url': 'https://example.com/icon',
+                'description': 'safe_app_description_1',
+                'networks': [1],
+                'provider': {
+                    'name': 'Provider 1',
+                    'url': 'https://provider1.com'
+                }
+            }
+        ]
+        url = reverse('v1:safe-apps') + f'{"?network_id=2&network_id=1"}'
+
+        response = self.client.get(path=url, data=None, format='json')
+
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(json.loads(response.content), json_response)
+
+    def test_provider_not_returned_in_response(self):
+        json_response = [
+            {
+                'url': 'https://example.com/2',
+                'name': 'test_safe_app_2',
+                'icon_url': 'https://example.com/icon',
+                'description': 'safe_app_description_2',
+                'networks': [2],
+                'provider': None,
+            }
+        ]
+        url = reverse('v1:safe-apps') + f'{"?network_id=2&network_id=2"}'
+
+        response = self.client.get(path=url, data=None, format='json')
+
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(json.loads(response.content), json_response)