Skip to content

Commit

Permalink
feat: openapi request bodies (sparckles#937)
Browse files Browse the repository at this point in the history
* refactor openapi.py

* implement requestBody

* update docs

* update tests

* add title

* update tests

* update docs

* support optional types

* default to object

* parse for custom classes and/or TypedDicts

* uupdate docs

* update tests

* update docs

---------
  • Loading branch information
VishnuSanal authored Sep 4, 2024
1 parent 12d8c0b commit d158bb7
Show file tree
Hide file tree
Showing 5 changed files with 294 additions and 20 deletions.
56 changes: 56 additions & 0 deletions docs_src/src/pages/documentation/api_reference/openapi.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,62 @@ app.include_router(subrouter)

</CodeGroup>

## Other Specification Params

We support all the params mentioned in the latest OpenAPI specifications (https://swagger.io/specification/). See an example of using request body below.

<CodeGroup title="Request Body">

```python {{ title: 'untyped' }}
class Initial(TypedDict):
is_present: bool
letter: Optional[str]


class FullName(TypedDict):
first: str
second: str
initial: Initial


class CreateItemBody(TypedDict):
name: FullName
description: str
price: float
tax: float


@app.post("/")
def create_item(request, body=CreateItemBody):
return request.body
```

```python {{ title: 'typed' }}
class Initial(TypedDict):
is_present: bool
letter: Optional[str]


class FullName(TypedDict):
first: str
second: str
initial: Initial


class CreateItemBody(TypedDict):
name: FullName
description: str
price: float
tax: float


@app.post("/")
def create_item(request: Request, body=CreateItemBody):
return request.body
```

</CodeGroup>

With the reference documentation deployed and running smoothly, Batman had a powerful new tool at his disposal. The Robyn framework had provided him with the flexibility, scalability, and performance needed to create an effective crime-fighting application, giving him a technological edge in his ongoing battle to protect Gotham City.


Expand Down
56 changes: 56 additions & 0 deletions docs_src/src/pages/documentation/example_app/openapi.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,62 @@ app.include_router(subrouter)

</CodeGroup>

## Other Specification Params

We support all the params mentioned in the latest OpenAPI specifications (https://swagger.io/specification/). See an example of using request body below.

<CodeGroup title="Request Body">

```python {{ title: 'untyped' }}
class Initial(TypedDict):
is_present: bool
letter: Optional[str]


class FullName(TypedDict):
first: str
second: str
initial: Initial


class CreateItemBody(TypedDict):
name: FullName
description: str
price: float
tax: float


@app.post("/")
def create_item(request, body=CreateItemBody):
return request.body
```

```python {{ title: 'typed' }}
class Initial(TypedDict):
is_present: bool
letter: Optional[str]


class FullName(TypedDict):
first: str
second: str
initial: Initial


class CreateItemBody(TypedDict):
name: FullName
description: str
price: float
tax: float


@app.post("/")
def create_item(request: Request, body=CreateItemBody):
return request.body
```

</CodeGroup>

With the reference documentation deployed and running smoothly, Batman had a powerful new tool at his disposal. The Robyn framework had provided him with the flexibility, scalability, and performance needed to create an effective crime-fighting application, giving him a technological edge in his ongoing battle to protect Gotham City.


Expand Down
28 changes: 25 additions & 3 deletions integration_tests/base_routes.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import os
import pathlib
from collections import defaultdict
from typing import Optional

from robyn import Headers
from typing import Optional, TypedDict

from integration_tests.subroutes import sub_router, di_subrouter
from integration_tests.views import SyncView, AsyncView
from robyn import Headers
from robyn import (
Request,
Response,
Expand Down Expand Up @@ -840,6 +839,29 @@ def sample_openapi_endpoint():
return 200


class Initial(TypedDict):
is_present: bool
letter: Optional[str]


class FullName(TypedDict):
first: str
second: str
initial: Initial


class CreateItemBody(TypedDict):
name: FullName
description: str
price: float
tax: float


@app.post("/openapi_request_body")
def create_item(request, body=CreateItemBody):
return request.body


def main():
app.set_response_header("server", "robyn")
app.serve_directory(
Expand Down
60 changes: 60 additions & 0 deletions integration_tests/test_openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,63 @@ def test_add_subrouter_paths():
assert route_type in openapi_spec["paths"][endpoint]
assert openapi_description == openapi_spec["paths"][endpoint][route_type]["description"]
assert openapi_tags == openapi_spec["paths"][endpoint][route_type]["tags"]


@pytest.mark.benchmark
def test_openapi_request_body():
openapi_spec = get("/openapi.json").json()

assert isinstance(openapi_spec, dict)

route_type = "post"
endpoint = "/openapi_request_body"

assert endpoint in openapi_spec["paths"]
assert route_type in openapi_spec["paths"][endpoint]
assert "requestBody" in openapi_spec["paths"][endpoint][route_type]
assert "content" in openapi_spec["paths"][endpoint][route_type]["requestBody"]
assert "application/json" in openapi_spec["paths"][endpoint][route_type]["requestBody"]["content"]
assert "schema" in openapi_spec["paths"][endpoint][route_type]["requestBody"]["content"]["application/json"]
assert "properties" in openapi_spec["paths"][endpoint][route_type]["requestBody"]["content"]["application/json"]["schema"]

assert "name" in openapi_spec["paths"][endpoint][route_type]["requestBody"]["content"]["application/json"]["schema"]["properties"]
assert "description" in openapi_spec["paths"][endpoint][route_type]["requestBody"]["content"]["application/json"]["schema"]["properties"]
assert "price" in openapi_spec["paths"][endpoint][route_type]["requestBody"]["content"]["application/json"]["schema"]["properties"]
assert "tax" in openapi_spec["paths"][endpoint][route_type]["requestBody"]["content"]["application/json"]["schema"]["properties"]

assert "string" == openapi_spec["paths"][endpoint][route_type]["requestBody"]["content"]["application/json"]["schema"]["properties"]["description"]["type"]
assert "number" == openapi_spec["paths"][endpoint][route_type]["requestBody"]["content"]["application/json"]["schema"]["properties"]["price"]["type"]
assert "number" == openapi_spec["paths"][endpoint][route_type]["requestBody"]["content"]["application/json"]["schema"]["properties"]["tax"]["type"]

assert "object" == openapi_spec["paths"][endpoint][route_type]["requestBody"]["content"]["application/json"]["schema"]["properties"]["name"]["type"]

assert "first" in openapi_spec["paths"][endpoint][route_type]["requestBody"]["content"]["application/json"]["schema"]["properties"]["name"]["properties"]
assert "second" in openapi_spec["paths"][endpoint][route_type]["requestBody"]["content"]["application/json"]["schema"]["properties"]["name"]["properties"]
assert "initial" in openapi_spec["paths"][endpoint][route_type]["requestBody"]["content"]["application/json"]["schema"]["properties"]["name"]["properties"]

assert (
"object"
in openapi_spec["paths"][endpoint][route_type]["requestBody"]["content"]["application/json"]["schema"]["properties"]["name"]["properties"]["initial"][
"type"
]
)

assert (
"is_present"
in openapi_spec["paths"][endpoint][route_type]["requestBody"]["content"]["application/json"]["schema"]["properties"]["name"]["properties"]["initial"][
"properties"
]
)
assert (
"letter"
in openapi_spec["paths"][endpoint][route_type]["requestBody"]["content"]["application/json"]["schema"]["properties"]["name"]["properties"]["initial"][
"properties"
]
)

assert {"type": "string"} in openapi_spec["paths"][endpoint][route_type]["requestBody"]["content"]["application/json"]["schema"]["properties"]["name"][
"properties"
]["initial"]["properties"]["letter"]["anyOf"]
assert {"type": "null"} in openapi_spec["paths"][endpoint][route_type]["requestBody"]["content"]["application/json"]["schema"]["properties"]["name"][
"properties"
]["initial"]["properties"]["letter"]["anyOf"]
Loading

0 comments on commit d158bb7

Please sign in to comment.