Skip to content
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

fix: JSON schema examples were OpenAPI formatted #3224

Merged

Conversation

tuukkamustonen
Copy link
Contributor

@tuukkamustonen tuukkamustonen commented Mar 18, 2024

Description

The generated examples in JSON schema objects were formatted as:

    "examples": {
      "some-id": {
        "description": "Lorem ipsum",
        "value": "the real beef"
      }
   }

However, above is OpenAPI example format, and must not be used in JSON schema
objects. Schema objects follow different formatting:

    "examples": [
      "the real beef"
   ]

This is explained in
https://medium.com/apis-you-wont-hate/openapi-v3-1-and-json-schema-2019-09-6862cf3db959

Schema objects spec in
https://spec.openapis.org/oas/v3.1.0#schema-object.

OpenAPI example format spec in
https://spec.openapis.org/oas/v3.1.0#example-object. This is referenced at
least from parameters, media types and components.

The technical change here is to define Schema.examples as list[Any] instead
of list[Example]. Examples can and must still be defined as list[Example]
for OpenAPI objects (e.g. Parameter, Body) but for JSON schema examples
the code now internally generates/converts list[Any] format instead.

Extra confusion here comes from the OpenAPI 3.0 vs OpenAPI 3.1 difference.
OpenAPI 3.0 only allowed example (singular) field in schema objects.
OpenAPI 3.1 supports the full JSON schema 2020-12 spec and so examples array
in schema objects.

Both example and examples seem to be supported, though the former is marked
as deprecated in the latest specs.

This can be tested over at https://editor-next.swagger.io by loading up the
OpenAPI 3.1 Pet store example. Then add examples in components.schemas.Pet
using the both ways and see the Swagger UI only render the example once it's
properly formatted (it ignores is otherwise).

Closes

n/a

Partially fixes #2849 (the OpenAPI UIs don't render badly formatted examples in the spec).

@tuukkamustonen tuukkamustonen changed the title WIP: fix: JSON schema examples were OpenAPI formatted fix: JSON schema examples were OpenAPI formatted Mar 18, 2024
Copy link

codecov bot commented Mar 18, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.23%. Comparing base (024dccf) to head (2ea9324).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3224      +/-   ##
==========================================
- Coverage   98.24%   98.23%   -0.01%     
==========================================
  Files         320      320              
  Lines       14445    14446       +1     
  Branches     2298     2298              
==========================================
  Hits        14191    14191              
  Misses        113      113              
- Partials      141      142       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@tuukkamustonen tuukkamustonen force-pushed the fix-json-schema-example-format branch from 5df2c3c to d8606b5 Compare March 18, 2024 09:35
@guacs
Copy link
Member

guacs commented Mar 18, 2024

@tuukkamustonen thanks for this! This is something I'd noticed and was meaning to fix, but hadn't gotten the time to actually investigate.

@tuukkamustonen
Copy link
Contributor Author

Let's see if I can be type linters pass... interesting errors...

@provinzkraut
Copy link
Member

Is this a regression @guacs? I vaguely remember a PR that fixed a similar (or actually the same) bug?

@guacs
Copy link
Member

guacs commented Mar 18, 2024

Is this a regression @guacs? I vaguely remember a PR that fixed a similar (or actually the same) bug?

If I remember correctly, that PR was for the rendering of examples of things like path parameters, query parameters etc. This is for the examples of the schemas we generate.

@tuukkamustonen
Copy link
Contributor Author

I'm getting these errors even on latest main:

=> Running mypy
tests/e2e/test_pydantic.py:11: error: Function is missing a type annotation  [no-untyped-def]
tests/unit/test_contrib/test_pydantic/test_schema_plugin.py:73: error: Function is missing a type annotation  [no-untyped-def]
tests/unit/test_openapi/test_responses.py:1: error: Cannot find implementation or library stub for module named "litestar.openapi.plugins"  [import-not-found]
tests/unit/test_openapi/test_responses.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
tests/unit/test_security/test_security.py:1: error: Cannot find implementation or library stub for module named "litestar.openapi.plugins"  [import-not-found]
Found 4 errors in 4 files (checked 691 source files)

In develop:

=> Running mypy
tests/e2e/test_pydantic.py:11: error: Function is missing a type annotation  [no-untyped-def]
tests/unit/test_contrib/test_pydantic/test_schema_plugin.py:73: error: Function is missing a type annotation  [no-untyped-def]
Found 2 errors in 2 files (checked 697 source files)

The generated `examples` in *JSON schema* objects were formatted as:

```json
    "examples": {
      "some-id": {
        "description": "Lorem ipsum",
        "value": "the real beef"
      }
   }
```

However, above is OpenAPI example format, and must not be used in JSON schema
objects. Schema objects follow different formatting:

```json
    "examples": [
      "the real beef"
   ]
```

This is explained in
https://medium.com/apis-you-wont-hate/openapi-v3-1-and-json-schema-2019-09-6862cf3db959

