Skip to content

Webhooks support #470

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ Use ``validate_request`` function to validate request against a given spec.
# raise error if request is invalid
result = validate_request(request, spec=spec)

Request object should implement OpenAPI Request protocol (See `Integrations <https://openapi-core.readthedocs.io/en/latest/integrations.html>`__).

Use the same function to validate webhook request against a given spec.

.. code-block:: python

# raise error if request is invalid
result = validate_request(webhook_request, spec=spec)

Webhook request object should implement OpenAPI WebhookRequest protocol (See `Integrations <https://openapi-core.readthedocs.io/en/latest/integrations.html>`__).

Retrieve request data from validation result

.. code-block:: python
Expand All @@ -95,8 +106,6 @@ Retrieve request data from validation result
# get security data
validated_security = result.security

Request object should implement OpenAPI Request protocol (See `Integrations <https://openapi-core.readthedocs.io/en/latest/integrations.html>`__).

Response
********

Expand All @@ -109,7 +118,16 @@ Use ``validate_response`` function to validate response against a given spec.
# raise error if response is invalid
result = validate_response(request, response, spec=spec)

and unmarshal response data from validation result
Response object should implement OpenAPI Response protocol (See `Integrations <https://openapi-core.readthedocs.io/en/latest/integrations.html>`__).

Use the same function to validate response from webhook request against a given spec.

.. code-block:: python

# raise error if request is invalid
result = validate_response(webhook_request, response, spec=spec)

Retrieve response data from validation result

.. code-block:: python

Expand All @@ -119,12 +137,10 @@ and unmarshal response data from validation result
# get data
validated_data = result.data

Response object should implement OpenAPI Response protocol (See `Integrations <https://openapi-core.readthedocs.io/en/latest/integrations.html>`__).

In order to explicitly validate a:

* OpenAPI 3.0 spec, import ``V30RequestValidator`` or ``V30ResponseValidator``
* OpenAPI 3.1 spec, import ``V31RequestValidator`` or ``V31ResponseValidator``
* OpenAPI 3.1 spec, import ``V31RequestValidator`` or ``V31ResponseValidator`` or ``V31WebhookRequestValidator`` or ``V31WebhookResponseValidator``

.. code:: python

Expand Down
11 changes: 11 additions & 0 deletions docs/integrations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,17 @@ You can use ``RequestsOpenAPIResponse`` as a Requests response factory:
result = validate_response(openapi_request, openapi_response, spec=spec)


You can use ``RequestsOpenAPIWebhookRequest`` as a Requests webhook request factory:

.. code-block:: python

from openapi_core import validate_request
from openapi_core.contrib.requests import RequestsOpenAPIWebhookRequest

openapi_webhook_request = RequestsOpenAPIWebhookRequest(requests_request, "my_webhook")
result = validate_request(openapi_webhook_request, spec=spec)


Starlette
---------

Expand Down
24 changes: 20 additions & 4 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ Use ``validate_request`` function to validate request against a given spec. By d
# raise error if request is invalid
result = validate_request(request, spec=spec)

Request object should implement OpenAPI Request protocol (See :doc:`integrations`).

Use the same function to validate webhook request against a given spec.

.. code-block:: python

# raise error if request is invalid
result = validate_request(webhook_request, spec=spec)

Webhook request object should implement OpenAPI WebhookRequest protocol (See :doc:`integrations`).

Retrieve validated and unmarshalled request data from validation result

.. code-block:: python
Expand All @@ -38,8 +49,6 @@ Retrieve validated and unmarshalled request data from validation result
# get security data
validated_security = result.security

Request object should implement OpenAPI Request protocol (See :doc:`integrations`).

Response
--------

Expand All @@ -52,6 +61,15 @@ Use ``validate_response`` function to validate response against a given spec. By
# raise error if response is invalid
result = validate_response(request, response, spec=spec)

Response object should implement OpenAPI Response protocol (See :doc:`integrations`).

Use the same function to validate response from webhook request against a given spec.

.. code-block:: python

# raise error if request is invalid
result = validate_response(webhook_request, response, spec=spec)

Retrieve validated and unmarshalled response data from validation result

.. code-block:: python
Expand All @@ -62,8 +80,6 @@ Retrieve validated and unmarshalled response data from validation result
# get data
validated_data = result.data

Response object should implement OpenAPI Response protocol (See :doc:`integrations`).

Security
--------

