-
-
Notifications
You must be signed in to change notification settings - Fork 132
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
AWS API Gateway with Amazon Lambda integrations support
- Loading branch information
Showing
18 changed files
with
976 additions
and
165 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
"""OpenAPI core contrib aws module""" | ||
from openapi_core.contrib.aws.finders import APIGatewayPathFinder | ||
from openapi_core.contrib.aws.requests import APIGatewayEventOpenAPIRequest | ||
from openapi_core.contrib.aws.requests import APIGatewayEventV2OpenAPIRequest | ||
from openapi_core.contrib.aws.responses import ( | ||
APIGatewayEventResponseOpenAPIResponse, | ||
) | ||
from openapi_core.contrib.aws.responses import ( | ||
APIGatewayEventV2ResponseOpenAPIResponse, | ||
) | ||
|
||
__all__ = [ | ||
"APIGatewayEventOpenAPIRequest", | ||
"APIGatewayEventV2OpenAPIRequest", | ||
"APIGatewayEventResponseOpenAPIResponse", | ||
"APIGatewayEventV2ResponseOpenAPIResponse", | ||
"APIGatewayPathFinder", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
from typing import Dict | ||
from typing import List | ||
from typing import Optional | ||
|
||
from pydantic import Field | ||
from pydantic.dataclasses import dataclass | ||
|
||
|
||
class APIGatewayEventConfig: | ||
extra = "allow" | ||
|
||
|
||
@dataclass(config=APIGatewayEventConfig, frozen=True) | ||
class APIGatewayEvent: | ||
"""AWS API Gateway event""" | ||
|
||
headers: Dict[str, str] | ||
|
||
path: str | ||
httpMethod: str | ||
resource: str | ||
|
||
queryStringParameters: Optional[Dict[str, str]] = None | ||
isBase64Encoded: Optional[bool] = None | ||
body: Optional[str] = None | ||
pathParameters: Optional[Dict[str, str]] = None | ||
stageVariables: Optional[Dict[str, str]] = None | ||
|
||
multiValueHeaders: Optional[Dict[str, List[str]]] = None | ||
version: Optional[str] = "1.0" | ||
multiValueQueryStringParameters: Optional[Dict[str, List[str]]] = None | ||
|
||
|
||
@dataclass(config=APIGatewayEventConfig, frozen=True) | ||
class APIGatewayEventV2: | ||
"""AWS API Gateway event v2""" | ||
|
||
headers: Dict[str, str] | ||
|
||
version: str | ||
routeKey: str | ||
rawPath: str | ||
rawQueryString: str | ||
|
||
queryStringParameters: Optional[Dict[str, str]] = None | ||
isBase64Encoded: Optional[bool] = None | ||
body: Optional[str] = None | ||
pathParameters: Optional[Dict[str, str]] = None | ||
stageVariables: Optional[Dict[str, str]] = None | ||
|
||
cookies: Optional[List[str]] = None | ||
|
||
|
||
@dataclass(config=APIGatewayEventConfig, frozen=True) | ||
class APIGatewayEventResponse: | ||
"""AWS API Gateway event response""" | ||
|
||
body: str | ||
isBase64Encoded: bool | ||
statusCode: int | ||
headers: Dict[str, str] | ||
multiValueHeaders: Dict[str, List[str]] | ||
|
||
|
||
@dataclass(config=APIGatewayEventConfig, frozen=True) | ||
class APIGatewayEventV2Response: | ||
"""AWS API Gateway event v2 response""" | ||
|
||
body: str | ||
isBase64Encoded: bool = False | ||
statusCode: int = 200 | ||
headers: Dict[str, str] = Field( | ||
default_factory=lambda: {"content-type": "application/json"} | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from openapi_core.templating.paths.finders import APICallPathFinder | ||
from openapi_core.templating.paths.iterators import AnyMethodOperationsIterator | ||
|
||
|
||
class APIGatewayPathFinder(APICallPathFinder): | ||
operations_iterator = AnyMethodOperationsIterator( | ||
any_method="x-amazon-apigateway-any-method", | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
from typing import Dict | ||
from typing import Optional | ||
|
||
from werkzeug.datastructures import Headers | ||
from werkzeug.datastructures import ImmutableMultiDict | ||
|
||
from openapi_core.contrib.aws.datatypes import APIGatewayEvent | ||
from openapi_core.contrib.aws.datatypes import APIGatewayEventV2 | ||
from openapi_core.contrib.aws.types import APIGatewayEventPayload | ||
from openapi_core.datatypes import RequestParameters | ||
|
||
|
||
class APIGatewayEventOpenAPIRequest: | ||
""" | ||
Converts an API Gateway event payload to an OpenAPI request | ||
""" | ||
|
||
def __init__(self, payload: APIGatewayEventPayload): | ||
self.event = APIGatewayEvent(**payload) | ||
|
||
self.parameters = RequestParameters( | ||
query=ImmutableMultiDict(self.query_params), | ||
header=Headers(self.event.headers), | ||
cookie=ImmutableMultiDict(), | ||
) | ||
|
||
@property | ||
def query_params(self) -> Dict[str, str]: | ||
params = self.event.queryStringParameters | ||
if params is None: | ||
return {} | ||
return params | ||
|
||
@property | ||
def proto(self) -> str: | ||
return self.event.headers.get("X-Forwarded-Proto", "https") | ||
|
||
@property | ||
def host(self) -> str: | ||
return self.event.headers["Host"] | ||
|
||
@property | ||
def host_url(self) -> str: | ||
return "://".join([self.proto, self.host]) | ||
|
||
@property | ||
def path(self) -> str: | ||
return self.event.resource | ||
|
||
@property | ||
def method(self) -> str: | ||
return self.event.httpMethod.lower() | ||
|
||
@property | ||
def body(self) -> Optional[str]: | ||
return self.event.body | ||
|
||
@property | ||
def mimetype(self) -> str: | ||
return self.event.headers.get("Content-Type", "") | ||
|
||
|
||
class APIGatewayEventV2OpenAPIRequest: | ||
""" | ||
Converts an API Gateway event v2 payload to an OpenAPI request | ||
""" | ||
|
||
def __init__(self, payload: APIGatewayEventPayload): | ||
self.event = APIGatewayEventV2(**payload) | ||
|
||
self.parameters = RequestParameters( | ||
query=ImmutableMultiDict(self.query_params), | ||
header=Headers(self.event.headers), | ||
cookie=ImmutableMultiDict(), | ||
) | ||
|
||
@property | ||
def query_params(self) -> Dict[str, str]: | ||
if self.event.queryStringParameters is None: | ||
return {} | ||
return self.event.queryStringParameters | ||
|
||
@property | ||
def proto(self) -> str: | ||
return self.event.headers.get("x-forwarded-proto", "https") | ||
|
||
@property | ||
def host(self) -> str: | ||
return self.event.headers["host"] | ||
|
||
@property | ||
def host_url(self) -> str: | ||
return "://".join([self.proto, self.host]) | ||
|
||
@property | ||
def path(self) -> str: | ||
return self.event.rawPath | ||
|
||
@property | ||
def method(self) -> str: | ||
return self.event.routeKey.lower() | ||
|
||
@property | ||
def body(self) -> Optional[str]: | ||
return self.event.body | ||
|
||
@property | ||
def mimetype(self) -> str: | ||
return self.event.headers.get("content-type", "") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
from json import dumps | ||
from typing import Union | ||
|
||
from werkzeug.datastructures import Headers | ||
|
||
from openapi_core.contrib.aws.datatypes import APIGatewayEventResponse | ||
from openapi_core.contrib.aws.datatypes import APIGatewayEventV2Response | ||
from openapi_core.contrib.aws.types import APIGatewayEventResponsePayload | ||
|
||
APIGatewayEventV2ResponseType = Union[APIGatewayEventV2Response, dict, str] | ||
|
||
|
||
class APIGatewayEventResponseOpenAPIResponse: | ||
""" | ||
Converts an API Gateway event response payload to an OpenAPI request | ||
""" | ||
|
||
def __init__(self, payload: APIGatewayEventResponsePayload): | ||
self.response = APIGatewayEventResponse(**payload) | ||
|
||
@property | ||
def data(self) -> str: | ||
return self.response.body | ||
|
||
@property | ||
def status_code(self) -> int: | ||
return self.response.statusCode | ||
|
||
@property | ||
def headers(self) -> Headers: | ||
return Headers(self.response.headers) | ||
|
||
@property | ||
def mimetype(self) -> str: | ||
content_type = self.response.headers.get("Content-Type", "") | ||
assert isinstance(content_type, str) | ||
return content_type | ||
|
||
|
||
class APIGatewayEventV2ResponseOpenAPIResponse: | ||
""" | ||
Converts an API Gateway event v2 response payload to an OpenAPI request | ||
""" | ||
|
||
def __init__(self, payload: Union[APIGatewayEventResponsePayload, str]): | ||
if not isinstance(payload, dict): | ||
payload = self._construct_payload(payload) | ||
elif "statusCode" not in payload: | ||
body = dumps(payload) | ||
payload = self._construct_payload(body) | ||
|
||
self.response = APIGatewayEventV2Response(**payload) | ||
|
||
@staticmethod | ||
def _construct_payload(body: str) -> APIGatewayEventResponsePayload: | ||
return { | ||
"isBase64Encoded": False, | ||
"statusCode": 200, | ||
"headers": { | ||
"content-type": "application/json", | ||
}, | ||
"body": body, | ||
} | ||
|
||
@property | ||
def data(self) -> str: | ||
return self.response.body | ||
|
||
@property | ||
def status_code(self) -> int: | ||
return self.response.statusCode | ||
|
||
@property | ||
def headers(self) -> Headers: | ||
return Headers(self.response.headers) | ||
|
||
@property | ||
def mimetype(self) -> str: | ||
content_type = self.response.headers.get( | ||
"content-type", "application/json" | ||
) | ||
assert isinstance(content_type, str) | ||
return content_type |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from typing import Any | ||
from typing import Dict | ||
|
||
APIGatewayEventPayload = Dict[str, Any] | ||
APIGatewayEventResponsePayload = Dict[str, Any] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from openapi_core.templating.paths.finders import APICallPathFinder | ||
from openapi_core.templating.paths.finders import WebhookPathFinder | ||
|
||
__all__ = [ | ||
"APICallPathFinder", | ||
"WebhookPathFinder", | ||
] |
Oops, something went wrong.