Adding upstream version 0.5.1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
303fa6e9d8
commit
97e6d74bac
110 changed files with 12006 additions and 0 deletions
23
.devcontainer/devcontainer.json
Normal file
23
.devcontainer/devcontainer.json
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"name": "openapi-pydantic",
|
||||||
|
"image": "mcr.microsoft.com/devcontainers/python:0-3.11",
|
||||||
|
"features": {
|
||||||
|
"ghcr.io/devcontainers-contrib/features/poetry:2": {
|
||||||
|
"version": "latest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"containerEnv": {
|
||||||
|
"POETRY_VIRTUALENVS_IN_PROJECT": "true"
|
||||||
|
},
|
||||||
|
"postCreateCommand": "poetry install && pip install --upgrade tox",
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"extensions": [
|
||||||
|
"ms-python.python",
|
||||||
|
"ms-python.vscode-pylance",
|
||||||
|
"ms-python.black-formatter",
|
||||||
|
"charliermarsh.ruff"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
.github/release.yml
vendored
Normal file
24
.github/release.yml
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
changelog:
|
||||||
|
exclude:
|
||||||
|
labels:
|
||||||
|
- ignore-for-release
|
||||||
|
categories:
|
||||||
|
- title: Breaking 💥
|
||||||
|
labels:
|
||||||
|
- breaking
|
||||||
|
- title: Added 🎉
|
||||||
|
labels:
|
||||||
|
- feature
|
||||||
|
- title: Changed 🛠
|
||||||
|
labels:
|
||||||
|
- change
|
||||||
|
- title: Fixed 🐛
|
||||||
|
labels:
|
||||||
|
- fix
|
||||||
|
- bug
|
||||||
|
- title: Dependencies 📦
|
||||||
|
labels:
|
||||||
|
- dependencies
|
||||||
|
- title: Docs 📝
|
||||||
|
labels:
|
||||||
|
- documentation
|
69
.github/workflows/publish.yml
vendored
Normal file
69
.github/workflows/publish.yml
vendored
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
name: Publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
release:
|
||||||
|
types:
|
||||||
|
- published
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build package
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: 3.12
|
||||||
|
- name: Install Poetry
|
||||||
|
uses: snok/install-poetry@v1
|
||||||
|
with:
|
||||||
|
virtualenvs-create: false
|
||||||
|
version: 1.8.3
|
||||||
|
- name: Build package distribution
|
||||||
|
run: poetry build
|
||||||
|
- name: Upload package artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: distribution
|
||||||
|
path: dist
|
||||||
|
test-release:
|
||||||
|
name: Publish release to Test PyPI
|
||||||
|
if: github.event_name == 'workflow_dispatch'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
environment:
|
||||||
|
name: test
|
||||||
|
url: https://test.pypi.org/p/openapi-pydantic
|
||||||
|
permissions:
|
||||||
|
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
||||||
|
steps:
|
||||||
|
- name: Download package distribution
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: distribution
|
||||||
|
path: dist
|
||||||
|
- name: Publish package distribution to Test PyPI
|
||||||
|
uses: pypa/gh-action-pypi-publish@release/v1
|
||||||
|
with:
|
||||||
|
repository-url: https://test.pypi.org/legacy/
|
||||||
|
release:
|
||||||
|
name: Publish release to PyPI
|
||||||
|
if: github.event_name == 'release'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
environment:
|
||||||
|
name: production
|
||||||
|
url: https://pypi.org/p/openapi-pydantic
|
||||||
|
permissions:
|
||||||
|
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
||||||
|
steps:
|
||||||
|
- name: Download package distribution
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: distribution
|
||||||
|
path: dist
|
||||||
|
- name: Publish package distribution to PyPI
|
||||||
|
uses: pypa/gh-action-pypi-publish@release/v1
|
33
.github/workflows/test.yml
vendored
Normal file
33
.github/workflows/test.yml
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
name: Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tox:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
python -m pip install tox tox-gh-actions
|
||||||
|
- name: Install Poetry
|
||||||
|
uses: snok/install-poetry@v1
|
||||||
|
with:
|
||||||
|
version: 1.8.3
|
||||||
|
virtualenvs-create: true
|
||||||
|
virtualenvs-in-project: true
|
||||||
|
- name: Run tox test suite
|
||||||
|
run: tox
|
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
__pycache__
|
||||||
|
/.idea
|
||||||
|
/.mypy_cache
|
||||||
|
/.pytest_cache
|
||||||
|
/.ruff_cache
|
||||||
|
/.tox
|
||||||
|
/.venv
|
||||||
|
/*.egg-info
|
||||||
|
/build
|
||||||
|
/dist
|
16
.vscode/settings.json
vendored
Normal file
16
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"python.testing.pytestArgs": [
|
||||||
|
"tests",
|
||||||
|
"-vv"
|
||||||
|
],
|
||||||
|
"python.testing.unittestEnabled": false,
|
||||||
|
"python.testing.pytestEnabled": true,
|
||||||
|
"python.analysis.typeCheckingMode": "basic",
|
||||||
|
"python.linting.enabled": true,
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll": "explicit",
|
||||||
|
"source.organizeImports": "explicit"
|
||||||
|
},
|
||||||
|
"editor.defaultFormatter": "ms-python.black-formatter"
|
||||||
|
}
|
42
CONTRIBUTING.md
Normal file
42
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# OpenAPI Pydantic Contribution Guide
|
||||||
|
|
||||||
|
We welcome all contributions!
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
|
||||||
|
Questions, feature requests and bug reports are all welcome as issues. When raising a bug or
|
||||||
|
question, please include as much information as possible including the specific version you
|
||||||
|
are using.
|
||||||
|
|
||||||
|
## Pull Requests
|
||||||
|
|
||||||
|
It should be very simple to get started and open a pull request, however for anything non-trivial
|
||||||
|
please open an issue to discuss your intended change _before_ creating your PR. This avoids wasting
|
||||||
|
time by ensuring that your changes will be accepted with fewer revisions down the line!
|
||||||
|
|
||||||
|
### Local Development
|
||||||
|
|
||||||
|
A [devcontainer](https://code.visualstudio.com/docs/devcontainers/containers) configuration is provided in the repo to get your environment setup automatically. Alternatively you can install [tox](https://tox.wiki/en/latest/) and [poetry](https://python-poetry.org/) manually.
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
Please ensure all changes have good test coverage and are formatted correctly. You can run the test
|
||||||
|
suite and linters using [tox](https://tox.wiki/en/latest/) - just run `tox` from the root of this
|
||||||
|
repo to run the checks. These will also be run automatically in CI once your PR is opened. Don't
|
||||||
|
worry about testing against every Ptyhon version - the CI action will do this for you!
|
||||||
|
|
||||||
|
### Tagging
|
||||||
|
|
||||||
|
When your PR is ready, please tag it with the appropriate tags: one of `feature`, `change`, `fix`,
|
||||||
|
as well as `breaking` if you've introduced backwards-incompatible changes to the public API or
|
||||||
|
behaviour.
|
||||||
|
|
||||||
|
## Review
|
||||||
|
|
||||||
|
We'll review your PR as soon as possible - either approving or requesting changes. Once the PR is
|
||||||
|
approved, it will be merged into main and cut into the next release.
|
||||||
|
|
||||||
|
## Releases
|
||||||
|
|
||||||
|
The release schedule is not set in stone and will depend on the number of changes in flight, but where possible we'll look to cut a release with your changes as soon as possible. Once a new version is tagged,
|
||||||
|
a package version is uploaded to PyPI automatically.
|
40
LICENSE
Normal file
40
LICENSE
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 mike-oakley
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Forked from the original implementation by Kuimono, under MIT licence, from the repository at:
|
||||||
|
https://github.com/kuimono/openapi-schema-pydantic
|
||||||
|
|
||||||
|
Copyright (c) 2020 Kuimono
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
License URL: https://github.com/kuimono/openapi-schema-pydantic/blob/master/LICENSE
|
322
README.md
Normal file
322
README.md
Normal file
|
@ -0,0 +1,322 @@
|
||||||
|
# openapi-pydantic
|
||||||
|
|
||||||
|
[](https://pypi.org/project/openapi-pydantic/)
|
||||||
|
[](https://github.com/mike-oakley/openapi-pydantic/blob/main/LICENSE)
|
||||||
|
|
||||||
|
OpenAPI schema implemented in [Pydantic](https://github.com/samuelcolvin/pydantic). Both Pydantic 1.8+ and 2.x are supported.
|
||||||
|
|
||||||
|
The naming of the classes follows the schema in
|
||||||
|
[OpenAPI specification](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.1.md#schema).
|
||||||
|
|
||||||
|
> This library is forked from [OpenAPI Schema Pydantic](https://github.com/kuimono/openapi-schema-pydantic) (at version [1.2.4](https://github.com/kuimono/openapi-schema-pydantic/releases/tag/v1.2.4)) which is no longer actively maintained.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
`pip install openapi-pydantic`
|
||||||
|
|
||||||
|
## Try me
|
||||||
|
|
||||||
|
```python
|
||||||
|
from openapi_pydantic import OpenAPI, Info, PathItem, Operation, Response
|
||||||
|
|
||||||
|
# Construct OpenAPI by pydantic objects
|
||||||
|
open_api = OpenAPI(
|
||||||
|
info=Info(
|
||||||
|
title="My own API",
|
||||||
|
version="v0.0.1",
|
||||||
|
),
|
||||||
|
paths={
|
||||||
|
"/ping": PathItem(
|
||||||
|
get=Operation(
|
||||||
|
responses={
|
||||||
|
"200": Response(
|
||||||
|
description="pong"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
# Note: for Pydantic 1.x, replace `model_dump_json` with `json`
|
||||||
|
print(open_api.model_dump_json(by_alias=True, exclude_none=True, indent=2))
|
||||||
|
```
|
||||||
|
|
||||||
|
Result:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"openapi": "3.1.1",
|
||||||
|
"info": {
|
||||||
|
"title": "My own API",
|
||||||
|
"version": "v0.0.1"
|
||||||
|
},
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"url": "/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"/ping": {
|
||||||
|
"get": {
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "pong"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Take advantage of Pydantic
|
||||||
|
|
||||||
|
Pydantic is a great tool. It allows you to use object / dict / mixed data for input.
|
||||||
|
|
||||||
|
The following examples give the same OpenAPI result as above:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from openapi_pydantic import parse_obj, OpenAPI, PathItem, Response
|
||||||
|
|
||||||
|
# Construct OpenAPI from dict, inferring the correct schema version
|
||||||
|
open_api = parse_obj({
|
||||||
|
"openapi": "3.1.1",
|
||||||
|
"info": {"title": "My own API", "version": "v0.0.1"},
|
||||||
|
"paths": {
|
||||||
|
"/ping": {
|
||||||
|
"get": {"responses": {"200": {"description": "pong"}}}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
# Construct OpenAPI v3.1 schema from dict
|
||||||
|
# Note: for Pydantic 1.x, replace `model_validate` with `parse_obj`
|
||||||
|
open_api = OpenAPI.model_validate({
|
||||||
|
"info": {"title": "My own API", "version": "v0.0.1"},
|
||||||
|
"paths": {
|
||||||
|
"/ping": {
|
||||||
|
"get": {"responses": {"200": {"description": "pong"}}}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
# Construct OpenAPI with mix of dict/object
|
||||||
|
# Note: for Pydantic 1.x, replace `model_validate` with `parse_obj`
|
||||||
|
open_api = OpenAPI.model_validate({
|
||||||
|
"info": {"title": "My own API", "version": "v0.0.1"},
|
||||||
|
"paths": {
|
||||||
|
"/ping": PathItem(
|
||||||
|
get={"responses": {"200": Response(description="pong")}}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use Pydantic classes as schema
|
||||||
|
|
||||||
|
- The [Schema Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.4.md#schemaObject)
|
||||||
|
in OpenAPI has definitions and tweaks in JSON Schema, which are hard to comprehend and define a good data class
|
||||||
|
- Pydantic already has a good way to [create JSON schema](https://pydantic-docs.helpmanual.io/usage/schema/).
|
||||||
|
Let's not reinvent the wheel.
|
||||||
|
|
||||||
|
The approach to deal with this:
|
||||||
|
|
||||||
|
1. Use `PydanticSchema` objects to represent the `Schema` in `OpenAPI` object
|
||||||
|
2. Invoke `construct_open_api_with_schema_class` to resolve the JSON schemas and references
|
||||||
|
|
||||||
|
```python
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from openapi_pydantic import OpenAPI
|
||||||
|
from openapi_pydantic.util import PydanticSchema, construct_open_api_with_schema_class
|
||||||
|
|
||||||
|
def construct_base_open_api() -> OpenAPI:
|
||||||
|
# Note: for Pydantic 1.x, replace `model_validate` with `parse_obj`
|
||||||
|
return OpenAPI.model_validate({
|
||||||
|
"info": {"title": "My own API", "version": "v0.0.1"},
|
||||||
|
"paths": {
|
||||||
|
"/ping": {
|
||||||
|
"post": {
|
||||||
|
"requestBody": {"content": {"application/json": {
|
||||||
|
"schema": PydanticSchema(schema_class=PingRequest)
|
||||||
|
}}},
|
||||||
|
"responses": {"200": {
|
||||||
|
"description": "pong",
|
||||||
|
"content": {"application/json": {
|
||||||
|
"schema": PydanticSchema(schema_class=PingResponse)
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
class PingRequest(BaseModel):
|
||||||
|
"""Ping Request"""
|
||||||
|
req_foo: str = Field(description="foo value of the request")
|
||||||
|
req_bar: str = Field(description="bar value of the request")
|
||||||
|
|
||||||
|
class PingResponse(BaseModel):
|
||||||
|
"""Ping response"""
|
||||||
|
resp_foo: str = Field(description="foo value of the response")
|
||||||
|
resp_bar: str = Field(description="bar value of the response")
|
||||||
|
|
||||||
|
open_api = construct_base_open_api()
|
||||||
|
open_api = construct_open_api_with_schema_class(open_api)
|
||||||
|
|
||||||
|
# print the result openapi.json
|
||||||
|
# Note: for Pydantic 1.x, replace `model_dump_json` with `json`
|
||||||
|
print(open_api.model_dump_json(by_alias=True, exclude_none=True, indent=2))
|
||||||
|
```
|
||||||
|
|
||||||
|
Result:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"openapi": "3.1.1",
|
||||||
|
"info": {
|
||||||
|
"title": "My own API",
|
||||||
|
"version": "v0.0.1"
|
||||||
|
},
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"url": "/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"/ping": {
|
||||||
|
"post": {
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/PingRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "pong",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/PingResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deprecated": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"PingRequest": {
|
||||||
|
"title": "PingRequest",
|
||||||
|
"required": [
|
||||||
|
"req_foo",
|
||||||
|
"req_bar"
|
||||||
|
],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"req_foo": {
|
||||||
|
"title": "Req Foo",
|
||||||
|
"type": "string",
|
||||||
|
"description": "foo value of the request"
|
||||||
|
},
|
||||||
|
"req_bar": {
|
||||||
|
"title": "Req Bar",
|
||||||
|
"type": "string",
|
||||||
|
"description": "bar value of the request"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "Ping Request"
|
||||||
|
},
|
||||||
|
"PingResponse": {
|
||||||
|
"title": "PingResponse",
|
||||||
|
"required": [
|
||||||
|
"resp_foo",
|
||||||
|
"resp_bar"
|
||||||
|
],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"resp_foo": {
|
||||||
|
"title": "Resp Foo",
|
||||||
|
"type": "string",
|
||||||
|
"description": "foo value of the response"
|
||||||
|
},
|
||||||
|
"resp_bar": {
|
||||||
|
"title": "Resp Bar",
|
||||||
|
"type": "string",
|
||||||
|
"description": "bar value of the response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "Ping response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
### Use of OpenAPI.model_dump() / OpenAPI.model_dump_json() / OpenAPI.json() / OpenAPI.dict()
|
||||||
|
|
||||||
|
When using `OpenAPI.model_dump()` / `OpenAPI.model_dump_json()` / `OpenAPI.json()` / `OpenAPI.dict()` functions,
|
||||||
|
the arguments `by_alias=True, exclude_none=True` have to be in place.
|
||||||
|
Otherwise the resulting json will not fit the OpenAPI standard.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# OK (Pydantic 2)
|
||||||
|
open_api.model_dump_json(by_alias=True, exclude_none=True, indent=2)
|
||||||
|
# OK (Pydantic 1)
|
||||||
|
open_api.json(by_alias=True, exclude_none=True, indent=2)
|
||||||
|
|
||||||
|
# Not good
|
||||||
|
open_api.model_dump_json(indent=2)
|
||||||
|
open_api.json(indent=2)
|
||||||
|
```
|
||||||
|
|
||||||
|
More info about field aliases:
|
||||||
|
|
||||||
|
| OpenAPI version | Field alias info |
|
||||||
|
| --------------- | ---------------- |
|
||||||
|
| 3.1 | [here](https://github.com/mike-oakley/openapi-pydantic/blob/main/openapi_pydantic/v3/v3_1/README.md#alias) |
|
||||||
|
| 3.0 | [here](https://github.com/mike-oakley/openapi-pydantic/blob/main/openapi_pydantic/v3/v3_0/README.md#alias) |
|
||||||
|
|
||||||
|
### Non-pydantic schema types
|
||||||
|
|
||||||
|
Some schema types are not implemented as pydantic classes.
|
||||||
|
Please refer to the following for more info:
|
||||||
|
|
||||||
|
| OpenAPI version | Non-pydantic schema type info |
|
||||||
|
| --------------- | ----------------------------- |
|
||||||
|
| 3.1 | [here](https://github.com/mike-oakley/openapi-pydantic/blob/main/openapi_pydantic/v3/v3_1/README.md#non-pydantic-schema-types) |
|
||||||
|
| 3.0 | [here](https://github.com/mike-oakley/openapi-pydantic/blob/main/openapi_pydantic/v3/v3_0/README.md#non-pydantic-schema-types) |
|
||||||
|
|
||||||
|
### Use OpenAPI 3.0 instead of 3.1
|
||||||
|
|
||||||
|
Some UI renderings (e.g. Swagger) still do not support OpenAPI 3.1.x.
|
||||||
|
The old 3.0.x version is available by importing from different paths:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from openapi_pydantic.v3.v3_0 import OpenAPI, ...
|
||||||
|
from openapi_pydantic.v3.v3_0.util import PydanticSchema, construct_open_api_with_schema_class
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pydantic version compatibility
|
||||||
|
|
||||||
|
Compatibility with both major versions of Pydantic (1.8+ and 2.*) is mostly achieved using a module called `compat.py`. It detects the installed version of Pydantic and exports version-specific symbols for use by the rest of the package. It also provides all symbols necessary for type checking. The `compat.py` module is not intended to be imported by other packages, but other packages may find it helpful as an example of how to span major versions of Pydantic.
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
This library is based from the original implementation by Kuimono of [OpenAPI Schema Pydantic](https://github.com/kuimono/openapi-schema-pydantic) which is no longer actively maintained.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT License](https://github.com/mike-oakley/openapi-pydantic/blob/main/LICENSE)
|
13
SECURITY.md
Normal file
13
SECURITY.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
As we're a small project and have limited maintainer capacity, only the latest major version of OpenAPI Pydantic is actively maintained. Whilst
|
||||||
|
we are pre-v1, this extends to the latest minor version - so only the latest 0.x version is actively maintained. Please update to the latest available
|
||||||
|
version before reporting bugs or security vulnerabilities.
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
To report a vulnerability associated with this project, use the [GitHub vulnerability reporting tool](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability).
|
||||||
|
Head over to the [Security](https://github.com/mike-oakley/openapi-pydantic/security) tab
|
||||||
|
and click **Report a vulnerability** to open the advisory form.
|
38
openapi_pydantic/__init__.py
Normal file
38
openapi_pydantic/__init__.py
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from .v3 import XML as XML
|
||||||
|
from .v3 import Callback as Callback
|
||||||
|
from .v3 import Components as Components
|
||||||
|
from .v3 import Contact as Contact
|
||||||
|
from .v3 import DataType as DataType
|
||||||
|
from .v3 import Discriminator as Discriminator
|
||||||
|
from .v3 import Encoding as Encoding
|
||||||
|
from .v3 import Example as Example
|
||||||
|
from .v3 import ExternalDocumentation as ExternalDocumentation
|
||||||
|
from .v3 import Header as Header
|
||||||
|
from .v3 import Info as Info
|
||||||
|
from .v3 import License as License
|
||||||
|
from .v3 import Link as Link
|
||||||
|
from .v3 import MediaType as MediaType
|
||||||
|
from .v3 import OAuthFlow as OAuthFlow
|
||||||
|
from .v3 import OAuthFlows as OAuthFlows
|
||||||
|
from .v3 import OpenAPI as OpenAPI
|
||||||
|
from .v3 import Operation as Operation
|
||||||
|
from .v3 import Parameter as Parameter
|
||||||
|
from .v3 import ParameterLocation as ParameterLocation
|
||||||
|
from .v3 import PathItem as PathItem
|
||||||
|
from .v3 import Paths as Paths
|
||||||
|
from .v3 import Reference as Reference
|
||||||
|
from .v3 import RequestBody as RequestBody
|
||||||
|
from .v3 import Response as Response
|
||||||
|
from .v3 import Responses as Responses
|
||||||
|
from .v3 import Schema as Schema
|
||||||
|
from .v3 import SecurityRequirement as SecurityRequirement
|
||||||
|
from .v3 import SecurityScheme as SecurityScheme
|
||||||
|
from .v3 import Server as Server
|
||||||
|
from .v3 import ServerVariable as ServerVariable
|
||||||
|
from .v3 import Tag as Tag
|
||||||
|
from .v3 import parse_obj as parse_obj
|
||||||
|
from .v3 import schema_validate as schema_validate
|
||||||
|
|
||||||
|
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
119
openapi_pydantic/compat.py
Normal file
119
openapi_pydantic/compat.py
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
"""Compatibility layer to make this package usable with Pydantic 1 or 2"""
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
|
from pydantic.version import VERSION as PYDANTIC_VERSION
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"PYDANTIC_V2",
|
||||||
|
"ConfigDict",
|
||||||
|
"JsonSchemaMode",
|
||||||
|
"models_json_schema",
|
||||||
|
"RootModel",
|
||||||
|
"Extra",
|
||||||
|
"v1_schema",
|
||||||
|
"DEFS_KEY",
|
||||||
|
"min_length_arg",
|
||||||
|
]
|
||||||
|
|
||||||
|
PYDANTIC_MAJOR_VERSION = int(PYDANTIC_VERSION.split(".", 1)[0])
|
||||||
|
PYDANTIC_MINOR_VERSION = int(PYDANTIC_VERSION.split(".")[1])
|
||||||
|
PYDANTIC_V2 = PYDANTIC_MAJOR_VERSION >= 2
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
# Provide stubs for either version of Pydantic
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Any, Literal, Type, TypedDict
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from pydantic import ConfigDict as PydanticConfigDict
|
||||||
|
|
||||||
|
def ConfigDict(
|
||||||
|
extra: Literal["allow", "ignore", "forbid"] = "allow",
|
||||||
|
json_schema_extra: Optional[Dict[str, Any]] = None,
|
||||||
|
populate_by_name: bool = True,
|
||||||
|
) -> PydanticConfigDict:
|
||||||
|
"""Stub for pydantic.ConfigDict in Pydantic 2"""
|
||||||
|
...
|
||||||
|
|
||||||
|
class Extra(Enum):
|
||||||
|
"""Stub for pydantic.Extra in Pydantic 1"""
|
||||||
|
|
||||||
|
allow = "allow"
|
||||||
|
ignore = "ignore"
|
||||||
|
forbid = "forbid"
|
||||||
|
|
||||||
|
class RootModel(BaseModel):
|
||||||
|
"""Stub for pydantic.RootModel in Pydantic 2"""
|
||||||
|
|
||||||
|
JsonSchemaMode = Literal["validation", "serialization"]
|
||||||
|
|
||||||
|
def models_json_schema(
|
||||||
|
models: List[Tuple[Type[BaseModel], JsonSchemaMode]],
|
||||||
|
*,
|
||||||
|
by_alias: bool = True,
|
||||||
|
ref_template: str = "#/$defs/{model}",
|
||||||
|
schema_generator: Optional[type] = None,
|
||||||
|
) -> Tuple[Dict, Dict[str, Any]]:
|
||||||
|
"""Stub for pydantic.json_schema.models_json_schema in Pydantic 2"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def v1_schema(
|
||||||
|
models: List[Type[BaseModel]],
|
||||||
|
*,
|
||||||
|
by_alias: bool = True,
|
||||||
|
ref_prefix: str = "#/$defs",
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""Stub for pydantic.schema.schema in Pydantic 1"""
|
||||||
|
...
|
||||||
|
|
||||||
|
DEFS_KEY = "$defs"
|
||||||
|
|
||||||
|
class MinLengthArg(TypedDict):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def min_length_arg(min_length: int) -> MinLengthArg:
|
||||||
|
"""Generate a min_length or min_items parameter for Field(...)"""
|
||||||
|
...
|
||||||
|
|
||||||
|
elif PYDANTIC_V2:
|
||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
|
from pydantic import ConfigDict, RootModel
|
||||||
|
from pydantic.json_schema import JsonSchemaMode, models_json_schema
|
||||||
|
|
||||||
|
# Pydantic 2 renders JSON schemas using the keyword "$defs"
|
||||||
|
DEFS_KEY = "$defs"
|
||||||
|
|
||||||
|
class MinLengthArg(TypedDict):
|
||||||
|
min_length: int
|
||||||
|
|
||||||
|
def min_length_arg(min_length: int) -> MinLengthArg:
|
||||||
|
return {"min_length": min_length}
|
||||||
|
|
||||||
|
# Create V1 stubs. These should not be used when PYDANTIC_V2 is true.
|
||||||
|
Extra = None
|
||||||
|
v1_schema = None
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
|
from pydantic import Extra
|
||||||
|
from pydantic.schema import schema as v1_schema
|
||||||
|
|
||||||
|
# Pydantic 1 renders JSON schemas using the keyword "definitions"
|
||||||
|
DEFS_KEY = "definitions"
|
||||||
|
|
||||||
|
class MinLengthArg(TypedDict):
|
||||||
|
min_items: int
|
||||||
|
|
||||||
|
def min_length_arg(min_length: int) -> MinLengthArg:
|
||||||
|
return {"min_items": min_length}
|
||||||
|
|
||||||
|
# Create V2 stubs. These should not be used when PYDANTIC_V2 is false.
|
||||||
|
ConfigDict = None
|
||||||
|
models_json_schema = None
|
||||||
|
JsonSchemaMode = None
|
||||||
|
RootModel = None
|
1
openapi_pydantic/py.typed
Normal file
1
openapi_pydantic/py.typed
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# Marker file for PEP 561
|
188
openapi_pydantic/util.py
Normal file
188
openapi_pydantic/util.py
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
from typing import Any, Dict, Generic, List, Optional, Set, Type, TypeVar, cast
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import (
|
||||||
|
DEFS_KEY,
|
||||||
|
PYDANTIC_V2,
|
||||||
|
JsonSchemaMode,
|
||||||
|
models_json_schema,
|
||||||
|
v1_schema,
|
||||||
|
)
|
||||||
|
|
||||||
|
from . import Components, OpenAPI, Reference, Schema, schema_validate
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
PydanticType = TypeVar("PydanticType", bound=BaseModel)
|
||||||
|
ref_prefix = "#/components/schemas/"
|
||||||
|
ref_template = "#/components/schemas/{model}"
|
||||||
|
|
||||||
|
|
||||||
|
class PydanticSchema(Schema, Generic[PydanticType]):
|
||||||
|
"""Special `Schema` class to indicate a reference from pydantic class"""
|
||||||
|
|
||||||
|
schema_class: Type[PydanticType]
|
||||||
|
"""the class that is used for generate the schema"""
|
||||||
|
|
||||||
|
|
||||||
|
def get_mode(
|
||||||
|
cls: Type[BaseModel], default: JsonSchemaMode = "validation"
|
||||||
|
) -> JsonSchemaMode:
|
||||||
|
"""Get the JSON schema mode for a model class.
|
||||||
|
|
||||||
|
The mode can be either "validation" or "serialization". In validation mode,
|
||||||
|
computed fields are dropped and optional fields remain optional. In
|
||||||
|
serialization mode, computed and optional fields are required.
|
||||||
|
"""
|
||||||
|
if not hasattr(cls, "model_config"):
|
||||||
|
return default
|
||||||
|
mode = cls.model_config.get("json_schema_mode", default)
|
||||||
|
if mode not in ("validation", "serialization"):
|
||||||
|
raise ValueError(f"invalid json_schema_mode: {mode}")
|
||||||
|
return cast(JsonSchemaMode, mode)
|
||||||
|
|
||||||
|
|
||||||
|
def construct_open_api_with_schema_class(
|
||||||
|
open_api: OpenAPI,
|
||||||
|
schema_classes: Optional[List[Type[BaseModel]]] = None,
|
||||||
|
scan_for_pydantic_schema_reference: bool = True,
|
||||||
|
by_alias: bool = True,
|
||||||
|
) -> OpenAPI:
|
||||||
|
"""
|
||||||
|
Construct a new OpenAPI object, utilising pydantic classes to produce JSON schemas.
|
||||||
|
|
||||||
|
:param open_api: the base `OpenAPI` object
|
||||||
|
:param schema_classes: Pydantic classes that their schema will be used
|
||||||
|
"#/components/schemas" values
|
||||||
|
:param scan_for_pydantic_schema_reference: flag to indicate if scanning for
|
||||||
|
`PydanticSchemaReference` class
|
||||||
|
is needed for "#/components/schemas"
|
||||||
|
value updates
|
||||||
|
:param by_alias: construct schema by alias (default is True)
|
||||||
|
:return: new OpenAPI object with "#/components/schemas" values updated.
|
||||||
|
If there is no update in "#/components/schemas" values, the original
|
||||||
|
`open_api` will be returned.
|
||||||
|
"""
|
||||||
|
copy_func = getattr(open_api, "model_copy" if PYDANTIC_V2 else "copy")
|
||||||
|
new_open_api: OpenAPI = copy_func(deep=True)
|
||||||
|
|
||||||
|
if scan_for_pydantic_schema_reference:
|
||||||
|
extracted_schema_classes = _handle_pydantic_schema(new_open_api)
|
||||||
|
if schema_classes:
|
||||||
|
schema_classes = list({*schema_classes, *extracted_schema_classes})
|
||||||
|
else:
|
||||||
|
schema_classes = extracted_schema_classes
|
||||||
|
|
||||||
|
if not schema_classes:
|
||||||
|
return open_api
|
||||||
|
|
||||||
|
schema_classes.sort(key=lambda x: x.__name__)
|
||||||
|
logger.debug("schema_classes: %s", schema_classes)
|
||||||
|
|
||||||
|
# update new_open_api with new #/components/schemas
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
_key_map, schema_definitions = models_json_schema(
|
||||||
|
[(c, get_mode(c)) for c in schema_classes],
|
||||||
|
by_alias=by_alias,
|
||||||
|
ref_template=ref_template,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
schema_definitions = v1_schema(
|
||||||
|
schema_classes, by_alias=by_alias, ref_prefix=ref_prefix
|
||||||
|
)
|
||||||
|
|
||||||
|
if not new_open_api.components:
|
||||||
|
new_open_api.components = Components()
|
||||||
|
if new_open_api.components.schemas:
|
||||||
|
for existing_key in new_open_api.components.schemas:
|
||||||
|
if existing_key in schema_definitions[DEFS_KEY]:
|
||||||
|
logger.warning(
|
||||||
|
f'"{existing_key}" already exists in {ref_prefix}. '
|
||||||
|
f'The value of "{ref_prefix}{existing_key}" will be overwritten.'
|
||||||
|
)
|
||||||
|
new_open_api.components.schemas.update(_validate_schemas(schema_definitions))
|
||||||
|
else:
|
||||||
|
new_open_api.components.schemas = _validate_schemas(schema_definitions)
|
||||||
|
return new_open_api
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_schemas(schema_definitions: Dict[str, Any]) -> Dict[str, Schema]:
|
||||||
|
"""Convert JSON Schema definitions to parsed OpenAPI objects"""
|
||||||
|
# Note: if an error occurs in schema_validate(), it may indicate that
|
||||||
|
# the generated JSON schemas are not compatible with the version
|
||||||
|
# of OpenAPI this module depends on.
|
||||||
|
return {
|
||||||
|
key: schema_validate(schema_dict)
|
||||||
|
for key, schema_dict in schema_definitions[DEFS_KEY].items()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _handle_pydantic_schema(open_api: OpenAPI) -> List[Type[BaseModel]]:
|
||||||
|
"""
|
||||||
|
This function traverses the `OpenAPI` object and
|
||||||
|
|
||||||
|
1. Replaces the `PydanticSchema` object with `Reference` object, with correct ref
|
||||||
|
value;
|
||||||
|
2. Extracts the involved schema class from `PydanticSchema` object.
|
||||||
|
|
||||||
|
**This function will mutate the input `OpenAPI` object.**
|
||||||
|
|
||||||
|
:param open_api: the `OpenAPI` object to be traversed and mutated
|
||||||
|
:return: a list of schema classes extracted from `PydanticSchema` objects
|
||||||
|
"""
|
||||||
|
|
||||||
|
pydantic_types: Set[Type[BaseModel]] = set()
|
||||||
|
|
||||||
|
def _traverse(obj: Any) -> None:
|
||||||
|
if isinstance(obj, BaseModel):
|
||||||
|
fields = getattr(
|
||||||
|
obj, "model_fields_set" if PYDANTIC_V2 else "__fields_set__"
|
||||||
|
)
|
||||||
|
for field in fields:
|
||||||
|
child_obj = obj.__getattribute__(field)
|
||||||
|
if isinstance(child_obj, PydanticSchema):
|
||||||
|
logger.debug("PydanticSchema found in %s: %s", obj, child_obj)
|
||||||
|
obj.__setattr__(field, _construct_ref_obj(child_obj))
|
||||||
|
pydantic_types.add(child_obj.schema_class)
|
||||||
|
else:
|
||||||
|
_traverse(child_obj)
|
||||||
|
elif isinstance(obj, list):
|
||||||
|
for index, elem in enumerate(obj):
|
||||||
|
if isinstance(elem, PydanticSchema):
|
||||||
|
logger.debug(f"PydanticSchema found in list: {elem}")
|
||||||
|
obj[index] = _construct_ref_obj(elem)
|
||||||
|
pydantic_types.add(elem.schema_class)
|
||||||
|
else:
|
||||||
|
_traverse(elem)
|
||||||
|
elif isinstance(obj, dict):
|
||||||
|
for key, value in obj.items():
|
||||||
|
if isinstance(value, PydanticSchema):
|
||||||
|
logger.debug(f"PydanticSchema found in dict: {value}")
|
||||||
|
obj[key] = _construct_ref_obj(value)
|
||||||
|
pydantic_types.add(value.schema_class)
|
||||||
|
else:
|
||||||
|
_traverse(value)
|
||||||
|
|
||||||
|
_traverse(open_api)
|
||||||
|
return list(pydantic_types)
|
||||||
|
|
||||||
|
|
||||||
|
def _construct_ref_obj(pydantic_schema: PydanticSchema[PydanticType]) -> Reference:
|
||||||
|
"""
|
||||||
|
Construct a reference object from the Pydantic schema name
|
||||||
|
|
||||||
|
characters in the schema name that are invalid/problematic
|
||||||
|
for JSONschema $ref names will get replaced with underscores.
|
||||||
|
Especially needed for Pydantic generic Models with brackets "[]"
|
||||||
|
|
||||||
|
see: https://github.com/pydantic/pydantic/blob/aee6057378ccfec02126bf9c984a9b6d6b411777/pydantic/json_schema.py#L2031
|
||||||
|
"""
|
||||||
|
ref_name = re.sub(
|
||||||
|
r"[^a-zA-Z0-9.\-_]", "_", pydantic_schema.schema_class.__name__
|
||||||
|
).replace(".", "__")
|
||||||
|
ref_obj = Reference(**{"$ref": ref_prefix + ref_name})
|
||||||
|
logger.debug(f"ref_obj={ref_obj}")
|
||||||
|
return ref_obj
|
34
openapi_pydantic/v3/__init__.py
Normal file
34
openapi_pydantic/v3/__init__.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
from .parser import parse_obj as parse_obj
|
||||||
|
from .v3_1 import XML as XML
|
||||||
|
from .v3_1 import Callback as Callback
|
||||||
|
from .v3_1 import Components as Components
|
||||||
|
from .v3_1 import Contact as Contact
|
||||||
|
from .v3_1 import DataType as DataType
|
||||||
|
from .v3_1 import Discriminator as Discriminator
|
||||||
|
from .v3_1 import Encoding as Encoding
|
||||||
|
from .v3_1 import Example as Example
|
||||||
|
from .v3_1 import ExternalDocumentation as ExternalDocumentation
|
||||||
|
from .v3_1 import Header as Header
|
||||||
|
from .v3_1 import Info as Info
|
||||||
|
from .v3_1 import License as License
|
||||||
|
from .v3_1 import Link as Link
|
||||||
|
from .v3_1 import MediaType as MediaType
|
||||||
|
from .v3_1 import OAuthFlow as OAuthFlow
|
||||||
|
from .v3_1 import OAuthFlows as OAuthFlows
|
||||||
|
from .v3_1 import OpenAPI as OpenAPI
|
||||||
|
from .v3_1 import Operation as Operation
|
||||||
|
from .v3_1 import Parameter as Parameter
|
||||||
|
from .v3_1 import ParameterLocation as ParameterLocation
|
||||||
|
from .v3_1 import PathItem as PathItem
|
||||||
|
from .v3_1 import Paths as Paths
|
||||||
|
from .v3_1 import Reference as Reference
|
||||||
|
from .v3_1 import RequestBody as RequestBody
|
||||||
|
from .v3_1 import Response as Response
|
||||||
|
from .v3_1 import Responses as Responses
|
||||||
|
from .v3_1 import Schema as Schema
|
||||||
|
from .v3_1 import SecurityRequirement as SecurityRequirement
|
||||||
|
from .v3_1 import SecurityScheme as SecurityScheme
|
||||||
|
from .v3_1 import Server as Server
|
||||||
|
from .v3_1 import ServerVariable as ServerVariable
|
||||||
|
from .v3_1 import Tag as Tag
|
||||||
|
from .v3_1 import schema_validate as schema_validate
|
33
openapi_pydantic/v3/parser.py
Normal file
33
openapi_pydantic/v3/parser.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
from typing import TYPE_CHECKING, Any, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2
|
||||||
|
|
||||||
|
from .v3_0 import OpenAPI as OpenAPIv3_0
|
||||||
|
from .v3_1 import OpenAPI as OpenAPIv3_1
|
||||||
|
|
||||||
|
OpenAPIv3 = Union[OpenAPIv3_1, OpenAPIv3_0]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
|
||||||
|
def parse_obj(data: Any) -> OpenAPIv3:
|
||||||
|
"""Parse a raw object into an OpenAPI model with version inference."""
|
||||||
|
...
|
||||||
|
|
||||||
|
elif PYDANTIC_V2:
|
||||||
|
from pydantic import RootModel
|
||||||
|
|
||||||
|
class _OpenAPI(RootModel):
|
||||||
|
root: OpenAPIv3 = Field(discriminator="openapi")
|
||||||
|
|
||||||
|
def parse_obj(data: Any) -> OpenAPIv3:
|
||||||
|
return _OpenAPI.model_validate(data).root
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class _OpenAPI(BaseModel):
|
||||||
|
__root__: OpenAPIv3 = Field(discriminator="openapi")
|
||||||
|
|
||||||
|
def parse_obj(data: Any) -> OpenAPIv3:
|
||||||
|
return _OpenAPI.parse_obj(data).__root__
|
39
openapi_pydantic/v3/v3_0/README.md
Normal file
39
openapi_pydantic/v3/v3_0/README.md
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
# OpenAPI v3.0 schema classes
|
||||||
|
|
||||||
|
## Alias
|
||||||
|
|
||||||
|
Due to the reserved words in python and pydantic,
|
||||||
|
the following fields are used with [alias](https://pydantic-docs.helpmanual.io/usage/schema/#field-customisation) feature provided by pydantic:
|
||||||
|
|
||||||
|
| Class | Field name in the class | Alias (as in OpenAPI spec) |
|
||||||
|
| ----- | ----------------------- | -------------------------- |
|
||||||
|
| Header[*](#header_param_in) | param_in | in |
|
||||||
|
| MediaType | media_type_schema | schema |
|
||||||
|
| Parameter | param_in | in |
|
||||||
|
| Parameter | param_schema | schema |
|
||||||
|
| PathItem | ref | $ref |
|
||||||
|
| Reference | ref | $ref |
|
||||||
|
| SecurityScheme | security_scheme_in | in |
|
||||||
|
| Schema | schema_format | format |
|
||||||
|
| Schema | schema_not | not |
|
||||||
|
|
||||||
|
> <a name="header_param_in"></a>The "in" field in Header object is actually a constant (`{"in": "header"}`).
|
||||||
|
|
||||||
|
> For convenience of object creation, the classes mentioned in above
|
||||||
|
> have configured `allow_population_by_field_name=True` (Pydantic V1) or `populate_by_name=True` (Pydantic V2).
|
||||||
|
>
|
||||||
|
> Reference: [Pydantic's Model Config](https://pydantic-docs.helpmanual.io/usage/model_config/)
|
||||||
|
|
||||||
|
## Non-pydantic schema types
|
||||||
|
|
||||||
|
Due to the constriants of python typing structure (not able to handle dynamic field names),
|
||||||
|
the following schema classes are actually just a typing of `Dict`:
|
||||||
|
|
||||||
|
| Schema Type | Implementation |
|
||||||
|
| ----------- | -------------- |
|
||||||
|
| Callback | `Callback = Dict[str, PathItem]` |
|
||||||
|
| Paths | `Paths = Dict[str, PathItem]` |
|
||||||
|
| Responses | `Responses = Dict[str, Union[Response, Reference]]` |
|
||||||
|
| SecurityRequirement | `SecurityRequirement = Dict[str, List[str]]` |
|
||||||
|
|
||||||
|
On creating such schema instances, please use python's `dict` type instead to instantiate.
|
59
openapi_pydantic/v3/v3_0/__init__.py
Normal file
59
openapi_pydantic/v3/v3_0/__init__.py
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
"""
|
||||||
|
OpenAPI v3.0 schema types, created according to the specification:
|
||||||
|
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.4.md
|
||||||
|
|
||||||
|
The type orders are according to the contents of the specification:
|
||||||
|
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.4.md#table-of-contents
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2
|
||||||
|
|
||||||
|
from .callback import Callback as Callback
|
||||||
|
from .components import Components as Components
|
||||||
|
from .contact import Contact as Contact
|
||||||
|
from .datatype import DataType as DataType
|
||||||
|
from .discriminator import Discriminator as Discriminator
|
||||||
|
from .encoding import Encoding as Encoding
|
||||||
|
from .example import Example as Example
|
||||||
|
from .external_documentation import ExternalDocumentation as ExternalDocumentation
|
||||||
|
from .header import Header as Header
|
||||||
|
from .info import Info as Info
|
||||||
|
from .license import License as License
|
||||||
|
from .link import Link as Link
|
||||||
|
from .media_type import MediaType as MediaType
|
||||||
|
from .oauth_flow import OAuthFlow as OAuthFlow
|
||||||
|
from .oauth_flows import OAuthFlows as OAuthFlows
|
||||||
|
from .open_api import OpenAPI as OpenAPI
|
||||||
|
from .operation import Operation as Operation
|
||||||
|
from .parameter import Parameter as Parameter
|
||||||
|
from .parameter import ParameterLocation as ParameterLocation
|
||||||
|
from .path_item import PathItem as PathItem
|
||||||
|
from .paths import Paths as Paths
|
||||||
|
from .reference import Reference as Reference
|
||||||
|
from .request_body import RequestBody as RequestBody
|
||||||
|
from .response import Response as Response
|
||||||
|
from .responses import Responses as Responses
|
||||||
|
from .schema import Schema as Schema
|
||||||
|
from .schema import schema_validate as schema_validate
|
||||||
|
from .security_requirement import SecurityRequirement as SecurityRequirement
|
||||||
|
from .security_scheme import SecurityScheme as SecurityScheme
|
||||||
|
from .server import Server as Server
|
||||||
|
from .server_variable import ServerVariable as ServerVariable
|
||||||
|
from .tag import Tag as Tag
|
||||||
|
from .xml import XML as XML
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
pass
|
||||||
|
elif PYDANTIC_V2:
|
||||||
|
# resolve forward references
|
||||||
|
Encoding.model_rebuild()
|
||||||
|
OpenAPI.model_rebuild()
|
||||||
|
Components.model_rebuild()
|
||||||
|
Operation.model_rebuild()
|
||||||
|
else:
|
||||||
|
# resolve forward references
|
||||||
|
Encoding.update_forward_refs(Header=Header)
|
||||||
|
Schema.update_forward_refs()
|
||||||
|
Operation.update_forward_refs(PathItem=PathItem)
|
24
openapi_pydantic/v3/v3_0/callback.py
Normal file
24
openapi_pydantic/v3/v3_0/callback.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
from typing import TYPE_CHECKING, Dict
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .path_item import PathItem
|
||||||
|
|
||||||
|
|
||||||
|
Callback = Dict[str, "PathItem"]
|
||||||
|
"""
|
||||||
|
A map of possible out-of band callbacks related to the parent operation.
|
||||||
|
Each value in the map is a [Path Item Object](#pathItemObject)
|
||||||
|
that describes a set of requests that may be initiated by the API provider and the
|
||||||
|
expected responses. The key value used to identify the path item object is an
|
||||||
|
expression, evaluated at runtime, that identifies a URL to use for the callback
|
||||||
|
operation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""Patterned Fields"""
|
||||||
|
|
||||||
|
# {expression}: 'PathItem' = ...
|
||||||
|
"""
|
||||||
|
A Path Item Object used to define a callback request and expected responses.
|
||||||
|
|
||||||
|
A [complete example](../examples/v3.0/callback-example.yaml) is available.
|
||||||
|
"""
|
138
openapi_pydantic/v3/v3_0/components.py
Normal file
138
openapi_pydantic/v3/v3_0/components.py
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
from typing import Dict, Optional, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .callback import Callback
|
||||||
|
from .example import Example
|
||||||
|
from .header import Header
|
||||||
|
from .link import Link
|
||||||
|
from .parameter import Parameter
|
||||||
|
from .reference import Reference
|
||||||
|
from .request_body import RequestBody
|
||||||
|
from .response import Response
|
||||||
|
from .schema import Schema
|
||||||
|
from .security_scheme import SecurityScheme
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"schemas": {
|
||||||
|
"GeneralError": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"code": {"type": "integer", "format": "int32"},
|
||||||
|
"message": {"type": "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Category": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {"type": "integer", "format": "int64"},
|
||||||
|
"name": {"type": "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Tag": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {"type": "integer", "format": "int64"},
|
||||||
|
"name": {"type": "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"parameters": {
|
||||||
|
"skipParam": {
|
||||||
|
"name": "skip",
|
||||||
|
"in": "query",
|
||||||
|
"description": "number of items to skip",
|
||||||
|
"required": True,
|
||||||
|
"schema": {"type": "integer", "format": "int32"},
|
||||||
|
},
|
||||||
|
"limitParam": {
|
||||||
|
"name": "limit",
|
||||||
|
"in": "query",
|
||||||
|
"description": "max records to return",
|
||||||
|
"required": True,
|
||||||
|
"schema": {"type": "integer", "format": "int32"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"NotFound": {"description": "Entity not found."},
|
||||||
|
"IllegalInput": {"description": "Illegal input for operation."},
|
||||||
|
"GeneralError": {
|
||||||
|
"description": "General Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {"$ref": "#/components/schemas/GeneralError"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"securitySchemes": {
|
||||||
|
"api_key": {
|
||||||
|
"type": "apiKey",
|
||||||
|
"name": "api_key",
|
||||||
|
"in": "header",
|
||||||
|
},
|
||||||
|
"petstore_auth": {
|
||||||
|
"type": "oauth2",
|
||||||
|
"flows": {
|
||||||
|
"implicit": {
|
||||||
|
"authorizationUrl": "http://example.org/api/oauth/dialog",
|
||||||
|
"scopes": {
|
||||||
|
"write:pets": "modify pets in your account",
|
||||||
|
"read:pets": "read your pets",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Components(BaseModel):
|
||||||
|
"""
|
||||||
|
Holds a set of reusable objects for different aspects of the OAS.
|
||||||
|
All objects defined within the components object will have no effect on the API
|
||||||
|
unless they are explicitly referenced from properties outside the components object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
schemas: Optional[Dict[str, Union[Reference, Schema]]] = None
|
||||||
|
"""An object to hold reusable [Schema Objects](#schemaObject)."""
|
||||||
|
|
||||||
|
responses: Optional[Dict[str, Union[Response, Reference]]] = None
|
||||||
|
"""An object to hold reusable [Response Objects](#responseObject)."""
|
||||||
|
|
||||||
|
parameters: Optional[Dict[str, Union[Parameter, Reference]]] = None
|
||||||
|
"""An object to hold reusable [Parameter Objects](#parameterObject)."""
|
||||||
|
|
||||||
|
examples: Optional[Dict[str, Union[Example, Reference]]] = None
|
||||||
|
"""An object to hold reusable [Example Objects](#exampleObject)."""
|
||||||
|
|
||||||
|
requestBodies: Optional[Dict[str, Union[RequestBody, Reference]]] = None
|
||||||
|
"""An object to hold reusable [Request Body Objects](#requestBodyObject)."""
|
||||||
|
|
||||||
|
headers: Optional[Dict[str, Union[Header, Reference]]] = None
|
||||||
|
"""An object to hold reusable [Header Objects](#headerObject)."""
|
||||||
|
|
||||||
|
securitySchemes: Optional[Dict[str, Union[SecurityScheme, Reference]]] = None
|
||||||
|
"""An object to hold reusable [Security Scheme Objects](#securitySchemeObject)."""
|
||||||
|
|
||||||
|
links: Optional[Dict[str, Union[Link, Reference]]] = None
|
||||||
|
"""An object to hold reusable [Link Objects](#linkObject)."""
|
||||||
|
|
||||||
|
callbacks: Optional[Dict[str, Union[Callback, Reference]]] = None
|
||||||
|
"""An object to hold reusable [Callback Objects](#callbackObject)."""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
48
openapi_pydantic/v3/v3_0/contact.py
Normal file
48
openapi_pydantic/v3/v3_0/contact.py
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"name": "API Support",
|
||||||
|
"url": "http://www.example.com/support",
|
||||||
|
"email": "support@example.com",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Contact(BaseModel):
|
||||||
|
"""
|
||||||
|
Contact information for the exposed API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
name: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The identifying name of the contact person/organization.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The URL pointing to the contact information.
|
||||||
|
MUST be in the format of a URL.
|
||||||
|
"""
|
||||||
|
|
||||||
|
email: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The email address of the contact person/organization.
|
||||||
|
MUST be in the format of an email address.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
15
openapi_pydantic/v3/v3_0/datatype.py
Normal file
15
openapi_pydantic/v3/v3_0/datatype.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import enum
|
||||||
|
|
||||||
|
|
||||||
|
class DataType(str, enum.Enum):
|
||||||
|
"""Data type of an object.
|
||||||
|
|
||||||
|
Note: OpenAPI 3.0.x does not support null as a data type.
|
||||||
|
"""
|
||||||
|
|
||||||
|
STRING = "string"
|
||||||
|
NUMBER = "number"
|
||||||
|
INTEGER = "integer"
|
||||||
|
BOOLEAN = "boolean"
|
||||||
|
ARRAY = "array"
|
||||||
|
OBJECT = "object"
|
52
openapi_pydantic/v3/v3_0/discriminator.py
Normal file
52
openapi_pydantic/v3/v3_0/discriminator.py
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
from typing import Dict, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"propertyName": "petType",
|
||||||
|
"mapping": {
|
||||||
|
"dog": "#/components/schemas/Dog",
|
||||||
|
"monster": "https://gigantic-server.com/schemas/Monster/schema.json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Discriminator(BaseModel):
|
||||||
|
"""
|
||||||
|
When request bodies or response payloads may be one of a number of different
|
||||||
|
schemas, a `discriminator` object can be used to aid in serialization,
|
||||||
|
deserialization, and validation.
|
||||||
|
|
||||||
|
The discriminator is a specific object in a schema which is used to inform the
|
||||||
|
consumer of the specification of an alternative schema based on the value
|
||||||
|
associated with it.
|
||||||
|
|
||||||
|
When using the discriminator, _inline_ schemas will not be considered.
|
||||||
|
"""
|
||||||
|
|
||||||
|
propertyName: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The name of the property in the payload that will hold the
|
||||||
|
discriminator value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
mapping: Optional[Dict[str, str]] = None
|
||||||
|
"""
|
||||||
|
An object to hold mappings between payload values and schema names or references.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
94
openapi_pydantic/v3/v3_0/encoding.py
Normal file
94
openapi_pydantic/v3/v3_0/encoding.py
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
from typing import TYPE_CHECKING, Dict, Optional, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .reference import Reference
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .header import Header
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"contentType": "image/png, image/jpeg",
|
||||||
|
"headers": {
|
||||||
|
"X-Rate-Limit-Limit": {
|
||||||
|
"description": "The number of allowed requests in the "
|
||||||
|
"current period",
|
||||||
|
"schema": {"type": "integer"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Encoding(BaseModel):
|
||||||
|
"""A single encoding definition applied to a single schema property."""
|
||||||
|
|
||||||
|
contentType: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The Content-Type for encoding a specific property.
|
||||||
|
Default value depends on the property type:
|
||||||
|
|
||||||
|
- for `string` with `format` being `binary` – `application/octet-stream`;
|
||||||
|
- for other primitive types – `text/plain`;
|
||||||
|
- for `object` - `application/json`;
|
||||||
|
- for `array` – the default is defined based on the inner type.
|
||||||
|
|
||||||
|
The value can be a specific media type (e.g. `application/json`), a wildcard media
|
||||||
|
type (e.g. `image/*`), or a comma-separated list of the two types.
|
||||||
|
"""
|
||||||
|
|
||||||
|
headers: Optional[Dict[str, Union["Header", Reference]]] = None
|
||||||
|
"""
|
||||||
|
A map allowing additional information to be provided as headers, for example
|
||||||
|
`Content-Disposition`.
|
||||||
|
|
||||||
|
`Content-Type` is described separately and SHALL be ignored in this section.
|
||||||
|
This property SHALL be ignored if the request body media type is not a `multipart`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
style: Optional[str] = None
|
||||||
|
"""
|
||||||
|
Describes how a specific property value will be serialized depending on its type.
|
||||||
|
|
||||||
|
See [Parameter Object](#parameterObject) for details on the
|
||||||
|
[`style`](#parameterStyle) property. The behavior follows the same values as
|
||||||
|
`query` parameters, including default values. This property SHALL be ignored if
|
||||||
|
the request body media type is not `application/x-www-form-urlencoded`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
explode: Optional[bool] = None
|
||||||
|
"""
|
||||||
|
When this is true, property values of type `array` or `object` generate separate
|
||||||
|
parameters for each value of the array, or key-value-pair of the map.
|
||||||
|
|
||||||
|
For other types of properties this property has no effect.
|
||||||
|
When [`style`](#encodingStyle) is `form`, the default value is `true`.
|
||||||
|
For all other styles, the default value is `false`.
|
||||||
|
This property SHALL be ignored if the request body media type is not
|
||||||
|
`application/x-www-form-urlencoded`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
allowReserved: bool = False
|
||||||
|
"""
|
||||||
|
Determines whether the parameter value SHOULD allow reserved characters,
|
||||||
|
as defined by [RFC3986](https://tools.ietf.org/html/rfc3986#section-2.2)
|
||||||
|
`:/?#[]@!$&'()*+,;=` to be included without percent-encoding.
|
||||||
|
The default value is `false`.
|
||||||
|
This property SHALL be ignored if the request body media type is not
|
||||||
|
`application/x-www-form-urlencoded`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
60
openapi_pydantic/v3/v3_0/example.py
Normal file
60
openapi_pydantic/v3/v3_0/example.py
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{"summary": "A foo example", "value": {"foo": "bar"}},
|
||||||
|
{
|
||||||
|
"summary": "This is an example in XML",
|
||||||
|
"externalValue": "http://example.org/examples/address-example.xml",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"summary": "This is a text example",
|
||||||
|
"externalValue": "http://foo.bar/examples/address-example.txt",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Example(BaseModel):
|
||||||
|
summary: Optional[str] = None
|
||||||
|
"""
|
||||||
|
Short description for the example.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
Long description for the example.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
value: Optional[Any] = None
|
||||||
|
"""
|
||||||
|
Embedded literal example.
|
||||||
|
The `value` field and `externalValue` field are mutually exclusive.
|
||||||
|
To represent examples of media types that cannot naturally represented in JSON or
|
||||||
|
YAML, use a string value to contain the example, escaping where necessary.
|
||||||
|
"""
|
||||||
|
|
||||||
|
externalValue: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A URL that points to the literal example.
|
||||||
|
This provides the capability to reference examples that cannot easily be included
|
||||||
|
in JSON or YAML documents.
|
||||||
|
|
||||||
|
The `value` field and `externalValue` field are mutually exclusive.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
36
openapi_pydantic/v3/v3_0/external_documentation.py
Normal file
36
openapi_pydantic/v3/v3_0/external_documentation.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
_examples = [{"description": "Find more info here", "url": "https://example.com"}]
|
||||||
|
|
||||||
|
|
||||||
|
class ExternalDocumentation(BaseModel):
|
||||||
|
"""Allows referencing an external resource for extended documentation."""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A short description of the target documentation.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The URL for the target documentation.
|
||||||
|
Value MUST be in the format of a URL.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
37
openapi_pydantic/v3/v3_0/header.py
Normal file
37
openapi_pydantic/v3/v3_0/header.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .parameter import ParameterBase
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"description": "The number of allowed requests in the current period",
|
||||||
|
"schema": {"type": "integer"},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Header(ParameterBase):
|
||||||
|
"""
|
||||||
|
The Header Object follows the structure of the
|
||||||
|
[Parameter Object](#parameterObject) with the following changes:
|
||||||
|
|
||||||
|
1. `name` MUST NOT be specified, it is given in the corresponding
|
||||||
|
`headers` map.
|
||||||
|
2. `in` MUST NOT be specified, it is implicitly in `header`.
|
||||||
|
3. All traits that are affected by the location MUST be applicable
|
||||||
|
to a location of `header` (for example, [`style`](#parameterStyle)).
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
populate_by_name=True,
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
schema_extra = {"examples": _examples}
|
81
openapi_pydantic/v3/v3_0/info.py
Normal file
81
openapi_pydantic/v3/v3_0/info.py
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .contact import Contact
|
||||||
|
from .license import License
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"title": "Sample Pet Store App",
|
||||||
|
"description": "This is a sample server for a pet store.",
|
||||||
|
"termsOfService": "http://example.com/terms/",
|
||||||
|
"contact": {
|
||||||
|
"name": "API Support",
|
||||||
|
"url": "http://www.example.com/support",
|
||||||
|
"email": "support@example.com",
|
||||||
|
},
|
||||||
|
"license": {
|
||||||
|
"name": "Apache 2.0",
|
||||||
|
"url": "https://www.apache.org/licenses/LICENSE-2.0.html",
|
||||||
|
},
|
||||||
|
"version": "1.0.1",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Info(BaseModel):
|
||||||
|
"""
|
||||||
|
The object provides metadata about the API.
|
||||||
|
The metadata MAY be used by the clients if needed,
|
||||||
|
and MAY be presented in editing or documentation generation tools for convenience.
|
||||||
|
"""
|
||||||
|
|
||||||
|
title: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The title of the API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A short description of the API.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
termsOfService: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A URL to the Terms of Service for the API.
|
||||||
|
MUST be in the format of a URL.
|
||||||
|
"""
|
||||||
|
|
||||||
|
contact: Optional[Contact] = None
|
||||||
|
"""
|
||||||
|
The contact information for the exposed API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
license: Optional[License] = None
|
||||||
|
"""
|
||||||
|
The license information for the exposed API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
version: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The version of the OpenAPI document
|
||||||
|
(which is distinct from the [OpenAPI Specification version](#oasVersion) or the API
|
||||||
|
implementation version).
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
41
openapi_pydantic/v3/v3_0/license.py
Normal file
41
openapi_pydantic/v3/v3_0/license.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"name": "Apache 2.0",
|
||||||
|
"url": "https://www.apache.org/licenses/LICENSE-2.0.html",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class License(BaseModel):
|
||||||
|
"""
|
||||||
|
License information for the exposed API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The license name used for the API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A URL to the license used for the API.
|
||||||
|
MUST be in the format of a URL.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
94
openapi_pydantic/v3/v3_0/link.py
Normal file
94
openapi_pydantic/v3/v3_0/link.py
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .server import Server
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"operationId": "getUserAddressByUUID",
|
||||||
|
"parameters": {"userUuid": "$response.body#/uuid"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"operationRef": "#/paths/~12.0~1repositories~1{username}/get",
|
||||||
|
"parameters": {"username": "$response.body#/username"},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Link(BaseModel):
|
||||||
|
"""
|
||||||
|
The `Link object` represents a possible design-time link for a response.
|
||||||
|
The presence of a link does not guarantee the caller's ability to successfully
|
||||||
|
invoke it, rather it provides a known relationship and traversal mechanism between
|
||||||
|
responses and other operations.
|
||||||
|
|
||||||
|
Unlike _dynamic_ links (i.e. links provided **in** the response payload),
|
||||||
|
the OAS linking mechanism does not require link information in the runtime response.
|
||||||
|
|
||||||
|
For computing links, and providing instructions to execute them,
|
||||||
|
a [runtime expression](#runtimeExpression) is used for accessing values in an
|
||||||
|
operation and using them as parameters while invoking the linked operation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
operationRef: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A relative or absolute URI reference to an OAS operation.
|
||||||
|
This field is mutually exclusive of the `operationId` field,
|
||||||
|
and MUST point to an [Operation Object](#operationObject).
|
||||||
|
Relative `operationRef` values MAY be used to locate an existing
|
||||||
|
[Operation Object](#operationObject) in the OpenAPI definition.
|
||||||
|
"""
|
||||||
|
|
||||||
|
operationId: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The name of an _existing_, resolvable OAS operation, as defined with a unique
|
||||||
|
`operationId`.
|
||||||
|
|
||||||
|
This field is mutually exclusive of the `operationRef` field.
|
||||||
|
"""
|
||||||
|
|
||||||
|
parameters: Optional[Dict[str, Any]] = None
|
||||||
|
"""
|
||||||
|
A map representing parameters to pass to an operation
|
||||||
|
as specified with `operationId` or identified via `operationRef`.
|
||||||
|
The key is the parameter name to be used,
|
||||||
|
whereas the value can be a constant or an expression to be evaluated and passed to
|
||||||
|
the linked operation.
|
||||||
|
|
||||||
|
The parameter name can be qualified using the [parameter location](#parameterIn)
|
||||||
|
`[{in}.]{name}` for operations that use the same parameter name in different
|
||||||
|
locations (e.g. path.id).
|
||||||
|
"""
|
||||||
|
|
||||||
|
requestBody: Optional[Any] = None
|
||||||
|
"""
|
||||||
|
A literal value or [{expression}](#runtimeExpression) to use as a request body when
|
||||||
|
calling the target operation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A description of the link.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
server: Optional[Server] = None
|
||||||
|
"""
|
||||||
|
A server object to be used by the target operation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
96
openapi_pydantic/v3/v3_0/media_type.py
Normal file
96
openapi_pydantic/v3/v3_0/media_type.py
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
from typing import Any, Dict, Optional, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .encoding import Encoding
|
||||||
|
from .example import Example
|
||||||
|
from .reference import Reference
|
||||||
|
from .schema import Schema
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"schema": {"$ref": "#/components/schemas/Pet"},
|
||||||
|
"examples": {
|
||||||
|
"cat": {
|
||||||
|
"summary": "An example of a cat",
|
||||||
|
"value": {
|
||||||
|
"name": "Fluffy",
|
||||||
|
"petType": "Cat",
|
||||||
|
"color": "White",
|
||||||
|
"gender": "male",
|
||||||
|
"breed": "Persian",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"dog": {
|
||||||
|
"summary": "An example of a dog with a cat's name",
|
||||||
|
"value": {
|
||||||
|
"name": "Puma",
|
||||||
|
"petType": "Dog",
|
||||||
|
"color": "Black",
|
||||||
|
"gender": "Female",
|
||||||
|
"breed": "Mixed",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class MediaType(BaseModel):
|
||||||
|
"""Each Media Type Object provides schema and examples for the media type
|
||||||
|
identified by its key."""
|
||||||
|
|
||||||
|
media_type_schema: Optional[Union[Reference, Schema]] = Field(
|
||||||
|
default=None, alias="schema"
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
The schema defining the content of the request, response, or parameter.
|
||||||
|
"""
|
||||||
|
|
||||||
|
example: Optional[Any] = None
|
||||||
|
"""
|
||||||
|
Example of the media type.
|
||||||
|
|
||||||
|
The example object SHOULD be in the correct format as specified by the media type.
|
||||||
|
|
||||||
|
The `example` field is mutually exclusive of the `examples` field.
|
||||||
|
|
||||||
|
Furthermore, if referencing a `schema` which contains an example,
|
||||||
|
the `example` value SHALL _override_ the example provided by the schema.
|
||||||
|
"""
|
||||||
|
|
||||||
|
examples: Optional[Dict[str, Union[Example, Reference]]] = None
|
||||||
|
"""
|
||||||
|
Examples of the media type.
|
||||||
|
|
||||||
|
Each example object SHOULD match the media type and specified schema if present.
|
||||||
|
|
||||||
|
The `examples` field is mutually exclusive of the `example` field.
|
||||||
|
|
||||||
|
Furthermore, if referencing a `schema` which contains an example,
|
||||||
|
the `examples` value SHALL _override_ the example provided by the schema.
|
||||||
|
"""
|
||||||
|
|
||||||
|
encoding: Optional[Dict[str, Encoding]] = None
|
||||||
|
"""
|
||||||
|
A map between a property name and its encoding information.
|
||||||
|
The key, being the property name, MUST exist in the schema as a property.
|
||||||
|
The encoding object SHALL only apply to `requestBody` objects
|
||||||
|
when the media type is `multipart` or `application/x-www-form-urlencoded`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
populate_by_name=True,
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
schema_extra = {"examples": _examples}
|
76
openapi_pydantic/v3/v3_0/oauth_flow.py
Normal file
76
openapi_pydantic/v3/v3_0/oauth_flow.py
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
from typing import Dict, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"authorizationUrl": "https://example.com/api/oauth/dialog",
|
||||||
|
"scopes": {
|
||||||
|
"write:pets": "modify pets in your account",
|
||||||
|
"read:pets": "read your pets",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"authorizationUrl": "https://example.com/api/oauth/dialog",
|
||||||
|
"tokenUrl": "https://example.com/api/oauth/token",
|
||||||
|
"scopes": {
|
||||||
|
"write:pets": "modify pets in your account",
|
||||||
|
"read:pets": "read your pets",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"authorizationUrl": "/api/oauth/dialog",
|
||||||
|
"tokenUrl": "/api/oauth/token",
|
||||||
|
"refreshUrl": "/api/oauth/token",
|
||||||
|
"scopes": {
|
||||||
|
"write:pets": "modify pets in your account",
|
||||||
|
"read:pets": "read your pets",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class OAuthFlow(BaseModel):
|
||||||
|
"""
|
||||||
|
Configuration details for a supported OAuth Flow
|
||||||
|
"""
|
||||||
|
|
||||||
|
authorizationUrl: Optional[str] = None
|
||||||
|
"""
|
||||||
|
**REQUIRED** for `oauth2 ("implicit", "authorizationCode")`.
|
||||||
|
The authorization URL to be used for this flow.
|
||||||
|
This MUST be in the form of a URL.
|
||||||
|
"""
|
||||||
|
|
||||||
|
tokenUrl: Optional[str] = None
|
||||||
|
"""
|
||||||
|
**REQUIRED** for `oauth2 ("password", "clientCredentials", "authorizationCode")`.
|
||||||
|
The token URL to be used for this flow.
|
||||||
|
This MUST be in the form of a URL.
|
||||||
|
"""
|
||||||
|
|
||||||
|
refreshUrl: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The URL to be used for obtaining refresh tokens. This MUST be in the form of a URL.
|
||||||
|
"""
|
||||||
|
|
||||||
|
scopes: Dict[str, str]
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The available scopes for the OAuth2 security scheme.
|
||||||
|
A map between the scope name and a short description for it.
|
||||||
|
The map MAY be empty.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
47
openapi_pydantic/v3/v3_0/oauth_flows.py
Normal file
47
openapi_pydantic/v3/v3_0/oauth_flows.py
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .oauth_flow import OAuthFlow
|
||||||
|
|
||||||
|
|
||||||
|
class OAuthFlows(BaseModel):
|
||||||
|
"""
|
||||||
|
Allows configuration of the supported OAuth Flows.
|
||||||
|
"""
|
||||||
|
|
||||||
|
implicit: Optional[OAuthFlow] = None
|
||||||
|
"""
|
||||||
|
Configuration for the OAuth Implicit flow
|
||||||
|
"""
|
||||||
|
|
||||||
|
password: Optional[OAuthFlow] = None
|
||||||
|
"""
|
||||||
|
Configuration for the OAuth Resource Owner Password flow
|
||||||
|
"""
|
||||||
|
|
||||||
|
clientCredentials: Optional[OAuthFlow] = None
|
||||||
|
"""
|
||||||
|
Configuration for the OAuth Client Credentials flow.
|
||||||
|
|
||||||
|
Previously called `application` in OpenAPI 2.0.
|
||||||
|
"""
|
||||||
|
|
||||||
|
authorizationCode: Optional[OAuthFlow] = None
|
||||||
|
"""
|
||||||
|
Configuration for the OAuth Authorization Code flow.
|
||||||
|
|
||||||
|
Previously called `accessCode` in OpenAPI 2.0.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
84
openapi_pydantic/v3/v3_0/open_api.py
Normal file
84
openapi_pydantic/v3/v3_0/open_api.py
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
from typing import List, Literal, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .components import Components
|
||||||
|
from .external_documentation import ExternalDocumentation
|
||||||
|
from .info import Info
|
||||||
|
from .paths import Paths
|
||||||
|
from .security_requirement import SecurityRequirement
|
||||||
|
from .server import Server
|
||||||
|
from .tag import Tag
|
||||||
|
|
||||||
|
|
||||||
|
class OpenAPI(BaseModel):
|
||||||
|
"""This is the root document object of the OpenAPI document."""
|
||||||
|
|
||||||
|
openapi: Literal["3.0.4", "3.0.3", "3.0.2", "3.0.1", "3.0.0"] = "3.0.4"
|
||||||
|
"""
|
||||||
|
**REQUIRED**. This string MUST be the [semantic version number](https://semver.org/spec/v2.0.0.html)
|
||||||
|
of the [OpenAPI Specification version](#versions) that the OpenAPI document uses.
|
||||||
|
The `openapi` field SHOULD be used by tooling specifications and clients to
|
||||||
|
interpret the OpenAPI document. This is *not* related to the API
|
||||||
|
[`info.version`](#infoVersion) string.
|
||||||
|
"""
|
||||||
|
|
||||||
|
info: Info
|
||||||
|
"""
|
||||||
|
**REQUIRED**. Provides metadata about the API. The metadata MAY be used by tooling
|
||||||
|
as required.
|
||||||
|
"""
|
||||||
|
|
||||||
|
servers: List[Server] = [Server(url="/")]
|
||||||
|
"""
|
||||||
|
An array of Server Objects, which provide connectivity information to a target
|
||||||
|
server. If the `servers` property is not provided, or is an empty array,
|
||||||
|
the default value would be a [Server Object](#serverObject) with a
|
||||||
|
[url](#serverUrl) value of `/`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
paths: Paths
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The available paths and operations for the API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
components: Optional[Components] = None
|
||||||
|
"""
|
||||||
|
An element to hold various schemas for the specification.
|
||||||
|
"""
|
||||||
|
|
||||||
|
security: Optional[List[SecurityRequirement]] = None
|
||||||
|
"""
|
||||||
|
A declaration of which security mechanisms can be used across the API.
|
||||||
|
The list of values includes alternative security requirement objects that can be
|
||||||
|
used. Only one of the security requirement objects need to be satisfied to
|
||||||
|
authorize a request. Individual operations can override this definition.
|
||||||
|
To make security optional, an empty security requirement (`{}`) can be included in
|
||||||
|
the array.
|
||||||
|
"""
|
||||||
|
|
||||||
|
tags: Optional[List[Tag]] = None
|
||||||
|
"""
|
||||||
|
A list of tags used by the specification with additional metadata.
|
||||||
|
The order of the tags can be used to reflect on their order by the parsing tools.
|
||||||
|
Not all tags that are used by the [Operation Object](#operationObject) must be
|
||||||
|
declared. The tags that are not declared MAY be organized randomly or based on the
|
||||||
|
tools' logic. Each tag name in the list MUST be unique.
|
||||||
|
"""
|
||||||
|
|
||||||
|
externalDocs: Optional[ExternalDocumentation] = None
|
||||||
|
"""
|
||||||
|
Additional external documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
173
openapi_pydantic/v3/v3_0/operation.py
Normal file
173
openapi_pydantic/v3/v3_0/operation.py
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
from typing import Dict, List, Optional, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .callback import Callback
|
||||||
|
from .external_documentation import ExternalDocumentation
|
||||||
|
from .parameter import Parameter
|
||||||
|
from .reference import Reference
|
||||||
|
from .request_body import RequestBody
|
||||||
|
from .responses import Responses
|
||||||
|
from .security_requirement import SecurityRequirement
|
||||||
|
from .server import Server
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"tags": ["pet"],
|
||||||
|
"summary": "Updates a pet in the store with form data",
|
||||||
|
"operationId": "updatePetWithForm",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "petId",
|
||||||
|
"in": "path",
|
||||||
|
"description": "ID of pet that needs to be updated",
|
||||||
|
"required": True,
|
||||||
|
"schema": {"type": "string"},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/x-www-form-urlencoded": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "Updated name of the pet",
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"description": "Updated status of the pet",
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": ["status"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Pet updated.",
|
||||||
|
"content": {"application/json": {}, "application/xml": {}},
|
||||||
|
},
|
||||||
|
"405": {
|
||||||
|
"description": "Method Not Allowed",
|
||||||
|
"content": {"application/json": {}, "application/xml": {}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"security": [{"petstore_auth": ["write:pets", "read:pets"]}],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Operation(BaseModel):
|
||||||
|
"""Describes a single API operation on a path."""
|
||||||
|
|
||||||
|
tags: Optional[List[str]] = None
|
||||||
|
"""
|
||||||
|
A list of tags for API documentation control.
|
||||||
|
Tags can be used for logical grouping of operations by resources or any other
|
||||||
|
qualifier.
|
||||||
|
"""
|
||||||
|
|
||||||
|
summary: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A short summary of what the operation does.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A verbose explanation of the operation behavior.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
externalDocs: Optional[ExternalDocumentation] = None
|
||||||
|
"""
|
||||||
|
Additional external documentation for this operation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
operationId: Optional[str] = None
|
||||||
|
"""
|
||||||
|
Unique string used to identify the operation.
|
||||||
|
The id MUST be unique among all operations described in the API.
|
||||||
|
The operationId value is **case-sensitive**.
|
||||||
|
Tools and libraries MAY use the operationId to uniquely identify an operation,
|
||||||
|
therefore, it is RECOMMENDED to follow common programming naming conventions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
parameters: Optional[List[Union[Parameter, Reference]]] = None
|
||||||
|
"""
|
||||||
|
A list of parameters that are applicable for this operation.
|
||||||
|
If a parameter is already defined at the [Path Item](#pathItemParameters),
|
||||||
|
the new definition will override it but can never remove it.
|
||||||
|
The list MUST NOT include duplicated parameters.
|
||||||
|
A unique parameter is defined by a combination of a [name](#parameterName) and
|
||||||
|
[location](#parameterIn). The list can use the [Reference Object](#referenceObject)
|
||||||
|
to link to parameters that are defined at the
|
||||||
|
[OpenAPI Object's components/parameters](#componentsParameters).
|
||||||
|
"""
|
||||||
|
|
||||||
|
requestBody: Optional[Union[RequestBody, Reference]] = None
|
||||||
|
"""
|
||||||
|
The request body applicable for this operation.
|
||||||
|
|
||||||
|
The `requestBody` is only supported in HTTP methods where the HTTP 1.1 specification
|
||||||
|
[RFC7231](https://tools.ietf.org/html/rfc7231#section-4.3.1) has explicitly defined
|
||||||
|
semantics for request bodies. In other cases where the HTTP spec is vague,
|
||||||
|
`requestBody` SHALL be ignored by consumers.
|
||||||
|
"""
|
||||||
|
|
||||||
|
responses: Responses
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The list of possible responses as they are returned from executing
|
||||||
|
this operation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
callbacks: Optional[Dict[str, Callback]] = None
|
||||||
|
"""
|
||||||
|
A map of possible out-of band callbacks related to the parent operation.
|
||||||
|
The key is a unique identifier for the Callback Object.
|
||||||
|
Each value in the map is a [Callback Object](#callbackObject)
|
||||||
|
that describes a request that may be initiated by the API provider and the expected
|
||||||
|
responses.
|
||||||
|
"""
|
||||||
|
|
||||||
|
deprecated: bool = False
|
||||||
|
"""
|
||||||
|
Declares this operation to be deprecated.
|
||||||
|
Consumers SHOULD refrain from usage of the declared operation.
|
||||||
|
Default value is `false`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
security: Optional[List[SecurityRequirement]] = None
|
||||||
|
"""
|
||||||
|
A declaration of which security mechanisms can be used for this operation.
|
||||||
|
The list of values includes alternative security requirement objects that can be
|
||||||
|
used. Only one of the security requirement objects need to be satisfied to
|
||||||
|
authorize a request. To make security optional, an empty security requirement
|
||||||
|
(`{}`) can be included in the array. This definition overrides any declared
|
||||||
|
top-level [`security`](#oasSecurity). To remove a top-level security declaration,
|
||||||
|
an empty array can be used.
|
||||||
|
"""
|
||||||
|
|
||||||
|
servers: Optional[List[Server]] = None
|
||||||
|
"""
|
||||||
|
An alternative `server` array to service this operation.
|
||||||
|
If an alternative `server` object is specified at the Path Item Object or Root
|
||||||
|
level, it will be overridden by this value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
234
openapi_pydantic/v3/v3_0/parameter.py
Normal file
234
openapi_pydantic/v3/v3_0/parameter.py
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
import enum
|
||||||
|
from typing import Any, Dict, Optional, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .example import Example
|
||||||
|
from .media_type import MediaType
|
||||||
|
from .reference import Reference
|
||||||
|
from .schema import Schema
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"name": "token",
|
||||||
|
"in": "header",
|
||||||
|
"description": "token to be passed as a header",
|
||||||
|
"required": True,
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "integer", "format": "int64"},
|
||||||
|
},
|
||||||
|
"style": "simple",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "username",
|
||||||
|
"in": "path",
|
||||||
|
"description": "username to fetch",
|
||||||
|
"required": True,
|
||||||
|
"schema": {"type": "string"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"in": "query",
|
||||||
|
"description": "ID of the object to fetch",
|
||||||
|
"required": False,
|
||||||
|
"schema": {"type": "array", "items": {"type": "string"}},
|
||||||
|
"style": "form",
|
||||||
|
"explode": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "freeForm",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {"type": "integer"},
|
||||||
|
},
|
||||||
|
"style": "form",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "coordinates",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"required": ["lat", "long"],
|
||||||
|
"properties": {
|
||||||
|
"lat": {"type": "number"},
|
||||||
|
"long": {"type": "number"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ParameterLocation(str, enum.Enum):
|
||||||
|
"""The location of a given parameter."""
|
||||||
|
|
||||||
|
QUERY = "query"
|
||||||
|
HEADER = "header"
|
||||||
|
PATH = "path"
|
||||||
|
COOKIE = "cookie"
|
||||||
|
|
||||||
|
|
||||||
|
class ParameterBase(BaseModel):
|
||||||
|
"""
|
||||||
|
Base class for Parameter and Header.
|
||||||
|
|
||||||
|
(Header is like Parameter, but has no `name` or `in` fields.)
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A brief description of the parameter.
|
||||||
|
This could contain examples of use.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
required: bool = False
|
||||||
|
"""
|
||||||
|
Determines whether this parameter is mandatory.
|
||||||
|
If the [parameter location](#parameterIn) is `"path"`, this property is
|
||||||
|
**REQUIRED** and its value MUST be `true`.
|
||||||
|
Otherwise, the property MAY be included and its default value is `false`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
deprecated: bool = False
|
||||||
|
"""
|
||||||
|
Specifies that a parameter is deprecated and SHOULD be transitioned out of usage.
|
||||||
|
Default value is `false`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
style: Optional[str] = None
|
||||||
|
"""
|
||||||
|
Describes how the parameter value will be serialized depending on the type of the
|
||||||
|
parameter value. Default values (based on value of `in`):
|
||||||
|
|
||||||
|
- for `query` - `form`;
|
||||||
|
- for `path` - `simple`;
|
||||||
|
- for `header` - `simple`;
|
||||||
|
- for `cookie` - `form`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
explode: Optional[bool] = None
|
||||||
|
"""
|
||||||
|
When this is true, parameter values of type `array` or `object` generate separate
|
||||||
|
parameters for each value of the array or key-value pair of the map.
|
||||||
|
For other types of parameters this property has no effect.
|
||||||
|
When [`style`](#parameterStyle) is `form`, the default value is `true`.
|
||||||
|
For all other styles, the default value is `false`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
param_schema: Optional[Union[Reference, Schema]] = Field(
|
||||||
|
default=None, alias="schema"
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
The schema defining the type used for the parameter.
|
||||||
|
"""
|
||||||
|
|
||||||
|
example: Optional[Any] = None
|
||||||
|
"""
|
||||||
|
Example of the parameter's potential value.
|
||||||
|
The example SHOULD match the specified schema and encoding properties if present.
|
||||||
|
The `example` field is mutually exclusive of the `examples` field.
|
||||||
|
Furthermore, if referencing a `schema` that contains an example,
|
||||||
|
the `example` value SHALL _override_ the example provided by the schema.
|
||||||
|
To represent examples of media types that cannot naturally be represented in JSON
|
||||||
|
or YAML, a string value can contain the example with escaping where necessary.
|
||||||
|
"""
|
||||||
|
|
||||||
|
examples: Optional[Dict[str, Union[Example, Reference]]] = None
|
||||||
|
"""
|
||||||
|
Examples of the parameter's potential value.
|
||||||
|
Each example SHOULD contain a value in the correct format as specified in the
|
||||||
|
parameter encoding. The `examples` field is mutually exclusive of the `example`
|
||||||
|
field. Furthermore, if referencing a `schema` that contains an example,
|
||||||
|
the `examples` value SHALL _override_ the example provided by the schema.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
For more complex scenarios, the [`content`](#parameterContent) property
|
||||||
|
can define the media type and schema of the parameter.
|
||||||
|
A parameter MUST contain either a `schema` property, or a `content` property, but
|
||||||
|
not both. When `example` or `examples` are provided in conjunction with the
|
||||||
|
`schema` object, the example MUST follow the prescribed serialization strategy for
|
||||||
|
the parameter.
|
||||||
|
"""
|
||||||
|
|
||||||
|
content: Optional[Dict[str, MediaType]] = None
|
||||||
|
"""
|
||||||
|
A map containing the representations for the parameter.
|
||||||
|
The key is the media type and the value describes it.
|
||||||
|
The map MUST only contain one entry.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
populate_by_name=True,
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
schema_extra = {"examples": _examples}
|
||||||
|
|
||||||
|
|
||||||
|
class Parameter(ParameterBase):
|
||||||
|
"""
|
||||||
|
Describes a single operation parameter.
|
||||||
|
|
||||||
|
A unique parameter is defined by a combination of a [name](#parameterName) and
|
||||||
|
[location](#parameterIn).
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""Fixed Fields"""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The name of the parameter.
|
||||||
|
Parameter names are *case sensitive*.
|
||||||
|
|
||||||
|
- If [`in`](#parameterIn) is `"path"`, the `name` field MUST correspond to a
|
||||||
|
template expression occurring within the [path](#pathsPath) field in the
|
||||||
|
[Paths Object](#pathsObject). See [Path Templating](#pathTemplating) for further
|
||||||
|
information.
|
||||||
|
- If [`in`](#parameterIn) is `"header"` and the `name` field is `"Accept"`,
|
||||||
|
`"Content-Type"` or `"Authorization"`, the parameter definition SHALL be ignored.
|
||||||
|
- For all other cases, the `name` corresponds to the parameter name used by the
|
||||||
|
[`in`](#parameterIn) property.
|
||||||
|
"""
|
||||||
|
|
||||||
|
param_in: ParameterLocation = Field(alias="in")
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The location of the parameter. Possible values are `"query"`,
|
||||||
|
`"header"`, `"path"` or `"cookie"`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
allowEmptyValue: bool = False
|
||||||
|
"""
|
||||||
|
Sets the ability to pass empty-valued parameters.
|
||||||
|
This is valid only for `query` parameters and allows sending a parameter with an
|
||||||
|
empty value. Default value is `false`.
|
||||||
|
If [`style`](#parameterStyle) is used, and if behavior is `n/a` (cannot be
|
||||||
|
serialized), the value of `allowEmptyValue` SHALL be ignored.
|
||||||
|
Use of this property is NOT RECOMMENDED, as it is likely to be removed in a later
|
||||||
|
revision.
|
||||||
|
"""
|
||||||
|
|
||||||
|
allowReserved: bool = False
|
||||||
|
"""
|
||||||
|
Determines whether the parameter value SHOULD allow reserved characters,
|
||||||
|
as defined by [RFC3986](https://tools.ietf.org/html/rfc3986#section-2.2)
|
||||||
|
`:/?#[]@!$&'()*+,;=` to be included without percent-encoding.
|
||||||
|
This property only applies to parameters with an `in` value of `query`.
|
||||||
|
The default value is `false`.
|
||||||
|
"""
|
152
openapi_pydantic/v3/v3_0/path_item.py
Normal file
152
openapi_pydantic/v3/v3_0/path_item.py
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .operation import Operation
|
||||||
|
from .parameter import Parameter
|
||||||
|
from .reference import Reference
|
||||||
|
from .server import Server
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"get": {
|
||||||
|
"description": "Returns pets based on ID",
|
||||||
|
"summary": "Find pets by ID",
|
||||||
|
"operationId": "getPetsById",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "pet response",
|
||||||
|
"content": {
|
||||||
|
"*/*": {
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/components/schemas/Pet"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"description": "error payload",
|
||||||
|
"content": {
|
||||||
|
"text/html": {
|
||||||
|
"schema": {"$ref": "#/components/schemas/ErrorModel"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"description": "ID of pet to use",
|
||||||
|
"required": True,
|
||||||
|
"schema": {"type": "array", "items": {"type": "string"}},
|
||||||
|
"style": "simple",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class PathItem(BaseModel):
|
||||||
|
"""
|
||||||
|
Describes the operations available on a single path.
|
||||||
|
A Path Item MAY be empty, due to [ACL constraints](#securityFiltering).
|
||||||
|
The path itself is still exposed to the documentation viewer
|
||||||
|
but they will not know which operations and parameters are available.
|
||||||
|
"""
|
||||||
|
|
||||||
|
ref: Optional[str] = Field(default=None, alias="$ref")
|
||||||
|
"""
|
||||||
|
Allows for an external definition of this path item.
|
||||||
|
The referenced structure MUST be in the format of a
|
||||||
|
[Path Item Object](#pathItemObject).
|
||||||
|
|
||||||
|
In case a Path Item Object field appears both in the defined object and the
|
||||||
|
referenced object, the behavior is undefined.
|
||||||
|
"""
|
||||||
|
|
||||||
|
summary: Optional[str] = None
|
||||||
|
"""
|
||||||
|
An optional, string summary, intended to apply to all operations in this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
An optional, string description, intended to apply to all operations in this path.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
get: Optional[Operation] = None
|
||||||
|
"""
|
||||||
|
A definition of a GET operation on this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
put: Optional[Operation] = None
|
||||||
|
"""
|
||||||
|
A definition of a PUT operation on this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
post: Optional[Operation] = None
|
||||||
|
"""
|
||||||
|
A definition of a POST operation on this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
delete: Optional[Operation] = None
|
||||||
|
"""
|
||||||
|
A definition of a DELETE operation on this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
options: Optional[Operation] = None
|
||||||
|
"""
|
||||||
|
A definition of a OPTIONS operation on this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
head: Optional[Operation] = None
|
||||||
|
"""
|
||||||
|
A definition of a HEAD operation on this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
patch: Optional[Operation] = None
|
||||||
|
"""
|
||||||
|
A definition of a PATCH operation on this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
trace: Optional[Operation] = None
|
||||||
|
"""
|
||||||
|
A definition of a TRACE operation on this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
servers: Optional[List[Server]] = None
|
||||||
|
"""
|
||||||
|
An alternative `server` array to service all operations in this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
parameters: Optional[List[Union[Parameter, Reference]]] = None
|
||||||
|
"""
|
||||||
|
A list of parameters that are applicable for all the operations described under
|
||||||
|
this path. These parameters can be overridden at the operation level, but cannot be
|
||||||
|
removed there. The list MUST NOT include duplicated parameters.
|
||||||
|
A unique parameter is defined by a combination of a [name](#parameterName) and
|
||||||
|
[location](#parameterIn). The list can use the [Reference Object](#referenceObject)
|
||||||
|
to link to parameters that are defined at the
|
||||||
|
[OpenAPI Object's components/parameters](#componentsParameters).
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
populate_by_name=True,
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
schema_extra = {"examples": _examples}
|
27
openapi_pydantic/v3/v3_0/paths.py
Normal file
27
openapi_pydantic/v3/v3_0/paths.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
from .path_item import PathItem
|
||||||
|
|
||||||
|
Paths = Dict[str, PathItem]
|
||||||
|
"""
|
||||||
|
Holds the relative paths to the individual endpoints and their operations.
|
||||||
|
The path is appended to the URL from the [`Server Object`](#serverObject) in order to
|
||||||
|
construct the full URL.
|
||||||
|
|
||||||
|
The Paths MAY be empty, due to [ACL constraints](#securityFiltering).
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""Patterned Fields"""
|
||||||
|
|
||||||
|
# "/{path}" : PathItem
|
||||||
|
"""
|
||||||
|
A relative path to an individual endpoint.
|
||||||
|
The field name MUST begin with a forward slash (`/`).
|
||||||
|
The path is **appended** (no relative URL resolution) to the expanded URL
|
||||||
|
from the [`Server Object`](#serverObject)'s `url` field in order to construct the full
|
||||||
|
URL. [Path templating](#pathTemplating) is allowed.
|
||||||
|
When matching URLs, concrete (non-templated) paths would be matched before their
|
||||||
|
templated counterparts. Templated paths with the same hierarchy but different templated
|
||||||
|
names MUST NOT exist as they are identical. In case of ambiguous matching, it's up to
|
||||||
|
the tooling to decide which one to use.
|
||||||
|
"""
|
38
openapi_pydantic/v3/v3_0/reference.py
Normal file
38
openapi_pydantic/v3/v3_0/reference.py
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{"$ref": "#/components/schemas/Pet"},
|
||||||
|
{"$ref": "Pet.json"},
|
||||||
|
{"$ref": "definitions.json#/Pet"},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Reference(BaseModel):
|
||||||
|
"""
|
||||||
|
A simple object to allow referencing other components in the specification.
|
||||||
|
|
||||||
|
The Reference Object is defined by [JSON Reference](https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03)
|
||||||
|
and follows the same structure, behavior and rules.
|
||||||
|
|
||||||
|
For this specification, reference resolution is accomplished as defined by the JSON
|
||||||
|
Reference specification and not by the JSON Schema specification.
|
||||||
|
"""
|
||||||
|
|
||||||
|
ref: str = Field(alias="$ref")
|
||||||
|
"""**REQUIRED**. The reference string."""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
populate_by_name=True,
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
schema_extra = {"examples": _examples}
|
95
openapi_pydantic/v3/v3_0/request_body.py
Normal file
95
openapi_pydantic/v3/v3_0/request_body.py
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
from typing import Dict, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .media_type import MediaType
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"description": "user to add to the system",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {"$ref": "#/components/schemas/User"},
|
||||||
|
"examples": {
|
||||||
|
"user": {
|
||||||
|
"summary": "User Example",
|
||||||
|
"externalValue": "http://foo.bar/examples/user-example.json",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"application/xml": {
|
||||||
|
"schema": {"$ref": "#/components/schemas/User"},
|
||||||
|
"examples": {
|
||||||
|
"user": {
|
||||||
|
"summary": "User example in XML",
|
||||||
|
"externalValue": "http://foo.bar/examples/user-example.xml",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"text/plain": {
|
||||||
|
"examples": {
|
||||||
|
"user": {
|
||||||
|
"summary": "User example in Plain text",
|
||||||
|
"externalValue": "http://foo.bar/examples/user-example.txt",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"*/*": {
|
||||||
|
"examples": {
|
||||||
|
"user": {
|
||||||
|
"summary": "User example in other format",
|
||||||
|
"externalValue": "http://foo.bar/examples/user-example.whatever",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "user to add to the system",
|
||||||
|
"content": {
|
||||||
|
"text/plain": {"schema": {"type": "array", "items": {"type": "string"}}}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class RequestBody(BaseModel):
|
||||||
|
"""Describes a single request body."""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A brief description of the request body.
|
||||||
|
This could contain examples of use.
|
||||||
|
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
content: Dict[str, MediaType]
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The content of the request body.
|
||||||
|
The key is a media type or [media type range](https://tools.ietf.org/html/rfc7231#appendix-D)
|
||||||
|
and the value describes it.
|
||||||
|
|
||||||
|
For requests that match multiple keys, only the most specific key is applicable.
|
||||||
|
e.g. text/plain overrides text/*
|
||||||
|
"""
|
||||||
|
|
||||||
|
required: bool = False
|
||||||
|
"""
|
||||||
|
Determines if the request body is required in the request. Defaults to `false`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
101
openapi_pydantic/v3/v3_0/response.py
Normal file
101
openapi_pydantic/v3/v3_0/response.py
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
from typing import Dict, Optional, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .header import Header
|
||||||
|
from .link import Link
|
||||||
|
from .media_type import MediaType
|
||||||
|
from .reference import Reference
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"description": "A complex object array response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/components/schemas/VeryComplexType"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "A simple string response",
|
||||||
|
"content": {"text/plain": {"schema": {"type": "string"}}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "A simple string response",
|
||||||
|
"content": {"text/plain": {"schema": {"type": "string", "example": "whoa!"}}},
|
||||||
|
"headers": {
|
||||||
|
"X-Rate-Limit-Limit": {
|
||||||
|
"description": ("The number of allowed requests in the current period"),
|
||||||
|
"schema": {"type": "integer"},
|
||||||
|
},
|
||||||
|
"X-Rate-Limit-Remaining": {
|
||||||
|
"description": (
|
||||||
|
"The number of remaining requests in the current period"
|
||||||
|
),
|
||||||
|
"schema": {"type": "integer"},
|
||||||
|
},
|
||||||
|
"X-Rate-Limit-Reset": {
|
||||||
|
"description": ("The number of seconds left in the current period"),
|
||||||
|
"schema": {"type": "integer"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{"description": "object created"},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Response(BaseModel):
|
||||||
|
"""
|
||||||
|
Describes a single response from an API Operation, including design-time,
|
||||||
|
static `links` to operations based on the response.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. A short description of the response.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
headers: Optional[Dict[str, Union[Header, Reference]]] = None
|
||||||
|
"""
|
||||||
|
Maps a header name to its definition.
|
||||||
|
[RFC7230](https://tools.ietf.org/html/rfc7230#page-22) states header names are case
|
||||||
|
insensitive. If a response header is defined with the name `"Content-Type"`, it
|
||||||
|
SHALL be ignored.
|
||||||
|
"""
|
||||||
|
|
||||||
|
content: Optional[Dict[str, MediaType]] = None
|
||||||
|
"""
|
||||||
|
A map containing descriptions of potential response payloads.
|
||||||
|
The key is a media type or [media type range](https://tools.ietf.org/html/rfc7231#appendix-D)
|
||||||
|
and the value describes it.
|
||||||
|
|
||||||
|
For responses that match multiple keys, only the most specific key is applicable.
|
||||||
|
e.g. text/plain overrides text/*
|
||||||
|
"""
|
||||||
|
|
||||||
|
links: Optional[Dict[str, Union[Link, Reference]]] = None
|
||||||
|
"""
|
||||||
|
A map of operations links that can be followed from the response.
|
||||||
|
The key of the map is a short name for the link,
|
||||||
|
following the naming constraints of the names for
|
||||||
|
[Component Objects](#componentsObject).
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
49
openapi_pydantic/v3/v3_0/responses.py
Normal file
49
openapi_pydantic/v3/v3_0/responses.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
from typing import Dict, Union
|
||||||
|
|
||||||
|
from .reference import Reference
|
||||||
|
from .response import Response
|
||||||
|
|
||||||
|
Responses = Dict[str, Union[Response, Reference]]
|
||||||
|
"""
|
||||||
|
A container for the expected responses of an operation.
|
||||||
|
The container maps a HTTP response code to the expected response.
|
||||||
|
|
||||||
|
The documentation is not necessarily expected to cover all possible HTTP response codes
|
||||||
|
because they may not be known in advance.
|
||||||
|
However, documentation is expected to cover a successful operation response and any
|
||||||
|
known errors.
|
||||||
|
|
||||||
|
The `default` MAY be used as a default response object for all HTTP codes
|
||||||
|
that are not covered individually by the specification.
|
||||||
|
|
||||||
|
The `Responses Object` MUST contain at least one response code, and it
|
||||||
|
SHOULD be the response for a successful operation call.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""Fixed Fields"""
|
||||||
|
|
||||||
|
# default: Optional[Union[Response, Reference]]
|
||||||
|
"""
|
||||||
|
The documentation of responses other than the ones declared for specific HTTP response
|
||||||
|
codes. Use this field to cover undeclared responses.
|
||||||
|
A [Reference Object](#referenceObject) can link to a response
|
||||||
|
that the [OpenAPI Object's components/responses](#componentsResponses) section defines.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""Patterned Fields"""
|
||||||
|
# {httpStatusCode]: Optional[Union[Response, Reference]]
|
||||||
|
"""
|
||||||
|
Any [HTTP status code](#httpCodes) can be used as the property name,
|
||||||
|
but only one property per code, to describe the expected response for that HTTP status
|
||||||
|
code.
|
||||||
|
|
||||||
|
A [Reference Object](#referenceObject) can link to a response
|
||||||
|
that is defined in the [OpenAPI Object's components/responses](#componentsResponses)
|
||||||
|
section. This field MUST be enclosed in quotation marks (for example, "200") for
|
||||||
|
compatibility between JSON and YAML. To define a range of response codes, this field
|
||||||
|
MAY contain the uppercase wildcard character `X`. For example, `2XX` represents all
|
||||||
|
response codes between `[200-299]`. Only the following range definitions are allowed:
|
||||||
|
`1XX`, `2XX`, `3XX`, `4XX`, and `5XX`.
|
||||||
|
If a response is defined using an explicit code,
|
||||||
|
the explicit code definition takes precedence over the range definition for that code.
|
||||||
|
"""
|
614
openapi_pydantic/v3/v3_0/schema.py
Normal file
614
openapi_pydantic/v3/v3_0/schema.py
Normal file
|
@ -0,0 +1,614 @@
|
||||||
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra, min_length_arg
|
||||||
|
|
||||||
|
from .datatype import DataType
|
||||||
|
from .discriminator import Discriminator
|
||||||
|
from .external_documentation import ExternalDocumentation
|
||||||
|
from .reference import Reference
|
||||||
|
from .xml import XML
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{"type": "string", "format": "email"},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": ["name"],
|
||||||
|
"properties": {
|
||||||
|
"name": {"type": "string"},
|
||||||
|
"address": {"$ref": "#/components/schemas/Address"},
|
||||||
|
"age": {"type": "integer", "format": "int32", "minimum": 0},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{"type": "object", "additionalProperties": {"type": "string"}},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {"$ref": "#/components/schemas/ComplexModel"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {"type": "integer", "format": "int64"},
|
||||||
|
"name": {"type": "string"},
|
||||||
|
},
|
||||||
|
"required": ["name"],
|
||||||
|
"example": {"name": "Puma", "id": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": ["message", "code"],
|
||||||
|
"properties": {
|
||||||
|
"message": {"type": "string"},
|
||||||
|
"code": {"type": "integer", "minimum": 100, "maximum": 600},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{"$ref": "#/components/schemas/ErrorModel"},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": ["rootCause"],
|
||||||
|
"properties": {"rootCause": {"type": "string"}},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"discriminator": {"propertyName": "petType"},
|
||||||
|
"properties": {
|
||||||
|
"name": {"type": "string"},
|
||||||
|
"petType": {"type": "string"},
|
||||||
|
},
|
||||||
|
"required": ["name", "petType"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "A representation of a cat. "
|
||||||
|
"Note that `Cat` will be used as the discriminator value.",
|
||||||
|
"allOf": [
|
||||||
|
{"$ref": "#/components/schemas/Pet"},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"huntingSkill": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The measured skill for hunting",
|
||||||
|
"default": "lazy",
|
||||||
|
"enum": [
|
||||||
|
"clueless",
|
||||||
|
"lazy",
|
||||||
|
"adventurous",
|
||||||
|
"aggressive",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["huntingSkill"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "A representation of a dog. "
|
||||||
|
"Note that `Dog` will be used as the discriminator value.",
|
||||||
|
"allOf": [
|
||||||
|
{"$ref": "#/components/schemas/Pet"},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"packSize": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32",
|
||||||
|
"description": ("the size of the pack the dog is from"),
|
||||||
|
"default": 0,
|
||||||
|
"minimum": 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["packSize"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Schema(BaseModel):
|
||||||
|
"""
|
||||||
|
The Schema Object allows the definition of input and output data types.
|
||||||
|
These types can be objects, but also primitives and arrays.
|
||||||
|
This object is an extended subset of the [JSON Schema Specification Wright Draft 00](https://json-schema.org/).
|
||||||
|
|
||||||
|
For more information about the properties,
|
||||||
|
see [JSON Schema Core](https://tools.ietf.org/html/draft-wright-json-schema-00)
|
||||||
|
and [JSON Schema Validation](https://tools.ietf.org/html/draft-wright-json-schema-validation-00).
|
||||||
|
Unless stated otherwise, the property definitions follow the JSON Schema.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
The following properties are taken directly from the JSON Schema definition and
|
||||||
|
follow the same specifications:
|
||||||
|
"""
|
||||||
|
|
||||||
|
title: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The value of "title" MUST be a string.
|
||||||
|
|
||||||
|
The title can be used to decorate a user interface with
|
||||||
|
information about the data produced by this user interface.
|
||||||
|
The title will preferrably be short.
|
||||||
|
"""
|
||||||
|
|
||||||
|
multipleOf: Optional[float] = Field(default=None, gt=0.0)
|
||||||
|
"""
|
||||||
|
The value of "multipleOf" MUST be a number, strictly greater than 0.
|
||||||
|
|
||||||
|
A numeric instance is only valid if division by this keyword's value
|
||||||
|
results in an integer.
|
||||||
|
"""
|
||||||
|
|
||||||
|
maximum: Optional[float] = None
|
||||||
|
"""
|
||||||
|
The value of "maximum" MUST be a number, representing an upper limit
|
||||||
|
for a numeric instance.
|
||||||
|
|
||||||
|
If the instance is a number, then this keyword validates if
|
||||||
|
"exclusiveMaximum" is true and instance is less than the provided
|
||||||
|
value, or else if the instance is less than or exactly equal to the
|
||||||
|
provided value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
exclusiveMaximum: Optional[bool] = None
|
||||||
|
"""
|
||||||
|
The value of "exclusiveMaximum" MUST be a boolean, representing
|
||||||
|
whether the limit in "maximum" is exclusive or not. An undefined
|
||||||
|
value is the same as false.
|
||||||
|
|
||||||
|
If "exclusiveMaximum" is true, then a numeric instance SHOULD NOT be
|
||||||
|
equal to the value specified in "maximum". If "exclusiveMaximum" is
|
||||||
|
false (or not specified), then a numeric instance MAY be equal to the
|
||||||
|
value of "maximum".
|
||||||
|
"""
|
||||||
|
|
||||||
|
minimum: Optional[float] = None
|
||||||
|
"""
|
||||||
|
The value of "minimum" MUST be a number, representing a lower limit
|
||||||
|
for a numeric instance.
|
||||||
|
|
||||||
|
If the instance is a number, then this keyword validates if
|
||||||
|
"exclusiveMinimum" is true and instance is greater than the provided
|
||||||
|
value, or else if the instance is greater than or exactly equal to
|
||||||
|
the provided value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
exclusiveMinimum: Optional[bool] = None
|
||||||
|
"""
|
||||||
|
The value of "exclusiveMinimum" MUST be a boolean, representing
|
||||||
|
whether the limit in "minimum" is exclusive or not. An undefined
|
||||||
|
value is the same as false.
|
||||||
|
|
||||||
|
If "exclusiveMinimum" is true, then a numeric instance SHOULD NOT be
|
||||||
|
equal to the value specified in "minimum". If "exclusiveMinimum" is
|
||||||
|
false (or not specified), then a numeric instance MAY be equal to the
|
||||||
|
value of "minimum".
|
||||||
|
"""
|
||||||
|
|
||||||
|
maxLength: Optional[int] = Field(default=None, ge=0)
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be a non-negative integer.
|
||||||
|
|
||||||
|
The value of this keyword MUST be an integer. This integer MUST be
|
||||||
|
greater than, or equal to, 0.
|
||||||
|
|
||||||
|
A string instance is valid against this keyword if its length is less
|
||||||
|
than, or equal to, the value of this keyword.
|
||||||
|
|
||||||
|
The length of a string instance is defined as the number of its
|
||||||
|
characters as defined by RFC 7159 [RFC7159].
|
||||||
|
"""
|
||||||
|
|
||||||
|
minLength: Optional[int] = Field(default=None, ge=0)
|
||||||
|
"""
|
||||||
|
A string instance is valid against this keyword if its length is
|
||||||
|
greater than, or equal to, the value of this keyword.
|
||||||
|
|
||||||
|
The length of a string instance is defined as the number of its
|
||||||
|
characters as defined by RFC 7159 [RFC7159].
|
||||||
|
|
||||||
|
The value of this keyword MUST be an integer. This integer MUST be
|
||||||
|
greater than, or equal to, 0.
|
||||||
|
|
||||||
|
"minLength", if absent, may be considered as being present with
|
||||||
|
integer value 0.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pattern: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be a string. This string SHOULD be a
|
||||||
|
valid regular expression, according to the ECMA 262 regular
|
||||||
|
expression dialect.
|
||||||
|
|
||||||
|
A string instance is considered valid if the regular expression
|
||||||
|
matches the instance successfully. Recall: regular expressions are
|
||||||
|
not implicitly anchored.
|
||||||
|
"""
|
||||||
|
|
||||||
|
maxItems: Optional[int] = Field(default=None, ge=0)
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be an integer. This integer MUST be
|
||||||
|
greater than, or equal to, 0.
|
||||||
|
|
||||||
|
An array instance is valid against "maxItems" if its size is less
|
||||||
|
than, or equal to, the value of this keyword.
|
||||||
|
"""
|
||||||
|
|
||||||
|
minItems: Optional[int] = Field(default=None, ge=0)
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be an integer. This integer MUST be
|
||||||
|
greater than, or equal to, 0.
|
||||||
|
|
||||||
|
An array instance is valid against "minItems" if its size is greater
|
||||||
|
than, or equal to, the value of this keyword.
|
||||||
|
|
||||||
|
If this keyword is not present, it may be considered present with a
|
||||||
|
value of 0.
|
||||||
|
"""
|
||||||
|
|
||||||
|
uniqueItems: Optional[bool] = None
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be a boolean.
|
||||||
|
|
||||||
|
If this keyword has boolean value false, the instance validates
|
||||||
|
successfully. If it has boolean value true, the instance validates
|
||||||
|
successfully if all of its elements are unique.
|
||||||
|
|
||||||
|
If not present, this keyword may be considered present with boolean
|
||||||
|
value false.
|
||||||
|
"""
|
||||||
|
|
||||||
|
maxProperties: Optional[int] = Field(default=None, ge=0)
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be an integer. This integer MUST be
|
||||||
|
greater than, or equal to, 0.
|
||||||
|
|
||||||
|
An object instance is valid against "maxProperties" if its number of
|
||||||
|
properties is less than, or equal to, the value of this keyword.
|
||||||
|
"""
|
||||||
|
|
||||||
|
minProperties: Optional[int] = Field(default=None, ge=0)
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be an integer. This integer MUST be
|
||||||
|
greater than, or equal to, 0.
|
||||||
|
|
||||||
|
An object instance is valid against "minProperties" if its number of
|
||||||
|
properties is greater than, or equal to, the value of this keyword.
|
||||||
|
|
||||||
|
If this keyword is not present, it may be considered present with a
|
||||||
|
value of 0.
|
||||||
|
"""
|
||||||
|
|
||||||
|
required: Optional[List[str]] = Field(default=None, **min_length_arg(1))
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be an array. This array MUST have at
|
||||||
|
least one element. Elements of this array MUST be strings, and MUST
|
||||||
|
be unique.
|
||||||
|
|
||||||
|
An object instance is valid against this keyword if its property set
|
||||||
|
contains all elements in this keyword's array value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
enum: Optional[List[Any]] = Field(default=None, **min_length_arg(1))
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be an array. This array SHOULD have
|
||||||
|
at least one element. Elements in the array SHOULD be unique.
|
||||||
|
|
||||||
|
Elements in the array MAY be of any type, including null.
|
||||||
|
|
||||||
|
An instance validates successfully against this keyword if its value
|
||||||
|
is equal to one of the elements in this keyword's array value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
The following properties are taken from the JSON Schema definition
|
||||||
|
but their definitions were adjusted to the OpenAPI Specification.
|
||||||
|
"""
|
||||||
|
|
||||||
|
type: Optional[DataType] = None
|
||||||
|
"""
|
||||||
|
**From OpenAPI spec:
|
||||||
|
Value MUST be a string. Multiple types via an array are not supported.**
|
||||||
|
|
||||||
|
From JSON Schema:
|
||||||
|
The value of this keyword MUST be either a string or an array. If it
|
||||||
|
is an array, elements of the array MUST be strings and MUST be
|
||||||
|
unique.
|
||||||
|
|
||||||
|
String values MUST be one of the seven primitive types defined by the
|
||||||
|
core specification.
|
||||||
|
|
||||||
|
An instance matches successfully if its primitive type is one of the
|
||||||
|
types defined by keyword. Recall: "number" includes "integer".
|
||||||
|
"""
|
||||||
|
|
||||||
|
allOf: Optional[List[Union[Reference, "Schema"]]] = None
|
||||||
|
"""
|
||||||
|
**From OpenAPI spec:
|
||||||
|
Inline or referenced schema MUST be of a [Schema Object](#schemaObject) and not a
|
||||||
|
standard JSON Schema.**
|
||||||
|
|
||||||
|
From JSON Schema:
|
||||||
|
This keyword's value MUST be an array. This array MUST have at least
|
||||||
|
one element.
|
||||||
|
|
||||||
|
Elements of the array MUST be objects. Each object MUST be a valid
|
||||||
|
JSON Schema.
|
||||||
|
|
||||||
|
An instance validates successfully against this keyword if it
|
||||||
|
validates successfully against all schemas defined by this keyword's
|
||||||
|
value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
oneOf: Optional[List[Union[Reference, "Schema"]]] = None
|
||||||
|
"""
|
||||||
|
**From OpenAPI spec:
|
||||||
|
Inline or referenced schema MUST be of a [Schema Object](#schemaObject) and not a
|
||||||
|
standard JSON Schema.**
|
||||||
|
|
||||||
|
From JSON Schema:
|
||||||
|
This keyword's value MUST be an array. This array MUST have at least
|
||||||
|
one element.
|
||||||
|
|
||||||
|
Elements of the array MUST be objects. Each object MUST be a valid
|
||||||
|
JSON Schema.
|
||||||
|
|
||||||
|
An instance validates successfully against this keyword if it
|
||||||
|
validates successfully against exactly one schema defined by this
|
||||||
|
keyword's value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
anyOf: Optional[List[Union[Reference, "Schema"]]] = None
|
||||||
|
"""
|
||||||
|
**From OpenAPI spec:
|
||||||
|
Inline or referenced schema MUST be of a [Schema Object](#schemaObject) and not a
|
||||||
|
standard JSON Schema.**
|
||||||
|
|
||||||
|
From JSON Schema:
|
||||||
|
This keyword's value MUST be an array. This array MUST have at least
|
||||||
|
one element.
|
||||||
|
|
||||||
|
Elements of the array MUST be objects. Each object MUST be a valid
|
||||||
|
JSON Schema.
|
||||||
|
|
||||||
|
An instance validates successfully against this keyword if it
|
||||||
|
validates successfully against at least one schema defined by this
|
||||||
|
keyword's value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
schema_not: Optional[Union[Reference, "Schema"]] = Field(default=None, alias="not")
|
||||||
|
"""
|
||||||
|
**From OpenAPI spec:
|
||||||
|
Inline or referenced schema MUST be of a [Schema Object](#schemaObject) and not a
|
||||||
|
standard JSON Schema.**
|
||||||
|
|
||||||
|
From JSON Schema:
|
||||||
|
This keyword's value MUST be an object. This object MUST be a valid
|
||||||
|
JSON Schema.
|
||||||
|
|
||||||
|
An instance is valid against this keyword if it fails to validate
|
||||||
|
successfully against the schema defined by this keyword.
|
||||||
|
"""
|
||||||
|
|
||||||
|
items: Optional[Union[Reference, "Schema"]] = None
|
||||||
|
"""
|
||||||
|
**From OpenAPI spec:
|
||||||
|
Value MUST be an object and not an array.
|
||||||
|
Inline or referenced schema MUST be of a [Schema Object](#schemaObject) and not a
|
||||||
|
standard JSON Schema. `items` MUST be present if the `type` is `array`.**
|
||||||
|
|
||||||
|
From JSON Schema:
|
||||||
|
The value of "items" MUST be either a schema or array of schemas.
|
||||||
|
|
||||||
|
Successful validation of an array instance with regards to these two
|
||||||
|
keywords is determined as follows:
|
||||||
|
|
||||||
|
- if "items" is not present, or its value is an object, validation
|
||||||
|
of the instance always succeeds, regardless of the value of
|
||||||
|
"additionalItems";
|
||||||
|
- if the value of "additionalItems" is boolean value true or an
|
||||||
|
object, validation of the instance always succeeds;
|
||||||
|
- if the value of "additionalItems" is boolean value false and the
|
||||||
|
value of "items" is an array, the instance is valid if its size is
|
||||||
|
less than, or equal to, the size of "items".
|
||||||
|
"""
|
||||||
|
|
||||||
|
properties: Optional[Dict[str, Union[Reference, "Schema"]]] = None
|
||||||
|
"""
|
||||||
|
**From OpenAPI spec:
|
||||||
|
Property definitions MUST be a [Schema Object](#schemaObject)
|
||||||
|
and not a standard JSON Schema (inline or referenced).**
|
||||||
|
|
||||||
|
From JSON Schema:
|
||||||
|
The value of "properties" MUST be an object. Each value of this
|
||||||
|
object MUST be an object, and each object MUST be a valid JSON
|
||||||
|
Schema.
|
||||||
|
|
||||||
|
If absent, it can be considered the same as an empty object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
additionalProperties: Optional[Union[bool, Reference, "Schema"]] = None
|
||||||
|
"""
|
||||||
|
**From OpenAPI spec:
|
||||||
|
Value can be boolean or object.
|
||||||
|
Inline or referenced schema MUST be of a [Schema Object](#schemaObject) and not a
|
||||||
|
standard JSON Schema.
|
||||||
|
Consistent with JSON Schema, `additionalProperties` defaults to `true`.**
|
||||||
|
|
||||||
|
From JSON Schema:
|
||||||
|
The value of "additionalProperties" MUST be a boolean or a schema.
|
||||||
|
|
||||||
|
If "additionalProperties" is absent, it may be considered present
|
||||||
|
with an empty schema as a value.
|
||||||
|
|
||||||
|
If "additionalProperties" is true, validation always succeeds.
|
||||||
|
|
||||||
|
If "additionalProperties" is false, validation succeeds only if the
|
||||||
|
instance is an object and all properties on the instance were covered
|
||||||
|
by "properties" and/or "patternProperties".
|
||||||
|
|
||||||
|
If "additionalProperties" is an object, validate the value as a
|
||||||
|
schema to all of the properties that weren't validated by
|
||||||
|
"properties" nor "patternProperties".
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
**From OpenAPI spec:
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.**
|
||||||
|
|
||||||
|
From JSON Schema:
|
||||||
|
The value "description" MUST be a string.
|
||||||
|
|
||||||
|
The description can be used to decorate a user interface with
|
||||||
|
information about the data produced by this user interface.
|
||||||
|
The description will provide explanation about the purpose of
|
||||||
|
the instance described by this schema.
|
||||||
|
"""
|
||||||
|
|
||||||
|
schema_format: Optional[str] = Field(default=None, alias="format")
|
||||||
|
"""
|
||||||
|
**From OpenAPI spec:
|
||||||
|
[Data Type Formats](#dataTypeFormat) for further details.
|
||||||
|
While relying on JSON Schema's defined formats, the OAS offers a few additional
|
||||||
|
predefined formats.**
|
||||||
|
|
||||||
|
From JSON Schema:
|
||||||
|
Structural validation alone may be insufficient to validate that an
|
||||||
|
instance meets all the requirements of an application. The "format"
|
||||||
|
keyword is defined to allow interoperable semantic validation for a
|
||||||
|
fixed subset of values which are accurately described by
|
||||||
|
authoritative resources, be they RFCs or other external
|
||||||
|
specifications.
|
||||||
|
|
||||||
|
The value of this keyword is called a format attribute. It MUST be a
|
||||||
|
string. A format attribute can generally only validate a given set
|
||||||
|
of instance types. If the type of the instance to validate is not in
|
||||||
|
this set, validation for this format attribute and instance SHOULD
|
||||||
|
succeed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
default: Optional[Any] = None
|
||||||
|
"""
|
||||||
|
**From OpenAPI spec:
|
||||||
|
The default value represents what would be assumed by the consumer of the input
|
||||||
|
as the value of the schema if one is not provided.
|
||||||
|
Unlike JSON Schema, the value MUST conform to the defined type for the Schema
|
||||||
|
Object defined at the same level. For example, if `type` is `string`, then
|
||||||
|
`default` can be `"foo"` but cannot be `1`.**
|
||||||
|
|
||||||
|
From JSON Schema:
|
||||||
|
There are no restrictions placed on the value of this keyword.
|
||||||
|
|
||||||
|
This keyword can be used to supply a default JSON value associated
|
||||||
|
with a particular schema. It is RECOMMENDED that a default value be
|
||||||
|
valid against the associated schema.
|
||||||
|
|
||||||
|
This keyword MAY be used in root schemas, and in any subschemas.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
Other than the JSON Schema subset fields, the following fields MAY be used for
|
||||||
|
further schema documentation:
|
||||||
|
"""
|
||||||
|
|
||||||
|
nullable: Optional[bool] = None
|
||||||
|
"""
|
||||||
|
A `true` value adds `"null"` to the allowed type specified by the `type` keyword,
|
||||||
|
only if `type` is explicitly defined within the same Schema Object.
|
||||||
|
Other Schema Object constraints retain their defined behavior,
|
||||||
|
and therefore may disallow the use of `null` as a value.
|
||||||
|
A `false` value leaves the specified or default `type` unmodified.
|
||||||
|
The default value is `false`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
discriminator: Optional[Discriminator] = None
|
||||||
|
"""
|
||||||
|
Adds support for polymorphism.
|
||||||
|
The discriminator is an object name that is used to differentiate between other
|
||||||
|
schemas which may satisfy the payload description.
|
||||||
|
See [Composition and Inheritance](#schemaComposition) for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
readOnly: Optional[bool] = None
|
||||||
|
"""
|
||||||
|
Relevant only for Schema `"properties"` definitions.
|
||||||
|
Declares the property as "read only".
|
||||||
|
This means that it MAY be sent as part of a response but SHOULD NOT be sent as part
|
||||||
|
of the request. If the property is marked as `readOnly` being `true` and is in the
|
||||||
|
`required` list, the `required` will take effect on the response only.
|
||||||
|
A property MUST NOT be marked as both `readOnly` and `writeOnly` being `true`.
|
||||||
|
Default value is `false`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
writeOnly: Optional[bool] = None
|
||||||
|
"""
|
||||||
|
Relevant only for Schema `"properties"` definitions.
|
||||||
|
Declares the property as "write only".
|
||||||
|
Therefore, it MAY be sent as part of a request but SHOULD NOT be sent as part of
|
||||||
|
the response. If the property is marked as `writeOnly` being `true` and is in the
|
||||||
|
`required` list, the `required` will take effect on the request only.
|
||||||
|
A property MUST NOT be marked as both `readOnly` and `writeOnly` being `true`.
|
||||||
|
Default value is `false`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
xml: Optional[XML] = None
|
||||||
|
"""
|
||||||
|
This MAY be used only on properties schemas.
|
||||||
|
It has no effect on root schemas.
|
||||||
|
Adds additional metadata to describe the XML representation of this property.
|
||||||
|
"""
|
||||||
|
|
||||||
|
externalDocs: Optional[ExternalDocumentation] = None
|
||||||
|
"""
|
||||||
|
Additional external documentation for this schema.
|
||||||
|
"""
|
||||||
|
|
||||||
|
example: Optional[Any] = None
|
||||||
|
"""
|
||||||
|
A free-form property to include an example of an instance for this schema.
|
||||||
|
To represent examples that cannot be naturally represented in JSON or YAML,
|
||||||
|
a string value can be used to contain the example with escaping where necessary.
|
||||||
|
"""
|
||||||
|
|
||||||
|
deprecated: Optional[bool] = None
|
||||||
|
"""
|
||||||
|
Specifies that a schema is deprecated and SHOULD be transitioned out of usage.
|
||||||
|
Default value is `false`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
populate_by_name=True,
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
schema_extra = {"examples": _examples}
|
||||||
|
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
|
||||||
|
def schema_validate(
|
||||||
|
obj: Any,
|
||||||
|
*,
|
||||||
|
strict: Optional[bool] = None,
|
||||||
|
from_attributes: Optional[bool] = None,
|
||||||
|
context: Optional[Dict[str, Any]] = None
|
||||||
|
) -> Schema: ...
|
||||||
|
|
||||||
|
elif PYDANTIC_V2:
|
||||||
|
schema_validate = Schema.model_validate
|
||||||
|
|
||||||
|
else:
|
||||||
|
schema_validate = Schema.parse_obj
|
32
openapi_pydantic/v3/v3_0/security_requirement.py
Normal file
32
openapi_pydantic/v3/v3_0/security_requirement.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
|
SecurityRequirement = Dict[str, List[str]]
|
||||||
|
"""
|
||||||
|
Lists the required security schemes to execute this operation.
|
||||||
|
The name used for each property MUST correspond to a security scheme declared in the
|
||||||
|
[Security Schemes](#componentsSecuritySchemes) under the
|
||||||
|
[Components Object](#componentsObject).
|
||||||
|
|
||||||
|
Security Requirement Objects that contain multiple schemes require that
|
||||||
|
all schemes MUST be satisfied for a request to be authorized.
|
||||||
|
This enables support for scenarios where multiple query parameters or HTTP headers
|
||||||
|
are required to convey security information.
|
||||||
|
|
||||||
|
When a list of Security Requirement Objects is defined on the
|
||||||
|
[OpenAPI Object](#oasObject) or [Operation Object](#operationObject),
|
||||||
|
only one of the Security Requirement Objects in the list needs to be satisfied to
|
||||||
|
authorize the request.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""Patterned Fields"""
|
||||||
|
|
||||||
|
# {name}: List[str]
|
||||||
|
"""
|
||||||
|
Each name MUST correspond to a security scheme which is declared
|
||||||
|
in the [Security Schemes](#componentsSecuritySchemes) under the
|
||||||
|
[Components Object](#componentsObject).
|
||||||
|
If the security scheme is of type `"oauth2"` or `"openIdConnect"`,
|
||||||
|
then the value is a list of scope names required for the execution,
|
||||||
|
and the list MAY be empty if authorization does not require a specified scope.
|
||||||
|
For other security scheme types, the array MUST be empty.
|
||||||
|
"""
|
112
openapi_pydantic/v3/v3_0/security_scheme.py
Normal file
112
openapi_pydantic/v3/v3_0/security_scheme.py
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .oauth_flows import OAuthFlows
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{"type": "http", "scheme": "basic"},
|
||||||
|
{"type": "apiKey", "name": "api_key", "in": "header"},
|
||||||
|
{"type": "http", "scheme": "bearer", "bearerFormat": "JWT"},
|
||||||
|
{
|
||||||
|
"type": "oauth2",
|
||||||
|
"flows": {
|
||||||
|
"implicit": {
|
||||||
|
"authorizationUrl": "https://example.com/api/oauth/dialog",
|
||||||
|
"scopes": {
|
||||||
|
"write:pets": "modify pets in your account",
|
||||||
|
"read:pets": "read your pets",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "openIdConnect",
|
||||||
|
"openIdConnectUrl": "https://example.com/openIdConnect",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "openIdConnect",
|
||||||
|
"openIdConnectUrl": "openIdConnect",
|
||||||
|
}, # #5: allow relative path
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SecurityScheme(BaseModel):
|
||||||
|
"""
|
||||||
|
Defines a security scheme that can be used by the operations.
|
||||||
|
Supported schemes are HTTP authentication,
|
||||||
|
an API key (either as a header, a cookie parameter or as a query parameter),
|
||||||
|
OAuth2's common flows (implicit, password, client credentials and authorization
|
||||||
|
code) as defined in [RFC6749](https://tools.ietf.org/html/rfc6749),
|
||||||
|
and [OpenID Connect Discovery](https://tools.ietf.org/html/draft-ietf-oauth-discovery-06).
|
||||||
|
"""
|
||||||
|
|
||||||
|
type: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The type of the security scheme.
|
||||||
|
Valid values are `"apiKey"`, `"http"`, `"oauth2"`, `"openIdConnect"`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A short description for security scheme.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
name: Optional[str] = None
|
||||||
|
"""
|
||||||
|
**REQUIRED** for `apiKey`. The name of the header, query or cookie parameter to be
|
||||||
|
used.
|
||||||
|
"""
|
||||||
|
|
||||||
|
security_scheme_in: Optional[str] = Field(alias="in", default=None)
|
||||||
|
"""
|
||||||
|
**REQUIRED** for `apiKey`. The location of the API key. Valid values are `"query"`,
|
||||||
|
`"header"` or `"cookie"`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
scheme: Optional[str] = None
|
||||||
|
"""
|
||||||
|
**REQUIRED** for `http`. The name of the HTTP Authorization scheme to be used in the
|
||||||
|
[Authorization header as defined in RFC7235](https://tools.ietf.org/html/rfc7235#section-5.1).
|
||||||
|
|
||||||
|
The values used SHOULD be registered in the
|
||||||
|
[IANA Authentication Scheme registry](https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml).
|
||||||
|
"""
|
||||||
|
|
||||||
|
bearerFormat: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A hint to the client to identify how the bearer token is formatted.
|
||||||
|
|
||||||
|
Bearer tokens are usually generated by an authorization server,
|
||||||
|
so this information is primarily for documentation purposes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
flows: Optional[OAuthFlows] = None
|
||||||
|
"""
|
||||||
|
**REQUIRED** for `oauth2`. An object containing configuration information for the
|
||||||
|
flow types supported.
|
||||||
|
"""
|
||||||
|
|
||||||
|
openIdConnectUrl: Optional[str] = None
|
||||||
|
"""
|
||||||
|
**REQUIRED** for `openIdConnect`. OpenId Connect URL to discover OAuth2
|
||||||
|
configuration values. This MUST be in the form of a URL.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
populate_by_name=True,
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
schema_extra = {"examples": _examples}
|
67
openapi_pydantic/v3/v3_0/server.py
Normal file
67
openapi_pydantic/v3/v3_0/server.py
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
from typing import Dict, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .server_variable import ServerVariable
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"url": "https://development.gigantic-server.com/v1",
|
||||||
|
"description": "Development server",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://{username}.gigantic-server.com:{port}/{basePath}",
|
||||||
|
"description": "The production API server",
|
||||||
|
"variables": {
|
||||||
|
"username": {
|
||||||
|
"default": "demo",
|
||||||
|
"description": "this value is assigned by the service"
|
||||||
|
"provider, in this example `gigantic-server.com`",
|
||||||
|
},
|
||||||
|
"port": {"enum": ["8443", "443"], "default": "8443"},
|
||||||
|
"basePath": {"default": "v2"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Server(BaseModel):
|
||||||
|
"""An object representing a Server."""
|
||||||
|
|
||||||
|
url: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. A URL to the target host.
|
||||||
|
|
||||||
|
This URL supports Server Variables and MAY be relative,
|
||||||
|
to indicate that the host location is relative to the location where the OpenAPI
|
||||||
|
document is being served.
|
||||||
|
Variable substitutions will be made when a variable is named in `{`brackets`}`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
An optional string describing the host designated by the URL.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
variables: Optional[Dict[str, ServerVariable]] = None
|
||||||
|
"""
|
||||||
|
A map between a variable name and its value.
|
||||||
|
|
||||||
|
The value is used for substitution in the server's URL template.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
42
openapi_pydantic/v3/v3_0/server_variable.py
Normal file
42
openapi_pydantic/v3/v3_0/server_variable.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
|
||||||
|
class ServerVariable(BaseModel):
|
||||||
|
"""An object representing a Server Variable for server URL template substitution."""
|
||||||
|
|
||||||
|
enum: Optional[List[str]] = None
|
||||||
|
"""
|
||||||
|
An enumeration of string values to be used if the substitution options are from a
|
||||||
|
limited set. The array SHOULD NOT be empty.
|
||||||
|
"""
|
||||||
|
|
||||||
|
default: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The default value to use for substitution,
|
||||||
|
which SHALL be sent if an alternate value is _not_ supplied.
|
||||||
|
Note this behavior is different than the [Schema Object's](#schemaObject) treatment
|
||||||
|
of default values, because in those cases parameter values are optional.
|
||||||
|
If the [`enum`](#serverVariableEnum) is defined, the value SHOULD exist in the
|
||||||
|
enum's values.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
An optional description for the server variable.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
47
openapi_pydantic/v3/v3_0/tag.py
Normal file
47
openapi_pydantic/v3/v3_0/tag.py
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .external_documentation import ExternalDocumentation
|
||||||
|
|
||||||
|
_examples = [{"name": "pet", "description": "Pets operations"}]
|
||||||
|
|
||||||
|
|
||||||
|
class Tag(BaseModel):
|
||||||
|
"""
|
||||||
|
Adds metadata to a single tag that is used by the
|
||||||
|
[Operation Object](#operationObject).
|
||||||
|
It is not mandatory to have a Tag Object per tag defined in the Operation Object
|
||||||
|
instances.
|
||||||
|
"""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The name of the tag.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A short description for the tag.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
externalDocs: Optional[ExternalDocumentation] = None
|
||||||
|
"""
|
||||||
|
Additional external documentation for this tag.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
260
openapi_pydantic/v3/v3_0/util.py
Normal file
260
openapi_pydantic/v3/v3_0/util.py
Normal file
|
@ -0,0 +1,260 @@
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
from typing import (
|
||||||
|
TYPE_CHECKING,
|
||||||
|
Any,
|
||||||
|
Dict,
|
||||||
|
Generic,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
Set,
|
||||||
|
Type,
|
||||||
|
TypeVar,
|
||||||
|
Union,
|
||||||
|
cast,
|
||||||
|
)
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import (
|
||||||
|
DEFS_KEY,
|
||||||
|
PYDANTIC_V2,
|
||||||
|
JsonSchemaMode,
|
||||||
|
models_json_schema,
|
||||||
|
v1_schema,
|
||||||
|
)
|
||||||
|
|
||||||
|
from . import Components, OpenAPI, Reference, Schema, schema_validate
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
PydanticType = TypeVar("PydanticType", bound=BaseModel)
|
||||||
|
ref_prefix = "#/components/schemas/"
|
||||||
|
ref_template = "#/components/schemas/{model}"
|
||||||
|
|
||||||
|
|
||||||
|
class PydanticSchema(Schema, Generic[PydanticType]):
|
||||||
|
"""Special `Schema` class to indicate a reference from pydantic class"""
|
||||||
|
|
||||||
|
schema_class: Type[PydanticType]
|
||||||
|
"""the class that is used for generate the schema"""
|
||||||
|
|
||||||
|
|
||||||
|
def get_mode(
|
||||||
|
cls: Type[BaseModel], default: JsonSchemaMode = "validation"
|
||||||
|
) -> JsonSchemaMode:
|
||||||
|
"""Get the JSON schema mode for a model class.
|
||||||
|
|
||||||
|
The mode can be either "validation" or "serialization". In validation mode,
|
||||||
|
computed fields are dropped and optional fields remain optional. In
|
||||||
|
serialization mode, computed and optional fields are required.
|
||||||
|
"""
|
||||||
|
if not hasattr(cls, "model_config"):
|
||||||
|
return default
|
||||||
|
mode = cls.model_config.get("json_schema_mode", default)
|
||||||
|
if mode not in ("validation", "serialization"):
|
||||||
|
raise ValueError(f"invalid json_schema_mode: {mode}")
|
||||||
|
return cast(JsonSchemaMode, mode)
|
||||||
|
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
|
||||||
|
class GenerateOpenAPI30Schema: ...
|
||||||
|
|
||||||
|
elif PYDANTIC_V2:
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue
|
||||||
|
from pydantic_core import core_schema
|
||||||
|
|
||||||
|
class GenerateOpenAPI30Schema(GenerateJsonSchema):
|
||||||
|
"""Modify the schema generation for OpenAPI 3.0."""
|
||||||
|
|
||||||
|
def nullable_schema(
|
||||||
|
self,
|
||||||
|
schema: core_schema.NullableSchema,
|
||||||
|
) -> JsonSchemaValue:
|
||||||
|
"""Generates a JSON schema that matches a schema that allows null values.
|
||||||
|
|
||||||
|
In OpenAPI 3.0, types can not be None, but a special "nullable" field is
|
||||||
|
available.
|
||||||
|
"""
|
||||||
|
inner_json_schema = self.generate_inner(schema["schema"])
|
||||||
|
inner_json_schema["nullable"] = True
|
||||||
|
return inner_json_schema
|
||||||
|
|
||||||
|
def literal_schema(self, schema: core_schema.LiteralSchema) -> JsonSchemaValue:
|
||||||
|
"""Generates a JSON schema that matches a literal value.
|
||||||
|
|
||||||
|
In OpenAPI 3.0, the "const" keyword is not supported, so this
|
||||||
|
version of this method skips that optimization.
|
||||||
|
"""
|
||||||
|
expected = [
|
||||||
|
v.value if isinstance(v, Enum) else v for v in schema["expected"]
|
||||||
|
]
|
||||||
|
|
||||||
|
types = {type(e) for e in expected}
|
||||||
|
if types == {str}:
|
||||||
|
return {"enum": expected, "type": "string"}
|
||||||
|
elif types == {int}:
|
||||||
|
return {"enum": expected, "type": "integer"}
|
||||||
|
elif types == {float}:
|
||||||
|
return {"enum": expected, "type": "number"}
|
||||||
|
elif types == {bool}:
|
||||||
|
return {"enum": expected, "type": "boolean"}
|
||||||
|
elif types == {list}:
|
||||||
|
return {"enum": expected, "type": "array"}
|
||||||
|
# there is not None case because if it's mixed it hits the final `else`
|
||||||
|
# if it's a single Literal[None] then it becomes a `const` schema above
|
||||||
|
else:
|
||||||
|
return {"enum": expected}
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class GenerateOpenAPI30Schema: ...
|
||||||
|
|
||||||
|
|
||||||
|
def construct_open_api_with_schema_class(
|
||||||
|
open_api: OpenAPI,
|
||||||
|
schema_classes: Optional[List[Type[BaseModel]]] = None,
|
||||||
|
scan_for_pydantic_schema_reference: bool = True,
|
||||||
|
by_alias: bool = True,
|
||||||
|
) -> OpenAPI:
|
||||||
|
"""
|
||||||
|
Construct a new OpenAPI object, utilising pydantic classes to produce JSON schemas.
|
||||||
|
|
||||||
|
:param open_api: the base `OpenAPI` object
|
||||||
|
:param schema_classes: Pydantic classes that their schema will be used
|
||||||
|
"#/components/schemas" values
|
||||||
|
:param scan_for_pydantic_schema_reference: flag to indicate if scanning for
|
||||||
|
`PydanticSchemaReference` class
|
||||||
|
is needed for "#/components/schemas"
|
||||||
|
value updates
|
||||||
|
:param by_alias: construct schema by alias (default is True)
|
||||||
|
:return: new OpenAPI object with "#/components/schemas" values updated.
|
||||||
|
If there is no update in "#/components/schemas" values, the original
|
||||||
|
`open_api` will be returned.
|
||||||
|
"""
|
||||||
|
copy_func = getattr(open_api, "model_copy" if PYDANTIC_V2 else "copy")
|
||||||
|
new_open_api: OpenAPI = copy_func(deep=True)
|
||||||
|
|
||||||
|
if scan_for_pydantic_schema_reference:
|
||||||
|
extracted_schema_classes = _handle_pydantic_schema(new_open_api)
|
||||||
|
if schema_classes:
|
||||||
|
schema_classes = list({*schema_classes, *extracted_schema_classes})
|
||||||
|
else:
|
||||||
|
schema_classes = extracted_schema_classes
|
||||||
|
|
||||||
|
if not schema_classes:
|
||||||
|
return open_api
|
||||||
|
|
||||||
|
schema_classes.sort(key=lambda x: x.__name__)
|
||||||
|
logger.debug("schema_classes: %s", schema_classes)
|
||||||
|
|
||||||
|
# update new_open_api with new #/components/schemas
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
_key_map, schema_definitions = models_json_schema(
|
||||||
|
[(c, get_mode(c)) for c in schema_classes],
|
||||||
|
by_alias=by_alias,
|
||||||
|
ref_template=ref_template,
|
||||||
|
schema_generator=GenerateOpenAPI30Schema,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
schema_definitions = v1_schema(
|
||||||
|
schema_classes, by_alias=by_alias, ref_prefix=ref_prefix
|
||||||
|
)
|
||||||
|
|
||||||
|
if not new_open_api.components:
|
||||||
|
new_open_api.components = Components()
|
||||||
|
if new_open_api.components.schemas:
|
||||||
|
for existing_key in new_open_api.components.schemas:
|
||||||
|
if existing_key in schema_definitions[DEFS_KEY]:
|
||||||
|
logger.warning(
|
||||||
|
f'"{existing_key}" already exists in {ref_prefix}. '
|
||||||
|
f'The value of "{ref_prefix}{existing_key}" will be overwritten.'
|
||||||
|
)
|
||||||
|
new_open_api.components.schemas.update(_validate_schemas(schema_definitions))
|
||||||
|
else:
|
||||||
|
new_open_api.components.schemas = _validate_schemas(schema_definitions)
|
||||||
|
return new_open_api
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_schemas(
|
||||||
|
schema_definitions: Dict[str, Any]
|
||||||
|
) -> Dict[str, Union[Reference, Schema]]:
|
||||||
|
"""Convert JSON Schema definitions to parsed OpenAPI objects"""
|
||||||
|
# Note: if an error occurs in schema_validate(), it may indicate that
|
||||||
|
# the generated JSON schemas are not compatible with the version
|
||||||
|
# of OpenAPI this module depends on.
|
||||||
|
return {
|
||||||
|
key: schema_validate(schema_dict)
|
||||||
|
for key, schema_dict in schema_definitions[DEFS_KEY].items()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _handle_pydantic_schema(open_api: OpenAPI) -> List[Type[BaseModel]]:
|
||||||
|
"""
|
||||||
|
This function traverses the `OpenAPI` object and
|
||||||
|
|
||||||
|
1. Replaces the `PydanticSchema` object with `Reference` object, with correct ref
|
||||||
|
value;
|
||||||
|
2. Extracts the involved schema class from `PydanticSchema` object.
|
||||||
|
|
||||||
|
**This function will mutate the input `OpenAPI` object.**
|
||||||
|
|
||||||
|
:param open_api: the `OpenAPI` object to be traversed and mutated
|
||||||
|
:return: a list of schema classes extracted from `PydanticSchema` objects
|
||||||
|
"""
|
||||||
|
|
||||||
|
pydantic_types: Set[Type[BaseModel]] = set()
|
||||||
|
|
||||||
|
def _traverse(obj: Any) -> None:
|
||||||
|
if isinstance(obj, BaseModel):
|
||||||
|
fields = getattr(
|
||||||
|
obj, "model_fields_set" if PYDANTIC_V2 else "__fields_set__"
|
||||||
|
)
|
||||||
|
for field in fields:
|
||||||
|
child_obj = obj.__getattribute__(field)
|
||||||
|
if isinstance(child_obj, PydanticSchema):
|
||||||
|
logger.debug("PydanticSchema found in %s: %s", obj, child_obj)
|
||||||
|
obj.__setattr__(field, _construct_ref_obj(child_obj))
|
||||||
|
pydantic_types.add(child_obj.schema_class)
|
||||||
|
else:
|
||||||
|
_traverse(child_obj)
|
||||||
|
elif isinstance(obj, list):
|
||||||
|
for index, elem in enumerate(obj):
|
||||||
|
if isinstance(elem, PydanticSchema):
|
||||||
|
logger.debug(f"PydanticSchema found in list: {elem}")
|
||||||
|
obj[index] = _construct_ref_obj(elem)
|
||||||
|
pydantic_types.add(elem.schema_class)
|
||||||
|
else:
|
||||||
|
_traverse(elem)
|
||||||
|
elif isinstance(obj, dict):
|
||||||
|
for key, value in obj.items():
|
||||||
|
if isinstance(value, PydanticSchema):
|
||||||
|
logger.debug(f"PydanticSchema found in dict: {value}")
|
||||||
|
obj[key] = _construct_ref_obj(value)
|
||||||
|
pydantic_types.add(value.schema_class)
|
||||||
|
else:
|
||||||
|
_traverse(value)
|
||||||
|
|
||||||
|
_traverse(open_api)
|
||||||
|
return list(pydantic_types)
|
||||||
|
|
||||||
|
|
||||||
|
def _construct_ref_obj(pydantic_schema: PydanticSchema[PydanticType]) -> Reference:
|
||||||
|
"""
|
||||||
|
Construct a reference object from the Pydantic schema name
|
||||||
|
|
||||||
|
characters in the schema name that are invalid/problematic
|
||||||
|
for JSONschema $ref names will get replaced with underscores.
|
||||||
|
Especially needed for Pydantic generic Models with brackets "[]"
|
||||||
|
|
||||||
|
see: https://github.com/pydantic/pydantic/blob/aee6057378ccfec02126bf9c984a9b6d6b411777/pydantic/json_schema.py#L2031
|
||||||
|
"""
|
||||||
|
ref_name = re.sub(
|
||||||
|
r"[^a-zA-Z0-9.\-_]", "_", pydantic_schema.schema_class.__name__
|
||||||
|
).replace(".", "__")
|
||||||
|
ref_obj = Reference(**{"$ref": ref_prefix + ref_name})
|
||||||
|
logger.debug(f"ref_obj={ref_obj}")
|
||||||
|
return ref_obj
|
68
openapi_pydantic/v3/v3_0/xml.py
Normal file
68
openapi_pydantic/v3/v3_0/xml.py
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{"namespace": "http://example.com/schema/sample", "prefix": "sample"},
|
||||||
|
{"name": "aliens", "wrapped": True},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class XML(BaseModel):
|
||||||
|
"""
|
||||||
|
A metadata object that allows for more fine-tuned XML model definitions.
|
||||||
|
|
||||||
|
When using arrays, XML element names are *not* inferred (for singular/plural forms)
|
||||||
|
and the `name` property SHOULD be used to add that information.
|
||||||
|
See examples for expected behavior.
|
||||||
|
"""
|
||||||
|
|
||||||
|
name: Optional[str] = None
|
||||||
|
"""
|
||||||
|
Replaces the name of the element/attribute used for the described schema property.
|
||||||
|
When defined within `items`, it will affect the name of the individual XML elements
|
||||||
|
within the list. When defined alongside `type` being `array` (outside the `items`),
|
||||||
|
it will affect the wrapping element and only if `wrapped` is `true`.
|
||||||
|
If `wrapped` is `false`, it will be ignored.
|
||||||
|
"""
|
||||||
|
|
||||||
|
namespace: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The URI of the namespace definition.
|
||||||
|
Value MUST be in the form of an absolute URI.
|
||||||
|
"""
|
||||||
|
|
||||||
|
prefix: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The prefix to be used for the [name](#xmlName).
|
||||||
|
"""
|
||||||
|
|
||||||
|
attribute: bool = False
|
||||||
|
"""
|
||||||
|
Declares whether the property definition translates to an attribute instead of an
|
||||||
|
element. Default value is `false`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
wrapped: bool = False
|
||||||
|
"""
|
||||||
|
MAY be used only for an array definition.
|
||||||
|
Signifies whether the array is wrapped (for example,
|
||||||
|
`<books><book/><book/></books>`) or unwrapped (`<book/><book/>`).
|
||||||
|
Default value is `false`.
|
||||||
|
The definition takes effect only when defined alongside `type` being `array`
|
||||||
|
(outside the `items`).
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
41
openapi_pydantic/v3/v3_1/README.md
Normal file
41
openapi_pydantic/v3/v3_1/README.md
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# OpenAPI v3.1 schema classes
|
||||||
|
|
||||||
|
## Alias
|
||||||
|
|
||||||
|
Due to the reserved words in python and pydantic,
|
||||||
|
the following fields are used with [alias](https://pydantic-docs.helpmanual.io/usage/schema/#field-customisation) feature provided by pydantic:
|
||||||
|
|
||||||
|
| Class | Field name in the class | Alias (as in OpenAPI spec) |
|
||||||
|
| ----- | ----------------------- | -------------------------- |
|
||||||
|
| Header[*](#header_param_in) | param_in | in |
|
||||||
|
| MediaType | media_type_schema | schema |
|
||||||
|
| Parameter | param_in | in |
|
||||||
|
| Parameter | param_schema | schema |
|
||||||
|
| PathItem | ref | $ref |
|
||||||
|
| Reference | ref | $ref |
|
||||||
|
| SecurityScheme | security_scheme_in | in |
|
||||||
|
| Schema | schema_format | format |
|
||||||
|
| Schema | schema_not | not |
|
||||||
|
| Schema | schema_if | if |
|
||||||
|
| Schema | schema_else | else |
|
||||||
|
|
||||||
|
> <a name="header_param_in"></a>The "in" field in Header object is actually a constant (`{"in": "header"}`).
|
||||||
|
|
||||||
|
> For convenience of object creation, the classes mentioned in above
|
||||||
|
> have configured `allow_population_by_field_name=True` (Pydantic V1) or `populate_by_name=True` (Pydantic V2).
|
||||||
|
>
|
||||||
|
> Reference: [Pydantic's Model Config](https://pydantic-docs.helpmanual.io/usage/model_config/)
|
||||||
|
|
||||||
|
## Non-pydantic schema types
|
||||||
|
|
||||||
|
Due to the constriants of python typing structure (not able to handle dynamic field names),
|
||||||
|
the following schema classes are actually just a typing of `Dict`:
|
||||||
|
|
||||||
|
| Schema Type | Implementation |
|
||||||
|
| ----------- | -------------- |
|
||||||
|
| Callback | `Callback = Dict[str, PathItem]` |
|
||||||
|
| Paths | `Paths = Dict[str, PathItem]` |
|
||||||
|
| Responses | `Responses = Dict[str, Union[Response, Reference]]` |
|
||||||
|
| SecurityRequirement | `SecurityRequirement = Dict[str, List[str]]` |
|
||||||
|
|
||||||
|
On creating such schema instances, please use python's `dict` type instead to instantiate.
|
59
openapi_pydantic/v3/v3_1/__init__.py
Normal file
59
openapi_pydantic/v3/v3_1/__init__.py
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
"""
|
||||||
|
OpenAPI v3.1 schema types, created according to the specification:
|
||||||
|
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.1.md
|
||||||
|
|
||||||
|
The type orders are according to the contents of the specification:
|
||||||
|
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.1.md#table-of-contents
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2
|
||||||
|
|
||||||
|
from .callback import Callback as Callback
|
||||||
|
from .components import Components as Components
|
||||||
|
from .contact import Contact as Contact
|
||||||
|
from .datatype import DataType as DataType
|
||||||
|
from .discriminator import Discriminator as Discriminator
|
||||||
|
from .encoding import Encoding as Encoding
|
||||||
|
from .example import Example as Example
|
||||||
|
from .external_documentation import ExternalDocumentation as ExternalDocumentation
|
||||||
|
from .header import Header as Header
|
||||||
|
from .info import Info as Info
|
||||||
|
from .license import License as License
|
||||||
|
from .link import Link as Link
|
||||||
|
from .media_type import MediaType as MediaType
|
||||||
|
from .oauth_flow import OAuthFlow as OAuthFlow
|
||||||
|
from .oauth_flows import OAuthFlows as OAuthFlows
|
||||||
|
from .open_api import OpenAPI as OpenAPI
|
||||||
|
from .operation import Operation as Operation
|
||||||
|
from .parameter import Parameter as Parameter
|
||||||
|
from .parameter import ParameterLocation as ParameterLocation
|
||||||
|
from .path_item import PathItem as PathItem
|
||||||
|
from .paths import Paths as Paths
|
||||||
|
from .reference import Reference as Reference
|
||||||
|
from .request_body import RequestBody as RequestBody
|
||||||
|
from .response import Response as Response
|
||||||
|
from .responses import Responses as Responses
|
||||||
|
from .schema import Schema as Schema
|
||||||
|
from .schema import schema_validate as schema_validate
|
||||||
|
from .security_requirement import SecurityRequirement as SecurityRequirement
|
||||||
|
from .security_scheme import SecurityScheme as SecurityScheme
|
||||||
|
from .server import Server as Server
|
||||||
|
from .server_variable import ServerVariable as ServerVariable
|
||||||
|
from .tag import Tag as Tag
|
||||||
|
from .xml import XML as XML
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
pass
|
||||||
|
elif PYDANTIC_V2:
|
||||||
|
# resolve forward references
|
||||||
|
Encoding.model_rebuild()
|
||||||
|
OpenAPI.model_rebuild()
|
||||||
|
Components.model_rebuild()
|
||||||
|
Operation.model_rebuild()
|
||||||
|
else:
|
||||||
|
# resolve forward references
|
||||||
|
Encoding.update_forward_refs(Header=Header)
|
||||||
|
Schema.update_forward_refs()
|
||||||
|
Operation.update_forward_refs(PathItem=PathItem)
|
26
openapi_pydantic/v3/v3_1/callback.py
Normal file
26
openapi_pydantic/v3/v3_1/callback.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
from typing import TYPE_CHECKING, Dict, Union
|
||||||
|
|
||||||
|
from .reference import Reference
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .path_item import PathItem
|
||||||
|
|
||||||
|
|
||||||
|
Callback = Dict[str, Union["PathItem", Reference]]
|
||||||
|
"""
|
||||||
|
A map of possible out-of band callbacks related to the parent operation.
|
||||||
|
Each value in the map is a [Path Item Object](#pathItemObject)
|
||||||
|
that describes a set of requests that may be initiated by the API provider and the
|
||||||
|
expected responses. The key value used to identify the path item object is an
|
||||||
|
expression, evaluated at runtime, that identifies a URL to use for the callback
|
||||||
|
operation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""Patterned Fields"""
|
||||||
|
|
||||||
|
# {expression}: 'PathItem' = ...
|
||||||
|
"""
|
||||||
|
A Path Item Object used to define a callback request and expected responses.
|
||||||
|
|
||||||
|
A [complete example](../examples/v3.0/callback-example.yaml) is available.
|
||||||
|
"""
|
142
openapi_pydantic/v3/v3_1/components.py
Normal file
142
openapi_pydantic/v3/v3_1/components.py
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
from typing import Dict, Optional, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .callback import Callback
|
||||||
|
from .example import Example
|
||||||
|
from .header import Header
|
||||||
|
from .link import Link
|
||||||
|
from .parameter import Parameter
|
||||||
|
from .path_item import PathItem
|
||||||
|
from .reference import Reference
|
||||||
|
from .request_body import RequestBody
|
||||||
|
from .response import Response
|
||||||
|
from .schema import Schema
|
||||||
|
from .security_scheme import SecurityScheme
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"schemas": {
|
||||||
|
"GeneralError": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"code": {"type": "integer", "format": "int32"},
|
||||||
|
"message": {"type": "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Category": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {"type": "integer", "format": "int64"},
|
||||||
|
"name": {"type": "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Tag": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {"type": "integer", "format": "int64"},
|
||||||
|
"name": {"type": "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"parameters": {
|
||||||
|
"skipParam": {
|
||||||
|
"name": "skip",
|
||||||
|
"in": "query",
|
||||||
|
"description": "number of items to skip",
|
||||||
|
"required": True,
|
||||||
|
"schema": {"type": "integer", "format": "int32"},
|
||||||
|
},
|
||||||
|
"limitParam": {
|
||||||
|
"name": "limit",
|
||||||
|
"in": "query",
|
||||||
|
"description": "max records to return",
|
||||||
|
"required": True,
|
||||||
|
"schema": {"type": "integer", "format": "int32"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"NotFound": {"description": "Entity not found."},
|
||||||
|
"IllegalInput": {"description": "Illegal input for operation."},
|
||||||
|
"GeneralError": {
|
||||||
|
"description": "General Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {"$ref": "#/components/schemas/GeneralError"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"securitySchemes": {
|
||||||
|
"api_key": {
|
||||||
|
"type": "apiKey",
|
||||||
|
"name": "api_key",
|
||||||
|
"in": "header",
|
||||||
|
},
|
||||||
|
"petstore_auth": {
|
||||||
|
"type": "oauth2",
|
||||||
|
"flows": {
|
||||||
|
"implicit": {
|
||||||
|
"authorizationUrl": "http://example.org/api/oauth/dialog",
|
||||||
|
"scopes": {
|
||||||
|
"write:pets": "modify pets in your account",
|
||||||
|
"read:pets": "read your pets",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Components(BaseModel):
|
||||||
|
"""
|
||||||
|
Holds a set of reusable objects for different aspects of the OAS.
|
||||||
|
All objects defined within the components object will have no effect on the API
|
||||||
|
unless they are explicitly referenced from properties outside the components object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
schemas: Optional[Dict[str, Schema]] = None
|
||||||
|
"""An object to hold reusable [Schema Objects](#schemaObject)."""
|
||||||
|
|
||||||
|
responses: Optional[Dict[str, Union[Response, Reference]]] = None
|
||||||
|
"""An object to hold reusable [Response Objects](#responseObject)."""
|
||||||
|
|
||||||
|
parameters: Optional[Dict[str, Union[Parameter, Reference]]] = None
|
||||||
|
"""An object to hold reusable [Parameter Objects](#parameterObject)."""
|
||||||
|
|
||||||
|
examples: Optional[Dict[str, Union[Example, Reference]]] = None
|
||||||
|
"""An object to hold reusable [Example Objects](#exampleObject)."""
|
||||||
|
|
||||||
|
requestBodies: Optional[Dict[str, Union[RequestBody, Reference]]] = None
|
||||||
|
"""An object to hold reusable [Request Body Objects](#requestBodyObject)."""
|
||||||
|
|
||||||
|
headers: Optional[Dict[str, Union[Header, Reference]]] = None
|
||||||
|
"""An object to hold reusable [Header Objects](#headerObject)."""
|
||||||
|
|
||||||
|
securitySchemes: Optional[Dict[str, Union[SecurityScheme, Reference]]] = None
|
||||||
|
"""An object to hold reusable [Security Scheme Objects](#securitySchemeObject)."""
|
||||||
|
|
||||||
|
links: Optional[Dict[str, Union[Link, Reference]]] = None
|
||||||
|
"""An object to hold reusable [Link Objects](#linkObject)."""
|
||||||
|
|
||||||
|
callbacks: Optional[Dict[str, Union[Callback, Reference]]] = None
|
||||||
|
"""An object to hold reusable [Callback Objects](#callbackObject)."""
|
||||||
|
|
||||||
|
pathItems: Optional[Dict[str, Union[PathItem, Reference]]] = None
|
||||||
|
"""An object to hold reusable [Path Item Object](#pathItemObject)."""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
48
openapi_pydantic/v3/v3_1/contact.py
Normal file
48
openapi_pydantic/v3/v3_1/contact.py
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"name": "API Support",
|
||||||
|
"url": "http://www.example.com/support",
|
||||||
|
"email": "support@example.com",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Contact(BaseModel):
|
||||||
|
"""
|
||||||
|
Contact information for the exposed API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
name: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The identifying name of the contact person/organization.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The URL pointing to the contact information.
|
||||||
|
MUST be in the form of a URL.
|
||||||
|
"""
|
||||||
|
|
||||||
|
email: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The email address of the contact person/organization.
|
||||||
|
MUST be in the form of an email address.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
13
openapi_pydantic/v3/v3_1/datatype.py
Normal file
13
openapi_pydantic/v3/v3_1/datatype.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class DataType(str, Enum):
|
||||||
|
"""Data type of an object."""
|
||||||
|
|
||||||
|
NULL = "null"
|
||||||
|
STRING = "string"
|
||||||
|
NUMBER = "number"
|
||||||
|
INTEGER = "integer"
|
||||||
|
BOOLEAN = "boolean"
|
||||||
|
ARRAY = "array"
|
||||||
|
OBJECT = "object"
|
52
openapi_pydantic/v3/v3_1/discriminator.py
Normal file
52
openapi_pydantic/v3/v3_1/discriminator.py
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
from typing import Dict, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"propertyName": "petType",
|
||||||
|
"mapping": {
|
||||||
|
"dog": "#/components/schemas/Dog",
|
||||||
|
"monster": "https://gigantic-server.com/schemas/Monster/schema.json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Discriminator(BaseModel):
|
||||||
|
"""
|
||||||
|
When request bodies or response payloads may be one of a number of different
|
||||||
|
schemas, a `discriminator` object can be used to aid in serialization,
|
||||||
|
deserialization, and validation.
|
||||||
|
|
||||||
|
The discriminator is a specific object in a schema which is used to inform the
|
||||||
|
consumer of the specification of an alternative schema based on the value
|
||||||
|
associated with it.
|
||||||
|
|
||||||
|
When using the discriminator, _inline_ schemas will not be considered.
|
||||||
|
"""
|
||||||
|
|
||||||
|
propertyName: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The name of the property in the payload that will hold the
|
||||||
|
discriminator value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
mapping: Optional[Dict[str, str]] = None
|
||||||
|
"""
|
||||||
|
An object to hold mappings between payload values and schema names or references.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
101
openapi_pydantic/v3/v3_1/encoding.py
Normal file
101
openapi_pydantic/v3/v3_1/encoding.py
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
from typing import TYPE_CHECKING, Dict, Optional, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .reference import Reference
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .header import Header
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"contentType": "image/png, image/jpeg",
|
||||||
|
"headers": {
|
||||||
|
"X-Rate-Limit-Limit": {
|
||||||
|
"description": "The number of allowed requests in the "
|
||||||
|
"current period",
|
||||||
|
"schema": {"type": "integer"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Encoding(BaseModel):
|
||||||
|
"""A single encoding definition applied to a single schema property."""
|
||||||
|
|
||||||
|
contentType: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The Content-Type for encoding a specific property.
|
||||||
|
Default value depends on the property type:
|
||||||
|
|
||||||
|
for `object` - `application/json`;
|
||||||
|
for `array` – the default is defined based on the inner type;
|
||||||
|
for all other cases the default is `application/octet-stream`.
|
||||||
|
|
||||||
|
The value can be a specific media type (e.g. `application/json`), a wildcard media
|
||||||
|
type (e.g. `image/*`), or a comma-separated list of the two types.
|
||||||
|
"""
|
||||||
|
|
||||||
|
headers: Optional[Dict[str, Union["Header", Reference]]] = None
|
||||||
|
"""
|
||||||
|
A map allowing additional information to be provided as headers, for example
|
||||||
|
`Content-Disposition`.
|
||||||
|
|
||||||
|
`Content-Type` is described separately and SHALL be ignored in this section.
|
||||||
|
This property SHALL be ignored if the request body media type is not a `multipart`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
style: Optional[str] = None
|
||||||
|
"""
|
||||||
|
Describes how a specific property value will be serialized depending on its type.
|
||||||
|
|
||||||
|
See [Parameter Object](#parameterObject) for details on the
|
||||||
|
[`style`](#parameterStyle) property. The behavior follows the same values as
|
||||||
|
`query` parameters, including default values.
|
||||||
|
This property SHALL be ignored if the request body media type
|
||||||
|
is not `application/x-www-form-urlencoded` or `multipart/form-data`.
|
||||||
|
If a value is explicitly defined, then the value of
|
||||||
|
[`contentType`](#encodingContentType) (implicit or explicit) SHALL be ignored.
|
||||||
|
"""
|
||||||
|
|
||||||
|
explode: Optional[bool] = None
|
||||||
|
"""
|
||||||
|
When this is true, property values of type `array` or `object` generate separate
|
||||||
|
parameters for each value of the array, or key-value-pair of the map.
|
||||||
|
|
||||||
|
For other types of properties this property has no effect.
|
||||||
|
When [`style`](#encodingStyle) is `form`, the default value is `true`.
|
||||||
|
For all other styles, the default value is `false`.
|
||||||
|
This property SHALL be ignored if the request body media type
|
||||||
|
is not `application/x-www-form-urlencoded` or `multipart/form-data`.
|
||||||
|
If a value is explicitly defined, then the value of
|
||||||
|
[`contentType`](#encodingContentType) (implicit or explicit) SHALL be ignored.
|
||||||
|
"""
|
||||||
|
|
||||||
|
allowReserved: bool = False
|
||||||
|
"""
|
||||||
|
Determines whether the parameter value SHOULD allow reserved characters,
|
||||||
|
as defined by [RFC3986](https://tools.ietf.org/html/rfc3986#section-2.2)
|
||||||
|
`:/?#[]@!$&'()*+,;=` to be included without percent-encoding.
|
||||||
|
The default value is `false`.
|
||||||
|
This property SHALL be ignored if the request body media type
|
||||||
|
is not `application/x-www-form-urlencoded` or `multipart/form-data`.
|
||||||
|
If a value is explicitly defined,
|
||||||
|
then the value of [`contentType`](#encodingContentType) (implicit or explicit)
|
||||||
|
SHALL be ignored.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
64
openapi_pydantic/v3/v3_1/example.py
Normal file
64
openapi_pydantic/v3/v3_1/example.py
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"summary": "A foo example",
|
||||||
|
"value": {"foo": "bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"summary": "This is an example in XML",
|
||||||
|
"externalValue": "http://example.org/examples/address-example.xml",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"summary": "This is a text example",
|
||||||
|
"externalValue": "http://foo.bar/examples/address-example.txt",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Example(BaseModel):
|
||||||
|
summary: Optional[str] = None
|
||||||
|
"""
|
||||||
|
Short description for the example.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
Long description for the example.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
value: Optional[Any] = None
|
||||||
|
"""
|
||||||
|
Embedded literal example.
|
||||||
|
The `value` field and `externalValue` field are mutually exclusive.
|
||||||
|
To represent examples of media types that cannot naturally represented in JSON or
|
||||||
|
YAML, use a string value to contain the example, escaping where necessary.
|
||||||
|
"""
|
||||||
|
|
||||||
|
externalValue: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A URL that points to the literal example.
|
||||||
|
This provides the capability to reference examples that cannot easily be included
|
||||||
|
in JSON or YAML documents.
|
||||||
|
|
||||||
|
The `value` field and `externalValue` field are mutually exclusive.
|
||||||
|
See the rules for resolving [Relative References](#relativeReferencesURI).
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
36
openapi_pydantic/v3/v3_1/external_documentation.py
Normal file
36
openapi_pydantic/v3/v3_1/external_documentation.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
_examples = [{"description": "Find more info here", "url": "https://example.com"}]
|
||||||
|
|
||||||
|
|
||||||
|
class ExternalDocumentation(BaseModel):
|
||||||
|
"""Allows referencing an external resource for extended documentation."""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A short description of the target documentation.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The URL for the target documentation.
|
||||||
|
Value MUST be in the form of a URL.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
37
openapi_pydantic/v3/v3_1/header.py
Normal file
37
openapi_pydantic/v3/v3_1/header.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .parameter import ParameterBase
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"description": "The number of allowed requests in the current period",
|
||||||
|
"schema": {"type": "integer"},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Header(ParameterBase):
|
||||||
|
"""
|
||||||
|
The Header Object follows the structure of the
|
||||||
|
[Parameter Object](#parameterObject) with the following changes:
|
||||||
|
|
||||||
|
1. `name` MUST NOT be specified, it is given in the corresponding
|
||||||
|
`headers` map.
|
||||||
|
2. `in` MUST NOT be specified, it is implicitly in `header`.
|
||||||
|
3. All traits that are affected by the location MUST be applicable
|
||||||
|
to a location of `header` (for example, [`style`](#parameterStyle)).
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
populate_by_name=True,
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
schema_extra = {"examples": _examples}
|
87
openapi_pydantic/v3/v3_1/info.py
Normal file
87
openapi_pydantic/v3/v3_1/info.py
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .contact import Contact
|
||||||
|
from .license import License
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"title": "Sample Pet Store App",
|
||||||
|
"summary": "A pet store manager.",
|
||||||
|
"description": "This is a sample server for a pet store.",
|
||||||
|
"termsOfService": "http://example.com/terms/",
|
||||||
|
"contact": {
|
||||||
|
"name": "API Support",
|
||||||
|
"url": "http://www.example.com/support",
|
||||||
|
"email": "support@example.com",
|
||||||
|
},
|
||||||
|
"license": {
|
||||||
|
"name": "Apache 2.0",
|
||||||
|
"url": "https://www.apache.org/licenses/LICENSE-2.0.html",
|
||||||
|
},
|
||||||
|
"version": "1.0.1",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Info(BaseModel):
|
||||||
|
"""
|
||||||
|
The object provides metadata about the API.
|
||||||
|
The metadata MAY be used by the clients if needed,
|
||||||
|
and MAY be presented in editing or documentation generation tools for convenience.
|
||||||
|
"""
|
||||||
|
|
||||||
|
title: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The title of the API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
summary: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A short summary of the API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A description of the API.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
termsOfService: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A URL to the Terms of Service for the API.
|
||||||
|
MUST be in the form of a URL.
|
||||||
|
"""
|
||||||
|
|
||||||
|
contact: Optional[Contact] = None
|
||||||
|
"""
|
||||||
|
The contact information for the exposed API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
license: Optional[License] = None
|
||||||
|
"""
|
||||||
|
The license information for the exposed API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
version: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The version of the OpenAPI document
|
||||||
|
(which is distinct from the [OpenAPI Specification version](#oasVersion) or the API
|
||||||
|
implementation version).
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
50
openapi_pydantic/v3/v3_1/license.py
Normal file
50
openapi_pydantic/v3/v3_1/license.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{"name": "Apache 2.0", "identifier": "Apache-2.0"},
|
||||||
|
{
|
||||||
|
"name": "Apache 2.0",
|
||||||
|
"url": "https://www.apache.org/licenses/LICENSE-2.0.html",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class License(BaseModel):
|
||||||
|
"""
|
||||||
|
License information for the exposed API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The license name used for the API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
identifier: Optional[str] = None
|
||||||
|
"""
|
||||||
|
An [SPDX](https://spdx.org/spdx-specification-21-web-version#h.jxpfx0ykyb60)
|
||||||
|
license expression for the API. The `identifier` field is mutually exclusive of the
|
||||||
|
`url` field.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A URL to the license used for the API.
|
||||||
|
This MUST be in the form of a URL.
|
||||||
|
The `url` field is mutually exclusive of the `identifier` field.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
95
openapi_pydantic/v3/v3_1/link.py
Normal file
95
openapi_pydantic/v3/v3_1/link.py
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .server import Server
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"operationId": "getUserAddressByUUID",
|
||||||
|
"parameters": {"userUuid": "$response.body#/uuid"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"operationRef": "#/paths/~12.0~1repositories~1{username}/get",
|
||||||
|
"parameters": {"username": "$response.body#/username"},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Link(BaseModel):
|
||||||
|
"""
|
||||||
|
The `Link object` represents a possible design-time link for a response.
|
||||||
|
The presence of a link does not guarantee the caller's ability to successfully
|
||||||
|
invoke it, rather it provides a known relationship and traversal mechanism between
|
||||||
|
responses and other operations.
|
||||||
|
|
||||||
|
Unlike _dynamic_ links (i.e. links provided **in** the response payload),
|
||||||
|
the OAS linking mechanism does not require link information in the runtime response.
|
||||||
|
|
||||||
|
For computing links, and providing instructions to execute them,
|
||||||
|
a [runtime expression](#runtimeExpression) is used for accessing values in an
|
||||||
|
operation and using them as parameters while invoking the linked operation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
operationRef: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A relative or absolute URI reference to an OAS operation.
|
||||||
|
This field is mutually exclusive of the `operationId` field,
|
||||||
|
and MUST point to an [Operation Object](#operationObject).
|
||||||
|
Relative `operationRef` values MAY be used to locate an existing
|
||||||
|
[Operation Object](#operationObject) in the OpenAPI definition. See the rules for
|
||||||
|
resolving [Relative References](#relativeReferencesURI).
|
||||||
|
"""
|
||||||
|
|
||||||
|
operationId: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The name of an _existing_, resolvable OAS operation, as defined with a unique
|
||||||
|
`operationId`.
|
||||||
|
|
||||||
|
This field is mutually exclusive of the `operationRef` field.
|
||||||
|
"""
|
||||||
|
|
||||||
|
parameters: Optional[Dict[str, Any]] = None
|
||||||
|
"""
|
||||||
|
A map representing parameters to pass to an operation
|
||||||
|
as specified with `operationId` or identified via `operationRef`.
|
||||||
|
The key is the parameter name to be used,
|
||||||
|
whereas the value can be a constant or an expression to be evaluated and passed to
|
||||||
|
the linked operation.
|
||||||
|
|
||||||
|
The parameter name can be qualified using the [parameter location](#parameterIn)
|
||||||
|
`[{in}.]{name}` for operations that use the same parameter name in different
|
||||||
|
locations (e.g. path.id).
|
||||||
|
"""
|
||||||
|
|
||||||
|
requestBody: Optional[Any] = None
|
||||||
|
"""
|
||||||
|
A literal value or [{expression}](#runtimeExpression) to use as a request body when
|
||||||
|
calling the target operation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A description of the link.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
server: Optional[Server] = None
|
||||||
|
"""
|
||||||
|
A server object to be used by the target operation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
97
openapi_pydantic/v3/v3_1/media_type.py
Normal file
97
openapi_pydantic/v3/v3_1/media_type.py
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
from typing import Any, Dict, Optional, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .encoding import Encoding
|
||||||
|
from .example import Example
|
||||||
|
from .reference import Reference
|
||||||
|
from .schema import Schema
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"schema": {"$ref": "#/components/schemas/Pet"},
|
||||||
|
"examples": {
|
||||||
|
"cat": {
|
||||||
|
"summary": "An example of a cat",
|
||||||
|
"value": {
|
||||||
|
"name": "Fluffy",
|
||||||
|
"petType": "Cat",
|
||||||
|
"color": "White",
|
||||||
|
"gender": "male",
|
||||||
|
"breed": "Persian",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"dog": {
|
||||||
|
"summary": "An example of a dog with a cat's name",
|
||||||
|
"value": {
|
||||||
|
"name": "Puma",
|
||||||
|
"petType": "Dog",
|
||||||
|
"color": "Black",
|
||||||
|
"gender": "Female",
|
||||||
|
"breed": "Mixed",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"frog": {"$ref": "#/components/examples/frog-example"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class MediaType(BaseModel):
|
||||||
|
"""Each Media Type Object provides schema and examples for the media type
|
||||||
|
identified by its key."""
|
||||||
|
|
||||||
|
media_type_schema: Optional[Union[Reference, Schema]] = Field(
|
||||||
|
default=None, alias="schema"
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
The schema defining the content of the request, response, or parameter.
|
||||||
|
"""
|
||||||
|
|
||||||
|
example: Optional[Any] = None
|
||||||
|
"""
|
||||||
|
Example of the media type.
|
||||||
|
|
||||||
|
The example object SHOULD be in the correct format as specified by the media type.
|
||||||
|
|
||||||
|
The `example` field is mutually exclusive of the `examples` field.
|
||||||
|
|
||||||
|
Furthermore, if referencing a `schema` which contains an example,
|
||||||
|
the `example` value SHALL _override_ the example provided by the schema.
|
||||||
|
"""
|
||||||
|
|
||||||
|
examples: Optional[Dict[str, Union[Example, Reference]]] = None
|
||||||
|
"""
|
||||||
|
Examples of the media type.
|
||||||
|
|
||||||
|
Each example object SHOULD match the media type and specified schema if present.
|
||||||
|
|
||||||
|
The `examples` field is mutually exclusive of the `example` field.
|
||||||
|
|
||||||
|
Furthermore, if referencing a `schema` which contains an example,
|
||||||
|
the `examples` value SHALL _override_ the example provided by the schema.
|
||||||
|
"""
|
||||||
|
|
||||||
|
encoding: Optional[Dict[str, Encoding]] = None
|
||||||
|
"""
|
||||||
|
A map between a property name and its encoding information.
|
||||||
|
The key, being the property name, MUST exist in the schema as a property.
|
||||||
|
The encoding object SHALL only apply to `requestBody` objects
|
||||||
|
when the media type is `multipart` or `application/x-www-form-urlencoded`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
populate_by_name=True,
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
schema_extra = {"examples": _examples}
|
80
openapi_pydantic/v3/v3_1/oauth_flow.py
Normal file
80
openapi_pydantic/v3/v3_1/oauth_flow.py
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
from typing import Dict, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"authorizationUrl": "https://example.com/api/oauth/dialog",
|
||||||
|
"scopes": {
|
||||||
|
"write:pets": "modify pets in your account",
|
||||||
|
"read:pets": "read your pets",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"authorizationUrl": "https://example.com/api/oauth/dialog",
|
||||||
|
"tokenUrl": "https://example.com/api/oauth/token",
|
||||||
|
"scopes": {
|
||||||
|
"write:pets": "modify pets in your account",
|
||||||
|
"read:pets": "read your pets",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"authorizationUrl": "/api/oauth/dialog",
|
||||||
|
"tokenUrl": "/api/oauth/token",
|
||||||
|
"refreshUrl": "/api/oauth/token",
|
||||||
|
"scopes": {
|
||||||
|
"write:pets": "modify pets in your account",
|
||||||
|
"read:pets": "read your pets",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class OAuthFlow(BaseModel):
|
||||||
|
"""
|
||||||
|
Configuration details for a supported OAuth Flow
|
||||||
|
"""
|
||||||
|
|
||||||
|
authorizationUrl: Optional[str] = None
|
||||||
|
"""
|
||||||
|
**REQUIRED** for `oauth2 ("implicit", "authorizationCode")`.
|
||||||
|
The authorization URL to be used for this flow.
|
||||||
|
This MUST be in the form of a URL.
|
||||||
|
The OAuth2 standard requires the use of TLS.
|
||||||
|
"""
|
||||||
|
|
||||||
|
tokenUrl: Optional[str] = None
|
||||||
|
"""
|
||||||
|
**REQUIRED** for `oauth2 ("password", "clientCredentials", "authorizationCode")`.
|
||||||
|
The token URL to be used for this flow.
|
||||||
|
This MUST be in the form of a URL.
|
||||||
|
The OAuth2 standard requires the use of TLS.
|
||||||
|
"""
|
||||||
|
|
||||||
|
refreshUrl: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The URL to be used for obtaining refresh tokens.
|
||||||
|
This MUST be in the form of a URL.
|
||||||
|
The OAuth2 standard requires the use of TLS.
|
||||||
|
"""
|
||||||
|
|
||||||
|
scopes: Optional[Dict[str, str]] = None
|
||||||
|
"""
|
||||||
|
**REQUIRED** for `oauth2`. The available scopes for the OAuth2 security scheme.
|
||||||
|
A map between the scope name and a short description for it.
|
||||||
|
The map MAY be empty.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
47
openapi_pydantic/v3/v3_1/oauth_flows.py
Normal file
47
openapi_pydantic/v3/v3_1/oauth_flows.py
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .oauth_flow import OAuthFlow
|
||||||
|
|
||||||
|
|
||||||
|
class OAuthFlows(BaseModel):
|
||||||
|
"""
|
||||||
|
Allows configuration of the supported OAuth Flows.
|
||||||
|
"""
|
||||||
|
|
||||||
|
implicit: Optional[OAuthFlow] = None
|
||||||
|
"""
|
||||||
|
Configuration for the OAuth Implicit flow
|
||||||
|
"""
|
||||||
|
|
||||||
|
password: Optional[OAuthFlow] = None
|
||||||
|
"""
|
||||||
|
Configuration for the OAuth Resource Owner Password flow
|
||||||
|
"""
|
||||||
|
|
||||||
|
clientCredentials: Optional[OAuthFlow] = None
|
||||||
|
"""
|
||||||
|
Configuration for the OAuth Client Credentials flow.
|
||||||
|
|
||||||
|
Previously called `application` in OpenAPI 2.0.
|
||||||
|
"""
|
||||||
|
|
||||||
|
authorizationCode: Optional[OAuthFlow] = None
|
||||||
|
"""
|
||||||
|
Configuration for the OAuth Authorization Code flow.
|
||||||
|
|
||||||
|
Previously called `accessCode` in OpenAPI 2.0.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
104
openapi_pydantic/v3/v3_1/open_api.py
Normal file
104
openapi_pydantic/v3/v3_1/open_api.py
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
from typing import Dict, List, Literal, Optional, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .components import Components
|
||||||
|
from .external_documentation import ExternalDocumentation
|
||||||
|
from .info import Info
|
||||||
|
from .path_item import PathItem
|
||||||
|
from .paths import Paths
|
||||||
|
from .reference import Reference
|
||||||
|
from .security_requirement import SecurityRequirement
|
||||||
|
from .server import Server
|
||||||
|
from .tag import Tag
|
||||||
|
|
||||||
|
|
||||||
|
class OpenAPI(BaseModel):
|
||||||
|
"""This is the root document object of the OpenAPI document."""
|
||||||
|
|
||||||
|
openapi: Literal["3.1.1", "3.1.0"] = "3.1.1"
|
||||||
|
"""
|
||||||
|
**REQUIRED**. This string MUST be the [version number](#versions)
|
||||||
|
of the OpenAPI Specification that the OpenAPI document uses.
|
||||||
|
The `openapi` field SHOULD be used by tooling to interpret the OpenAPI document.
|
||||||
|
This is *not* related to the API [`info.version`](#infoVersion) string.
|
||||||
|
"""
|
||||||
|
|
||||||
|
info: Info
|
||||||
|
"""
|
||||||
|
**REQUIRED**. Provides metadata about the API. The metadata MAY be used by tooling
|
||||||
|
as required.
|
||||||
|
"""
|
||||||
|
|
||||||
|
jsonSchemaDialect: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The default value for the `$schema` keyword within [Schema Objects](#schemaObject)
|
||||||
|
contained within this OAS document. This MUST be in the form of a URI.
|
||||||
|
"""
|
||||||
|
|
||||||
|
servers: List[Server] = [Server(url="/")]
|
||||||
|
"""
|
||||||
|
An array of Server Objects, which provide connectivity information to a target
|
||||||
|
server. If the `servers` property is not provided, or is an empty array,
|
||||||
|
the default value would be a [Server Object](#serverObject) with a
|
||||||
|
[url](#serverUrl) value of `/`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
paths: Optional[Paths] = None
|
||||||
|
"""
|
||||||
|
The available paths and operations for the API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
webhooks: Optional[Dict[str, Union[PathItem, Reference]]] = None
|
||||||
|
"""
|
||||||
|
The incoming webhooks that MAY be received as part of this API and that the API
|
||||||
|
consumer MAY choose to implement.
|
||||||
|
Closely related to the `callbacks` feature, this section describes requests
|
||||||
|
initiated other than by an API call,
|
||||||
|
for example by an out of band registration.
|
||||||
|
The key name is a unique string to refer to each webhook,
|
||||||
|
while the (optionally referenced) Path Item Object describes a request
|
||||||
|
that may be initiated by the API provider and the expected responses.
|
||||||
|
An [example](../examples/v3.1/webhook-example.yaml) is available.
|
||||||
|
"""
|
||||||
|
|
||||||
|
components: Optional[Components] = None
|
||||||
|
"""
|
||||||
|
An element to hold various schemas for the document.
|
||||||
|
"""
|
||||||
|
|
||||||
|
security: Optional[List[SecurityRequirement]] = None
|
||||||
|
"""
|
||||||
|
A declaration of which security mechanisms can be used across the API.
|
||||||
|
The list of values includes alternative security requirement objects that can be
|
||||||
|
used. Only one of the security requirement objects need to be satisfied to
|
||||||
|
authorize a request. Individual operations can override this definition.
|
||||||
|
To make security optional, an empty security requirement (`{}`) can be included in
|
||||||
|
the array.
|
||||||
|
"""
|
||||||
|
|
||||||
|
tags: Optional[List[Tag]] = None
|
||||||
|
"""
|
||||||
|
A list of tags used by the document with additional metadata.
|
||||||
|
The order of the tags can be used to reflect on their order by the parsing tools.
|
||||||
|
Not all tags that are used by the [Operation Object](#operationObject) must be
|
||||||
|
declared. The tags that are not declared MAY be organized randomly or based on the
|
||||||
|
tools' logic. Each tag name in the list MUST be unique.
|
||||||
|
"""
|
||||||
|
|
||||||
|
externalDocs: Optional[ExternalDocumentation] = None
|
||||||
|
"""
|
||||||
|
Additional external documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
176
openapi_pydantic/v3/v3_1/operation.py
Normal file
176
openapi_pydantic/v3/v3_1/operation.py
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
from typing import Dict, List, Optional, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .callback import Callback
|
||||||
|
from .external_documentation import ExternalDocumentation
|
||||||
|
from .parameter import Parameter
|
||||||
|
from .reference import Reference
|
||||||
|
from .request_body import RequestBody
|
||||||
|
from .responses import Responses
|
||||||
|
from .security_requirement import SecurityRequirement
|
||||||
|
from .server import Server
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"tags": ["pet"],
|
||||||
|
"summary": "Updates a pet in the store with form data",
|
||||||
|
"operationId": "updatePetWithForm",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "petId",
|
||||||
|
"in": "path",
|
||||||
|
"description": "ID of pet that needs to be updated",
|
||||||
|
"required": True,
|
||||||
|
"schema": {"type": "string"},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/x-www-form-urlencoded": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "Updated name of the pet",
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"description": "Updated status of the pet",
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": ["status"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Pet updated.",
|
||||||
|
"content": {"application/json": {}, "application/xml": {}},
|
||||||
|
},
|
||||||
|
"405": {
|
||||||
|
"description": "Method Not Allowed",
|
||||||
|
"content": {"application/json": {}, "application/xml": {}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"security": [{"petstore_auth": ["write:pets", "read:pets"]}],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Operation(BaseModel):
|
||||||
|
"""Describes a single API operation on a path."""
|
||||||
|
|
||||||
|
tags: Optional[List[str]] = None
|
||||||
|
"""
|
||||||
|
A list of tags for API documentation control.
|
||||||
|
Tags can be used for logical grouping of operations by resources or any other
|
||||||
|
qualifier.
|
||||||
|
"""
|
||||||
|
|
||||||
|
summary: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A short summary of what the operation does.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A verbose explanation of the operation behavior.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
externalDocs: Optional[ExternalDocumentation] = None
|
||||||
|
"""
|
||||||
|
Additional external documentation for this operation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
operationId: Optional[str] = None
|
||||||
|
"""
|
||||||
|
Unique string used to identify the operation.
|
||||||
|
The id MUST be unique among all operations described in the API.
|
||||||
|
The operationId value is **case-sensitive**.
|
||||||
|
Tools and libraries MAY use the operationId to uniquely identify an operation,
|
||||||
|
therefore, it is RECOMMENDED to follow common programming naming conventions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
parameters: Optional[List[Union[Parameter, Reference]]] = None
|
||||||
|
"""
|
||||||
|
A list of parameters that are applicable for this operation.
|
||||||
|
If a parameter is already defined at the [Path Item](#pathItemParameters),
|
||||||
|
the new definition will override it but can never remove it.
|
||||||
|
The list MUST NOT include duplicated parameters.
|
||||||
|
A unique parameter is defined by a combination of a [name](#parameterName) and
|
||||||
|
[location](#parameterIn). The list can use the [Reference Object](#referenceObject)
|
||||||
|
to link to parameters that are defined at the
|
||||||
|
[OpenAPI Object's components/parameters](#componentsParameters).
|
||||||
|
"""
|
||||||
|
|
||||||
|
requestBody: Optional[Union[RequestBody, Reference]] = None
|
||||||
|
"""
|
||||||
|
The request body applicable for this operation.
|
||||||
|
|
||||||
|
The `requestBody` is fully supported in HTTP methods where the HTTP 1.1
|
||||||
|
specification [RFC7231](https://tools.ietf.org/html/rfc7231#section-4.3.1) has
|
||||||
|
explicitly defined semantics for request bodies.
|
||||||
|
In other cases where the HTTP spec is vague (such as [GET](https://tools.ietf.org/html/rfc7231#section-4.3.1),
|
||||||
|
[HEAD](https://tools.ietf.org/html/rfc7231#section-4.3.2)
|
||||||
|
and [DELETE](https://tools.ietf.org/html/rfc7231#section-4.3.5)),
|
||||||
|
`requestBody` is permitted but does not have well-defined semantics and SHOULD be
|
||||||
|
avoided if possible.
|
||||||
|
"""
|
||||||
|
|
||||||
|
responses: Optional[Responses] = None
|
||||||
|
"""
|
||||||
|
The list of possible responses as they are returned from executing this operation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
callbacks: Optional[Dict[str, Union[Callback, Reference]]] = None
|
||||||
|
"""
|
||||||
|
A map of possible out-of band callbacks related to the parent operation.
|
||||||
|
The key is a unique identifier for the Callback Object.
|
||||||
|
Each value in the map is a [Callback Object](#callbackObject)
|
||||||
|
that describes a request that may be initiated by the API provider and the expected
|
||||||
|
responses.
|
||||||
|
"""
|
||||||
|
|
||||||
|
deprecated: bool = False
|
||||||
|
"""
|
||||||
|
Declares this operation to be deprecated.
|
||||||
|
Consumers SHOULD refrain from usage of the declared operation.
|
||||||
|
Default value is `false`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
security: Optional[List[SecurityRequirement]] = None
|
||||||
|
"""
|
||||||
|
A declaration of which security mechanisms can be used for this operation.
|
||||||
|
The list of values includes alternative security requirement objects that can be
|
||||||
|
used. Only one of the security requirement objects need to be satisfied to
|
||||||
|
authorize a request. To make security optional, an empty security requirement
|
||||||
|
(`{}`) can be included in the array. This definition overrides any declared
|
||||||
|
top-level [`security`](#oasSecurity). To remove a top-level security declaration,
|
||||||
|
an empty array can be used.
|
||||||
|
"""
|
||||||
|
|
||||||
|
servers: Optional[List[Server]] = None
|
||||||
|
"""
|
||||||
|
An alternative `server` array to service this operation.
|
||||||
|
If an alternative `server` object is specified at the Path Item Object or Root
|
||||||
|
level, it will be overridden by this value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
235
openapi_pydantic/v3/v3_1/parameter.py
Normal file
235
openapi_pydantic/v3/v3_1/parameter.py
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
import enum
|
||||||
|
from typing import Any, Dict, Optional, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .example import Example
|
||||||
|
from .media_type import MediaType
|
||||||
|
from .reference import Reference
|
||||||
|
from .schema import Schema
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"name": "token",
|
||||||
|
"in": "header",
|
||||||
|
"description": "token to be passed as a header",
|
||||||
|
"required": True,
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "integer", "format": "int64"},
|
||||||
|
},
|
||||||
|
"style": "simple",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "username",
|
||||||
|
"in": "path",
|
||||||
|
"description": "username to fetch",
|
||||||
|
"required": True,
|
||||||
|
"schema": {"type": "string"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"in": "query",
|
||||||
|
"description": "ID of the object to fetch",
|
||||||
|
"required": False,
|
||||||
|
"schema": {"type": "array", "items": {"type": "string"}},
|
||||||
|
"style": "form",
|
||||||
|
"explode": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "freeForm",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {"type": "integer"},
|
||||||
|
},
|
||||||
|
"style": "form",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "coordinates",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"required": ["lat", "long"],
|
||||||
|
"properties": {
|
||||||
|
"lat": {"type": "number"},
|
||||||
|
"long": {"type": "number"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ParameterLocation(str, enum.Enum):
|
||||||
|
"""The location of a given parameter."""
|
||||||
|
|
||||||
|
QUERY = "query"
|
||||||
|
HEADER = "header"
|
||||||
|
PATH = "path"
|
||||||
|
COOKIE = "cookie"
|
||||||
|
|
||||||
|
|
||||||
|
class ParameterBase(BaseModel):
|
||||||
|
"""
|
||||||
|
Base class for Parameter and Header.
|
||||||
|
|
||||||
|
(Header is like Parameter, but has no `name` or `in` fields.)
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A brief description of the parameter.
|
||||||
|
This could contain examples of use.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
required: bool = False
|
||||||
|
"""
|
||||||
|
Determines whether this parameter is mandatory.
|
||||||
|
If the [parameter location](#parameterIn) is `"path"`, this property is
|
||||||
|
**REQUIRED** and its value MUST be `true`.
|
||||||
|
Otherwise, the property MAY be included and its default value is `false`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
deprecated: bool = False
|
||||||
|
"""
|
||||||
|
Specifies that a parameter is deprecated and SHOULD be transitioned out of usage.
|
||||||
|
Default value is `false`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
style: Optional[str] = None
|
||||||
|
"""
|
||||||
|
Describes how the parameter value will be serialized depending on the type of the
|
||||||
|
parameter value. Default values (based on value of `in`):
|
||||||
|
|
||||||
|
- for `query` - `form`;
|
||||||
|
- for `path` - `simple`;
|
||||||
|
- for `header` - `simple`;
|
||||||
|
- for `cookie` - `form`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
explode: Optional[bool] = None
|
||||||
|
"""
|
||||||
|
When this is true, parameter values of type `array` or `object` generate separate
|
||||||
|
parameters for each value of the array or key-value pair of the map.
|
||||||
|
For other types of parameters this property has no effect.
|
||||||
|
When [`style`](#parameterStyle) is `form`, the default value is `true`.
|
||||||
|
For all other styles, the default value is `false`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
param_schema: Optional[Union[Schema, Reference]] = Field(
|
||||||
|
default=None, alias="schema"
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
The schema defining the type used for the parameter.
|
||||||
|
"""
|
||||||
|
|
||||||
|
example: Optional[Any] = None
|
||||||
|
"""
|
||||||
|
Example of the parameter's potential value.
|
||||||
|
The example SHOULD match the specified schema and encoding properties if present.
|
||||||
|
The `example` field is mutually exclusive of the `examples` field.
|
||||||
|
Furthermore, if referencing a `schema` that contains an example,
|
||||||
|
the `example` value SHALL _override_ the example provided by the schema.
|
||||||
|
To represent examples of media types that cannot naturally be represented in JSON
|
||||||
|
or YAML, a string value can contain the example with escaping where necessary.
|
||||||
|
"""
|
||||||
|
|
||||||
|
examples: Optional[Dict[str, Union[Example, Reference]]] = None
|
||||||
|
"""
|
||||||
|
Examples of the parameter's potential value.
|
||||||
|
Each example SHOULD contain a value in the correct format as specified in the
|
||||||
|
parameter encoding. The `examples` field is mutually exclusive of the `example`
|
||||||
|
field.
|
||||||
|
Furthermore, if referencing a `schema` that contains an example,
|
||||||
|
the `examples` value SHALL _override_ the example provided by the schema.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
For more complex scenarios, the [`content`](#parameterContent) property
|
||||||
|
can define the media type and schema of the parameter.
|
||||||
|
A parameter MUST contain either a `schema` property, or a `content` property, but
|
||||||
|
not both.
|
||||||
|
When `example` or `examples` are provided in conjunction with the `schema` object,
|
||||||
|
the example MUST follow the prescribed serialization strategy for the parameter.
|
||||||
|
"""
|
||||||
|
|
||||||
|
content: Optional[Dict[str, MediaType]] = None
|
||||||
|
"""
|
||||||
|
A map containing the representations for the parameter.
|
||||||
|
The key is the media type and the value describes it.
|
||||||
|
The map MUST only contain one entry.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
populate_by_name=True,
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
schema_extra = {"examples": _examples}
|
||||||
|
|
||||||
|
|
||||||
|
class Parameter(ParameterBase):
|
||||||
|
"""
|
||||||
|
Describes a single operation parameter.
|
||||||
|
|
||||||
|
A unique parameter is defined by a combination of a [name](#parameterName) and
|
||||||
|
[location](#parameterIn).
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""Fixed Fields"""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The name of the parameter.
|
||||||
|
Parameter names are *case sensitive*.
|
||||||
|
|
||||||
|
- If [`in`](#parameterIn) is `"path"`, the `name` field MUST correspond to a
|
||||||
|
template expression occurring within the [path](#pathsPath) field in the
|
||||||
|
[Paths Object](#pathsObject).
|
||||||
|
See [Path Templating](#pathTemplating) for further information.
|
||||||
|
- If [`in`](#parameterIn) is `"header"` and the `name` field is `"Accept"`,
|
||||||
|
`"Content-Type"` or `"Authorization"`, the parameter definition SHALL be ignored.
|
||||||
|
- For all other cases, the `name` corresponds to the parameter name used by the
|
||||||
|
[`in`](#parameterIn) property.
|
||||||
|
"""
|
||||||
|
|
||||||
|
param_in: ParameterLocation = Field(alias="in")
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The location of the parameter. Possible values are `"query"`,
|
||||||
|
`"header"`, `"path"` or `"cookie"`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
allowEmptyValue: bool = False
|
||||||
|
"""
|
||||||
|
Sets the ability to pass empty-valued parameters.
|
||||||
|
This is valid only for `query` parameters and allows sending a parameter with an
|
||||||
|
empty value. Default value is `false`.
|
||||||
|
If [`style`](#parameterStyle) is used, and if behavior is `n/a` (cannot be
|
||||||
|
serialized), the value of `allowEmptyValue` SHALL be ignored.
|
||||||
|
Use of this property is NOT RECOMMENDED, as it is likely to be removed in a later
|
||||||
|
revision.
|
||||||
|
"""
|
||||||
|
|
||||||
|
allowReserved: bool = False
|
||||||
|
"""
|
||||||
|
Determines whether the parameter value SHOULD allow reserved characters,
|
||||||
|
as defined by [RFC3986](https://tools.ietf.org/html/rfc3986#section-2.2)
|
||||||
|
`:/?#[]@!$&'()*+,;=` to be included without percent-encoding.
|
||||||
|
This property only applies to parameters with an `in` value of `query`.
|
||||||
|
The default value is `false`.
|
||||||
|
"""
|
153
openapi_pydantic/v3/v3_1/path_item.py
Normal file
153
openapi_pydantic/v3/v3_1/path_item.py
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .operation import Operation
|
||||||
|
from .parameter import Parameter
|
||||||
|
from .reference import Reference
|
||||||
|
from .server import Server
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"get": {
|
||||||
|
"description": "Returns pets based on ID",
|
||||||
|
"summary": "Find pets by ID",
|
||||||
|
"operationId": "getPetsById",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "pet response",
|
||||||
|
"content": {
|
||||||
|
"*/*": {
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/components/schemas/Pet"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"description": "error payload",
|
||||||
|
"content": {
|
||||||
|
"text/html": {
|
||||||
|
"schema": {"$ref": "#/components/schemas/ErrorModel"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"description": "ID of pet to use",
|
||||||
|
"required": True,
|
||||||
|
"schema": {"type": "array", "items": {"type": "string"}},
|
||||||
|
"style": "simple",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class PathItem(BaseModel):
|
||||||
|
"""
|
||||||
|
Describes the operations available on a single path.
|
||||||
|
A Path Item MAY be empty, due to [ACL constraints](#securityFiltering).
|
||||||
|
The path itself is still exposed to the documentation viewer
|
||||||
|
but they will not know which operations and parameters are available.
|
||||||
|
"""
|
||||||
|
|
||||||
|
ref: Optional[str] = Field(default=None, alias="$ref")
|
||||||
|
"""
|
||||||
|
Allows for an external definition of this path item.
|
||||||
|
The referenced structure MUST be in the format of a
|
||||||
|
[Path Item Object](#pathItemObject).
|
||||||
|
|
||||||
|
In case a Path Item Object field appears both in the defined object and the
|
||||||
|
referenced object, the behavior is undefined.
|
||||||
|
See the rules for resolving [Relative References](#relativeReferencesURI).
|
||||||
|
"""
|
||||||
|
|
||||||
|
summary: Optional[str] = None
|
||||||
|
"""
|
||||||
|
An optional, string summary, intended to apply to all operations in this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
An optional, string description, intended to apply to all operations in this path.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
get: Optional[Operation] = None
|
||||||
|
"""
|
||||||
|
A definition of a GET operation on this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
put: Optional[Operation] = None
|
||||||
|
"""
|
||||||
|
A definition of a PUT operation on this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
post: Optional[Operation] = None
|
||||||
|
"""
|
||||||
|
A definition of a POST operation on this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
delete: Optional[Operation] = None
|
||||||
|
"""
|
||||||
|
A definition of a DELETE operation on this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
options: Optional[Operation] = None
|
||||||
|
"""
|
||||||
|
A definition of a OPTIONS operation on this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
head: Optional[Operation] = None
|
||||||
|
"""
|
||||||
|
A definition of a HEAD operation on this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
patch: Optional[Operation] = None
|
||||||
|
"""
|
||||||
|
A definition of a PATCH operation on this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
trace: Optional[Operation] = None
|
||||||
|
"""
|
||||||
|
A definition of a TRACE operation on this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
servers: Optional[List[Server]] = None
|
||||||
|
"""
|
||||||
|
An alternative `server` array to service all operations in this path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
parameters: Optional[List[Union[Parameter, Reference]]] = None
|
||||||
|
"""
|
||||||
|
A list of parameters that are applicable for all the operations described under
|
||||||
|
this path. These parameters can be overridden at the operation level, but cannot be
|
||||||
|
removed there. The list MUST NOT include duplicated parameters.
|
||||||
|
A unique parameter is defined by a combination of a [name](#parameterName) and
|
||||||
|
[location](#parameterIn). The list can use the [Reference Object](#referenceObject)
|
||||||
|
to link to parameters that are defined at the
|
||||||
|
[OpenAPI Object's components/parameters](#componentsParameters).
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
populate_by_name=True,
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
schema_extra = {"examples": _examples}
|
28
openapi_pydantic/v3/v3_1/paths.py
Normal file
28
openapi_pydantic/v3/v3_1/paths.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
from .path_item import PathItem
|
||||||
|
|
||||||
|
Paths = Dict[str, PathItem]
|
||||||
|
"""
|
||||||
|
Holds the relative paths to the individual endpoints and their operations.
|
||||||
|
The path is appended to the URL from the [`Server Object`](#serverObject) in order to
|
||||||
|
construct the full URL.
|
||||||
|
|
||||||
|
The Paths MAY be empty, due to
|
||||||
|
[Access Control List (ACL) constraints](#securityFiltering).
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""Patterned Fields"""
|
||||||
|
|
||||||
|
# "/{path}" : PathItem
|
||||||
|
"""
|
||||||
|
A relative path to an individual endpoint.
|
||||||
|
The field name MUST begin with a forward slash (`/`).
|
||||||
|
The path is **appended** (no relative URL resolution) to the expanded URL
|
||||||
|
from the [`Server Object`](#serverObject)'s `url` field in order to construct the full
|
||||||
|
URL. [Path templating](#pathTemplating) is allowed.
|
||||||
|
When matching URLs, concrete (non-templated) paths would be matched before their
|
||||||
|
templated counterparts. Templated paths with the same hierarchy but different templated
|
||||||
|
names MUST NOT exist as they are identical. In case of ambiguous matching, it's up to
|
||||||
|
the tooling to decide which one to use.
|
||||||
|
"""
|
55
openapi_pydantic/v3/v3_1/reference.py
Normal file
55
openapi_pydantic/v3/v3_1/reference.py
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{"$ref": "#/components/schemas/Pet"},
|
||||||
|
{"$ref": "Pet.json"},
|
||||||
|
{"$ref": "definitions.json#/Pet"},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Reference(BaseModel):
|
||||||
|
"""
|
||||||
|
A simple object to allow referencing other components in the OpenAPI document.
|
||||||
|
|
||||||
|
The `$ref` string value contains a URI [RFC3986](https://tools.ietf.org/html/rfc3986),
|
||||||
|
which identifies the location of the value being referenced.
|
||||||
|
|
||||||
|
See the rules for resolving [Relative References](#relativeReferencesURI).
|
||||||
|
"""
|
||||||
|
|
||||||
|
ref: str = Field(alias="$ref")
|
||||||
|
"""**REQUIRED**. The reference identifier. This MUST be in the form of a URI."""
|
||||||
|
|
||||||
|
summary: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A short summary which by default SHOULD override that of the referenced component.
|
||||||
|
If the referenced object-type does not allow a `summary` field, then this field has
|
||||||
|
no effect.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A description which by default SHOULD override that of the referenced component.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
If the referenced object-type does not allow a `description` field, then this field
|
||||||
|
has no effect.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
populate_by_name=True,
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
schema_extra = {"examples": _examples}
|
95
openapi_pydantic/v3/v3_1/request_body.py
Normal file
95
openapi_pydantic/v3/v3_1/request_body.py
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
from typing import Dict, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .media_type import MediaType
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"description": "user to add to the system",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {"$ref": "#/components/schemas/User"},
|
||||||
|
"examples": {
|
||||||
|
"user": {
|
||||||
|
"summary": "User Example",
|
||||||
|
"externalValue": "http://foo.bar/examples/user-example.json",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"application/xml": {
|
||||||
|
"schema": {"$ref": "#/components/schemas/User"},
|
||||||
|
"examples": {
|
||||||
|
"user": {
|
||||||
|
"summary": "User example in XML",
|
||||||
|
"externalValue": "http://foo.bar/examples/user-example.xml",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"text/plain": {
|
||||||
|
"examples": {
|
||||||
|
"user": {
|
||||||
|
"summary": "User example in Plain text",
|
||||||
|
"externalValue": "http://foo.bar/examples/user-example.txt",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"*/*": {
|
||||||
|
"examples": {
|
||||||
|
"user": {
|
||||||
|
"summary": "User example in other format",
|
||||||
|
"externalValue": "http://foo.bar/examples/user-example.whatever",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "user to add to the system",
|
||||||
|
"content": {
|
||||||
|
"text/plain": {"schema": {"type": "array", "items": {"type": "string"}}}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class RequestBody(BaseModel):
|
||||||
|
"""Describes a single request body."""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A brief description of the request body.
|
||||||
|
This could contain examples of use.
|
||||||
|
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
content: Dict[str, MediaType]
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The content of the request body.
|
||||||
|
The key is a media type or [media type range](https://tools.ietf.org/html/rfc7231#appendix-D)
|
||||||
|
and the value describes it.
|
||||||
|
|
||||||
|
For requests that match multiple keys, only the most specific key is applicable.
|
||||||
|
e.g. text/plain overrides text/*
|
||||||
|
"""
|
||||||
|
|
||||||
|
required: bool = False
|
||||||
|
"""
|
||||||
|
Determines if the request body is required in the request. Defaults to `false`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
100
openapi_pydantic/v3/v3_1/response.py
Normal file
100
openapi_pydantic/v3/v3_1/response.py
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
from typing import Dict, Optional, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .header import Header
|
||||||
|
from .link import Link
|
||||||
|
from .media_type import MediaType
|
||||||
|
from .reference import Reference
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"description": "A complex object array response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/components/schemas/VeryComplexType"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "A simple string response",
|
||||||
|
"content": {"text/plain": {"schema": {"type": "string"}}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "A simple string response",
|
||||||
|
"content": {"text/plain": {"schema": {"type": "string", "example": "whoa!"}}},
|
||||||
|
"headers": {
|
||||||
|
"X-Rate-Limit-Limit": {
|
||||||
|
"description": "The number of allowed requests in the "
|
||||||
|
"current period",
|
||||||
|
"schema": {"type": "integer"},
|
||||||
|
},
|
||||||
|
"X-Rate-Limit-Remaining": {
|
||||||
|
"description": "The number of remaining requests in the "
|
||||||
|
"current period",
|
||||||
|
"schema": {"type": "integer"},
|
||||||
|
},
|
||||||
|
"X-Rate-Limit-Reset": {
|
||||||
|
"description": "The number of seconds left in the current period",
|
||||||
|
"schema": {"type": "integer"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{"description": "object created"},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Response(BaseModel):
|
||||||
|
"""
|
||||||
|
Describes a single response from an API Operation, including design-time,
|
||||||
|
static `links` to operations based on the response.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. A short description of the response.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
headers: Optional[Dict[str, Union[Header, Reference]]] = None
|
||||||
|
"""
|
||||||
|
Maps a header name to its definition.
|
||||||
|
[RFC7230](https://tools.ietf.org/html/rfc7230#page-22) states header names are case
|
||||||
|
insensitive.
|
||||||
|
If a response header is defined with the name `"Content-Type"`, it SHALL be ignored.
|
||||||
|
"""
|
||||||
|
|
||||||
|
content: Optional[Dict[str, MediaType]] = None
|
||||||
|
"""
|
||||||
|
A map containing descriptions of potential response payloads.
|
||||||
|
The key is a media type or [media type range](https://tools.ietf.org/html/rfc7231#appendix-D)
|
||||||
|
and the value describes it.
|
||||||
|
|
||||||
|
For responses that match multiple keys, only the most specific key is applicable.
|
||||||
|
e.g. text/plain overrides text/*
|
||||||
|
"""
|
||||||
|
|
||||||
|
links: Optional[Dict[str, Union[Link, Reference]]] = None
|
||||||
|
"""
|
||||||
|
A map of operations links that can be followed from the response.
|
||||||
|
The key of the map is a short name for the link, following the naming constraints
|
||||||
|
of the names for [Component Objects](#componentsObject).
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
52
openapi_pydantic/v3/v3_1/responses.py
Normal file
52
openapi_pydantic/v3/v3_1/responses.py
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
from typing import Dict, Union
|
||||||
|
|
||||||
|
from .reference import Reference
|
||||||
|
from .response import Response
|
||||||
|
|
||||||
|
Responses = Dict[str, Union[Response, Reference]]
|
||||||
|
"""
|
||||||
|
A container for the expected responses of an operation.
|
||||||
|
The container maps a HTTP response code to the expected response.
|
||||||
|
|
||||||
|
The documentation is not necessarily expected to cover all possible HTTP response codes
|
||||||
|
because they may not be known in advance.
|
||||||
|
However, documentation is expected to cover a successful operation response and any
|
||||||
|
known errors.
|
||||||
|
|
||||||
|
The `default` MAY be used as a default response object for all HTTP codes
|
||||||
|
that are not covered individually by the specification.
|
||||||
|
|
||||||
|
The `Responses Object` MUST contain at least one response code, and it
|
||||||
|
SHOULD be the response for a successful operation call.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""Fixed Fields"""
|
||||||
|
|
||||||
|
# default: Optional[Union[Response, Reference]]
|
||||||
|
"""
|
||||||
|
The documentation of responses other than the ones declared for specific HTTP response
|
||||||
|
codes.
|
||||||
|
Use this field to cover undeclared responses.
|
||||||
|
A [Reference Object](#referenceObject) can link to a response
|
||||||
|
that the [OpenAPI Object's components/responses](#componentsResponses) section defines.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""Patterned Fields"""
|
||||||
|
# {httpStatusCode}: Optional[Union[Response, Reference]]
|
||||||
|
"""
|
||||||
|
Any [HTTP status code](#httpCodes) can be used as the property name,
|
||||||
|
but only one property per code, to describe the expected response for that HTTP status
|
||||||
|
code.
|
||||||
|
|
||||||
|
A [Reference Object](#referenceObject) can link to a response
|
||||||
|
that is defined in the [OpenAPI Object's components/responses](#componentsResponses)
|
||||||
|
section.
|
||||||
|
This field MUST be enclosed in quotation marks (for example, "200") for compatibility
|
||||||
|
between JSON and YAML.
|
||||||
|
To define a range of response codes, this field MAY contain the uppercase wildcard
|
||||||
|
character `X`.
|
||||||
|
For example, `2XX` represents all response codes between `[200-299]`.
|
||||||
|
Only the following range definitions are allowed: `1XX`, `2XX`, `3XX`, `4XX`, and `5XX`.
|
||||||
|
If a response is defined using an explicit code,
|
||||||
|
the explicit code definition takes precedence over the range definition for that code.
|
||||||
|
"""
|
971
openapi_pydantic/v3/v3_1/schema.py
Normal file
971
openapi_pydantic/v3/v3_1/schema.py
Normal file
|
@ -0,0 +1,971 @@
|
||||||
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra, min_length_arg
|
||||||
|
|
||||||
|
from .datatype import DataType
|
||||||
|
from .discriminator import Discriminator
|
||||||
|
from .external_documentation import ExternalDocumentation
|
||||||
|
from .reference import Reference
|
||||||
|
from .xml import XML
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{"type": "string", "format": "email"},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": ["name"],
|
||||||
|
"properties": {
|
||||||
|
"name": {"type": "string"},
|
||||||
|
"address": {"$ref": "#/components/schemas/Address"},
|
||||||
|
"age": {"type": "integer", "format": "int32", "minimum": 0},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{"type": "object", "additionalProperties": {"type": "string"}},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {"$ref": "#/components/schemas/ComplexModel"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {"type": "integer", "format": "int64"},
|
||||||
|
"name": {"type": "string"},
|
||||||
|
},
|
||||||
|
"required": ["name"],
|
||||||
|
"example": {"name": "Puma", "id": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": ["message", "code"],
|
||||||
|
"properties": {
|
||||||
|
"message": {"type": "string"},
|
||||||
|
"code": {"type": "integer", "minimum": 100, "maximum": 600},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{"$ref": "#/components/schemas/ErrorModel"},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": ["rootCause"],
|
||||||
|
"properties": {"rootCause": {"type": "string"}},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"discriminator": {"propertyName": "petType"},
|
||||||
|
"properties": {
|
||||||
|
"name": {"type": "string"},
|
||||||
|
"petType": {"type": "string"},
|
||||||
|
},
|
||||||
|
"required": ["name", "petType"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "A representation of a cat. "
|
||||||
|
"Note that `Cat` will be used as the discriminator value.",
|
||||||
|
"allOf": [
|
||||||
|
{"$ref": "#/components/schemas/Pet"},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"huntingSkill": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The measured skill for hunting",
|
||||||
|
"default": "lazy",
|
||||||
|
"enum": [
|
||||||
|
"clueless",
|
||||||
|
"lazy",
|
||||||
|
"adventurous",
|
||||||
|
"aggressive",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["huntingSkill"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "A representation of a dog. "
|
||||||
|
"Note that `Dog` will be used as the discriminator value.",
|
||||||
|
"allOf": [
|
||||||
|
{"$ref": "#/components/schemas/Pet"},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"packSize": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32",
|
||||||
|
"description": "the size of the pack the dog is from",
|
||||||
|
"default": 0,
|
||||||
|
"minimum": 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["packSize"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Schema(BaseModel):
|
||||||
|
"""
|
||||||
|
The Schema Object allows the definition of input and output data types.
|
||||||
|
These types can be objects, but also primitives and arrays.
|
||||||
|
This object is a superset of
|
||||||
|
the [JSON Schema Specification Draft 2020-12](https://tools.ietf.org/html/draft-bhutton-json-schema-00).
|
||||||
|
|
||||||
|
For more information about the properties,
|
||||||
|
see [JSON Schema Core](https://tools.ietf.org/html/draft-wright-json-schema-00)
|
||||||
|
and [JSON Schema Validation](https://tools.ietf.org/html/draft-wright-json-schema-validation-00).
|
||||||
|
|
||||||
|
Unless stated otherwise, the property definitions follow those of JSON Schema
|
||||||
|
and do not add any additional semantics.
|
||||||
|
Where JSON Schema indicates that behavior is defined by the application (e.g. for
|
||||||
|
annotations), OAS also defers the definition of semantics to the application
|
||||||
|
consuming the OpenAPI document.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
The following properties are taken directly from the
|
||||||
|
[JSON Schema Core](https://tools.ietf.org/html/draft-wright-json-schema-00)
|
||||||
|
and follow the same specifications:
|
||||||
|
"""
|
||||||
|
|
||||||
|
allOf: Optional[List[Union[Reference, "Schema"]]] = None
|
||||||
|
"""
|
||||||
|
This keyword's value MUST be a non-empty array. Each item of the
|
||||||
|
array MUST be a valid JSON Schema.
|
||||||
|
|
||||||
|
An instance validates successfully against this keyword if it
|
||||||
|
validates successfully against all schemas defined by this keyword's
|
||||||
|
value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
anyOf: Optional[List[Union[Reference, "Schema"]]] = None
|
||||||
|
"""
|
||||||
|
This keyword's value MUST be a non-empty array. Each item of the
|
||||||
|
array MUST be a valid JSON Schema.
|
||||||
|
|
||||||
|
An instance validates successfully against this keyword if it
|
||||||
|
validates successfully against at least one schema defined by this
|
||||||
|
keyword's value. Note that when annotations are being collected, all
|
||||||
|
subschemas MUST be examined so that annotations are collected from
|
||||||
|
each subschema that validates successfully.
|
||||||
|
"""
|
||||||
|
|
||||||
|
oneOf: Optional[List[Union[Reference, "Schema"]]] = None
|
||||||
|
"""
|
||||||
|
This keyword's value MUST be a non-empty array. Each item of the
|
||||||
|
array MUST be a valid JSON Schema.
|
||||||
|
|
||||||
|
An instance validates successfully against this keyword if it
|
||||||
|
validates successfully against exactly one schema defined by this
|
||||||
|
keyword's value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
schema_not: Optional[Union[Reference, "Schema"]] = Field(default=None, alias="not")
|
||||||
|
"""
|
||||||
|
This keyword's value MUST be a valid JSON Schema.
|
||||||
|
|
||||||
|
An instance is valid against this keyword if it fails to validate
|
||||||
|
successfully against the schema defined by this keyword.
|
||||||
|
"""
|
||||||
|
|
||||||
|
schema_if: Optional[Union[Reference, "Schema"]] = Field(default=None, alias="if")
|
||||||
|
"""
|
||||||
|
This keyword's value MUST be a valid JSON Schema.
|
||||||
|
|
||||||
|
This validation outcome of this keyword's subschema has no direct
|
||||||
|
effect on the overall validation result. Rather, it controls which
|
||||||
|
of the "then" or "else" keywords are evaluated.
|
||||||
|
|
||||||
|
Instances that successfully validate against this keyword's subschema
|
||||||
|
MUST also be valid against the subschema value of the "then" keyword,
|
||||||
|
if present.
|
||||||
|
|
||||||
|
Instances that fail to validate against this keyword's subschema MUST
|
||||||
|
also be valid against the subschema value of the "else" keyword, if
|
||||||
|
present.
|
||||||
|
|
||||||
|
If annotations (Section 7.7) are being collected, they are collected
|
||||||
|
from this keyword's subschema in the usual way, including when the
|
||||||
|
keyword is present without either "then" or "else".
|
||||||
|
"""
|
||||||
|
|
||||||
|
then: Optional[Union[Reference, "Schema"]] = None
|
||||||
|
"""
|
||||||
|
This keyword's value MUST be a valid JSON Schema.
|
||||||
|
|
||||||
|
When "if" is present, and the instance successfully validates against
|
||||||
|
its subschema, then validation succeeds against this keyword if the
|
||||||
|
instance also successfully validates against this keyword's
|
||||||
|
subschema.
|
||||||
|
|
||||||
|
This keyword has no effect when "if" is absent, or when the instance
|
||||||
|
fails to validate against its subschema. Implementations MUST NOT
|
||||||
|
evaluate the instance against this keyword, for either validation or
|
||||||
|
annotation collection purposes, in such cases.
|
||||||
|
"""
|
||||||
|
|
||||||
|
schema_else: Optional[Union[Reference, "Schema"]] = Field(
|
||||||
|
default=None, alias="else"
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
This keyword's value MUST be a valid JSON Schema.
|
||||||
|
|
||||||
|
When "if" is present, and the instance fails to validate against its
|
||||||
|
subschema, then validation succeeds against this keyword if the
|
||||||
|
instance successfully validates against this keyword's subschema.
|
||||||
|
|
||||||
|
This keyword has no effect when "if" is absent, or when the instance
|
||||||
|
successfully validates against its subschema. Implementations MUST
|
||||||
|
NOT evaluate the instance against this keyword, for either validation
|
||||||
|
or annotation collection purposes, in such cases.
|
||||||
|
"""
|
||||||
|
|
||||||
|
dependentSchemas: Optional[Dict[str, Union[Reference, "Schema"]]] = None
|
||||||
|
"""
|
||||||
|
This keyword specifies subschemas that are evaluated if the instance
|
||||||
|
is an object and contains a certain property.
|
||||||
|
|
||||||
|
This keyword's value MUST be an object. Each value in the object
|
||||||
|
MUST be a valid JSON Schema.
|
||||||
|
|
||||||
|
If the object key is a property in the instance, the entire instance
|
||||||
|
must validate against the subschema. Its use is dependent on the
|
||||||
|
presence of the property.
|
||||||
|
|
||||||
|
Omitting this keyword has the same behavior as an empty object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
prefixItems: Optional[List[Union[Reference, "Schema"]]] = None
|
||||||
|
"""
|
||||||
|
The value of "prefixItems" MUST be a non-empty array of valid JSON
|
||||||
|
Schemas.
|
||||||
|
|
||||||
|
Validation succeeds if each element of the instance validates against
|
||||||
|
the schema at the same position, if any. This keyword does not
|
||||||
|
constrain the length of the array. If the array is longer than this
|
||||||
|
keyword's value, this keyword validates only the prefix of matching
|
||||||
|
length.
|
||||||
|
|
||||||
|
This keyword produces an annotation value which is the largest index
|
||||||
|
to which this keyword applied a subschema. The value MAY be a
|
||||||
|
boolean true if a subschema was applied to every index of the
|
||||||
|
instance, such as is produced by the "items" keyword. This
|
||||||
|
annotation affects the behavior of "items" and "unevaluatedItems".
|
||||||
|
|
||||||
|
Omitting this keyword has the same assertion behavior as an empty
|
||||||
|
array.
|
||||||
|
"""
|
||||||
|
|
||||||
|
items: Optional[Union[Reference, "Schema"]] = None
|
||||||
|
"""
|
||||||
|
The value of "items" MUST be a valid JSON Schema.
|
||||||
|
|
||||||
|
This keyword applies its subschema to all instance elements at
|
||||||
|
indexes greater than the length of the "prefixItems" array in the
|
||||||
|
same schema object, as reported by the annotation result of that
|
||||||
|
"prefixItems" keyword. If no such annotation result exists, "items"
|
||||||
|
applies its subschema to all instance array elements. [[CREF11: Note
|
||||||
|
that the behavior of "items" without "prefixItems" is identical to
|
||||||
|
that of the schema form of "items" in prior drafts. When
|
||||||
|
"prefixItems" is present, the behavior of "items" is identical to the
|
||||||
|
former "additionalItems" keyword. ]]
|
||||||
|
|
||||||
|
If the "items" subschema is applied to any positions within the
|
||||||
|
instance array, it produces an annotation result of boolean true,
|
||||||
|
indicating that all remaining array elements have been evaluated
|
||||||
|
against this keyword's subschema.
|
||||||
|
|
||||||
|
Omitting this keyword has the same assertion behavior as an empty
|
||||||
|
schema.
|
||||||
|
|
||||||
|
Implementations MAY choose to implement or optimize this keyword in
|
||||||
|
another way that produces the same effect, such as by directly
|
||||||
|
checking for the presence and size of a "prefixItems" array.
|
||||||
|
Implementations that do not support annotation collection MUST do so.
|
||||||
|
"""
|
||||||
|
|
||||||
|
contains: Optional[Union[Reference, "Schema"]] = None
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be a valid JSON Schema.
|
||||||
|
|
||||||
|
An array instance is valid against "contains" if at least one of its
|
||||||
|
elements is valid against the given schema. The subschema MUST be
|
||||||
|
applied to every array element even after the first match has been
|
||||||
|
found, in order to collect annotations for use by other keywords.
|
||||||
|
This is to ensure that all possible annotations are collected.
|
||||||
|
|
||||||
|
Logically, the validation result of applying the value subschema to
|
||||||
|
each item in the array MUST be ORed with "false", resulting in an
|
||||||
|
overall validation result.
|
||||||
|
|
||||||
|
This keyword produces an annotation value which is an array of the
|
||||||
|
indexes to which this keyword validates successfully when applying
|
||||||
|
its subschema, in ascending order. The value MAY be a boolean "true"
|
||||||
|
if the subschema validates successfully when applied to every index
|
||||||
|
of the instance. The annotation MUST be present if the instance
|
||||||
|
array to which this keyword's schema applies is empty.
|
||||||
|
"""
|
||||||
|
|
||||||
|
properties: Optional[Dict[str, Union[Reference, "Schema"]]] = None
|
||||||
|
"""
|
||||||
|
The value of "properties" MUST be an object. Each value of this
|
||||||
|
object MUST be a valid JSON Schema.
|
||||||
|
|
||||||
|
Validation succeeds if, for each name that appears in both the
|
||||||
|
instance and as a name within this keyword's value, the child
|
||||||
|
instance for that name successfully validates against the
|
||||||
|
corresponding schema.
|
||||||
|
|
||||||
|
The annotation result of this keyword is the set of instance property
|
||||||
|
names matched by this keyword.
|
||||||
|
|
||||||
|
Omitting this keyword has the same assertion behavior as an empty
|
||||||
|
object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
patternProperties: Optional[Dict[str, Union[Reference, "Schema"]]] = None
|
||||||
|
"""
|
||||||
|
The value of "patternProperties" MUST be an object. Each property
|
||||||
|
name of this object SHOULD be a valid regular expression, according
|
||||||
|
to the ECMA-262 regular expression dialect. Each property value of
|
||||||
|
this object MUST be a valid JSON Schema.
|
||||||
|
|
||||||
|
Validation succeeds if, for each instance name that matches any
|
||||||
|
regular expressions that appear as a property name in this keyword's
|
||||||
|
value, the child instance for that name successfully validates
|
||||||
|
against each schema that corresponds to a matching regular
|
||||||
|
expression.
|
||||||
|
|
||||||
|
The annotation result of this keyword is the set of instance property
|
||||||
|
names matched by this keyword.
|
||||||
|
|
||||||
|
Omitting this keyword has the same assertion behavior as an empty
|
||||||
|
object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
additionalProperties: Optional[Union[Reference, "Schema", bool]] = None
|
||||||
|
"""
|
||||||
|
The value of "additionalProperties" MUST be a valid JSON Schema.
|
||||||
|
|
||||||
|
The behavior of this keyword depends on the presence and annotation
|
||||||
|
results of "properties" and "patternProperties" within the same
|
||||||
|
schema object. Validation with "additionalProperties" applies only
|
||||||
|
to the child values of instance names that do not appear in the
|
||||||
|
annotation results of either "properties" or "patternProperties".
|
||||||
|
|
||||||
|
For all such properties, validation succeeds if the child instance
|
||||||
|
validates against the "additionalProperties" schema.
|
||||||
|
|
||||||
|
The annotation result of this keyword is the set of instance property
|
||||||
|
names validated by this keyword's subschema.
|
||||||
|
|
||||||
|
Omitting this keyword has the same assertion behavior as an empty
|
||||||
|
schema.
|
||||||
|
|
||||||
|
Implementations MAY choose to implement or optimize this keyword in
|
||||||
|
another way that produces the same effect, such as by directly
|
||||||
|
checking the names in "properties" and the patterns in
|
||||||
|
"patternProperties" against the instance property set.
|
||||||
|
Implementations that do not support annotation collection MUST do so.
|
||||||
|
"""
|
||||||
|
|
||||||
|
propertyNames: Optional[Union[Reference, "Schema"]] = None
|
||||||
|
"""
|
||||||
|
The value of "propertyNames" MUST be a valid JSON Schema.
|
||||||
|
|
||||||
|
If the instance is an object, this keyword validates if every
|
||||||
|
property name in the instance validates against the provided schema.
|
||||||
|
Note the property name that the schema is testing will always be a
|
||||||
|
string.
|
||||||
|
|
||||||
|
Omitting this keyword has the same behavior as an empty schema.
|
||||||
|
"""
|
||||||
|
|
||||||
|
unevaluatedItems: Optional[Union[Reference, "Schema"]] = None
|
||||||
|
"""
|
||||||
|
The value of "unevaluatedItems" MUST be a valid JSON Schema.
|
||||||
|
|
||||||
|
The behavior of this keyword depends on the annotation results of
|
||||||
|
adjacent keywords that apply to the instance location being
|
||||||
|
validated. Specifically, the annotations from "prefixItems",
|
||||||
|
"items", and "contains", which can come from those keywords when they
|
||||||
|
are adjacent to the "unevaluatedItems" keyword. Those three
|
||||||
|
annotations, as well as "unevaluatedItems", can also result from any
|
||||||
|
and all adjacent in-place applicator (Section 10.2) keywords. This
|
||||||
|
includes but is not limited to the in-place applicators defined in
|
||||||
|
this document.
|
||||||
|
|
||||||
|
If no relevant annotations are present, the "unevaluatedItems"
|
||||||
|
subschema MUST be applied to all locations in the array. If a
|
||||||
|
boolean true value is present from any of the relevant annotations,
|
||||||
|
"unevaluatedItems" MUST be ignored. Otherwise, the subschema MUST be
|
||||||
|
applied to any index greater than the largest annotation value for
|
||||||
|
"prefixItems", which does not appear in any annotation value for
|
||||||
|
"contains".
|
||||||
|
|
||||||
|
This means that "prefixItems", "items", "contains", and all in-place
|
||||||
|
applicators MUST be evaluated before this keyword can be evaluated.
|
||||||
|
Authors of extension keywords MUST NOT define an in-place applicator
|
||||||
|
that would need to be evaluated after this keyword.
|
||||||
|
|
||||||
|
If the "unevaluatedItems" subschema is applied to any positions
|
||||||
|
within the instance array, it produces an annotation result of
|
||||||
|
boolean true, analogous to the behavior of "items".
|
||||||
|
|
||||||
|
Omitting this keyword has the same assertion behavior as an empty
|
||||||
|
schema.
|
||||||
|
"""
|
||||||
|
|
||||||
|
unevaluatedProperties: Optional[Union[Reference, "Schema"]] = None
|
||||||
|
"""
|
||||||
|
The value of "unevaluatedProperties" MUST be a valid JSON Schema.
|
||||||
|
|
||||||
|
The behavior of this keyword depends on the annotation results of
|
||||||
|
adjacent keywords that apply to the instance location being
|
||||||
|
validated. Specifically, the annotations from "properties",
|
||||||
|
"patternProperties", and "additionalProperties", which can come from
|
||||||
|
those keywords when they are adjacent to the "unevaluatedProperties"
|
||||||
|
keyword. Those three annotations, as well as
|
||||||
|
"unevaluatedProperties", can also result from any and all adjacent
|
||||||
|
in-place applicator (Section 10.2) keywords. This includes but is
|
||||||
|
not limited to the in-place applicators defined in this document.
|
||||||
|
|
||||||
|
Validation with "unevaluatedProperties" applies only to the child
|
||||||
|
values of instance names that do not appear in the "properties",
|
||||||
|
"patternProperties", "additionalProperties", or
|
||||||
|
"unevaluatedProperties" annotation results that apply to the instance
|
||||||
|
location being validated.
|
||||||
|
|
||||||
|
For all such properties, validation succeeds if the child instance
|
||||||
|
validates against the "unevaluatedProperties" schema.
|
||||||
|
|
||||||
|
This means that "properties", "patternProperties",
|
||||||
|
"additionalProperties", and all in-place applicators MUST be
|
||||||
|
evaluated before this keyword can be evaluated. Authors of extension
|
||||||
|
keywords MUST NOT define an in-place applicator that would need to be
|
||||||
|
evaluated after this keyword.
|
||||||
|
|
||||||
|
The annotation result of this keyword is the set of instance property
|
||||||
|
names validated by this keyword's subschema.
|
||||||
|
|
||||||
|
Omitting this keyword has the same assertion behavior as an empty
|
||||||
|
schema.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
The following properties are taken directly from the
|
||||||
|
[JSON Schema Validation](https://tools.ietf.org/html/draft-wright-json-schema-validation-00)
|
||||||
|
and follow the same specifications:
|
||||||
|
"""
|
||||||
|
|
||||||
|
type: Optional[Union[DataType, List[DataType]]] = None
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be either a string or an array. If it
|
||||||
|
is an array, elements of the array MUST be strings and MUST be
|
||||||
|
unique.
|
||||||
|
|
||||||
|
String values MUST be one of the six primitive types ("null",
|
||||||
|
"boolean", "object", "array", "number", or "string"), or "integer"
|
||||||
|
which matches any number with a zero fractional part.
|
||||||
|
|
||||||
|
An instance validates if and only if the instance is in any of the
|
||||||
|
sets listed for this keyword.
|
||||||
|
"""
|
||||||
|
|
||||||
|
enum: Optional[List[Any]] = Field(default=None, **min_length_arg(1))
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be an array. This array SHOULD have
|
||||||
|
at least one element. Elements in the array SHOULD be unique.
|
||||||
|
|
||||||
|
An instance validates successfully against this keyword if its value
|
||||||
|
is equal to one of the elements in this keyword's array value.
|
||||||
|
|
||||||
|
Elements in the array might be of any type, including null.
|
||||||
|
"""
|
||||||
|
|
||||||
|
const: Optional[Any] = None
|
||||||
|
"""
|
||||||
|
The value of this keyword MAY be of any type, including null.
|
||||||
|
|
||||||
|
Use of this keyword is functionally equivalent to an "enum"
|
||||||
|
(Section 6.1.2) with a single value.
|
||||||
|
|
||||||
|
An instance validates successfully against this keyword if its value
|
||||||
|
is equal to the value of the keyword.
|
||||||
|
"""
|
||||||
|
|
||||||
|
multipleOf: Optional[float] = Field(default=None, gt=0.0)
|
||||||
|
"""
|
||||||
|
The value of "multipleOf" MUST be a number, strictly greater than 0.
|
||||||
|
|
||||||
|
A numeric instance is only valid if division by this keyword's value
|
||||||
|
results in an integer.
|
||||||
|
"""
|
||||||
|
|
||||||
|
maximum: Optional[float] = None
|
||||||
|
"""
|
||||||
|
The value of "maximum" MUST be a number, representing an inclusive
|
||||||
|
upper limit for a numeric instance.
|
||||||
|
|
||||||
|
If the instance is a number, then this keyword validates only if the
|
||||||
|
instance is less than or exactly equal to "maximum".
|
||||||
|
"""
|
||||||
|
|
||||||
|
exclusiveMaximum: Optional[float] = None
|
||||||
|
"""
|
||||||
|
The value of "exclusiveMaximum" MUST be a number, representing an
|
||||||
|
exclusive upper limit for a numeric instance.
|
||||||
|
|
||||||
|
If the instance is a number, then the instance is valid only if it
|
||||||
|
has a value strictly less than (not equal to) "exclusiveMaximum".
|
||||||
|
"""
|
||||||
|
|
||||||
|
minimum: Optional[float] = None
|
||||||
|
"""
|
||||||
|
The value of "minimum" MUST be a number, representing an inclusive
|
||||||
|
lower limit for a numeric instance.
|
||||||
|
|
||||||
|
If the instance is a number, then this keyword validates only if the
|
||||||
|
instance is greater than or exactly equal to "minimum".
|
||||||
|
"""
|
||||||
|
|
||||||
|
exclusiveMinimum: Optional[float] = None
|
||||||
|
"""
|
||||||
|
The value of "exclusiveMinimum" MUST be a number, representing an
|
||||||
|
exclusive lower limit for a numeric instance.
|
||||||
|
|
||||||
|
If the instance is a number, then the instance is valid only if it
|
||||||
|
has a value strictly greater than (not equal to) "exclusiveMinimum".
|
||||||
|
"""
|
||||||
|
|
||||||
|
maxLength: Optional[int] = Field(default=None, ge=0)
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be a non-negative integer.
|
||||||
|
|
||||||
|
A string instance is valid against this keyword if its length is less
|
||||||
|
than, or equal to, the value of this keyword.
|
||||||
|
|
||||||
|
The length of a string instance is defined as the number of its
|
||||||
|
characters as defined by RFC 8259 [RFC8259].
|
||||||
|
"""
|
||||||
|
|
||||||
|
minLength: Optional[int] = Field(default=None, ge=0)
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be a non-negative integer.
|
||||||
|
|
||||||
|
A string instance is valid against this keyword if its length is
|
||||||
|
greater than, or equal to, the value of this keyword.
|
||||||
|
|
||||||
|
The length of a string instance is defined as the number of its
|
||||||
|
characters as defined by RFC 8259 [RFC8259].
|
||||||
|
|
||||||
|
Omitting this keyword has the same behavior as a value of 0.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pattern: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be a string. This string SHOULD be a
|
||||||
|
valid regular expression, according to the ECMA-262 regular
|
||||||
|
expression dialect.
|
||||||
|
|
||||||
|
A string instance is considered valid if the regular expression
|
||||||
|
matches the instance successfully. Recall: regular expressions are
|
||||||
|
not implicitly anchored.
|
||||||
|
"""
|
||||||
|
|
||||||
|
maxItems: Optional[int] = Field(default=None, ge=0)
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be a non-negative integer.
|
||||||
|
|
||||||
|
An array instance is valid against "maxItems" if its size is less
|
||||||
|
than, or equal to, the value of this keyword.
|
||||||
|
"""
|
||||||
|
|
||||||
|
minItems: Optional[int] = Field(default=None, ge=0)
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be a non-negative integer.
|
||||||
|
|
||||||
|
An array instance is valid against "minItems" if its size is greater
|
||||||
|
than, or equal to, the value of this keyword.
|
||||||
|
|
||||||
|
Omitting this keyword has the same behavior as a value of 0.
|
||||||
|
"""
|
||||||
|
|
||||||
|
uniqueItems: Optional[bool] = None
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be a boolean.
|
||||||
|
|
||||||
|
If this keyword has boolean value false, the instance validates
|
||||||
|
successfully. If it has boolean value true, the instance validates
|
||||||
|
successfully if all of its elements are unique.
|
||||||
|
|
||||||
|
Omitting this keyword has the same behavior as a value of false.
|
||||||
|
"""
|
||||||
|
|
||||||
|
maxContains: Optional[int] = Field(default=None, ge=0)
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be a non-negative integer.
|
||||||
|
|
||||||
|
If "contains" is not present within the same schema object, then this
|
||||||
|
keyword has no effect.
|
||||||
|
|
||||||
|
An instance array is valid against "maxContains" in two ways,
|
||||||
|
depending on the form of the annotation result of an adjacent
|
||||||
|
"contains" [json-schema] keyword. The first way is if the annotation
|
||||||
|
result is an array and the length of that array is less than or equal
|
||||||
|
to the "maxContains" value. The second way is if the annotation
|
||||||
|
result is a boolean "true" and the instance array length is less than
|
||||||
|
or equal to the "maxContains" value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
minContains: Optional[int] = Field(default=None, ge=0)
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be a non-negative integer.
|
||||||
|
|
||||||
|
If "contains" is not present within the same schema object, then this
|
||||||
|
keyword has no effect.
|
||||||
|
|
||||||
|
An instance array is valid against "minContains" in two ways,
|
||||||
|
depending on the form of the annotation result of an adjacent
|
||||||
|
"contains" [json-schema] keyword. The first way is if the annotation
|
||||||
|
result is an array and the length of that array is greater than or
|
||||||
|
equal to the "minContains" value. The second way is if the
|
||||||
|
annotation result is a boolean "true" and the instance array length
|
||||||
|
is greater than or equal to the "minContains" value.
|
||||||
|
|
||||||
|
A value of 0 is allowed, but is only useful for setting a range of
|
||||||
|
occurrences from 0 to the value of "maxContains". A value of 0 with
|
||||||
|
no "maxContains" causes "contains" to always pass validation.
|
||||||
|
|
||||||
|
Omitting this keyword has the same behavior as a value of 1.
|
||||||
|
"""
|
||||||
|
|
||||||
|
maxProperties: Optional[int] = Field(default=None, ge=0)
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be a non-negative integer.
|
||||||
|
|
||||||
|
An object instance is valid against "maxProperties" if its number of
|
||||||
|
properties is less than, or equal to, the value of this keyword.
|
||||||
|
"""
|
||||||
|
|
||||||
|
minProperties: Optional[int] = Field(default=None, ge=0)
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be a non-negative integer.
|
||||||
|
|
||||||
|
An object instance is valid against "minProperties" if its number of
|
||||||
|
properties is greater than, or equal to, the value of this keyword.
|
||||||
|
|
||||||
|
Omitting this keyword has the same behavior as a value of 0.
|
||||||
|
"""
|
||||||
|
|
||||||
|
required: Optional[List[str]] = None
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be an array. Elements of this array,
|
||||||
|
if any, MUST be strings, and MUST be unique.
|
||||||
|
|
||||||
|
An object instance is valid against this keyword if every item in the
|
||||||
|
array is the name of a property in the instance.
|
||||||
|
|
||||||
|
Omitting this keyword has the same behavior as an empty array.
|
||||||
|
"""
|
||||||
|
|
||||||
|
dependentRequired: Optional[Dict[str, List[str]]] = None
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be an object. Properties in this
|
||||||
|
object, if any, MUST be arrays. Elements in each array, if any, MUST
|
||||||
|
be strings, and MUST be unique.
|
||||||
|
|
||||||
|
This keyword specifies properties that are required if a specific
|
||||||
|
other property is present. Their requirement is dependent on the
|
||||||
|
presence of the other property.
|
||||||
|
|
||||||
|
Validation succeeds if, for each name that appears in both the
|
||||||
|
instance and as a name within this keyword's value, every item in the
|
||||||
|
corresponding array is also the name of a property in the instance.
|
||||||
|
|
||||||
|
Omitting this keyword has the same behavior as an empty object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
schema_format: Optional[str] = Field(default=None, alias="format")
|
||||||
|
"""
|
||||||
|
From OpenAPI:
|
||||||
|
See [Data Type Formats](#dataTypeFormat) for further details.
|
||||||
|
While relying on JSON Schema's defined formats, the OAS offers a few additional
|
||||||
|
predefined formats.
|
||||||
|
|
||||||
|
From JSON Schema:
|
||||||
|
Structural validation alone may be insufficient to allow an
|
||||||
|
application to correctly utilize certain values. The "format"
|
||||||
|
annotation keyword is defined to allow schema authors to convey
|
||||||
|
semantic information for a fixed subset of values which are
|
||||||
|
accurately described by authoritative resources, be they RFCs or
|
||||||
|
other external specifications.
|
||||||
|
|
||||||
|
The value of this keyword is called a format attribute. It MUST be a
|
||||||
|
string. A format attribute can generally only validate a given set
|
||||||
|
of instance types. If the type of the instance to validate is not in
|
||||||
|
this set, validation for this format attribute and instance SHOULD
|
||||||
|
succeed. All format attributes defined in this section apply to
|
||||||
|
strings, but a format attribute can be specified to apply to any
|
||||||
|
instance types defined in the data model defined in the core JSON
|
||||||
|
Schema. [json-schema] [[CREF1: Note that the "type" keyword in this
|
||||||
|
specification defines an "integer" type which is not part of the data
|
||||||
|
model. Therefore a format attribute can be limited to numbers, but
|
||||||
|
not specifically to integers. However, a numeric format can be used
|
||||||
|
alongside the "type" keyword with a value of "integer", or could be
|
||||||
|
explicitly defined to always pass if the number is not an integer,
|
||||||
|
which produces essentially the same behavior as only applying to
|
||||||
|
integers. ]]
|
||||||
|
"""
|
||||||
|
|
||||||
|
contentEncoding: Optional[str] = None
|
||||||
|
"""
|
||||||
|
If the instance value is a string, this property defines that the
|
||||||
|
string SHOULD be interpreted as binary data and decoded using the
|
||||||
|
encoding named by this property.
|
||||||
|
|
||||||
|
Possible values indicating base 16, 32, and 64 encodings with several
|
||||||
|
variations are listed in RFC 4648 [RFC4648]. Additionally, sections
|
||||||
|
6.7 and 6.8 of RFC 2045 [RFC2045] provide encodings used in MIME. As
|
||||||
|
"base64" is defined in both RFCs, the definition from RFC 4648 SHOULD
|
||||||
|
be assumed unless the string is specifically intended for use in a
|
||||||
|
MIME context. Note that all of these encodings result in strings
|
||||||
|
consisting only of 7-bit ASCII characters. Therefore, this keyword
|
||||||
|
has no meaning for strings containing characters outside of that
|
||||||
|
range.
|
||||||
|
|
||||||
|
If this keyword is absent, but "contentMediaType" is present, this
|
||||||
|
indicates that the encoding is the identity encoding, meaning that no
|
||||||
|
transformation was needed in order to represent the content in a
|
||||||
|
UTF-8 string.
|
||||||
|
"""
|
||||||
|
|
||||||
|
contentMediaType: Optional[str] = None
|
||||||
|
"""
|
||||||
|
If the instance is a string, this property indicates the media type
|
||||||
|
of the contents of the string. If "contentEncoding" is present, this
|
||||||
|
property describes the decoded string.
|
||||||
|
|
||||||
|
The value of this property MUST be a string, which MUST be a media
|
||||||
|
type, as defined by RFC 2046 [RFC2046].
|
||||||
|
"""
|
||||||
|
|
||||||
|
contentSchema: Optional[Union[Reference, "Schema"]] = None
|
||||||
|
"""
|
||||||
|
If the instance is a string, and if "contentMediaType" is present,
|
||||||
|
this property contains a schema which describes the structure of the
|
||||||
|
string.
|
||||||
|
|
||||||
|
This keyword MAY be used with any media type that can be mapped into
|
||||||
|
JSON Schema's data model.
|
||||||
|
|
||||||
|
The value of this property MUST be a valid JSON schema. It SHOULD be
|
||||||
|
ignored if "contentMediaType" is not present.
|
||||||
|
"""
|
||||||
|
|
||||||
|
title: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The value of "title" MUST be a string.
|
||||||
|
|
||||||
|
The title can be used to decorate a user interface with
|
||||||
|
information about the data produced by this user interface.
|
||||||
|
A title will preferably be short.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
From OpenAPI:
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
|
||||||
|
From JSON Schema:
|
||||||
|
The value "description" MUST be a string.
|
||||||
|
|
||||||
|
The description can be used to decorate a user interface with
|
||||||
|
information about the data produced by this user interface.
|
||||||
|
A description will provide explanation about the purpose of
|
||||||
|
the instance described by this schema.
|
||||||
|
"""
|
||||||
|
|
||||||
|
default: Optional[Any] = None
|
||||||
|
"""
|
||||||
|
There are no restrictions placed on the value of this keyword. When
|
||||||
|
multiple occurrences of this keyword are applicable to a single sub-
|
||||||
|
instance, implementations SHOULD remove duplicates.
|
||||||
|
|
||||||
|
This keyword can be used to supply a default JSON value associated
|
||||||
|
with a particular schema. It is RECOMMENDED that a default value be
|
||||||
|
valid against the associated schema.
|
||||||
|
"""
|
||||||
|
|
||||||
|
deprecated: Optional[bool] = None
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be a boolean. When multiple
|
||||||
|
occurrences of this keyword are applicable to a single sub-instance,
|
||||||
|
applications SHOULD consider the instance location to be deprecated
|
||||||
|
if any occurrence specifies a true value.
|
||||||
|
|
||||||
|
If "deprecated" has a value of boolean true, it indicates that
|
||||||
|
applications SHOULD refrain from usage of the declared property. It
|
||||||
|
MAY mean the property is going to be removed in the future.
|
||||||
|
|
||||||
|
A root schema containing "deprecated" with a value of true indicates
|
||||||
|
that the entire resource being described MAY be removed in the
|
||||||
|
future.
|
||||||
|
|
||||||
|
The "deprecated" keyword applies to each instance location to which
|
||||||
|
the schema object containing the keyword successfully applies. This
|
||||||
|
can result in scenarios where every array item or object property is
|
||||||
|
deprecated even though the containing array or object is not.
|
||||||
|
|
||||||
|
Omitting this keyword has the same behavior as a value of false.
|
||||||
|
"""
|
||||||
|
|
||||||
|
readOnly: Optional[bool] = None
|
||||||
|
"""
|
||||||
|
The value of "readOnly" MUST be a boolean. When multiple
|
||||||
|
occurrences of this keyword are applicable to a single sub-instance,
|
||||||
|
the resulting behavior SHOULD be as for a true value if any
|
||||||
|
occurrence specifies a true value, and SHOULD be as for a false value
|
||||||
|
otherwise.
|
||||||
|
|
||||||
|
If "readOnly" has a value of boolean true, it indicates that the
|
||||||
|
value of the instance is managed exclusively by the owning authority,
|
||||||
|
and attempts by an application to modify the value of this property
|
||||||
|
are expected to be ignored or rejected by that owning authority.
|
||||||
|
|
||||||
|
An instance document that is marked as "readOnly" for the entire
|
||||||
|
document MAY be ignored if sent to the owning authority, or MAY
|
||||||
|
result in an error, at the authority's discretion.
|
||||||
|
|
||||||
|
For example, "readOnly" would be used to mark a database-generated
|
||||||
|
serial number as read-only, while "writeOnly" would be used to mark a
|
||||||
|
password input field.
|
||||||
|
|
||||||
|
This keyword can be used to assist in user interface instance
|
||||||
|
generation. In particular, an application MAY choose to use a widget
|
||||||
|
that hides input values as they are typed for write-only fields.
|
||||||
|
|
||||||
|
Omitting these keywords has the same behavior as values of false.
|
||||||
|
"""
|
||||||
|
|
||||||
|
writeOnly: Optional[bool] = None
|
||||||
|
"""
|
||||||
|
The value of "writeOnly" MUST be a boolean. When multiple
|
||||||
|
occurrences of this keyword are applicable to a single sub-instance,
|
||||||
|
the resulting behavior SHOULD be as for a true value if any
|
||||||
|
occurrence specifies a true value, and SHOULD be as for a false value
|
||||||
|
otherwise.
|
||||||
|
|
||||||
|
If "writeOnly" has a value of boolean true, it indicates that the
|
||||||
|
value is never present when the instance is retrieved from the owning
|
||||||
|
authority. It can be present when sent to the owning authority to
|
||||||
|
update or create the document (or the resource it represents), but it
|
||||||
|
will not be included in any updated or newly created version of the
|
||||||
|
instance.
|
||||||
|
|
||||||
|
An instance document that is marked as "writeOnly" for the entire
|
||||||
|
document MAY be returned as a blank document of some sort, or MAY
|
||||||
|
produce an error upon retrieval, or have the retrieval request
|
||||||
|
ignored, at the authority's discretion.
|
||||||
|
|
||||||
|
For example, "readOnly" would be used to mark a database-generated
|
||||||
|
serial number as read-only, while "writeOnly" would be used to mark a
|
||||||
|
password input field.
|
||||||
|
|
||||||
|
This keyword can be used to assist in user interface instance
|
||||||
|
generation. In particular, an application MAY choose to use a widget
|
||||||
|
that hides input values as they are typed for write-only fields.
|
||||||
|
|
||||||
|
Omitting these keywords has the same behavior as values of false.
|
||||||
|
"""
|
||||||
|
|
||||||
|
examples: Optional[List[Any]] = None
|
||||||
|
"""
|
||||||
|
The value of this keyword MUST be an array. There are no
|
||||||
|
restrictions placed on the values within the array. When multiple
|
||||||
|
occurrences of this keyword are applicable to a single sub-instance,
|
||||||
|
implementations MUST provide a flat array of all values rather than
|
||||||
|
an array of arrays.
|
||||||
|
|
||||||
|
This keyword can be used to provide sample JSON values associated
|
||||||
|
with a particular schema, for the purpose of illustrating usage. It
|
||||||
|
is RECOMMENDED that these values be valid against the associated
|
||||||
|
schema.
|
||||||
|
|
||||||
|
Implementations MAY use the value(s) of "default", if present, as an
|
||||||
|
additional example. If "examples" is absent, "default" MAY still be
|
||||||
|
used in this manner.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
The OpenAPI Specification's base vocabulary is comprised of the following keywords:
|
||||||
|
"""
|
||||||
|
|
||||||
|
discriminator: Optional[Discriminator] = None
|
||||||
|
"""
|
||||||
|
Adds support for polymorphism.
|
||||||
|
The discriminator is an object name that is used to differentiate between other
|
||||||
|
schemas which may satisfy the payload description.
|
||||||
|
See [Composition and Inheritance](#schemaComposition) for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
xml: Optional[XML] = None
|
||||||
|
"""
|
||||||
|
This MAY be used only on properties schemas.
|
||||||
|
It has no effect on root schemas.
|
||||||
|
Adds additional metadata to describe the XML representation of this property.
|
||||||
|
"""
|
||||||
|
|
||||||
|
externalDocs: Optional[ExternalDocumentation] = None
|
||||||
|
"""
|
||||||
|
Additional external documentation for this schema.
|
||||||
|
"""
|
||||||
|
|
||||||
|
example: Optional[Any] = None
|
||||||
|
"""
|
||||||
|
A free-form property to include an example of an instance for this schema.
|
||||||
|
To represent examples that cannot be naturally represented in JSON or YAML,
|
||||||
|
a string value can be used to contain the example with escaping where necessary.
|
||||||
|
|
||||||
|
Deprecated: The example property has been deprecated in favor of the JSON Schema
|
||||||
|
examples keyword.
|
||||||
|
Use of example is discouraged, and later versions of this specification may remove
|
||||||
|
it.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
populate_by_name=True,
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
schema_extra = {"examples": _examples}
|
||||||
|
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
|
||||||
|
def schema_validate(
|
||||||
|
obj: Any,
|
||||||
|
*,
|
||||||
|
strict: Optional[bool] = None,
|
||||||
|
from_attributes: Optional[bool] = None,
|
||||||
|
context: Optional[Dict[str, Any]] = None
|
||||||
|
) -> Schema: ...
|
||||||
|
|
||||||
|
elif PYDANTIC_V2:
|
||||||
|
schema_validate = Schema.model_validate
|
||||||
|
|
||||||
|
else:
|
||||||
|
schema_validate = Schema.parse_obj
|
33
openapi_pydantic/v3/v3_1/security_requirement.py
Normal file
33
openapi_pydantic/v3/v3_1/security_requirement.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
|
SecurityRequirement = Dict[str, List[str]]
|
||||||
|
"""
|
||||||
|
Lists the required security schemes to execute this operation.
|
||||||
|
The name used for each property MUST correspond to a security scheme declared in the
|
||||||
|
[Security Schemes](#componentsSecuritySchemes) under the
|
||||||
|
[Components Object](#componentsObject).
|
||||||
|
|
||||||
|
Security Requirement Objects that contain multiple schemes require that
|
||||||
|
all schemes MUST be satisfied for a request to be authorized.
|
||||||
|
This enables support for scenarios where multiple query parameters or HTTP headers
|
||||||
|
are required to convey security information.
|
||||||
|
|
||||||
|
When a list of Security Requirement Objects is defined on the
|
||||||
|
[OpenAPI Object](#oasObject) or [Operation Object](#operationObject),
|
||||||
|
only one of the Security Requirement Objects in the list needs to be satisfied to
|
||||||
|
authorize the request.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""Patterned Fields"""
|
||||||
|
|
||||||
|
# {name}: List[str]
|
||||||
|
"""
|
||||||
|
Each name MUST correspond to a security scheme which is declared
|
||||||
|
in the [Security Schemes](#componentsSecuritySchemes) under the
|
||||||
|
[Components Object](#componentsObject).
|
||||||
|
If the security scheme is of type `"oauth2"` or `"openIdConnect"`,
|
||||||
|
then the value is a list of scope names required for the execution,
|
||||||
|
and the list MAY be empty if authorization does not require a specified scope.
|
||||||
|
For other security scheme types, the array MAY contain a list of role names which are
|
||||||
|
required for the execution, but are not otherwise defined or exchanged in-band.
|
||||||
|
"""
|
119
openapi_pydantic/v3/v3_1/security_scheme.py
Normal file
119
openapi_pydantic/v3/v3_1/security_scheme.py
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .oauth_flows import OAuthFlows
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{"type": "http", "scheme": "basic"},
|
||||||
|
{"type": "apiKey", "name": "api_key", "in": "header"},
|
||||||
|
{"type": "http", "scheme": "bearer", "bearerFormat": "JWT"},
|
||||||
|
{
|
||||||
|
"type": "oauth2",
|
||||||
|
"flows": {
|
||||||
|
"implicit": {
|
||||||
|
"authorizationUrl": "https://example.com/api/oauth/dialog",
|
||||||
|
"scopes": {
|
||||||
|
"write:pets": "modify pets in your account",
|
||||||
|
"read:pets": "read your pets",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "openIdConnect",
|
||||||
|
"openIdConnectUrl": "https://example.com/openIdConnect",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "openIdConnect",
|
||||||
|
"openIdConnectUrl": "openIdConnect",
|
||||||
|
}, # issue #5: allow relative path
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SecurityScheme(BaseModel):
|
||||||
|
"""
|
||||||
|
Defines a security scheme that can be used by the operations.
|
||||||
|
|
||||||
|
Supported schemes are HTTP authentication,
|
||||||
|
an API key (either as a header, a cookie parameter or as a query parameter),
|
||||||
|
mutual TLS (use of a client certificate),
|
||||||
|
OAuth2's common flows (implicit, password, client credentials and authorization
|
||||||
|
code) as defined in [RFC6749](https://tools.ietf.org/html/rfc6749),
|
||||||
|
and [OpenID Connect Discovery](https://tools.ietf.org/html/draft-ietf-oauth-discovery-06).
|
||||||
|
|
||||||
|
Please note that as of 2020, the implicit flow is about to be deprecated by
|
||||||
|
[OAuth 2.0 Security Best Current Practice](https://tools.ietf.org/html/draft-ietf-oauth-security-topics).
|
||||||
|
Recommended for most use case is Authorization Code Grant flow with PKCE.
|
||||||
|
"""
|
||||||
|
|
||||||
|
type: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The type of the security scheme.
|
||||||
|
Valid values are `"apiKey"`, `"http"`, "mutualTLS", `"oauth2"`, `"openIdConnect"`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A description for security scheme.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
name: Optional[str] = None
|
||||||
|
"""
|
||||||
|
**REQUIRED** for `apiKey`. The name of the header, query or cookie parameter to be
|
||||||
|
used.
|
||||||
|
"""
|
||||||
|
|
||||||
|
security_scheme_in: Optional[str] = Field(alias="in", default=None)
|
||||||
|
"""
|
||||||
|
**REQUIRED** for `apiKey`. The location of the API key. Valid values are `"query"`,
|
||||||
|
`"header"` or `"cookie"`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
scheme: Optional[str] = None
|
||||||
|
"""
|
||||||
|
**REQUIRED** for `http`. The name of the HTTP Authorization scheme to be used in the
|
||||||
|
[Authorization header as defined in RFC7235](https://tools.ietf.org/html/rfc7235#section-5.1).
|
||||||
|
|
||||||
|
The values used SHOULD be registered in the
|
||||||
|
[IANA Authentication Scheme registry](https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml).
|
||||||
|
"""
|
||||||
|
|
||||||
|
bearerFormat: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A hint to the client to identify how the bearer token is formatted.
|
||||||
|
|
||||||
|
Bearer tokens are usually generated by an authorization server,
|
||||||
|
so this information is primarily for documentation purposes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
flows: Optional[OAuthFlows] = None
|
||||||
|
"""
|
||||||
|
**REQUIRED** for `oauth2`. An object containing configuration information for the
|
||||||
|
flow types supported.
|
||||||
|
"""
|
||||||
|
|
||||||
|
openIdConnectUrl: Optional[str] = None
|
||||||
|
"""
|
||||||
|
**REQUIRED** for `openIdConnect`. OpenId Connect URL to discover OAuth2
|
||||||
|
configuration values. This MUST be in the form of a URL. The OpenID Connect
|
||||||
|
standard requires the use of TLS.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
populate_by_name=True,
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
schema_extra = {"examples": _examples}
|
67
openapi_pydantic/v3/v3_1/server.py
Normal file
67
openapi_pydantic/v3/v3_1/server.py
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
from typing import Dict, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .server_variable import ServerVariable
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{
|
||||||
|
"url": "https://development.gigantic-server.com/v1",
|
||||||
|
"description": "Development server",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://{username}.gigantic-server.com:{port}/{basePath}",
|
||||||
|
"description": "The production API server",
|
||||||
|
"variables": {
|
||||||
|
"username": {
|
||||||
|
"default": "demo",
|
||||||
|
"description": "this value is assigned by the service "
|
||||||
|
"provider, in this example `gigantic-server.com`",
|
||||||
|
},
|
||||||
|
"port": {"enum": ["8443", "443"], "default": "8443"},
|
||||||
|
"basePath": {"default": "v2"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Server(BaseModel):
|
||||||
|
"""An object representing a Server."""
|
||||||
|
|
||||||
|
url: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. A URL to the target host.
|
||||||
|
|
||||||
|
This URL supports Server Variables and MAY be relative,
|
||||||
|
to indicate that the host location is relative to the location where the OpenAPI
|
||||||
|
document is being served.
|
||||||
|
Variable substitutions will be made when a variable is named in `{`brackets`}`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
An optional string describing the host designated by the URL.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
variables: Optional[Dict[str, ServerVariable]] = None
|
||||||
|
"""
|
||||||
|
A map between a variable name and its value.
|
||||||
|
|
||||||
|
The value is used for substitution in the server's URL template.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
42
openapi_pydantic/v3/v3_1/server_variable.py
Normal file
42
openapi_pydantic/v3/v3_1/server_variable.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
|
||||||
|
class ServerVariable(BaseModel):
|
||||||
|
"""An object representing a Server Variable for server URL template substitution."""
|
||||||
|
|
||||||
|
enum: Optional[List[str]] = None
|
||||||
|
"""
|
||||||
|
An enumeration of string values to be used if the substitution options are from a
|
||||||
|
limited set. The array SHOULD NOT be empty.
|
||||||
|
"""
|
||||||
|
|
||||||
|
default: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The default value to use for substitution,
|
||||||
|
which SHALL be sent if an alternate value is _not_ supplied.
|
||||||
|
Note this behavior is different than the [Schema Object's](#schemaObject) treatment
|
||||||
|
of default values, because in those cases parameter values are optional.
|
||||||
|
If the [`enum`](#serverVariableEnum) is defined, the value MUST exist in the enum's
|
||||||
|
values.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
An optional description for the server variable.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
47
openapi_pydantic/v3/v3_1/tag.py
Normal file
47
openapi_pydantic/v3/v3_1/tag.py
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
from .external_documentation import ExternalDocumentation
|
||||||
|
|
||||||
|
_examples = [{"name": "pet", "description": "Pets operations"}]
|
||||||
|
|
||||||
|
|
||||||
|
class Tag(BaseModel):
|
||||||
|
"""
|
||||||
|
Adds metadata to a single tag that is used by the
|
||||||
|
[Operation Object](#operationObject).
|
||||||
|
It is not mandatory to have a Tag Object per tag defined in the Operation Object
|
||||||
|
instances.
|
||||||
|
"""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
"""
|
||||||
|
**REQUIRED**. The name of the tag.
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: Optional[str] = None
|
||||||
|
"""
|
||||||
|
A short description for the tag.
|
||||||
|
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text
|
||||||
|
representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
externalDocs: Optional[ExternalDocumentation] = None
|
||||||
|
"""
|
||||||
|
Additional external documentation for this tag.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
72
openapi_pydantic/v3/v3_1/xml.py
Normal file
72
openapi_pydantic/v3/v3_1/xml.py
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
|
||||||
|
|
||||||
|
_examples = [
|
||||||
|
{"name": "animal"},
|
||||||
|
{"attribute": True},
|
||||||
|
{"wrapped": True},
|
||||||
|
{"namespace": "http://example.com/schema/sample", "prefix": "sample"},
|
||||||
|
{"name": "aliens", "wrapped": True},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class XML(BaseModel):
|
||||||
|
"""
|
||||||
|
A metadata object that allows for more fine-tuned XML model definitions.
|
||||||
|
|
||||||
|
When using arrays, XML element names are *not* inferred (for singular/plural forms)
|
||||||
|
and the `name` property SHOULD be used to add that information.
|
||||||
|
See examples for expected behavior.
|
||||||
|
"""
|
||||||
|
|
||||||
|
name: Optional[str] = None
|
||||||
|
"""
|
||||||
|
Replaces the name of the element/attribute used for the described schema property.
|
||||||
|
When defined within `items`, it will affect the name of the individual XML elements
|
||||||
|
within the list.
|
||||||
|
When defined alongside `type` being `array` (outside the `items`),
|
||||||
|
it will affect the wrapping element and only if `wrapped` is `true`.
|
||||||
|
If `wrapped` is `false`, it will be ignored.
|
||||||
|
"""
|
||||||
|
|
||||||
|
namespace: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The URI of the namespace definition.
|
||||||
|
Value MUST be in the form of an absolute URI.
|
||||||
|
"""
|
||||||
|
|
||||||
|
prefix: Optional[str] = None
|
||||||
|
"""
|
||||||
|
The prefix to be used for the [name](#xmlName).
|
||||||
|
"""
|
||||||
|
|
||||||
|
attribute: bool = False
|
||||||
|
"""
|
||||||
|
Declares whether the property definition translates to an attribute instead of an
|
||||||
|
element. Default value is `false`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
wrapped: bool = False
|
||||||
|
"""
|
||||||
|
MAY be used only for an array definition.
|
||||||
|
Signifies whether the array is wrapped
|
||||||
|
(for example, `<books><book/><book/></books>`) or unwrapped (`<book/><book/>`).
|
||||||
|
Default value is `false`.
|
||||||
|
The definition takes effect only when defined alongside `type` being `array`
|
||||||
|
(outside the `items`).
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="allow",
|
||||||
|
json_schema_extra={"examples": _examples},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.allow
|
||||||
|
schema_extra = {"examples": _examples}
|
1250
poetry.lock
generated
Normal file
1250
poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
57
pyproject.toml
Normal file
57
pyproject.toml
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
[tool.poetry]
|
||||||
|
name = "openapi-pydantic"
|
||||||
|
version = "0.5.1"
|
||||||
|
description = "Pydantic OpenAPI schema implementation"
|
||||||
|
authors = ["Mike Oakley <mike-oakley@users.noreply.github.com>"]
|
||||||
|
readme = "README.md"
|
||||||
|
repository = "https://github.com/mike-oakley/openapi-pydantic"
|
||||||
|
license = "MIT"
|
||||||
|
keywords = [
|
||||||
|
"openapi",
|
||||||
|
"schema",
|
||||||
|
"parser",
|
||||||
|
"pydantic",
|
||||||
|
"validation",
|
||||||
|
]
|
||||||
|
classifiers = [
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"License :: OSI Approved :: MIT License",
|
||||||
|
"Operating System :: OS Independent",
|
||||||
|
"Framework :: Pydantic",
|
||||||
|
]
|
||||||
|
include = ["openapi_pydantic/py.typed"]
|
||||||
|
|
||||||
|
[tool.poetry.urls]
|
||||||
|
changelog = "https://github.com/mike-oakley/openapi-pydantic/releases"
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = "^3.8"
|
||||||
|
pydantic = ">=1.8"
|
||||||
|
|
||||||
|
[tool.poetry.group.test.dependencies]
|
||||||
|
pytest = "^8.2.2"
|
||||||
|
pytest-cov = "^5.0.0"
|
||||||
|
openapi-spec-validator = "^0.7.0"
|
||||||
|
|
||||||
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
black = "^24.4.2"
|
||||||
|
mypy = "^1.8.0"
|
||||||
|
pre-commit = "^2.16.0"
|
||||||
|
ruff = "^0.7.2"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core>=1.0.0"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
warn_unused_ignores = true
|
||||||
|
warn_redundant_casts = true
|
||||||
|
warn_unused_configs = true
|
||||||
|
warn_unreachable = true
|
||||||
|
warn_return_any = true
|
||||||
|
strict = true
|
||||||
|
disallow_any_generics = false
|
||||||
|
implicit_reexport = false
|
||||||
|
show_error_codes = true
|
||||||
|
files = ["openapi_pydantic/", "tests/"]
|
||||||
|
plugins = ["pydantic.mypy"]
|
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
1103
tests/data/swagger_openapi_v3.0.1.json
Normal file
1103
tests/data/swagger_openapi_v3.0.1.json
Normal file
File diff suppressed because it is too large
Load diff
0
tests/schema_classes/__init__.py
Normal file
0
tests/schema_classes/__init__.py
Normal file
64
tests/schema_classes/test_schema.py
Normal file
64
tests/schema_classes/test_schema.py
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from openapi_pydantic import Reference, schema_validate
|
||||||
|
from openapi_pydantic.compat import (
|
||||||
|
DEFS_KEY,
|
||||||
|
PYDANTIC_V2,
|
||||||
|
ConfigDict,
|
||||||
|
Extra,
|
||||||
|
models_json_schema,
|
||||||
|
v1_schema,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_schema() -> None:
|
||||||
|
schema = schema_validate(
|
||||||
|
{
|
||||||
|
"title": "reference list",
|
||||||
|
"description": "schema for list of reference type",
|
||||||
|
"allOf": [{"$ref": "#/definitions/TestType"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
logging.debug(f"schema.allOf={schema.allOf}")
|
||||||
|
assert schema.allOf
|
||||||
|
assert isinstance(schema.allOf, list)
|
||||||
|
assert isinstance(schema.allOf[0], Reference)
|
||||||
|
assert schema.allOf[0].ref == "#/definitions/TestType"
|
||||||
|
|
||||||
|
|
||||||
|
def test_additional_properties_is_bool() -> None:
|
||||||
|
class TestModel(BaseModel):
|
||||||
|
test_field: str
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(
|
||||||
|
extra="forbid",
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
extra = Extra.forbid
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
_key_map, schema_definition = models_json_schema([(TestModel, "validation")])
|
||||||
|
else:
|
||||||
|
schema_definition = v1_schema([TestModel])
|
||||||
|
|
||||||
|
assert schema_definition == {
|
||||||
|
DEFS_KEY: {
|
||||||
|
"TestModel": {
|
||||||
|
"title": "TestModel",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {"test_field": {"title": "Test Field", "type": "string"}},
|
||||||
|
"required": ["test_field"],
|
||||||
|
"additionalProperties": False,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# allow "additionalProperties" to have boolean value
|
||||||
|
result = schema_validate(schema_definition[DEFS_KEY]["TestModel"])
|
||||||
|
assert result.additionalProperties is False
|
46
tests/schema_classes/test_security_scheme.py
Normal file
46
tests/schema_classes/test_security_scheme.py
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
from openapi_pydantic import SecurityScheme
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2
|
||||||
|
|
||||||
|
|
||||||
|
def test_oidc_parsing() -> None:
|
||||||
|
security_scheme_1 = SecurityScheme(
|
||||||
|
type="openIdConnect", openIdConnectUrl="https://example.com/openIdConnect"
|
||||||
|
)
|
||||||
|
assert isinstance(security_scheme_1.openIdConnectUrl, str)
|
||||||
|
dump1 = getattr(security_scheme_1, "model_dump_json" if PYDANTIC_V2 else "json")
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
assert dump1(by_alias=True, exclude_none=True) == (
|
||||||
|
'{"type":"openIdConnect","openIdConnectUrl":"https://example.com/openIdConnect"}'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
assert dump1(by_alias=True, exclude_none=True) == (
|
||||||
|
'{"type": "openIdConnect", "openIdConnectUrl": "https://example.com/openIdConnect"}'
|
||||||
|
)
|
||||||
|
|
||||||
|
security_scheme_2 = SecurityScheme(
|
||||||
|
type="openIdConnect", openIdConnectUrl="/openIdConnect"
|
||||||
|
)
|
||||||
|
assert isinstance(security_scheme_2.openIdConnectUrl, str)
|
||||||
|
dump2 = getattr(security_scheme_2, "model_dump_json" if PYDANTIC_V2 else "json")
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
assert dump2(by_alias=True, exclude_none=True) == (
|
||||||
|
'{"type":"openIdConnect","openIdConnectUrl":"/openIdConnect"}'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
assert dump2(by_alias=True, exclude_none=True) == (
|
||||||
|
'{"type": "openIdConnect", "openIdConnectUrl": "/openIdConnect"}'
|
||||||
|
)
|
||||||
|
|
||||||
|
security_scheme_3 = SecurityScheme(
|
||||||
|
type="openIdConnect", openIdConnectUrl="openIdConnect"
|
||||||
|
)
|
||||||
|
assert isinstance(security_scheme_3.openIdConnectUrl, str)
|
||||||
|
dump3 = getattr(security_scheme_3, "model_dump_json" if PYDANTIC_V2 else "json")
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
assert dump3(by_alias=True, exclude_none=True) == (
|
||||||
|
'{"type":"openIdConnect","openIdConnectUrl":"openIdConnect"}'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
assert dump3(by_alias=True, exclude_none=True) == (
|
||||||
|
'{"type": "openIdConnect", "openIdConnectUrl": "openIdConnect"}'
|
||||||
|
)
|
78
tests/test_alias.py
Normal file
78
tests/test_alias.py
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
from openapi_pydantic import (
|
||||||
|
MediaType,
|
||||||
|
Parameter,
|
||||||
|
PathItem,
|
||||||
|
Reference,
|
||||||
|
Schema,
|
||||||
|
SecurityScheme,
|
||||||
|
)
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2
|
||||||
|
|
||||||
|
validate_func_name = "model_validate" if PYDANTIC_V2 else "parse_obj"
|
||||||
|
|
||||||
|
|
||||||
|
def test_media_type_alias() -> None:
|
||||||
|
media_type_1 = MediaType(media_type_schema=Schema())
|
||||||
|
media_type_2 = MediaType(schema=Schema())
|
||||||
|
model_validate: Callable[[dict], MediaType] = getattr(MediaType, validate_func_name)
|
||||||
|
media_type_3 = model_validate({"media_type_schema": Schema()})
|
||||||
|
media_type_4 = model_validate({"schema": Schema()})
|
||||||
|
assert media_type_1 == media_type_2 == media_type_3 == media_type_4
|
||||||
|
|
||||||
|
|
||||||
|
def test_parameter_alias() -> None:
|
||||||
|
parameter_1 = Parameter( # type: ignore
|
||||||
|
name="test",
|
||||||
|
param_in="path",
|
||||||
|
param_schema=Schema(),
|
||||||
|
)
|
||||||
|
parameter_2 = Parameter( # type: ignore
|
||||||
|
name="test",
|
||||||
|
param_in="path",
|
||||||
|
schema=Schema(),
|
||||||
|
)
|
||||||
|
model_validate: Callable[[dict], Parameter] = getattr(Parameter, validate_func_name)
|
||||||
|
parameter_3 = model_validate(
|
||||||
|
{"name": "test", "param_in": "path", "param_schema": Schema()}
|
||||||
|
)
|
||||||
|
parameter_4 = model_validate({"name": "test", "in": "path", "schema": Schema()})
|
||||||
|
assert parameter_1 == parameter_2 == parameter_3 == parameter_4
|
||||||
|
|
||||||
|
|
||||||
|
def test_path_item_alias() -> None:
|
||||||
|
path_item_1 = PathItem(ref="#/dummy")
|
||||||
|
model_validate: Callable[[dict], PathItem] = getattr(PathItem, validate_func_name)
|
||||||
|
path_item_2 = model_validate({"ref": "#/dummy"})
|
||||||
|
path_item_3 = model_validate({"$ref": "#/dummy"})
|
||||||
|
assert path_item_1 == path_item_2 == path_item_3
|
||||||
|
|
||||||
|
|
||||||
|
def test_reference_alias() -> None:
|
||||||
|
reference_1 = Reference(ref="#/dummy") # type: ignore
|
||||||
|
reference_2 = Reference(**{"$ref": "#/dummy"})
|
||||||
|
model_validate: Callable[[dict], Reference] = getattr(Reference, validate_func_name)
|
||||||
|
reference_3 = model_validate({"ref": "#/dummy"})
|
||||||
|
reference_4 = model_validate({"$ref": "#/dummy"})
|
||||||
|
assert reference_1 == reference_2 == reference_3 == reference_4
|
||||||
|
|
||||||
|
|
||||||
|
def test_security_scheme() -> None:
|
||||||
|
security_scheme_1 = SecurityScheme(type="apiKey", security_scheme_in="header")
|
||||||
|
model_validate: Callable[[dict], SecurityScheme] = getattr(
|
||||||
|
SecurityScheme, validate_func_name
|
||||||
|
)
|
||||||
|
security_scheme_2 = model_validate(
|
||||||
|
{"type": "apiKey", "security_scheme_in": "header"}
|
||||||
|
)
|
||||||
|
security_scheme_3 = model_validate({"type": "apiKey", "in": "header"})
|
||||||
|
assert security_scheme_1 == security_scheme_2 == security_scheme_3
|
||||||
|
|
||||||
|
|
||||||
|
def test_schema() -> None:
|
||||||
|
schema_1 = Schema(schema_not=Schema(), schema_format="email")
|
||||||
|
model_validate: Callable[[dict], Schema] = getattr(Schema, validate_func_name)
|
||||||
|
schema_2 = model_validate({"schema_not": Schema(), "schema_format": "email"})
|
||||||
|
schema_3 = model_validate({"not": Schema(), "format": "email"})
|
||||||
|
assert schema_1 == schema_2 == schema_3
|
95
tests/test_config_example.py
Normal file
95
tests/test_config_example.py
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from openapi_pydantic import (
|
||||||
|
XML,
|
||||||
|
Callback,
|
||||||
|
Components,
|
||||||
|
Contact,
|
||||||
|
Discriminator,
|
||||||
|
Encoding,
|
||||||
|
Example,
|
||||||
|
ExternalDocumentation,
|
||||||
|
Header,
|
||||||
|
Info,
|
||||||
|
License,
|
||||||
|
Link,
|
||||||
|
MediaType,
|
||||||
|
OAuthFlow,
|
||||||
|
OAuthFlows,
|
||||||
|
OpenAPI,
|
||||||
|
Operation,
|
||||||
|
Parameter,
|
||||||
|
PathItem,
|
||||||
|
Paths,
|
||||||
|
Reference,
|
||||||
|
RequestBody,
|
||||||
|
Response,
|
||||||
|
Responses,
|
||||||
|
Schema,
|
||||||
|
SecurityRequirement,
|
||||||
|
SecurityScheme,
|
||||||
|
Server,
|
||||||
|
ServerVariable,
|
||||||
|
Tag,
|
||||||
|
)
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2
|
||||||
|
|
||||||
|
|
||||||
|
def test_config_example() -> None:
|
||||||
|
all_types = [
|
||||||
|
OpenAPI,
|
||||||
|
Info,
|
||||||
|
Contact,
|
||||||
|
License,
|
||||||
|
Server,
|
||||||
|
ServerVariable,
|
||||||
|
Components,
|
||||||
|
Paths,
|
||||||
|
PathItem,
|
||||||
|
Operation,
|
||||||
|
ExternalDocumentation,
|
||||||
|
Parameter,
|
||||||
|
RequestBody,
|
||||||
|
MediaType,
|
||||||
|
Encoding,
|
||||||
|
Responses,
|
||||||
|
Response,
|
||||||
|
Callback,
|
||||||
|
Example,
|
||||||
|
Link,
|
||||||
|
Header,
|
||||||
|
Tag,
|
||||||
|
Reference,
|
||||||
|
Schema,
|
||||||
|
Discriminator,
|
||||||
|
XML,
|
||||||
|
SecurityScheme,
|
||||||
|
OAuthFlows,
|
||||||
|
OAuthFlow,
|
||||||
|
SecurityRequirement,
|
||||||
|
]
|
||||||
|
for schema_type in all_types:
|
||||||
|
_assert_config_examples(schema_type)
|
||||||
|
|
||||||
|
|
||||||
|
def _assert_config_examples(schema_type: Any) -> None:
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
if not hasattr(schema_type, "model_config"):
|
||||||
|
return
|
||||||
|
extra = schema_type.model_config.get("json_schema_extra")
|
||||||
|
if extra is not None:
|
||||||
|
examples = extra["examples"]
|
||||||
|
if examples is None:
|
||||||
|
breakpoint()
|
||||||
|
for example_dict in examples:
|
||||||
|
obj = schema_type.model_validate(example_dict)
|
||||||
|
assert obj.model_fields_set
|
||||||
|
|
||||||
|
else:
|
||||||
|
Config = getattr(schema_type, "Config", None)
|
||||||
|
schema_extra = getattr(Config, "schema_extra", None)
|
||||||
|
if schema_extra is not None:
|
||||||
|
examples = schema_extra["examples"]
|
||||||
|
for example_dict in examples:
|
||||||
|
obj = schema_type(**example_dict)
|
||||||
|
assert obj.__fields_set__
|
67
tests/test_example.py
Normal file
67
tests/test_example.py
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import logging
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
from openapi_pydantic import Info, OpenAPI, Operation, PathItem, Response
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2
|
||||||
|
|
||||||
|
|
||||||
|
def test_readme_example() -> None:
|
||||||
|
open_api_1 = readme_example_1()
|
||||||
|
assert open_api_1
|
||||||
|
dump_json = getattr(open_api_1, "model_dump_json" if PYDANTIC_V2 else "json")
|
||||||
|
open_api_json_1 = dump_json(by_alias=True, exclude_none=True, indent=2)
|
||||||
|
logging.debug(open_api_json_1)
|
||||||
|
assert open_api_json_1
|
||||||
|
|
||||||
|
open_api_2 = readme_example_2()
|
||||||
|
assert open_api_1 == open_api_2
|
||||||
|
|
||||||
|
open_api_3 = readme_example_3()
|
||||||
|
assert open_api_1 == open_api_3
|
||||||
|
|
||||||
|
|
||||||
|
def readme_example_1() -> OpenAPI:
|
||||||
|
"""Construct OpenAPI using data class"""
|
||||||
|
return OpenAPI(
|
||||||
|
info=Info(
|
||||||
|
title="My own API",
|
||||||
|
version="v0.0.1",
|
||||||
|
),
|
||||||
|
paths={
|
||||||
|
"/ping": PathItem(
|
||||||
|
get=Operation(responses={"200": Response(description="pong")})
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def readme_example_2() -> OpenAPI:
|
||||||
|
"""Construct OpenAPI from raw data object"""
|
||||||
|
openapi_validate: Callable[[dict], OpenAPI] = getattr(
|
||||||
|
OpenAPI, "model_validate" if PYDANTIC_V2 else "parse_obj"
|
||||||
|
)
|
||||||
|
return openapi_validate(
|
||||||
|
{
|
||||||
|
"info": {"title": "My own API", "version": "v0.0.1"},
|
||||||
|
"paths": {
|
||||||
|
"/ping": {"get": {"responses": {"200": {"description": "pong"}}}}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def readme_example_3() -> OpenAPI:
|
||||||
|
"""Construct OpenAPI from mixed object"""
|
||||||
|
openapi_validate: Callable[[dict], OpenAPI] = getattr(
|
||||||
|
OpenAPI, "model_validate" if PYDANTIC_V2 else "parse_obj"
|
||||||
|
)
|
||||||
|
return openapi_validate(
|
||||||
|
{
|
||||||
|
"info": {"title": "My own API", "version": "v0.0.1"},
|
||||||
|
"paths": {
|
||||||
|
"/ping": PathItem(
|
||||||
|
get={"responses": {"200": Response(description="pong")}}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
81
tests/test_openapi.py
Normal file
81
tests/test_openapi.py
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2
|
||||||
|
from openapi_pydantic.v3 import v3_0, v3_1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("version", ["3.0.4", "3.1.1"])
|
||||||
|
def test_parse_with_callback(version: str) -> None:
|
||||||
|
data = {
|
||||||
|
"openapi": version,
|
||||||
|
"info": {"title": "API with Callback", "version": ""},
|
||||||
|
"paths": {
|
||||||
|
"/create": {
|
||||||
|
"post": {
|
||||||
|
"responses": {"200": {"description": "Success"}},
|
||||||
|
"callbacks": {
|
||||||
|
"event": {
|
||||||
|
"callback": {
|
||||||
|
"post": {
|
||||||
|
"responses": {"200": {"description": "Success"}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if version == "3.0.4":
|
||||||
|
model_validate_3_0: Callable[[dict], v3_0.OpenAPI] = getattr(
|
||||||
|
v3_0.OpenAPI, "model_validate" if PYDANTIC_V2 else "parse_obj"
|
||||||
|
)
|
||||||
|
assert model_validate_3_0(data) == v3_0.OpenAPI(
|
||||||
|
info=v3_0.Info(title="API with Callback", version=""),
|
||||||
|
paths={
|
||||||
|
"/create": v3_0.PathItem(
|
||||||
|
post=v3_0.Operation(
|
||||||
|
responses={"200": v3_0.Response(description="Success")},
|
||||||
|
callbacks={
|
||||||
|
"event": {
|
||||||
|
"callback": v3_0.PathItem(
|
||||||
|
post=v3_0.Operation(
|
||||||
|
responses={
|
||||||
|
"200": v3_0.Response(description="Success")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
model_validate_3_1: Callable[[dict], v3_1.OpenAPI] = getattr(
|
||||||
|
v3_1.OpenAPI, "model_validate" if PYDANTIC_V2 else "parse_obj"
|
||||||
|
)
|
||||||
|
assert model_validate_3_1(data) == v3_1.OpenAPI(
|
||||||
|
info=v3_1.Info(title="API with Callback", version=""),
|
||||||
|
paths={
|
||||||
|
"/create": v3_1.PathItem(
|
||||||
|
post=v3_1.Operation(
|
||||||
|
responses={"200": v3_1.Response(description="Success")},
|
||||||
|
callbacks={
|
||||||
|
"event": {
|
||||||
|
"callback": v3_1.PathItem(
|
||||||
|
post=v3_1.Operation(
|
||||||
|
responses={
|
||||||
|
"200": v3_1.Response(description="Success")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
42
tests/test_parse.py
Normal file
42
tests/test_parse.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from openapi_pydantic import parse_obj
|
||||||
|
from openapi_pydantic.v3 import v3_0, v3_1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("version", ["3.0.4", "3.0.3", "3.0.2", "3.0.1", "3.0.0"])
|
||||||
|
def test_parse_obj_3_0(
|
||||||
|
version: Literal["3.0.4", "3.0.3", "3.0.2", "3.0.1", "3.0.0"]
|
||||||
|
) -> None:
|
||||||
|
result = parse_obj(
|
||||||
|
{
|
||||||
|
"openapi": version,
|
||||||
|
"info": {"title": "foo", "version": "0.1.0"},
|
||||||
|
"paths": {"/": {}},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result == v3_0.OpenAPI(
|
||||||
|
openapi=version,
|
||||||
|
info=v3_0.Info(title="foo", version="0.1.0"),
|
||||||
|
paths={"/": v3_0.PathItem()},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("version", ["3.1.1", "3.1.0"])
|
||||||
|
def test_parse_obj_3_1(version: Literal["3.1.1", "3.1.0"]) -> None:
|
||||||
|
result = parse_obj(
|
||||||
|
{
|
||||||
|
"openapi": version,
|
||||||
|
"info": {"title": "foo", "version": "0.1.0"},
|
||||||
|
"paths": {"/": {}},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result == v3_1.OpenAPI(
|
||||||
|
openapi=version,
|
||||||
|
info=v3_1.Info(title="foo", version="0.1.0"),
|
||||||
|
paths={"/": v3_1.PathItem()},
|
||||||
|
)
|
47
tests/test_swagger_openapi_v3.py
Normal file
47
tests/test_swagger_openapi_v3.py
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
from typing import Dict, Optional
|
||||||
|
|
||||||
|
from pydantic import Field
|
||||||
|
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict
|
||||||
|
from openapi_pydantic.v3.v3_0 import OpenAPI, Operation, PathItem
|
||||||
|
|
||||||
|
|
||||||
|
def test_swagger_openapi_v3() -> None:
|
||||||
|
with open("tests/data/swagger_openapi_v3.0.1.json") as f:
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
validate = getattr(ExtendedOpenAPI, "model_validate_json")
|
||||||
|
else:
|
||||||
|
validate = getattr(ExtendedOpenAPI, "parse_raw")
|
||||||
|
open_api = validate(f.read())
|
||||||
|
assert open_api
|
||||||
|
|
||||||
|
|
||||||
|
class ExtendedOperation(Operation):
|
||||||
|
"""Override classes to use "x-codegen-request-body-name" in Operation"""
|
||||||
|
|
||||||
|
xCodegenRequestBodyName: Optional[str] = Field(
|
||||||
|
default=None, alias="x-codegen-request-body-name"
|
||||||
|
)
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
model_config = ConfigDict(populate_by_name=True)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
|
||||||
|
|
||||||
|
class ExtendedPathItem(PathItem):
|
||||||
|
get: Optional[ExtendedOperation] = None
|
||||||
|
put: Optional[ExtendedOperation] = None
|
||||||
|
post: Optional[ExtendedOperation] = None
|
||||||
|
delete: Optional[ExtendedOperation] = None
|
||||||
|
options: Optional[ExtendedOperation] = None
|
||||||
|
head: Optional[ExtendedOperation] = None
|
||||||
|
patch: Optional[ExtendedOperation] = None
|
||||||
|
trace: Optional[ExtendedOperation] = None
|
||||||
|
|
||||||
|
|
||||||
|
class ExtendedOpenAPI(OpenAPI):
|
||||||
|
paths: Dict[str, ExtendedPathItem] # type: ignore[assignment]
|
0
tests/util/__init__.py
Normal file
0
tests/util/__init__.py
Normal file
114
tests/util/test_optional_and_computed.py
Normal file
114
tests/util/test_optional_and_computed.py
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
# mypy: ignore-errors
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from openapi_pydantic import (
|
||||||
|
Info,
|
||||||
|
MediaType,
|
||||||
|
OpenAPI,
|
||||||
|
Operation,
|
||||||
|
PathItem,
|
||||||
|
RequestBody,
|
||||||
|
Response,
|
||||||
|
Schema,
|
||||||
|
)
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2
|
||||||
|
from openapi_pydantic.util import PydanticSchema, construct_open_api_with_schema_class
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not PYDANTIC_V2, reason="computed fields require Pydantic V2")
|
||||||
|
def test_optional_and_computed_fields() -> None:
|
||||||
|
api = construct_sample_api()
|
||||||
|
|
||||||
|
result = construct_open_api_with_schema_class(api)
|
||||||
|
assert result.components is not None
|
||||||
|
assert result.components.schemas is not None
|
||||||
|
|
||||||
|
req_schema = result.components.schemas["SampleRequest"]
|
||||||
|
assert isinstance(req_schema, Schema)
|
||||||
|
assert req_schema.properties is not None
|
||||||
|
assert req_schema.required is not None
|
||||||
|
|
||||||
|
resp_schema = result.components.schemas["SampleResponse"]
|
||||||
|
assert isinstance(resp_schema, Schema)
|
||||||
|
assert resp_schema.properties is not None
|
||||||
|
assert resp_schema.required is not None
|
||||||
|
|
||||||
|
# When validating:
|
||||||
|
# - required fields are still required
|
||||||
|
# - optional fields are still optional
|
||||||
|
# - computed fields don't exist
|
||||||
|
assert "req" in req_schema.properties
|
||||||
|
assert "opt" in req_schema.properties
|
||||||
|
assert "comp" not in req_schema.properties
|
||||||
|
assert set(req_schema.required) == {"req"}
|
||||||
|
|
||||||
|
# When serializing:
|
||||||
|
# - required fields are still required
|
||||||
|
# - optional fields are still optional
|
||||||
|
# (except when json_schema_serialization_defaults_required is enabled)
|
||||||
|
# - computed fields are required
|
||||||
|
assert "req" in resp_schema.properties
|
||||||
|
assert "opt" in resp_schema.properties
|
||||||
|
assert "comp" in resp_schema.properties
|
||||||
|
assert set(resp_schema.required) == {"req", "comp"}
|
||||||
|
|
||||||
|
|
||||||
|
def construct_sample_api() -> OpenAPI:
|
||||||
|
from typing import TYPE_CHECKING, Callable
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
|
||||||
|
def computed_field(x: Callable) -> Callable: ...
|
||||||
|
|
||||||
|
else:
|
||||||
|
from pydantic import computed_field
|
||||||
|
|
||||||
|
class SampleModel(BaseModel):
|
||||||
|
req: bool
|
||||||
|
opt: Optional[bool] = None
|
||||||
|
|
||||||
|
@computed_field
|
||||||
|
@property
|
||||||
|
def comp(self) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
class SampleRequest(SampleModel):
|
||||||
|
model_config = {"json_schema_mode": "validation"}
|
||||||
|
|
||||||
|
class SampleResponse(SampleModel):
|
||||||
|
model_config = {"json_schema_mode": "serialization"}
|
||||||
|
|
||||||
|
return OpenAPI(
|
||||||
|
info=Info(
|
||||||
|
title="Sample API",
|
||||||
|
version="v0.0.1",
|
||||||
|
),
|
||||||
|
paths={
|
||||||
|
"/callme": PathItem(
|
||||||
|
post=Operation(
|
||||||
|
requestBody=RequestBody(
|
||||||
|
content={
|
||||||
|
"application/json": MediaType(
|
||||||
|
schema=PydanticSchema(schema_class=SampleRequest)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
responses={
|
||||||
|
"200": Response(
|
||||||
|
description="resp",
|
||||||
|
content={
|
||||||
|
"application/json": MediaType(
|
||||||
|
schema=PydanticSchema(schema_class=SampleResponse)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
163
tests/util/test_pydantic_field.py
Normal file
163
tests/util/test_pydantic_field.py
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
from typing import Dict, List, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from typing_extensions import Literal
|
||||||
|
|
||||||
|
from openapi_pydantic import (
|
||||||
|
Discriminator,
|
||||||
|
Info,
|
||||||
|
MediaType,
|
||||||
|
OpenAPI,
|
||||||
|
Operation,
|
||||||
|
PathItem,
|
||||||
|
Reference,
|
||||||
|
RequestBody,
|
||||||
|
Response,
|
||||||
|
Schema,
|
||||||
|
)
|
||||||
|
from openapi_pydantic.compat import (
|
||||||
|
DEFS_KEY,
|
||||||
|
PYDANTIC_MINOR_VERSION,
|
||||||
|
PYDANTIC_V2,
|
||||||
|
models_json_schema,
|
||||||
|
v1_schema,
|
||||||
|
)
|
||||||
|
from openapi_pydantic.util import PydanticSchema, construct_open_api_with_schema_class
|
||||||
|
|
||||||
|
|
||||||
|
class DataAModel(BaseModel):
|
||||||
|
kind: Literal["a"]
|
||||||
|
|
||||||
|
|
||||||
|
class DataBModel(BaseModel):
|
||||||
|
kind: Literal["b"]
|
||||||
|
|
||||||
|
|
||||||
|
class RequestModel(BaseModel):
|
||||||
|
data: Union[DataAModel, DataBModel] = Field(discriminator="kind")
|
||||||
|
|
||||||
|
|
||||||
|
def construct_base_open_api() -> OpenAPI:
|
||||||
|
return OpenAPI(
|
||||||
|
info=Info(
|
||||||
|
title="My own API",
|
||||||
|
version="v0.0.1",
|
||||||
|
),
|
||||||
|
paths={
|
||||||
|
"/ping": PathItem(
|
||||||
|
post=Operation(
|
||||||
|
requestBody=RequestBody(
|
||||||
|
content={
|
||||||
|
"application/json": MediaType(
|
||||||
|
media_type_schema=PydanticSchema(
|
||||||
|
schema_class=RequestModel
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
responses={"200": Response(description="pong")},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_pydantic_discriminator_schema_generation() -> None:
|
||||||
|
"""https://github.com/kuimono/openapi-schema-pydantic/issues/8"""
|
||||||
|
|
||||||
|
a_kind: Dict[str, Union[str, List[str]]] = {"title": "Kind", "type": "string"}
|
||||||
|
b_kind: Dict[str, Union[str, List[str]]] = {"title": "Kind", "type": "string"}
|
||||||
|
|
||||||
|
if PYDANTIC_V2:
|
||||||
|
_key_map, json_schema = models_json_schema([(RequestModel, "validation")])
|
||||||
|
# In Pydantic v2, string literal types are mapped to consts.
|
||||||
|
a_kind["const"] = "a"
|
||||||
|
b_kind["const"] = "b"
|
||||||
|
if PYDANTIC_MINOR_VERSION < 10:
|
||||||
|
# Prior to 2.10, string literal types are also mapped to enums with a single entry.
|
||||||
|
a_kind["enum"] = ["a"]
|
||||||
|
b_kind["enum"] = ["b"]
|
||||||
|
else:
|
||||||
|
json_schema = v1_schema([RequestModel])
|
||||||
|
# In Pydantic v1, string literal types are mapped to enums with a single entry.
|
||||||
|
a_kind["enum"] = ["a"]
|
||||||
|
b_kind["enum"] = ["b"]
|
||||||
|
|
||||||
|
assert json_schema == {
|
||||||
|
DEFS_KEY: {
|
||||||
|
"DataAModel": {
|
||||||
|
"properties": {
|
||||||
|
"kind": a_kind,
|
||||||
|
},
|
||||||
|
"required": ["kind"],
|
||||||
|
"title": "DataAModel",
|
||||||
|
"type": "object",
|
||||||
|
},
|
||||||
|
"DataBModel": {
|
||||||
|
"properties": {
|
||||||
|
"kind": b_kind,
|
||||||
|
},
|
||||||
|
"required": ["kind"],
|
||||||
|
"title": "DataBModel",
|
||||||
|
"type": "object",
|
||||||
|
},
|
||||||
|
"RequestModel": {
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"oneOf": [
|
||||||
|
{"$ref": f"#/{DEFS_KEY}/DataAModel"},
|
||||||
|
{"$ref": f"#/{DEFS_KEY}/DataBModel"},
|
||||||
|
],
|
||||||
|
"discriminator": {
|
||||||
|
"mapping": {
|
||||||
|
"a": f"#/{DEFS_KEY}/DataAModel",
|
||||||
|
"b": f"#/{DEFS_KEY}/DataBModel",
|
||||||
|
},
|
||||||
|
"propertyName": "kind",
|
||||||
|
},
|
||||||
|
"title": "Data",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["data"],
|
||||||
|
"title": "RequestModel",
|
||||||
|
"type": "object",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_pydantic_discriminator_openapi_generation() -> None:
|
||||||
|
"""https://github.com/kuimono/openapi-schema-pydantic/issues/8"""
|
||||||
|
|
||||||
|
open_api = construct_open_api_with_schema_class(construct_base_open_api())
|
||||||
|
assert open_api.components is not None
|
||||||
|
assert open_api.components.schemas is not None
|
||||||
|
json_schema = open_api.components.schemas["RequestModel"]
|
||||||
|
assert json_schema.properties == {
|
||||||
|
"data": Schema(
|
||||||
|
oneOf=[
|
||||||
|
Reference(
|
||||||
|
**{
|
||||||
|
"$ref": "#/components/schemas/DataAModel",
|
||||||
|
"summary": None,
|
||||||
|
"description": None,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
Reference(
|
||||||
|
**{
|
||||||
|
"$ref": "#/components/schemas/DataBModel",
|
||||||
|
"summary": None,
|
||||||
|
"description": None,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
],
|
||||||
|
title="Data",
|
||||||
|
discriminator=Discriminator(
|
||||||
|
propertyName="kind",
|
||||||
|
mapping={
|
||||||
|
"a": "#/components/schemas/DataAModel",
|
||||||
|
"b": "#/components/schemas/DataBModel",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
271
tests/util/test_util.py
Normal file
271
tests/util/test_util.py
Normal file
|
@ -0,0 +1,271 @@
|
||||||
|
import logging
|
||||||
|
from typing import Callable, Generic, TypeVar
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from openapi_pydantic import (
|
||||||
|
Info,
|
||||||
|
MediaType,
|
||||||
|
OpenAPI,
|
||||||
|
Operation,
|
||||||
|
PathItem,
|
||||||
|
Reference,
|
||||||
|
RequestBody,
|
||||||
|
Response,
|
||||||
|
)
|
||||||
|
from openapi_pydantic.compat import PYDANTIC_V2
|
||||||
|
from openapi_pydantic.util import PydanticSchema, construct_open_api_with_schema_class
|
||||||
|
|
||||||
|
|
||||||
|
def test_construct_open_api_with_schema_class_1() -> None:
|
||||||
|
open_api = construct_base_open_api_1()
|
||||||
|
result_open_api_1 = construct_open_api_with_schema_class(open_api)
|
||||||
|
result_open_api_2 = construct_open_api_with_schema_class(
|
||||||
|
open_api, [PingRequest, PingResponse]
|
||||||
|
)
|
||||||
|
assert result_open_api_1.components == result_open_api_2.components
|
||||||
|
assert result_open_api_1 == result_open_api_2
|
||||||
|
|
||||||
|
dump_json = getattr(result_open_api_1, "model_dump_json" if PYDANTIC_V2 else "json")
|
||||||
|
open_api_json = dump_json(by_alias=True, exclude_none=True, indent=2)
|
||||||
|
logging.debug(open_api_json)
|
||||||
|
|
||||||
|
|
||||||
|
def test_construct_open_api_with_schema_class_2() -> None:
|
||||||
|
open_api_1 = construct_base_open_api_1()
|
||||||
|
open_api_2 = construct_base_open_api_2()
|
||||||
|
result_open_api_1 = construct_open_api_with_schema_class(open_api_1)
|
||||||
|
result_open_api_2 = construct_open_api_with_schema_class(
|
||||||
|
open_api_2, [PingRequest, PingResponse]
|
||||||
|
)
|
||||||
|
assert result_open_api_1 == result_open_api_2
|
||||||
|
|
||||||
|
|
||||||
|
def test_construct_open_api_with_schema_class_3() -> None:
|
||||||
|
open_api_3 = construct_base_open_api_3()
|
||||||
|
|
||||||
|
result_with_alias_1 = construct_open_api_with_schema_class(open_api_3)
|
||||||
|
assert result_with_alias_1.components is not None
|
||||||
|
assert result_with_alias_1.components.schemas is not None
|
||||||
|
schema_with_alias = result_with_alias_1.components.schemas["PongResponse"]
|
||||||
|
assert schema_with_alias.properties is not None
|
||||||
|
assert "pong_foo" in schema_with_alias.properties
|
||||||
|
assert "pong_bar" in schema_with_alias.properties
|
||||||
|
|
||||||
|
result_with_alias_2 = construct_open_api_with_schema_class(
|
||||||
|
open_api_3, by_alias=True
|
||||||
|
)
|
||||||
|
assert result_with_alias_1 == result_with_alias_2
|
||||||
|
|
||||||
|
result_without_alias = construct_open_api_with_schema_class(
|
||||||
|
open_api_3, by_alias=False
|
||||||
|
)
|
||||||
|
assert result_without_alias.components is not None
|
||||||
|
assert result_without_alias.components.schemas is not None
|
||||||
|
schema_without_alias = result_without_alias.components.schemas["PongResponse"]
|
||||||
|
assert schema_without_alias.properties is not None
|
||||||
|
assert "resp_foo" in schema_without_alias.properties
|
||||||
|
assert "resp_bar" in schema_without_alias.properties
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(PYDANTIC_V2, reason="generic type for Pydantic V1")
|
||||||
|
def test_construct_open_api_with_schema_class_4_generic_response_v1() -> None:
|
||||||
|
DataT = TypeVar("DataT")
|
||||||
|
from pydantic.v1.generics import GenericModel
|
||||||
|
|
||||||
|
class GenericResponse(GenericModel, Generic[DataT]):
|
||||||
|
msg: str = Field(description="message of the generic response")
|
||||||
|
data: DataT = Field(description="data value of the generic response")
|
||||||
|
|
||||||
|
open_api_4 = construct_base_open_api_4_generic_response(
|
||||||
|
GenericResponse[PongResponse]
|
||||||
|
)
|
||||||
|
|
||||||
|
result = construct_open_api_with_schema_class(open_api_4)
|
||||||
|
assert result.components is not None
|
||||||
|
assert result.components.schemas is not None
|
||||||
|
assert "GenericResponse_PongResponse_" in result.components.schemas
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not PYDANTIC_V2, reason="generic type for Pydantic V2")
|
||||||
|
def test_construct_open_api_with_schema_class_4_generic_response_v2() -> None:
|
||||||
|
DataT = TypeVar("DataT")
|
||||||
|
|
||||||
|
class GenericResponse(BaseModel, Generic[DataT]):
|
||||||
|
msg: str = Field(description="message of the generic response")
|
||||||
|
data: DataT = Field(description="data value of the generic response")
|
||||||
|
|
||||||
|
open_api_4 = construct_base_open_api_4_generic_response(
|
||||||
|
GenericResponse[PongResponse]
|
||||||
|
)
|
||||||
|
|
||||||
|
result = construct_open_api_with_schema_class(open_api_4)
|
||||||
|
assert result.components is not None
|
||||||
|
assert result.components.schemas is not None
|
||||||
|
assert "GenericResponse_PongResponse_" in result.components.schemas
|
||||||
|
|
||||||
|
|
||||||
|
def construct_base_open_api_1() -> OpenAPI:
|
||||||
|
model_validate: Callable[[dict], OpenAPI] = getattr(
|
||||||
|
OpenAPI, "model_validate" if PYDANTIC_V2 else "parse_obj"
|
||||||
|
)
|
||||||
|
return model_validate(
|
||||||
|
{
|
||||||
|
"info": {"title": "My own API", "version": "v0.0.1"},
|
||||||
|
"paths": {
|
||||||
|
"/ping": {
|
||||||
|
"post": {
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": PydanticSchema(schema_class=PingRequest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "pong",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": PydanticSchema(
|
||||||
|
schema_class=PingResponse
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def construct_base_open_api_2() -> OpenAPI:
|
||||||
|
return OpenAPI(
|
||||||
|
info=Info(
|
||||||
|
title="My own API",
|
||||||
|
version="v0.0.1",
|
||||||
|
),
|
||||||
|
paths={
|
||||||
|
"/ping": PathItem(
|
||||||
|
post=Operation(
|
||||||
|
requestBody=RequestBody(
|
||||||
|
content={
|
||||||
|
"application/json": MediaType(
|
||||||
|
media_type_schema=Reference(
|
||||||
|
**{"$ref": "#/components/schemas/PingRequest"}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
responses={
|
||||||
|
"200": Response(
|
||||||
|
description="pong",
|
||||||
|
content={
|
||||||
|
"application/json": MediaType(
|
||||||
|
media_type_schema=Reference(
|
||||||
|
**{"$ref": "#/components/schemas/PingResponse"}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def construct_base_open_api_3() -> OpenAPI:
|
||||||
|
return OpenAPI(
|
||||||
|
info=Info(
|
||||||
|
title="My own API",
|
||||||
|
version="v0.0.1",
|
||||||
|
),
|
||||||
|
paths={
|
||||||
|
"/ping": PathItem(
|
||||||
|
post=Operation(
|
||||||
|
requestBody=RequestBody(
|
||||||
|
content={
|
||||||
|
"application/json": MediaType(
|
||||||
|
media_type_schema=PydanticSchema(
|
||||||
|
schema_class=PingRequest
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
responses={
|
||||||
|
"200": Response(
|
||||||
|
description="pong",
|
||||||
|
content={
|
||||||
|
"application/json": MediaType(
|
||||||
|
media_type_schema=PydanticSchema(
|
||||||
|
schema_class=PongResponse
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def construct_base_open_api_4_generic_response(response_schema: type) -> OpenAPI:
|
||||||
|
return OpenAPI(
|
||||||
|
info=Info(
|
||||||
|
title="My own API",
|
||||||
|
version="v0.0.1",
|
||||||
|
),
|
||||||
|
paths={
|
||||||
|
"/ping": PathItem(
|
||||||
|
post=Operation(
|
||||||
|
requestBody=RequestBody(
|
||||||
|
content={
|
||||||
|
"application/json": MediaType(
|
||||||
|
media_type_schema=PydanticSchema(
|
||||||
|
schema_class=PingRequest
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
responses={
|
||||||
|
"200": Response(
|
||||||
|
description="pong",
|
||||||
|
content={
|
||||||
|
"application/json": MediaType(
|
||||||
|
media_type_schema=PydanticSchema(
|
||||||
|
schema_class=response_schema
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PingRequest(BaseModel):
|
||||||
|
"""Ping Request"""
|
||||||
|
|
||||||
|
req_foo: str = Field(description="foo value of the request")
|
||||||
|
req_bar: str = Field(description="bar value of the request")
|
||||||
|
|
||||||
|
|
||||||
|
class PingResponse(BaseModel):
|
||||||
|
"""Ping response"""
|
||||||
|
|
||||||
|
resp_foo: str = Field(description="foo value of the response")
|
||||||
|
resp_bar: str = Field(description="bar value of the response")
|
||||||
|
|
||||||
|
|
||||||
|
class PongResponse(BaseModel):
|
||||||
|
"""Pong response"""
|
||||||
|
|
||||||
|
resp_foo: str = Field(alias="pong_foo", description="foo value of the response")
|
||||||
|
resp_bar: str = Field(alias="pong_bar", description="bar value of the response")
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue