From d6685eec1f9a7efaf1c9218fd073febe37049f48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20Poyatos?= Date: Thu, 9 Oct 2025 11:14:13 +0200 Subject: [PATCH] feat(api-keys): support include parameter for entity details (#8876) --- api/src/backend/api/specs/v1.yaml | 23 +++++++++++++ api/src/backend/api/tests/test_views.py | 43 +++++++++++++++++++++++++ api/src/backend/api/v1/serializers.py | 21 ++++++++++++ 3 files changed, 87 insertions(+) diff --git a/api/src/backend/api/specs/v1.yaml b/api/src/backend/api/specs/v1.yaml index 3d68533d48..daec67a79e 100644 --- a/api/src/backend/api/specs/v1.yaml +++ b/api/src/backend/api/specs/v1.yaml @@ -86,6 +86,17 @@ paths: description: A search term. schema: type: string + - in: query + name: include + schema: + type: array + items: + type: string + enum: + - entity + description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false - name: page[number] required: false in: query @@ -186,6 +197,17 @@ paths: format: uuid description: A UUID string identifying this tenant api key. required: true + - in: query + name: include + schema: + type: array + items: + type: string + enum: + - entity + description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false tags: - API Keys security: @@ -4706,6 +4728,7 @@ paths: type: array items: type: string + x-spec-enum-id: 4c1e219dad1cc0e7 enum: - aws - azure diff --git a/api/src/backend/api/tests/test_views.py b/api/src/backend/api/tests/test_views.py index 416da2e1c9..cfad06c777 100644 --- a/api/src/backend/api/tests/test_views.py +++ b/api/src/backend/api/tests/test_views.py @@ -8024,6 +8024,49 @@ class TestTenantApiKeyViewSet: assert data["relationships"]["entity"]["data"]["type"] == "users" assert data["relationships"]["entity"]["data"]["id"] == str(api_key.entity.id) + def test_api_keys_retrieve_with_entity_include( + self, authenticated_client, api_keys_fixture + ): + """Test retrieving API key with ?include=entity returns user data without memberships.""" + api_key = api_keys_fixture[0] + response = authenticated_client.get( + reverse("api-key-detail", kwargs={"pk": api_key.id}), + {"include": "entity"}, + ) + assert response.status_code == status.HTTP_200_OK + response_data = response.json() + + # Verify the main data contains the entity relationship + data = response_data["data"] + assert "entity" in data["relationships"] + assert data["relationships"]["entity"]["data"]["type"] == "users" + assert data["relationships"]["entity"]["data"]["id"] == str(api_key.entity.id) + + # Verify included section exists + assert "included" in response_data + assert len(response_data["included"]) == 1 + + # Verify included user data + included_user = response_data["included"][0] + assert included_user["type"] == "users" + assert included_user["id"] == str(api_key.entity.id) + + # Verify UserIncludeSerializer fields are present + user_attrs = included_user["attributes"] + assert "name" in user_attrs + assert "email" in user_attrs + assert "company_name" in user_attrs + assert "date_joined" in user_attrs + assert user_attrs["name"] == api_key.entity.name + assert user_attrs["email"] == api_key.entity.email + + # Verify memberships field is NOT included (excluded by UserIncludeSerializer) + assert "memberships" not in user_attrs + + # Verify roles relationship is present + assert "relationships" in included_user + assert "roles" in included_user["relationships"] + def test_api_keys_entity_auto_assigned_on_create( self, authenticated_client, create_test_user ): diff --git a/api/src/backend/api/v1/serializers.py b/api/src/backend/api/v1/serializers.py index c8e3e02f08..e20fb4fab1 100644 --- a/api/src/backend/api/v1/serializers.py +++ b/api/src/backend/api/v1/serializers.py @@ -317,6 +317,23 @@ class UserSerializer(BaseSerializerV1): ) +class UserIncludeSerializer(UserSerializer): + class Meta: + model = User + fields = [ + "id", + "name", + "email", + "company_name", + "date_joined", + "roles", + ] + + included_serializers = { + "roles": "api.v1.serializers.RoleIncludeSerializer", + } + + class UserCreateSerializer(BaseWriteSerializer): password = serializers.CharField(write_only=True) company_name = serializers.CharField(required=False) @@ -2779,6 +2796,10 @@ class TenantApiKeySerializer(RLSSerializer): "entity", ] + included_serializers = { + "entity": "api.v1.serializers.UserIncludeSerializer", + } + class TenantApiKeyCreateSerializer(RLSSerializer, BaseWriteSerializer): """Serializer for creating new API keys."""