Schema objects spec in
https://spec.openapis.org/oas/v3.1.0#schema-object.

OpenAPI example format spec in
https://spec.openapis.org/oas/v3.1.0#example-object. This is referenced at
least from parameters, media types and components.

The technical change here is to define `Schema.examples` as `list[Any]` instead
of `list[Example]`. Examples can and must still be defined as `list[Example]`
for OpenAPI objects (e.g. `Parameter`, `Body`) but for JSON schema `examples`
the code now internally generates/converts `list[Any]` format instead.

Extra confusion here comes from the OpenAPI 3.0 vs OpenAPI 3.1 difference.
OpenAPI 3.0 only allowed `example` (singular) field in schema objects.
OpenAPI 3.1 supports the full JSON schema 2020-12 spec and so `examples` array
in schema objects.

Both `example` and `examples` seem to be supported, though the former is marked
as deprecated in the latest specs.

This can be tested over at https://editor-next.swagger.io by loading up the
OpenAPI 3.1 Pet store example. Then add `examples` in `components.schemas.Pet`
using the both ways and see the Swagger UI only render the example once it's
properly formatted (it ignores is otherwise).
@tuukkamustonen tuukkamustonen force-pushed the fix-json-schema-example-format branch from d8606b5 to 29e018a Compare March 18, 2024 21:05
@tuukkamustonen tuukkamustonen marked this pull request as ready for review March 18, 2024 21:22
@tuukkamustonen tuukkamustonen requested review from a team as code owners March 18, 2024 21:22
@tuukkamustonen
Copy link
Contributor Author

Worked after rebase on latest main now, okay...

@peterschutt
Copy link
Contributor

Is this a regression @guacs? I vaguely remember a PR that fixed a similar (or actually the same) bug?

I think this #2660 which was a fix for #2272

@peterschutt
Copy link
Contributor

This was the MCVE from #2272:

from typing_extensions import Annotated
from msgspec import Struct, Meta
from litestar import Litestar, post


class TestBody(Struct):
    field1: Annotated[str, Meta(examples=["the answer"])]
    field2: Annotated[int, Meta(examples=[42])]


@post(sync_to_thread=False)
def test(data: TestBody) -> None:
    ...


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app=Litestar(route_handlers=[test]), port=8080)

If I run that app on main we get:
image

Which doesn't include the example values for those fields, so either we've had a regression or that wasn't fixed.

Same app on this branch:

image

So LGTM on that front.

litestar/typing.py Outdated Show resolved Hide resolved
litestar/_openapi/schema_generation/schema.py Outdated Show resolved Hide resolved
litestar/_openapi/schema_generation/utils.py Outdated Show resolved Hide resolved
@guacs
Copy link
Member

guacs commented Mar 20, 2024

This was the MCVE from #2272:

from typing_extensions import Annotated
from msgspec import Struct, Meta
from litestar import Litestar, post


class TestBody(Struct):
    field1: Annotated[str, Meta(examples=["the answer"])]
    field2: Annotated[int, Meta(examples=[42])]


@post(sync_to_thread=False)
def test(data: TestBody) -> None:
    ...


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app=Litestar(route_handlers=[test]), port=8080)

If I run that app on main we get: image

Which doesn't include the example values for those fields, so either we've had a regression or that wasn't fixed.

Same app on this branch:

image

So LGTM on that front.

@peterschutt how were you able to verify that things were working with the fix introduced in the linked PR? I tried running the app in this comment after checking out to 680c2df00, but I still was getting this incorrect example. I think I may be doing something wrong.

@tuukkamustonen
Copy link
Contributor Author

@peterschutt Fixed as suggested in the fixup commit. Great that you spotted that! Take another look.

@guacs I also tried the MVCE there, and it does work for me 🤷🏻‍♂️

@tuukkamustonen tuukkamustonen force-pushed the fix-json-schema-example-format branch from 12ef244 to 636e036 Compare March 20, 2024 19:29
Copy link

Documentation preview will be available shortly at https://litestar-org.github.io/litestar-docs-preview/3224

@peterschutt
Copy link
Contributor

@peterschutt how were you able to verify that things were working with the fix introduced in the linked PR? I tried running the app in this comment after checking out to 680c2df00, but I still was getting this incorrect example. I think I may be doing something wrong.

My verification was that it was incorrect on main and correct on this branch - given that you get the incorrect behavior on 680c2df00, and I get same on main and 680c2df00 was merged into main, I think we are saying the same thing. Unless I'm misunderstanding (very possible :D)

Copy link
Contributor

@peterschutt peterschutt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @tuukkamustonen!

@peterschutt peterschutt merged commit e763cea into litestar-org:main Mar 21, 2024
20 checks passed
@tuukkamustonen tuukkamustonen deleted the fix-json-schema-example-format branch May 24, 2024 06:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Enhancement: Support OpenAPI examples defined in Structs, Pydantic models and dataclasses
4 participants