From eaf2d57e387313351f736d300ed2be6c1d171117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Us=C3=A1n?= Date: Mon, 6 Mar 2023 14:15:23 +0100 Subject: [PATCH 1/9] added MAXIMUM_EXPANSION_DEPTH and RECURSIVE_EXPANSION_PERMITTED --- rest_flex_fields/__init__.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/rest_flex_fields/__init__.py b/rest_flex_fields/__init__.py index c24a953..108f477 100644 --- a/rest_flex_fields/__init__.py +++ b/rest_flex_fields/__init__.py @@ -5,6 +5,8 @@ EXPAND_PARAM = FLEX_FIELDS_OPTIONS.get("EXPAND_PARAM", "expand") FIELDS_PARAM = FLEX_FIELDS_OPTIONS.get("FIELDS_PARAM", "fields") OMIT_PARAM = FLEX_FIELDS_OPTIONS.get("OMIT_PARAM", "omit") +MAXIMUM_EXPANSION_DEPTH = FLEX_FIELDS_OPTIONS.get("MAXIMUM_EXPANSION_DEPTH", None) +RECURSIVE_EXPANSION_PERMITTED = FLEX_FIELDS_OPTIONS.get("RECURSIVE_EXPANSION_PERMITTED", True) WILDCARD_ALL = "~all" WILDCARD_ASTERISK = "*" @@ -20,9 +22,12 @@ assert isinstance(FIELDS_PARAM, str), "'FIELDS_PARAM' should be a string" assert isinstance(OMIT_PARAM, str), "'OMIT_PARAM' should be a string" -if type(WILDCARD_VALUES) not in (list, None): +if type(WILDCARD_VALUES) not in (list, type(None)): raise ValueError("'WILDCARD_EXPAND_VALUES' should be a list of strings or None") - +if type(MAXIMUM_EXPANSION_DEPTH) not in (int, type(None)): + raise ValueError("'MAXIMUM_EXPANSION_DEPTH' should be a int or None") +if type(RECURSIVE_EXPANSION_PERMITTED) is not bool: + raise ValueError("'RECURSIVE_EXPANSION_PERMITTED' should be a bool") from .utils import * from .serializers import FlexFieldsModelSerializer From f45b4a6b1b7e53006a9cd766dfb5324d118aeb7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Us=C3=A1n?= Date: Mon, 6 Mar 2023 14:16:11 +0100 Subject: [PATCH 2/9] Contributed _validate_recursive_expansion and _validate_expansion_depth, which check if conditions are met --- rest_flex_fields/serializers.py | 71 +++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/rest_flex_fields/serializers.py b/rest_flex_fields/serializers.py index bce1dcb..8e15818 100644 --- a/rest_flex_fields/serializers.py +++ b/rest_flex_fields/serializers.py @@ -2,6 +2,7 @@ import importlib from typing import List, Optional, Tuple +from django.conf import settings from rest_framework import serializers from rest_flex_fields import ( @@ -22,6 +23,8 @@ class FlexFieldsSerializerMixin(object): """ expandable_fields = {} + _maximum_expansion_depth: Optional[int] = None + _recursive_expansion_permitted: Optional[bool] = None def __init__(self, *args, **kwargs): expand = list(kwargs.pop(EXPAND_PARAM, [])) @@ -58,6 +61,24 @@ def __init__(self, *args, **kwargs): + self._flex_options_rep_only["omit"], } + @property + def maximum_expansion_depth(self) -> Optional[int]: + """ + Defined at serializer level or based on MAXIMUM_EXPANSION_DEPTH setting + """ + return self._maximum_expansion_depth \ + or settings.REST_FLEX_FIELDS.get("MAXIMUM_EXPANSION_DEPTH", None) + + @property + def recursive_expansion_permitted(self) -> bool: + """ + Defined at serializer level or based on RECURSIVE_EXPANSION_PERMITTED setting + """ + if self._recursive_expansion_permitted is not None: + return self._recursive_expansion_permitted + else: + return settings.REST_FLEX_FIELDS.get("RECURSIVE_EXPANSION_PERMITTED", True) + def to_representation(self, instance): if not self._flex_fields_rep_applied: self.apply_flex_fields(self.fields, self._flex_options_rep_only) @@ -269,6 +290,52 @@ def _get_query_param_value(self, field: str) -> List[str]: return values or [] + def _split_expand_field(self, expand_path: str) -> List[str]: + return expand_path.split('.') + + def _recursive_expansion_found(self): + """ + A customized exception can be raised when recursive expansion is found, default ValidationError + """ + raise serializers.ValidationError(detail="Recursive expansion found") + + def _validate_recursive_expansion(self, expand_path: str) -> None: + """ + Given an expand_path, a dotted-separated string, + an Exception is raised when a recursive + expansion is detected. + Only applies when REST_FLEX_FIELDS["RECURSIVE_EXPANSION"] setting is False. + """ + if self.recursive_expansion_permitted is True: + return + + expansion_path = self._split_expand_field(expand_path) + expansion_length = len(expansion_path) + expansion_length_unique = len(set(expansion_path)) + if expansion_length != expansion_length_unique: + self._recursive_expansion_found() + + def _expansion_depth_exceeded(self): + """ + A customized exception can be raised when expansion depth is found, default ValidationError + """ + raise serializers.ValidationError(detail="Expansion depth exceeded") + + def _validate_expansion_depth(self, expand_path: str) -> None: + """ + Given an expand_path, a dotted-separated string, + an Exception is raised when expansion level is + greater than the `expansion_depth` property configuration. + Only applies when REST_FLEX_FIELDS["EXPANSION_DEPTH"] setting is set + or serializer has its own expansion configuration through default_expansion_depth attribute. + """ + if self.maximum_expansion_depth is None: + return + + expansion_path = self._split_expand_field(expand_path) + if len(expansion_path) > self.maximum_expansion_depth: + self._expansion_depth_exceeded() + def _get_permitted_expands_from_query_param(self, expand_param: str) -> List[str]: """ If a list of permitted_expands has been passed to context, @@ -277,6 +344,10 @@ def _get_permitted_expands_from_query_param(self, expand_param: str) -> List[str """ expand = self._get_query_param_value(expand_param) + for expand_path in expand: + self._validate_recursive_expansion(expand_path) + self._validate_expansion_depth(expand_path) + if "permitted_expands" in self.context: permitted_expands = self.context["permitted_expands"] From 8c273ab04b609de498af2157f55aa23f76db1b55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Us=C3=A1n?= Date: Mon, 6 Mar 2023 14:16:18 +0100 Subject: [PATCH 3/9] Added tests --- tests/test_flex_fields_model_serializer.py | 78 +++++++++++++++++++++- tests/test_serializer.py | 4 +- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/tests/test_flex_fields_model_serializer.py b/tests/test_flex_fields_model_serializer.py index b0a0551..93baf8a 100644 --- a/tests/test_flex_fields_model_serializer.py +++ b/tests/test_flex_fields_model_serializer.py @@ -1,11 +1,17 @@ from unittest import TestCase +from unittest.mock import patch, MagicMock, PropertyMock +from django.test import override_settings from django.utils.datastructures import MultiValueDict +from rest_framework import serializers + from rest_flex_fields import FlexFieldsModelSerializer class MockRequest(object): - def __init__(self, query_params=MultiValueDict(), method="GET"): + def __init__(self, query_params=None, method="GET"): + if query_params is None: + query_params = MultiValueDict() self.query_params = query_params self.method = method @@ -178,3 +184,73 @@ def test_import_serializer_class(self): def test_make_expanded_field_serializer(self): pass + + @override_settings(REST_FLEX_FIELDS={"RECURSIVE_EXPANSION_PERMITTED": False}) + def test_recursive_expansion(self): + with self.assertRaises(serializers.ValidationError): + FlexFieldsModelSerializer( + context={ + "request": MockRequest( + method="GET", query_params=MultiValueDict({"expand": ["dog.leg.dog"]}) + ) + } + ) + + @patch('rest_flex_fields.FlexFieldsModelSerializer._recursive_expansion_permitted', new_callable=PropertyMock) + def test_recursive_expansion_serializer_level(self, mock_recursive_expansion_permitted): + mock_recursive_expansion_permitted.return_value = False + + with self.assertRaises(serializers.ValidationError): + FlexFieldsModelSerializer( + context={ + "request": MockRequest( + method="GET", query_params=MultiValueDict({"expand": ["dog.leg.dog"]}) + ) + } + ) + + @override_settings(REST_FLEX_FIELDS={"MAXIMUM_EXPANSION_DEPTH": 3}) + def test_expansion_depth(self): + serializer = FlexFieldsModelSerializer( + context={ + "request": MockRequest( + method="GET", query_params=MultiValueDict({"expand": ["dog.leg.paws"]}) + ) + } + ) + self.assertEqual(serializer._flex_options_all["expand"], ["dog.leg.paws"]) + + @override_settings(REST_FLEX_FIELDS={"MAXIMUM_EXPANSION_DEPTH": 2}) + def test_expansion_depth_exception(self): + with self.assertRaises(serializers.ValidationError): + FlexFieldsModelSerializer( + context={ + "request": MockRequest( + method="GET", query_params=MultiValueDict({"expand": ["dog.leg.paws"]}) + ) + } + ) + + @patch('rest_flex_fields.FlexFieldsModelSerializer._maximum_expansion_depth', new_callable=PropertyMock) + def test_expansion_depth_serializer_level(self, mock_maximum_expansion_depth): + mock_maximum_expansion_depth.return_value = 3 + serializer = FlexFieldsModelSerializer( + context={ + "request": MockRequest( + method="GET", query_params=MultiValueDict({"expand": ["dog.leg.paws"]}) + ) + } + ) + self.assertEqual(serializer._flex_options_all["expand"], ["dog.leg.paws"]) + + @patch('rest_flex_fields.FlexFieldsModelSerializer._maximum_expansion_depth', new_callable=PropertyMock) + def test_expansion_depth_serializer_level_exception(self, mock_maximum_expansion_depth): + mock_maximum_expansion_depth.return_value = 2 + with self.assertRaises(serializers.ValidationError): + FlexFieldsModelSerializer( + context={ + "request": MockRequest( + method="GET", query_params=MultiValueDict({"expand": ["dog.leg.paws"]}) + ) + } + ) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 9f24df5..0f529c6 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -10,7 +10,9 @@ class MockRequest(object): - def __init__(self, query_params={}, method="GET"): + def __init__(self, query_params=None, method="GET"): + if query_params is None: + query_params = {} self.query_params = query_params self.method = method From 8b799efdbb0f53f29ce9e4966ccd637a624245f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Us=C3=A1n?= Date: Mon, 6 Mar 2023 14:16:34 +0100 Subject: [PATCH 4/9] Updated README --- README.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1f8b23f..e7725d5 100644 --- a/README.md +++ b/README.md @@ -483,12 +483,14 @@ class PersonSerializer(FlexFieldsModelSerializer): Parameter names and wildcard values can be configured within a Django setting, named `REST_FLEX_FIELDS`. -| Option | Description | Default | -| --------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | --------------- | -| EXPAND_PARAM | The name of the parameter with the fields to be expanded | `"expand"` | -| FIELDS_PARAM | The name of the parameter with the fields to be included (others will be omitted) | `"fields"` | -| OMIT_PARAM | The name of the parameter with the fields to be omitted | `"omit"` | -| WILDCARD_VALUES | List of values that stand in for all field names. Can be used with the `fields` and `expand` parameters.

When used with `expand`, a wildcard value will trigger the expansion of all `expandable_fields` at a given level.

When used with `fields`, all fields are included at a given level. For example, you could pass `fields=name,state.*` if you have a city resource with a nested state in order to expand only the city's name field and all of the state's fields.

To disable use of wildcards, set this setting to `None`. | `["*", "~all"]` | +| Option | Description | Default | +|-------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|-----------------| +| EXPAND_PARAM | The name of the parameter with the fields to be expanded | `"expand"` | +| MAXIMUM_EXPANSION_DEPTH | The number of maximum depth permitted expansion | `None` | +| FIELDS_PARAM | The name of the parameter with the fields to be included (others will be omitted) | `"fields"` | +| OMIT_PARAM | The name of the parameter with the fields to be omitted | `"omit"` | +| RECURSIVE_EXPANSION_PERMITTED | If `False`, an exception is raised when a recursive pattern is found | `True` | +| WILDCARD_VALUES | List of values that stand in for all field names. Can be used with the `fields` and `expand` parameters.

When used with `expand`, a wildcard value will trigger the expansion of all `expandable_fields` at a given level.

When used with `fields`, all fields are included at a given level. For example, you could pass `fields=name,state.*` if you have a city resource with a nested state in order to expand only the city's name field and all of the state's fields.

To disable use of wildcards, set this setting to `None`. | `["*", "~all"]` | For example, if you want your API to work a bit more like [JSON API](https://jsonapi.org/format/#fetching-includes), you could do: @@ -496,6 +498,15 @@ For example, if you want your API to work a bit more like [JSON API](https://jso REST_FLEX_FIELDS = {"EXPAND_PARAM": "include"} ``` +### Defining expansion and recursive limits at serializer level + +`maximum_expansion_depth` property can be overridden at serializer level. It can be configured as `int` or `None`. + +`recursive_expansion_permitted` property can be overridden at serializer level. It must be `bool`. + +Both settings raise `serializers.ValidationError` when conditions are met but exceptions can be overridden in `_recursive_expansion_found` and `_expansion_depth_exceeded` methods. + + ## Serializer Introspection When using an instance of `FlexFieldsModelSerializer`, you can examine the property `expanded_fields` to discover which fields, if any, have been dynamically expanded. From 6b8ff1a42c23b39dae56bf43b7d696012c804328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Us=C3=A1n?= Date: Mon, 6 Mar 2023 14:16:56 +0100 Subject: [PATCH 5/9] Added .idea and venv folders to gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a9c50ad..22c1e96 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,9 @@ local-dev.txt dist/ MANIFEST .mypy_cache/ +.idea/ .vscode/ drf_flex_fields.egg-info/ venv.sh -.venv \ No newline at end of file +.venv +venv/ From 9f1e04b006251cde5269512d430157a9ebb53729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Us=C3=A1n?= Date: Mon, 6 Mar 2023 14:27:24 +0100 Subject: [PATCH 6/9] Removed unused MagicMock --- tests/test_flex_fields_model_serializer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_flex_fields_model_serializer.py b/tests/test_flex_fields_model_serializer.py index 93baf8a..ef2aced 100644 --- a/tests/test_flex_fields_model_serializer.py +++ b/tests/test_flex_fields_model_serializer.py @@ -1,5 +1,5 @@ from unittest import TestCase -from unittest.mock import patch, MagicMock, PropertyMock +from unittest.mock import patch, PropertyMock from django.test import override_settings from django.utils.datastructures import MultiValueDict From e348f3c281f50ea2820f7651fafd852dc523be8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Us=C3=A1n?= Date: Thu, 9 Mar 2023 14:11:05 +0100 Subject: [PATCH 7/9] Renamed _recursive_expansion_found in favor of _recursive_expansion_not_permitted --- rest_flex_fields/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rest_flex_fields/serializers.py b/rest_flex_fields/serializers.py index 8e15818..b1c9d60 100644 --- a/rest_flex_fields/serializers.py +++ b/rest_flex_fields/serializers.py @@ -293,7 +293,7 @@ def _get_query_param_value(self, field: str) -> List[str]: def _split_expand_field(self, expand_path: str) -> List[str]: return expand_path.split('.') - def _recursive_expansion_found(self): + def _recursive_expansion_not_permitted(self): """ A customized exception can be raised when recursive expansion is found, default ValidationError """ @@ -313,7 +313,7 @@ def _validate_recursive_expansion(self, expand_path: str) -> None: expansion_length = len(expansion_path) expansion_length_unique = len(set(expansion_path)) if expansion_length != expansion_length_unique: - self._recursive_expansion_found() + self._recursive_expansion_not_permitted() def _expansion_depth_exceeded(self): """ From 8582218c667fed41feaab1fb4f1e62ab80a2afd6 Mon Sep 17 00:00:00 2001 From: andruten Date: Fri, 10 Mar 2023 09:09:33 +0100 Subject: [PATCH 8/9] Removed _ in exception methods. Renamed recursive_expansion_found in favor of recursive_expansion_not_permitted. Adapted tests. --- rest_flex_fields/serializers.py | 33 +++++++++++----------- tests/test_flex_fields_model_serializer.py | 6 ++-- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/rest_flex_fields/serializers.py b/rest_flex_fields/serializers.py index b1c9d60..fb29b41 100644 --- a/rest_flex_fields/serializers.py +++ b/rest_flex_fields/serializers.py @@ -23,8 +23,8 @@ class FlexFieldsSerializerMixin(object): """ expandable_fields = {} - _maximum_expansion_depth: Optional[int] = None - _recursive_expansion_permitted: Optional[bool] = None + maximum_expansion_depth: Optional[int] = None + recursive_expansion_permitted: Optional[bool] = None def __init__(self, *args, **kwargs): expand = list(kwargs.pop(EXPAND_PARAM, [])) @@ -61,21 +61,18 @@ def __init__(self, *args, **kwargs): + self._flex_options_rep_only["omit"], } - @property - def maximum_expansion_depth(self) -> Optional[int]: + def get_maximum_expansion_depth(self) -> Optional[int]: """ Defined at serializer level or based on MAXIMUM_EXPANSION_DEPTH setting """ - return self._maximum_expansion_depth \ - or settings.REST_FLEX_FIELDS.get("MAXIMUM_EXPANSION_DEPTH", None) + return self.maximum_expansion_depth or settings.REST_FLEX_FIELDS.get("MAXIMUM_EXPANSION_DEPTH", None) - @property - def recursive_expansion_permitted(self) -> bool: + def get_recursive_expansion_permitted(self) -> bool: """ Defined at serializer level or based on RECURSIVE_EXPANSION_PERMITTED setting """ - if self._recursive_expansion_permitted is not None: - return self._recursive_expansion_permitted + if self.recursive_expansion_permitted is not None: + return self.recursive_expansion_permitted else: return settings.REST_FLEX_FIELDS.get("RECURSIVE_EXPANSION_PERMITTED", True) @@ -293,7 +290,7 @@ def _get_query_param_value(self, field: str) -> List[str]: def _split_expand_field(self, expand_path: str) -> List[str]: return expand_path.split('.') - def _recursive_expansion_not_permitted(self): + def recursive_expansion_not_permitted(self): """ A customized exception can be raised when recursive expansion is found, default ValidationError """ @@ -306,16 +303,17 @@ def _validate_recursive_expansion(self, expand_path: str) -> None: expansion is detected. Only applies when REST_FLEX_FIELDS["RECURSIVE_EXPANSION"] setting is False. """ - if self.recursive_expansion_permitted is True: + recursive_expansion_permitted = self.get_recursive_expansion_permitted() + if recursive_expansion_permitted is True: return expansion_path = self._split_expand_field(expand_path) expansion_length = len(expansion_path) expansion_length_unique = len(set(expansion_path)) if expansion_length != expansion_length_unique: - self._recursive_expansion_not_permitted() + self.recursive_expansion_not_permitted() - def _expansion_depth_exceeded(self): + def expansion_depth_exceeded(self): """ A customized exception can be raised when expansion depth is found, default ValidationError """ @@ -329,12 +327,13 @@ def _validate_expansion_depth(self, expand_path: str) -> None: Only applies when REST_FLEX_FIELDS["EXPANSION_DEPTH"] setting is set or serializer has its own expansion configuration through default_expansion_depth attribute. """ - if self.maximum_expansion_depth is None: + maximum_expansion_depth = self.get_maximum_expansion_depth() + if maximum_expansion_depth is None: return expansion_path = self._split_expand_field(expand_path) - if len(expansion_path) > self.maximum_expansion_depth: - self._expansion_depth_exceeded() + if len(expansion_path) > maximum_expansion_depth: + self.expansion_depth_exceeded() def _get_permitted_expands_from_query_param(self, expand_param: str) -> List[str]: """ diff --git a/tests/test_flex_fields_model_serializer.py b/tests/test_flex_fields_model_serializer.py index ef2aced..27fea58 100644 --- a/tests/test_flex_fields_model_serializer.py +++ b/tests/test_flex_fields_model_serializer.py @@ -196,7 +196,7 @@ def test_recursive_expansion(self): } ) - @patch('rest_flex_fields.FlexFieldsModelSerializer._recursive_expansion_permitted', new_callable=PropertyMock) + @patch('rest_flex_fields.FlexFieldsModelSerializer.recursive_expansion_permitted', new_callable=PropertyMock) def test_recursive_expansion_serializer_level(self, mock_recursive_expansion_permitted): mock_recursive_expansion_permitted.return_value = False @@ -231,7 +231,7 @@ def test_expansion_depth_exception(self): } ) - @patch('rest_flex_fields.FlexFieldsModelSerializer._maximum_expansion_depth', new_callable=PropertyMock) + @patch('rest_flex_fields.FlexFieldsModelSerializer.maximum_expansion_depth', new_callable=PropertyMock) def test_expansion_depth_serializer_level(self, mock_maximum_expansion_depth): mock_maximum_expansion_depth.return_value = 3 serializer = FlexFieldsModelSerializer( @@ -243,7 +243,7 @@ def test_expansion_depth_serializer_level(self, mock_maximum_expansion_depth): ) self.assertEqual(serializer._flex_options_all["expand"], ["dog.leg.paws"]) - @patch('rest_flex_fields.FlexFieldsModelSerializer._maximum_expansion_depth', new_callable=PropertyMock) + @patch('rest_flex_fields.FlexFieldsModelSerializer.maximum_expansion_depth', new_callable=PropertyMock) def test_expansion_depth_serializer_level_exception(self, mock_maximum_expansion_depth): mock_maximum_expansion_depth.return_value = 2 with self.assertRaises(serializers.ValidationError): From 0d3da72fd76009cf65cfde42ef81d1944da8845d Mon Sep 17 00:00:00 2001 From: andruten Date: Fri, 10 Mar 2023 09:20:48 +0100 Subject: [PATCH 9/9] Moved expansion validations from _get_permitted_expands_from_query_param to _get_query_param_value, which is executed through the three approaches --- rest_flex_fields/serializers.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rest_flex_fields/serializers.py b/rest_flex_fields/serializers.py index fb29b41..ea9a1a0 100644 --- a/rest_flex_fields/serializers.py +++ b/rest_flex_fields/serializers.py @@ -282,6 +282,10 @@ def _get_query_param_value(self, field: str) -> List[str]: if not values: values = self.context["request"].query_params.getlist("{}[]".format(field)) + for expand_path in values: + self._validate_recursive_expansion(expand_path) + self._validate_expansion_depth(expand_path) + if values and len(values) == 1: return values[0].split(",") @@ -343,10 +347,6 @@ def _get_permitted_expands_from_query_param(self, expand_param: str) -> List[str """ expand = self._get_query_param_value(expand_param) - for expand_path in expand: - self._validate_recursive_expansion(expand_path) - self._validate_expansion_depth(expand_path) - if "permitted_expands" in self.context: permitted_expands = self.context["permitted_expands"]