@@ -1774,72 +1774,114 @@ def get_openapi_schema(
1774
1774
OpenAPI: pydantic model
1775
1775
The OpenAPI schema as a pydantic model.
1776
1776
"""
1777
+ # Resolve configuration with fallbacks to openapi_config
1778
+ config = self ._resolve_openapi_config (
1779
+ title = title ,
1780
+ version = version ,
1781
+ openapi_version = openapi_version ,
1782
+ summary = summary ,
1783
+ description = description ,
1784
+ tags = tags ,
1785
+ servers = servers ,
1786
+ terms_of_service = terms_of_service ,
1787
+ contact = contact ,
1788
+ license_info = license_info ,
1789
+ security_schemes = security_schemes ,
1790
+ security = security ,
1791
+ external_documentation = external_documentation ,
1792
+ openapi_extensions = openapi_extensions ,
1793
+ )
1777
1794
1778
- # DEPRECATION: Will be removed in v4.0.0. Use configure_api() instead.
1779
- # Maintained for backwards compatibility.
1780
- # See: https://github.com/aws-powertools/powertools-lambda-python/issues/6122
1781
- if title == DEFAULT_OPENAPI_TITLE and self .openapi_config .title :
1782
- title = self .openapi_config .title
1783
-
1784
- if version == DEFAULT_API_VERSION and self .openapi_config .version :
1785
- version = self .openapi_config .version
1786
-
1787
- if openapi_version == DEFAULT_OPENAPI_VERSION and self .openapi_config .openapi_version :
1788
- openapi_version = self .openapi_config .openapi_version
1789
-
1790
- summary = summary or self .openapi_config .summary
1791
- description = description or self .openapi_config .description
1792
- tags = tags or self .openapi_config .tags
1793
- servers = servers or self .openapi_config .servers
1794
- terms_of_service = terms_of_service or self .openapi_config .terms_of_service
1795
- contact = contact or self .openapi_config .contact
1796
- license_info = license_info or self .openapi_config .license_info
1797
- security_schemes = security_schemes or self .openapi_config .security_schemes
1798
- security = security or self .openapi_config .security
1799
- external_documentation = external_documentation or self .openapi_config .external_documentation
1800
- openapi_extensions = openapi_extensions or self .openapi_config .openapi_extensions
1795
+ # Build base OpenAPI structure
1796
+ output = self ._build_base_openapi_structure (config )
1801
1797
1802
- from pydantic .json_schema import GenerateJsonSchema
1798
+ # Process routes and build paths/components
1799
+ paths , definitions = self ._process_routes_for_openapi (config ["security_schemes" ])
1803
1800
1804
- from aws_lambda_powertools .event_handler .openapi .compat import (
1805
- get_compat_model_name_map ,
1806
- get_definitions ,
1807
- )
1808
- from aws_lambda_powertools .event_handler .openapi .models import OpenAPI , PathItem , Tag
1809
- from aws_lambda_powertools .event_handler .openapi .types import (
1810
- COMPONENT_REF_TEMPLATE ,
1811
- )
1801
+ # Build final components and paths
1802
+ components = self ._build_openapi_components (definitions , config ["security_schemes" ])
1803
+ output .update (self ._finalize_openapi_output (components , config ["tags" ], paths , config ["external_documentation" ]))
1812
1804
1813
- openapi_version = self ._determine_openapi_version (openapi_version )
1805
+ # Apply schema fixes and return result
1806
+ return self ._apply_schema_fixes (output )
1807
+
1808
+ def _resolve_openapi_config (self , ** kwargs ) -> dict [str , Any ]:
1809
+ """Resolve OpenAPI configuration with fallbacks to openapi_config."""
1810
+ # DEPRECATION: Will be removed in v4.0.0. Use configure_api() instead.
1811
+ # Maintained for backwards compatibility.
1812
+ # See: https://github.com/aws-powertools/powertools-lambda-python/issues/6122
1813
+ resolved = {}
1814
+
1815
+ # Handle title with fallback
1816
+ resolved ["title" ] = kwargs ["title" ]
1817
+ if kwargs ["title" ] == DEFAULT_OPENAPI_TITLE and self .openapi_config .title :
1818
+ resolved ["title" ] = self .openapi_config .title
1819
+
1820
+ # Handle version with fallback
1821
+ resolved ["version" ] = kwargs ["version" ]
1822
+ if kwargs ["version" ] == DEFAULT_API_VERSION and self .openapi_config .version :
1823
+ resolved ["version" ] = self .openapi_config .version
1824
+
1825
+ # Handle openapi_version with fallback
1826
+ resolved ["openapi_version" ] = kwargs ["openapi_version" ]
1827
+ if kwargs ["openapi_version" ] == DEFAULT_OPENAPI_VERSION and self .openapi_config .openapi_version :
1828
+ resolved ["openapi_version" ] = self .openapi_config .openapi_version
1829
+
1830
+ # Resolve other fields with fallbacks
1831
+ resolved .update ({
1832
+ "summary" : kwargs ["summary" ] or self .openapi_config .summary ,
1833
+ "description" : kwargs ["description" ] or self .openapi_config .description ,
1834
+ "tags" : kwargs ["tags" ] or self .openapi_config .tags ,
1835
+ "servers" : kwargs ["servers" ] or self .openapi_config .servers ,
1836
+ "terms_of_service" : kwargs ["terms_of_service" ] or self .openapi_config .terms_of_service ,
1837
+ "contact" : kwargs ["contact" ] or self .openapi_config .contact ,
1838
+ "license_info" : kwargs ["license_info" ] or self .openapi_config .license_info ,
1839
+ "security_schemes" : kwargs ["security_schemes" ] or self .openapi_config .security_schemes ,
1840
+ "security" : kwargs ["security" ] or self .openapi_config .security ,
1841
+ "external_documentation" : kwargs ["external_documentation" ] or self .openapi_config .external_documentation ,
1842
+ "openapi_extensions" : kwargs ["openapi_extensions" ] or self .openapi_config .openapi_extensions ,
1843
+ })
1844
+
1845
+ return resolved
1846
+
1847
+ def _build_base_openapi_structure (self , config : dict [str , Any ]) -> dict [str , Any ]:
1848
+ """Build the base OpenAPI structure with info, servers, and security."""
1849
+ openapi_version = self ._determine_openapi_version (config ["openapi_version" ])
1814
1850
1815
1851
# Start with the bare minimum required for a valid OpenAPI schema
1816
- info : dict [str , Any ] = {"title" : title , "version" : version }
1852
+ info : dict [str , Any ] = {"title" : config [ " title" ] , "version" : config [ " version" ] }
1817
1853
1818
1854
optional_fields = {
1819
- "summary" : summary ,
1820
- "description" : description ,
1821
- "termsOfService" : terms_of_service ,
1822
- "contact" : contact ,
1823
- "license" : license_info ,
1855
+ "summary" : config [ " summary" ] ,
1856
+ "description" : config [ " description" ] ,
1857
+ "termsOfService" : config [ " terms_of_service" ] ,
1858
+ "contact" : config [ " contact" ] ,
1859
+ "license" : config [ " license_info" ] ,
1824
1860
}
1825
1861
1826
1862
info .update ({field : value for field , value in optional_fields .items () if value })
1827
1863
1864
+ openapi_extensions = config ["openapi_extensions" ]
1828
1865
if not isinstance (openapi_extensions , dict ):
1829
1866
openapi_extensions = {}
1830
1867
1831
- output : dict [ str , Any ] = {
1868
+ return {
1832
1869
"openapi" : openapi_version ,
1833
1870
"info" : info ,
1834
- "servers" : self ._get_openapi_servers (servers ),
1835
- "security" : self ._get_openapi_security (security , security_schemes ),
1871
+ "servers" : self ._get_openapi_servers (config [ " servers" ] ),
1872
+ "security" : self ._get_openapi_security (config [ " security" ], config [ " security_schemes" ] ),
1836
1873
** openapi_extensions ,
1837
1874
}
1838
1875
1839
- if external_documentation :
1840
- output ["externalDocs" ] = external_documentation
1876
+ def _process_routes_for_openapi (self , security_schemes : dict [str , SecurityScheme ] | None ) -> tuple [dict [str , dict [str , Any ]], dict [str , dict [str , Any ]]]:
1877
+ """Process all routes and build paths and definitions."""
1878
+ from pydantic .json_schema import GenerateJsonSchema
1879
+ from aws_lambda_powertools .event_handler .openapi .compat import (
1880
+ get_compat_model_name_map ,
1881
+ get_definitions ,
1882
+ )
1883
+ from aws_lambda_powertools .event_handler .openapi .types import COMPONENT_REF_TEMPLATE
1841
1884
1842
- components : dict [str , dict [str , Any ]] = {}
1843
1885
paths : dict [str , dict [str , Any ]] = {}
1844
1886
operation_ids : set [str ] = set ()
1845
1887
@@ -1857,15 +1899,8 @@ def get_openapi_schema(
1857
1899
1858
1900
# Add routes to the OpenAPI schema
1859
1901
for route in all_routes :
1860
- if route .security and not _validate_openapi_security_parameters (
1861
- security = route .security ,
1862
- security_schemes = security_schemes ,
1863
- ):
1864
- raise SchemaValidationError (
1865
- "Security configuration was not found in security_schemas or security_schema was not defined. "
1866
- "See: https://docs.powertools.aws.dev/lambda/python/latest/core/event_handler/api_gateway/#security-schemes" ,
1867
- )
1868
-
1902
+ self ._validate_route_security (route , security_schemes )
1903
+
1869
1904
if not route .include_in_schema :
1870
1905
continue
1871
1906
@@ -1883,19 +1918,50 @@ def get_openapi_schema(
1883
1918
if path_definitions :
1884
1919
definitions .update (path_definitions )
1885
1920
1921
+ return paths , definitions
1922
+
1923
+ def _validate_route_security (self , route , security_schemes : dict [str , SecurityScheme ] | None ) -> None :
1924
+ """Validate route security configuration."""
1925
+ if route .security and not _validate_openapi_security_parameters (
1926
+ security = route .security ,
1927
+ security_schemes = security_schemes ,
1928
+ ):
1929
+ raise SchemaValidationError (
1930
+ "Security configuration was not found in security_schemas or security_schema was not defined. "
1931
+ "See: https://docs.powertools.aws.dev/lambda/python/latest/core/event_handler/api_gateway/#security-schemes" ,
1932
+ )
1933
+
1934
+ def _build_openapi_components (self , definitions : dict [str , dict [str , Any ]], security_schemes : dict [str , SecurityScheme ] | None ) -> dict [str , dict [str , Any ]]:
1935
+ """Build the components section of the OpenAPI schema."""
1936
+ components : dict [str , dict [str , Any ]] = {}
1937
+
1886
1938
if definitions :
1887
1939
components ["schemas" ] = self ._generate_schemas (definitions )
1888
1940
if security_schemes :
1889
1941
components ["securitySchemes" ] = security_schemes
1942
+
1943
+ return components
1944
+
1945
+ def _finalize_openapi_output (self , components : dict [str , dict [str , Any ]], tags , paths : dict [str , dict [str , Any ]], external_documentation ) -> dict [str , Any ]:
1946
+ """Finalize the OpenAPI output with components, tags, and paths."""
1947
+ from aws_lambda_powertools .event_handler .openapi .models import PathItem , Tag
1948
+
1949
+ output = {}
1950
+
1890
1951
if components :
1891
1952
output ["components" ] = components
1892
1953
if tags :
1893
1954
output ["tags" ] = [Tag (name = tag ) if isinstance (tag , str ) else tag for tag in tags ]
1955
+ if external_documentation :
1956
+ output ["externalDocs" ] = external_documentation
1894
1957
1895
1958
output ["paths" ] = {k : PathItem (** v ) for k , v in paths .items ()}
1959
+
1960
+ return output
1896
1961
1897
- # Apply patches to fix any issues with the OpenAPI schema
1898
- # Import here to avoid circular imports
1962
+ def _apply_schema_fixes (self , output : dict [str , Any ]) -> OpenAPI :
1963
+ """Apply schema fixes and return the final OpenAPI model."""
1964
+ from aws_lambda_powertools .event_handler .openapi .models import OpenAPI
1899
1965
from aws_lambda_powertools .event_handler .openapi .upload_file_fix import fix_upload_file_schema
1900
1966
1901
1967
# First create the OpenAPI model
@@ -1906,9 +1972,7 @@ def get_openapi_schema(
1906
1972
fixed_dict = fix_upload_file_schema (result_dict )
1907
1973
1908
1974
# Reconstruct the model with the fixed dict
1909
- result = OpenAPI (** fixed_dict )
1910
-
1911
- return result
1975
+ return OpenAPI (** fixed_dict )
1912
1976
1913
1977
@staticmethod
1914
1978
def _get_openapi_servers (servers : list [Server ] | None ) -> list [Server ]:
0 commit comments