Expand Down
10 changes: 9 additions & 1 deletion openapi_core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""OpenAPI core module"""
from openapi_core.spec import Spec
from openapi_core.validation.request import V3RequestValidator
from openapi_core.validation.request import V3WebhookRequestValidator
from openapi_core.validation.request import V30RequestValidator
from openapi_core.validation.request import V31RequestValidator
from openapi_core.validation.request import V31WebhookRequestValidator
from openapi_core.validation.request import openapi_request_body_validator
from openapi_core.validation.request import (
openapi_request_parameters_validator,
Expand All @@ -13,8 +15,10 @@
from openapi_core.validation.request import openapi_v30_request_validator
from openapi_core.validation.request import openapi_v31_request_validator
from openapi_core.validation.response import V3ResponseValidator
from openapi_core.validation.response import V3WebhookResponseValidator
from openapi_core.validation.response import V30ResponseValidator
from openapi_core.validation.response import V31ResponseValidator
from openapi_core.validation.response import V31WebhookResponseValidator
from openapi_core.validation.response import openapi_response_data_validator
from openapi_core.validation.response import openapi_response_headers_validator
from openapi_core.validation.response import openapi_response_validator
Expand All @@ -36,10 +40,14 @@
"validate_response",
"V30RequestValidator",
"V31RequestValidator",
"V3RequestValidator",
"V30ResponseValidator",
"V31ResponseValidator",
"V31WebhookRequestValidator",
"V31WebhookResponseValidator",
"V3RequestValidator",
"V3ResponseValidator",
"V3WebhookRequestValidator",
"V3WebhookResponseValidator",
"openapi_v3_request_validator",
"openapi_v30_request_validator",
"openapi_v31_request_validator",
Expand Down
4 changes: 4 additions & 0 deletions openapi_core/contrib/requests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from openapi_core.contrib.requests.requests import RequestsOpenAPIRequest
from openapi_core.contrib.requests.requests import (
RequestsOpenAPIWebhookRequest,
)
from openapi_core.contrib.requests.responses import RequestsOpenAPIResponse

__all__ = [
"RequestsOpenAPIRequest",
"RequestsOpenAPIResponse",
"RequestsOpenAPIWebhookRequest",
]
15 changes: 14 additions & 1 deletion openapi_core/contrib/requests/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

class RequestsOpenAPIRequest:
"""
Converts a requests request to an OpenAPI one
Converts a requests request to an OpenAPI request

Internally converts to a `PreparedRequest` first to parse the exact
payload being sent
Expand Down Expand Up @@ -76,3 +76,16 @@ def mimetype(self) -> str:
self.request.headers.get("Content-Type")
or self.request.headers.get("Accept")
)


class RequestsOpenAPIWebhookRequest(RequestsOpenAPIRequest):
"""
Converts a requests request to an OpenAPI Webhook request

Internally converts to a `PreparedRequest` first to parse the exact
payload being sent
"""

def __init__(self, request: Union[Request, PreparedRequest], name: str):
super().__init__(request)
self.name = name
16 changes: 8 additions & 8 deletions openapi_core/security/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,37 @@

from openapi_core.security.exceptions import SecurityError
from openapi_core.spec import Spec
from openapi_core.validation.request.protocols import Request
from openapi_core.validation.request.datatypes import RequestParameters


class BaseProvider:
def __init__(self, scheme: Spec):
self.scheme = scheme

def __call__(self, request: Request) -> Any:
def __call__(self, parameters: RequestParameters) -> Any:
raise NotImplementedError


class UnsupportedProvider(BaseProvider):
def __call__(self, request: Request) -> Any:
def __call__(self, parameters: RequestParameters) -> Any:
warnings.warn("Unsupported scheme type")


class ApiKeyProvider(BaseProvider):
def __call__(self, request: Request) -> Any:
def __call__(self, parameters: RequestParameters) -> Any:
name = self.scheme["name"]
location = self.scheme["in"]
source = getattr(request.parameters, location)
source = getattr(parameters, location)
if name not in source:
raise SecurityError("Missing api key parameter.")
return source[name]


class HttpProvider(BaseProvider):
def __call__(self, request: Request) -> Any:
if "Authorization" not in request.parameters.header:
def __call__(self, parameters: RequestParameters) -> Any:
if "Authorization" not in parameters.header:
raise SecurityError("Missing authorization header.")
auth_header = request.parameters.header["Authorization"]
auth_header = parameters.header["Authorization"]
try:
auth_type, encoded_credentials = auth_header.split(" ", 1)
except ValueError:
Expand Down
8 changes: 4 additions & 4 deletions openapi_core/templating/paths/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
from collections import namedtuple

Path = namedtuple("Path", ["path", "path_result"])
OperationPath = namedtuple(
"OperationPath", ["path", "operation", "path_result"]
PathOperation = namedtuple(
"PathOperation", ["path", "operation", "path_result"]
)
ServerOperationPath = namedtuple(
"ServerOperationPath",
PathOperationServer = namedtuple(
"PathOperationServer",
["path", "operation", "server", "path_result", "server_result"],
)
Loading