Skip to content

Commit 3b94fb2

Browse files
committed
refactor: reduce cognitive complexity in additional OpenAPI methods
- Refactored _resolve_openapi_config method to reduce complexity from 17 to 15 - Split into 4 helper methods for better maintainability - Improved separation of concerns for config resolution logic - Refactored get_openapi_schema method in example file to reduce complexity from 23 to 15 - Split into 6 helper methods with clear responsibilities - Enhanced readability and maintainability of schema fixing logic - All OpenAPI tests continue to pass (220/220) - Example functionality validated and working correctly
1 parent 3093afe commit 3b94fb2

File tree

2 files changed

+75
-35
lines changed

2 files changed

+75
-35
lines changed

aws_lambda_powertools/event_handler/api_gateway.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1812,22 +1812,36 @@ def _resolve_openapi_config(self, **kwargs) -> dict[str, Any]:
18121812
# See: https://github.com/aws-powertools/powertools-lambda-python/issues/6122
18131813
resolved = {}
18141814

1815-
# Handle title with fallback
1815+
# Handle fields with specific default value checks
1816+
self._resolve_title_config(resolved, kwargs)
1817+
self._resolve_version_config(resolved, kwargs)
1818+
self._resolve_openapi_version_config(resolved, kwargs)
1819+
1820+
# Resolve other fields with simple fallbacks
1821+
self._resolve_remaining_config_fields(resolved, kwargs)
1822+
1823+
return resolved
1824+
1825+
def _resolve_title_config(self, resolved: dict[str, Any], kwargs: dict[str, Any]) -> None:
1826+
"""Resolve title configuration with fallback to openapi_config."""
18161827
resolved["title"] = kwargs["title"]
18171828
if kwargs["title"] == DEFAULT_OPENAPI_TITLE and self.openapi_config.title:
18181829
resolved["title"] = self.openapi_config.title
18191830

1820-
# Handle version with fallback
1831+
def _resolve_version_config(self, resolved: dict[str, Any], kwargs: dict[str, Any]) -> None:
1832+
"""Resolve version configuration with fallback to openapi_config."""
18211833
resolved["version"] = kwargs["version"]
18221834
if kwargs["version"] == DEFAULT_API_VERSION and self.openapi_config.version:
18231835
resolved["version"] = self.openapi_config.version
18241836

1825-
# Handle openapi_version with fallback
1837+
def _resolve_openapi_version_config(self, resolved: dict[str, Any], kwargs: dict[str, Any]) -> None:
1838+
"""Resolve openapi_version configuration with fallback to openapi_config."""
18261839
resolved["openapi_version"] = kwargs["openapi_version"]
18271840
if kwargs["openapi_version"] == DEFAULT_OPENAPI_VERSION and self.openapi_config.openapi_version:
18281841
resolved["openapi_version"] = self.openapi_config.openapi_version
18291842

1830-
# Resolve other fields with fallbacks
1843+
def _resolve_remaining_config_fields(self, resolved: dict[str, Any], kwargs: dict[str, Any]) -> None:
1844+
"""Resolve remaining configuration fields with simple fallbacks."""
18311845
resolved.update({
18321846
"summary": kwargs["summary"] or self.openapi_config.summary,
18331847
"description": kwargs["description"] or self.openapi_config.description,
@@ -1842,8 +1856,6 @@ def _resolve_openapi_config(self, **kwargs) -> dict[str, Any]:
18421856
"openapi_extensions": kwargs["openapi_extensions"] or self.openapi_config.openapi_extensions,
18431857
})
18441858

1845-
return resolved
1846-
18471859
def _build_base_openapi_structure(self, config: dict[str, Any]) -> dict[str, Any]:
18481860
"""Build the base OpenAPI structure with info, servers, and security."""
18491861
openapi_version = self._determine_openapi_version(config["openapi_version"])

examples/event_handler/openapi_schema_fix_example.py

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -49,38 +49,69 @@ def get_openapi_schema(self, **kwargs):
4949
schema_dict = schema.model_dump(by_alias=True)
5050

5151
# Find all multipart/form-data references that might be missing
52-
missing_refs = []
53-
paths = schema_dict.get("paths", {})
54-
for path_item in paths.values():
55-
for _method, operation in path_item.items():
56-
if not isinstance(operation, dict):
57-
continue
58-
59-
if "requestBody" not in operation:
60-
continue
61-
62-
req_body = operation.get("requestBody", {})
63-
content = req_body.get("content", {})
64-
multipart = content.get("multipart/form-data", {})
65-
schema_ref = multipart.get("schema", {})
66-
67-
if "$ref" in schema_ref:
68-
ref = schema_ref["$ref"]
69-
if ref.startswith("#/components/schemas/"):
70-
component_name = ref[len("#/components/schemas/") :]
71-
72-
# Check if the component exists
73-
components = schema_dict.get("components", {})
74-
schemas = components.get("schemas", {})
75-
76-
if component_name not in schemas:
77-
missing_refs.append((component_name, ref))
52+
missing_refs = self._find_missing_component_references(schema_dict)
7853

7954
# If no missing references, return the original schema
8055
if not missing_refs:
8156
return schema
8257

8358
# Add missing components to the schema
59+
self._add_missing_components(schema_dict, missing_refs)
60+
61+
# Rebuild the schema with the added components
62+
return schema.__class__(**schema_dict)
63+
64+
def _find_missing_component_references(self, schema_dict: dict) -> list[tuple[str, str]]:
65+
"""Find all missing component references in multipart/form-data schemas."""
66+
missing_refs = []
67+
paths = schema_dict.get("paths", {})
68+
69+
for path_item in paths.values():
70+
self._check_path_item_for_missing_refs(path_item, schema_dict, missing_refs)
71+
72+
return missing_refs
73+
74+
def _check_path_item_for_missing_refs(
75+
self,
76+
path_item: dict,
77+
schema_dict: dict,
78+
missing_refs: list[tuple[str, str]]
79+
) -> None:
80+
"""Check a single path item for missing component references."""
81+
for _method, operation in path_item.items():
82+
if not isinstance(operation, dict) or "requestBody" not in operation:
83+
continue
84+
85+
self._check_operation_for_missing_refs(operation, schema_dict, missing_refs)
86+
87+
def _check_operation_for_missing_refs(
88+
self,
89+
operation: dict,
90+
schema_dict: dict,
91+
missing_refs: list[tuple[str, str]]
92+
) -> None:
93+
"""Check a single operation for missing component references."""
94+
req_body = operation.get("requestBody", {})
95+
content = req_body.get("content", {})
96+
multipart = content.get("multipart/form-data", {})
97+
schema_ref = multipart.get("schema", {})
98+
99+
if "$ref" in schema_ref:
100+
ref = schema_ref["$ref"]
101+
if ref.startswith("#/components/schemas/"):
102+
component_name = ref[len("#/components/schemas/") :]
103+
104+
if self._is_component_missing(schema_dict, component_name):
105+
missing_refs.append((component_name, ref))
106+
107+
def _is_component_missing(self, schema_dict: dict, component_name: str) -> bool:
108+
"""Check if a component is missing from the schema."""
109+
components = schema_dict.get("components", {})
110+
schemas = components.get("schemas", {})
111+
return component_name not in schemas
112+
113+
def _add_missing_components(self, schema_dict: dict, missing_refs: list[tuple[str, str]]) -> None:
114+
"""Add missing components to the schema."""
84115
components = schema_dict.setdefault("components", {})
85116
schemas = components.setdefault("schemas", {})
86117

@@ -98,9 +129,6 @@ def get_openapi_schema(self, **kwargs):
98129
"required": ["file"],
99130
}
100131

101-
# Rebuild the schema with the added components
102-
return schema.__class__(**schema_dict)
103-
104132

105133
def create_test_app():
106134
"""Create a test app with the fixed resolver."""

0 commit comments

Comments
 (0)