Compare commits
5 Commits
0.0.5
...
6e3939d68c
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e3939d68c | |||
| 658a98cf19 | |||
| 0efe68b052 | |||
| c95de5a6e9 | |||
| b3f3068f8d |
22
.drone.yml
22
.drone.yml
@@ -32,6 +32,26 @@ steps:
|
|||||||
echo "🆕 New version detected: $PACKAGE_NAME==$VERSION"
|
echo "🆕 New version detected: $PACKAGE_NAME==$VERSION"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: quality-gate
|
||||||
|
image: python:3.13-slim
|
||||||
|
environment:
|
||||||
|
PIP_REPO_URL:
|
||||||
|
from_secret: PIP_REPO_URL
|
||||||
|
PIP_USERNAME:
|
||||||
|
from_secret: PIP_USERNAME
|
||||||
|
PIP_PASSWORD:
|
||||||
|
from_secret: PIP_PASSWORD
|
||||||
|
commands:
|
||||||
|
- pip install --upgrade pip build
|
||||||
|
- |
|
||||||
|
AUTH_URL="https://${PIP_USERNAME}:${PIP_PASSWORD}@$(echo "${PIP_REPO_URL#*://}" | sed 's:/*$::')/simple"
|
||||||
|
pip install --index-url "$AUTH_URL" --extra-index-url https://pypi.org/simple/ -U ".[dev]"
|
||||||
|
- echo "🛡️ Running quality gate..."
|
||||||
|
- python -m black --check .
|
||||||
|
- python -m ruff check .
|
||||||
|
- python -m mypy
|
||||||
|
- python -m pytest
|
||||||
|
|
||||||
- name: build-package
|
- name: build-package
|
||||||
image: python:3.13-slim
|
image: python:3.13-slim
|
||||||
commands:
|
commands:
|
||||||
@@ -126,4 +146,4 @@ steps:
|
|||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
event:
|
event:
|
||||||
- custom
|
- custom
|
||||||
21
CHANGELOG.md
Normal file
21
CHANGELOG.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- `py.typed` marker for PEP 561 type information.
|
||||||
|
- `.drone.yml` CI with a quality-gate step (black, ruff, mypy, pytest).
|
||||||
|
- Canonical `docforge.nav.yml`, generated `mkdocs.yml` and `mcp_docs/` via doc-forge.
|
||||||
|
- MIT `LICENSE`.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Standardized `pyproject.toml` (canonical packaging, lint tool config, extras).
|
||||||
|
- `templates/` excluded from black/ruff scans.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Stub fixes for typed API surfaces.
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Aetoskia Platform
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
home: index.md
|
home: lib/index.md
|
||||||
groups:
|
groups:
|
||||||
Application Bootstrap:
|
Application Bootstrap:
|
||||||
- app.md
|
- lib/app.md
|
||||||
- binder.md
|
- lib/binder.md
|
||||||
Core Utilities:
|
Core Utilities:
|
||||||
- loader.md
|
- lib/loader.md
|
||||||
- errors.md
|
- lib/errors.md
|
||||||
OpenAPI Client:
|
OpenAPI Client:
|
||||||
- client.md
|
- lib/client.md
|
||||||
|
icon:
|
||||||
|
logo: material/api
|
||||||
|
repo: fontawesome/brands/github
|
||||||
3
docs/lib/codegen.md
Normal file
3
docs/lib/codegen.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Codegen
|
||||||
|
|
||||||
|
::: openapi_first.codegen
|
||||||
3
docs/lib/codegen_routes.md
Normal file
3
docs/lib/codegen_routes.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Codegen Routes
|
||||||
|
|
||||||
|
::: openapi_first.codegen_routes
|
||||||
3
docs/lib/security.md
Normal file
3
docs/lib/security.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Security
|
||||||
|
|
||||||
|
::: openapi_first.security
|
||||||
3
docs/lib/templates/vet_app/data.md
Normal file
3
docs/lib/templates/vet_app/data.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Data
|
||||||
|
|
||||||
|
::: openapi_first.templates.vet_app.data
|
||||||
3
docs/lib/templates/vet_app/index.md
Normal file
3
docs/lib/templates/vet_app/index.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Vet App
|
||||||
|
|
||||||
|
::: openapi_first.templates.vet_app
|
||||||
3
docs/lib/templates/vet_app/main.md
Normal file
3
docs/lib/templates/vet_app/main.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Main
|
||||||
|
|
||||||
|
::: openapi_first.templates.vet_app.main
|
||||||
3
docs/lib/templates/vet_app/models.md
Normal file
3
docs/lib/templates/vet_app/models.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Models
|
||||||
|
|
||||||
|
::: openapi_first.templates.vet_app.models
|
||||||
3
docs/lib/templates/vet_app/routes.md
Normal file
3
docs/lib/templates/vet_app/routes.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Routes
|
||||||
|
|
||||||
|
::: openapi_first.templates.vet_app.routes
|
||||||
3
docs/lib/templates/vet_app/sse.md
Normal file
3
docs/lib/templates/vet_app/sse.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Sse
|
||||||
|
|
||||||
|
::: openapi_first.templates.vet_app.sse
|
||||||
3
docs/lib/templates/vet_app/test_vet_app.md
Normal file
3
docs/lib/templates/vet_app/test_vet_app.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Test Vet App
|
||||||
|
|
||||||
|
::: openapi_first.templates.vet_app.test_vet_app
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"project": "openapi_first",
|
"project": "openapi_first",
|
||||||
"type": "docforge-model",
|
"type": "docforge-model",
|
||||||
"modules_count": 22,
|
"modules_count": 32,
|
||||||
"source": "docforge"
|
"source": "docforge"
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,20 @@
|
|||||||
"path": "openapi_first.app",
|
"path": "openapi_first.app",
|
||||||
"docstring": "# Summary\n\nOpenAPI-first application bootstrap for FastAPI.\n\nThis module provides `OpenAPIFirstApp`, a thin but strict abstraction\nthat enforces OpenAPI as the single source of truth for a FastAPI service.\n\nNotes:\n **Core Principles:**\n\n - The OpenAPI specification (JSON or YAML) defines the entire API surface.\n - Every `operationId` in the OpenAPI spec must have a corresponding\n Python handler function.\n - Handlers are plain Python callables (no FastAPI decorators).\n - FastAPI route registration is derived exclusively from the spec.\n - FastAPI's autogenerated OpenAPI schema is fully overridden.\n\n **Responsibilities:**\n\n - Loads and validates an OpenAPI 3.x specification.\n - Dynamically binds HTTP routes to handler functions using `operationId`.\n - Registers routes with FastAPI at application startup.\n - Ensures runtime behavior matches the OpenAPI contract exactly.\n\n **Constraints:**\n\n - This module intentionally does NOT:\n - Generate OpenAPI specs.\n - Generate client code.\n - Introduce a new framework or lifecycle.\n - Alter FastAPI dependency injection semantics.",
|
"docstring": "# Summary\n\nOpenAPI-first application bootstrap for FastAPI.\n\nThis module provides `OpenAPIFirstApp`, a thin but strict abstraction\nthat enforces OpenAPI as the single source of truth for a FastAPI service.\n\nNotes:\n **Core Principles:**\n\n - The OpenAPI specification (JSON or YAML) defines the entire API surface.\n - Every `operationId` in the OpenAPI spec must have a corresponding\n Python handler function.\n - Handlers are plain Python callables (no FastAPI decorators).\n - FastAPI route registration is derived exclusively from the spec.\n - FastAPI's autogenerated OpenAPI schema is fully overridden.\n\n **Responsibilities:**\n\n - Loads and validates an OpenAPI 3.x specification.\n - Dynamically binds HTTP routes to handler functions using `operationId`.\n - Registers routes with FastAPI at application startup.\n - Ensures runtime behavior matches the OpenAPI contract exactly.\n\n **Constraints:**\n\n - This module intentionally does NOT:\n - Generate OpenAPI specs.\n - Generate client code.\n - Introduce a new framework or lifecycle.\n - Alter FastAPI dependency injection semantics.",
|
||||||
"objects": {
|
"objects": {
|
||||||
|
"os": {
|
||||||
|
"name": "os",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.app.os",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('os', 'os')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"re": {
|
||||||
|
"name": "re",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.app.re",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('re', 're')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
"FastAPI": {
|
"FastAPI": {
|
||||||
"name": "FastAPI",
|
"name": "FastAPI",
|
||||||
"kind": "alias",
|
"kind": "alias",
|
||||||
@@ -11,6 +25,13 @@
|
|||||||
"signature": "<bound method Alias.signature of Alias('FastAPI', 'fastapi.FastAPI')>",
|
"signature": "<bound method Alias.signature of Alias('FastAPI', 'fastapi.FastAPI')>",
|
||||||
"docstring": null
|
"docstring": null
|
||||||
},
|
},
|
||||||
|
"bind_routes": {
|
||||||
|
"name": "bind_routes",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.app.bind_routes",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('bind_routes', 'openapi_first.binder.bind_routes')>",
|
||||||
|
"docstring": "Bind OpenAPI operations to FastAPI routes.\n\nArgs:\n app (fastapi.FastAPI):\n The FastAPI application instance to which routes will be added.\n spec (dict):\n Parsed OpenAPI 3.x specification dictionary.\n routes_module (module):\n Python module containing handler functions. Each handler's name MUST\n exactly match an OpenAPI `operationId`.\n security_deps (dict | None):\n Optional mapping of ``METHOD:/path`` → ``list[Depends(...)]``\n generated from the spec's ``securitySchemes`` and per-operation\n ``security`` fields.\n\nRaises:\n MissingOperationHandler:\n If an ``operationId`` is missing from the spec or if no corresponding\n handler function exists in the routes module.\n\nNotes:\n **Responsibilities:**\n\n - Iterates through the OpenAPI specification paths and methods.\n - Resolves each ``operationId`` to a handler function, and registers\n a corresponding ``APIRoute`` on the FastAPI application.\n - Injects FastAPI ``Depends()`` for each security requirement found\n on the operation or inherited from the top-level ``security`` field.\n\n **Guarantees:**\n\n - Route registration is deterministic and spec-driven. No route\n decorators are required or supported. Handler resolution errors\n surface at application startup."
|
||||||
|
},
|
||||||
"load_openapi": {
|
"load_openapi": {
|
||||||
"name": "load_openapi",
|
"name": "load_openapi",
|
||||||
"kind": "function",
|
"kind": "function",
|
||||||
@@ -18,19 +39,26 @@
|
|||||||
"signature": "<bound method Alias.signature of Alias('load_openapi', 'openapi_first.loader.load_openapi')>",
|
"signature": "<bound method Alias.signature of Alias('load_openapi', 'openapi_first.loader.load_openapi')>",
|
||||||
"docstring": "Load and validate an OpenAPI 3.x specification from disk.\n\nArgs:\n path (str | Path):\n Filesystem path to an OpenAPI specification file. Supported\n extensions: `.json`, `.yaml`, `.yml`.\n\nReturns:\n dict[str, Any]:\n Parsed and validated OpenAPI specification.\n\nRaises:\n OpenAPISpecLoadError:\n If the file does not exist, cannot be parsed, or fails OpenAPI\n schema validation.\n\nNotes:\n **Guarantees:**\n\n - The specification is parsed based on file extension and validated\n using a strict OpenAPI schema validator.\n - Any error results in an immediate exception, preventing\n application startup."
|
"docstring": "Load and validate an OpenAPI 3.x specification from disk.\n\nArgs:\n path (str | Path):\n Filesystem path to an OpenAPI specification file. Supported\n extensions: `.json`, `.yaml`, `.yml`.\n\nReturns:\n dict[str, Any]:\n Parsed and validated OpenAPI specification.\n\nRaises:\n OpenAPISpecLoadError:\n If the file does not exist, cannot be parsed, or fails OpenAPI\n schema validation.\n\nNotes:\n **Guarantees:**\n\n - The specification is parsed based on file extension and validated\n using a strict OpenAPI schema validator.\n - Any error results in an immediate exception, preventing\n application startup."
|
||||||
},
|
},
|
||||||
"bind_routes": {
|
"make_security_dependencies": {
|
||||||
"name": "bind_routes",
|
"name": "make_security_dependencies",
|
||||||
"kind": "function",
|
"kind": "function",
|
||||||
"path": "openapi_first.app.bind_routes",
|
"path": "openapi_first.app.make_security_dependencies",
|
||||||
"signature": "<bound method Alias.signature of Alias('bind_routes', 'openapi_first.binder.bind_routes')>",
|
"signature": "<bound method Alias.signature of Alias('make_security_dependencies', 'openapi_first.security.make_security_dependencies')>",
|
||||||
"docstring": "Bind OpenAPI operations to FastAPI routes.\n\nArgs:\n app (fastapi.FastAPI):\n The FastAPI application instance to which routes will be added.\n spec (dict):\n Parsed OpenAPI 3.x specification dictionary.\n routes_module (module):\n Python module containing handler functions. Each handler's name MUST\n exactly match an OpenAPI `operationId`.\n\nRaises:\n MissingOperationHandler:\n If an `operationId` is missing from the spec or if no corresponding\n handler function exists in the routes module.\n\nNotes:\n **Responsibilities:**\n\n - Iterates through the OpenAPI specification paths and methods.\n - Resolves each `operationId` to a handler function, and registers\n a corresponding `APIRoute` on the FastAPI application.\n\n **Guarantees:**\n\n - Route registration is deterministic and spec-driven. No route\n decorators are required or supported. Handler resolution errors\n surface at application startup."
|
"docstring": "Build a mapping of ``METHOD:/path`` → list of ``Depends(...)``.\n\nThe effective security for each operation is resolved by:\n\n1. Using the operation-level ``security`` field if present.\n2. Falling back to the top-level ``security`` field.\n3. An empty list means *no auth required* for that operation."
|
||||||
|
},
|
||||||
|
"parse_security_schemes": {
|
||||||
|
"name": "parse_security_schemes",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.app.parse_security_schemes",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('parse_security_schemes', 'openapi_first.security.parse_security_schemes')>",
|
||||||
|
"docstring": "Extract and resolve environment variables in security schemes."
|
||||||
},
|
},
|
||||||
"OpenAPIFirstApp": {
|
"OpenAPIFirstApp": {
|
||||||
"name": "OpenAPIFirstApp",
|
"name": "OpenAPIFirstApp",
|
||||||
"kind": "class",
|
"kind": "class",
|
||||||
"path": "openapi_first.app.OpenAPIFirstApp",
|
"path": "openapi_first.app.OpenAPIFirstApp",
|
||||||
"signature": "<bound method Class.signature of Class('OpenAPIFirstApp', 41, 115)>",
|
"signature": "<bound method Class.signature of Class('OpenAPIFirstApp', 67, 150)>",
|
||||||
"docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each `operationId` is mapped to a Python function in the\n supplied routes module.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and `/openapi.json` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```",
|
"docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each ``operationId`` is mapped to a Python function in the\n supplied routes module.\n - Auth dependencies are auto-injected from the spec's\n ``securitySchemes`` and per-operation ``security`` fields.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and ``/openapi.json`` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n - Auth enforcement is driven entirely by the spec — no manual\n middleware or decorators required.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```",
|
||||||
"members": {
|
"members": {
|
||||||
"openapi": {
|
"openapi": {
|
||||||
"name": "openapi",
|
"name": "openapi",
|
||||||
@@ -4,6 +4,13 @@
|
|||||||
"path": "openapi_first.binder",
|
"path": "openapi_first.binder",
|
||||||
"docstring": "# Summary\n\nOpenAPI-driven route binding for FastAPI.\n\nThis module is responsible for translating an OpenAPI 3.x specification\ninto concrete FastAPI routes. It enforces a strict one-to-one mapping\nbetween OpenAPI operations and Python handler functions using `operationId`.\n\nNotes:\n **Core Responsibility:**\n\n - Read path + method definitions from an OpenAPI specification.\n - Resolve each `operationId` to a Python callable.\n - Register routes with FastAPI using `APIRoute`.\n - Fail fast when contract violations are detected.\n\n **Design Constraints:**\n\n - All routes MUST be declared in the OpenAPI specification.\n - All OpenAPI operations MUST define an `operationId`.\n - Every `operationId` MUST resolve to a handler function.\n - Handlers are plain Python callables (no decorators required).\n - No implicit route creation or inference is allowed.\n\n **Constraints:**\n\n - This module intentionally does NOT:\n - Perform request or response validation.\n - Generate Pydantic models.\n - Modify FastAPI dependency injection.\n - Interpret OpenAPI semantics beyond routing metadata.",
|
"docstring": "# Summary\n\nOpenAPI-driven route binding for FastAPI.\n\nThis module is responsible for translating an OpenAPI 3.x specification\ninto concrete FastAPI routes. It enforces a strict one-to-one mapping\nbetween OpenAPI operations and Python handler functions using `operationId`.\n\nNotes:\n **Core Responsibility:**\n\n - Read path + method definitions from an OpenAPI specification.\n - Resolve each `operationId` to a Python callable.\n - Register routes with FastAPI using `APIRoute`.\n - Fail fast when contract violations are detected.\n\n **Design Constraints:**\n\n - All routes MUST be declared in the OpenAPI specification.\n - All OpenAPI operations MUST define an `operationId`.\n - Every `operationId` MUST resolve to a handler function.\n - Handlers are plain Python callables (no decorators required).\n - No implicit route creation or inference is allowed.\n\n **Constraints:**\n\n - This module intentionally does NOT:\n - Perform request or response validation.\n - Generate Pydantic models.\n - Modify FastAPI dependency injection.\n - Interpret OpenAPI semantics beyond routing metadata.",
|
||||||
"objects": {
|
"objects": {
|
||||||
|
"Any": {
|
||||||
|
"name": "Any",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.binder.Any",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Any', 'typing.Any')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
"APIRoute": {
|
"APIRoute": {
|
||||||
"name": "APIRoute",
|
"name": "APIRoute",
|
||||||
"kind": "alias",
|
"kind": "alias",
|
||||||
@@ -22,22 +29,8 @@
|
|||||||
"name": "bind_routes",
|
"name": "bind_routes",
|
||||||
"kind": "function",
|
"kind": "function",
|
||||||
"path": "openapi_first.binder.bind_routes",
|
"path": "openapi_first.binder.bind_routes",
|
||||||
"signature": "<bound method Function.signature of Function('bind_routes', 40, 100)>",
|
"signature": "<bound method Function.signature of Function('bind_routes', 42, 124)>",
|
||||||
"docstring": "Bind OpenAPI operations to FastAPI routes.\n\nArgs:\n app (fastapi.FastAPI):\n The FastAPI application instance to which routes will be added.\n spec (dict):\n Parsed OpenAPI 3.x specification dictionary.\n routes_module (module):\n Python module containing handler functions. Each handler's name MUST\n exactly match an OpenAPI `operationId`.\n\nRaises:\n MissingOperationHandler:\n If an `operationId` is missing from the spec or if no corresponding\n handler function exists in the routes module.\n\nNotes:\n **Responsibilities:**\n\n - Iterates through the OpenAPI specification paths and methods.\n - Resolves each `operationId` to a handler function, and registers\n a corresponding `APIRoute` on the FastAPI application.\n\n **Guarantees:**\n\n - Route registration is deterministic and spec-driven. No route\n decorators are required or supported. Handler resolution errors\n surface at application startup."
|
"docstring": "Bind OpenAPI operations to FastAPI routes.\n\nArgs:\n app (fastapi.FastAPI):\n The FastAPI application instance to which routes will be added.\n spec (dict):\n Parsed OpenAPI 3.x specification dictionary.\n routes_module (module):\n Python module containing handler functions. Each handler's name MUST\n exactly match an OpenAPI `operationId`.\n security_deps (dict | None):\n Optional mapping of ``METHOD:/path`` → ``list[Depends(...)]``\n generated from the spec's ``securitySchemes`` and per-operation\n ``security`` fields.\n\nRaises:\n MissingOperationHandler:\n If an ``operationId`` is missing from the spec or if no corresponding\n handler function exists in the routes module.\n\nNotes:\n **Responsibilities:**\n\n - Iterates through the OpenAPI specification paths and methods.\n - Resolves each ``operationId`` to a handler function, and registers\n a corresponding ``APIRoute`` on the FastAPI application.\n - Injects FastAPI ``Depends()`` for each security requirement found\n on the operation or inherited from the top-level ``security`` field.\n\n **Guarantees:**\n\n - Route registration is deterministic and spec-driven. No route\n decorators are required or supported. Handler resolution errors\n surface at application startup."
|
||||||
},
|
|
||||||
"Any": {
|
|
||||||
"name": "Any",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.binder.Any",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('Any', 'typing.Any')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"Dict": {
|
|
||||||
"name": "Dict",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.binder.Dict",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('Dict', 'typing.Dict')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
},
|
||||||
"FastAPI": {
|
"FastAPI": {
|
||||||
"name": "FastAPI",
|
"name": "FastAPI",
|
||||||
@@ -18,13 +18,6 @@
|
|||||||
"signature": "<bound method Alias.signature of Alias('shutil', 'shutil')>",
|
"signature": "<bound method Alias.signature of Alias('shutil', 'shutil')>",
|
||||||
"docstring": null
|
"docstring": null
|
||||||
},
|
},
|
||||||
"Path": {
|
|
||||||
"name": "Path",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.cli.Path",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('Path', 'pathlib.Path')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"resources": {
|
"resources": {
|
||||||
"name": "resources",
|
"name": "resources",
|
||||||
"kind": "alias",
|
"kind": "alias",
|
||||||
@@ -32,6 +25,13 @@
|
|||||||
"signature": "<bound method Alias.signature of Alias('resources', 'importlib.resources')>",
|
"signature": "<bound method Alias.signature of Alias('resources', 'importlib.resources')>",
|
||||||
"docstring": null
|
"docstring": null
|
||||||
},
|
},
|
||||||
|
"Path": {
|
||||||
|
"name": "Path",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.cli.Path",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Path', 'pathlib.Path')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
"DEFAULT_TEMPLATE": {
|
"DEFAULT_TEMPLATE": {
|
||||||
"name": "DEFAULT_TEMPLATE",
|
"name": "DEFAULT_TEMPLATE",
|
||||||
"kind": "attribute",
|
"kind": "attribute",
|
||||||
@@ -43,21 +43,21 @@
|
|||||||
"name": "available_templates",
|
"name": "available_templates",
|
||||||
"kind": "function",
|
"kind": "function",
|
||||||
"path": "openapi_first.cli.available_templates",
|
"path": "openapi_first.cli.available_templates",
|
||||||
"signature": "<bound method Function.signature of Function('available_templates', 21, 34)>",
|
"signature": "<bound method Function.signature of Function('available_templates', 20, 33)>",
|
||||||
"docstring": "Return a list of available application templates.\n\nReturns:\n list[str]:\n Sorted list of template names found in the internal templates directory."
|
"docstring": "Return a list of available application templates.\n\nReturns:\n list[str]:\n Sorted list of template names found in the internal templates directory."
|
||||||
},
|
},
|
||||||
"copy_template": {
|
"copy_template": {
|
||||||
"name": "copy_template",
|
"name": "copy_template",
|
||||||
"kind": "function",
|
"kind": "function",
|
||||||
"path": "openapi_first.cli.copy_template",
|
"path": "openapi_first.cli.copy_template",
|
||||||
"signature": "<bound method Function.signature of Function('copy_template', 37, 64)>",
|
"signature": "<bound method Function.signature of Function('copy_template', 36, 64)>",
|
||||||
"docstring": "Copy a bundled OpenAPI-first application template into a directory.\n\nArgs:\n template (str):\n Name of the template to copy.\n target_dir (Path):\n Filesystem path where the template should be copied.\n\nRaises:\n FileNotFoundError:\n If the requested template does not exist."
|
"docstring": "Copy a bundled OpenAPI-first application template into a directory.\n\nArgs:\n template (str):\n Name of the template to copy.\n target_dir (Path):\n Filesystem path where the template should be copied.\n\nRaises:\n FileNotFoundError:\n If the requested template does not exist."
|
||||||
},
|
},
|
||||||
"main": {
|
"main": {
|
||||||
"name": "main",
|
"name": "main",
|
||||||
"kind": "function",
|
"kind": "function",
|
||||||
"path": "openapi_first.cli.main",
|
"path": "openapi_first.cli.main",
|
||||||
"signature": "<bound method Function.signature of Function('main', 67, 103)>",
|
"signature": "<bound method Function.signature of Function('main', 67, 196)>",
|
||||||
"docstring": null
|
"docstring": null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,13 @@
|
|||||||
"path": "openapi_first.client",
|
"path": "openapi_first.client",
|
||||||
"docstring": "# Summary\n\nOpenAPI-first HTTP client for contract-driven services.\n\nThis module provides `OpenAPIClient`, a thin, strict HTTP client that\nderives all callable operations directly from an OpenAPI 3.x specification.\n\nIt is the client counterpart to `OpenAPIFirstApp`.\n\nNotes:\n **Core Principles:**\n\n - The OpenAPI specification is the single source of truth\n - Each operationId becomes a callable Python method\n - No implicit schema mutation or inference\n - No code generation step\n - Minimal abstraction over httpx\n\n **Responsibilities:**\n\n - Parses an OpenAPI 3.x specification\n - Dynamically creates one callable per operationId\n - Enforces presence of servers, paths, and operationId\n - Formats path parameters safely\n - Handles JSON request bodies explicitly\n - Returns raw `httpx.Response` objects\n\n **Constraints:**\n\n - This module intentionally does NOT: Generate client code, validate request/response schemas, deserialize responses, retry requests, implement authentication helpers, or assume non-2xx responses are failures.",
|
"docstring": "# Summary\n\nOpenAPI-first HTTP client for contract-driven services.\n\nThis module provides `OpenAPIClient`, a thin, strict HTTP client that\nderives all callable operations directly from an OpenAPI 3.x specification.\n\nIt is the client counterpart to `OpenAPIFirstApp`.\n\nNotes:\n **Core Principles:**\n\n - The OpenAPI specification is the single source of truth\n - Each operationId becomes a callable Python method\n - No implicit schema mutation or inference\n - No code generation step\n - Minimal abstraction over httpx\n\n **Responsibilities:**\n\n - Parses an OpenAPI 3.x specification\n - Dynamically creates one callable per operationId\n - Enforces presence of servers, paths, and operationId\n - Formats path parameters safely\n - Handles JSON request bodies explicitly\n - Returns raw `httpx.Response` objects\n\n **Constraints:**\n\n - This module intentionally does NOT: Generate client code, validate request/response schemas, deserialize responses, retry requests, implement authentication helpers, or assume non-2xx responses are failures.",
|
||||||
"objects": {
|
"objects": {
|
||||||
|
"Callable": {
|
||||||
|
"name": "Callable",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.client.Callable",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Callable', 'collections.abc.Callable')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
"Any": {
|
"Any": {
|
||||||
"name": "Any",
|
"name": "Any",
|
||||||
"kind": "alias",
|
"kind": "alias",
|
||||||
@@ -11,27 +18,6 @@
|
|||||||
"signature": "<bound method Alias.signature of Alias('Any', 'typing.Any')>",
|
"signature": "<bound method Alias.signature of Alias('Any', 'typing.Any')>",
|
||||||
"docstring": null
|
"docstring": null
|
||||||
},
|
},
|
||||||
"Callable": {
|
|
||||||
"name": "Callable",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.client.Callable",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('Callable', 'typing.Callable')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"Dict": {
|
|
||||||
"name": "Dict",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.client.Dict",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('Dict', 'typing.Dict')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"Optional": {
|
|
||||||
"name": "Optional",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.client.Optional",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('Optional', 'typing.Optional')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"urljoin": {
|
"urljoin": {
|
||||||
"name": "urljoin",
|
"name": "urljoin",
|
||||||
"kind": "alias",
|
"kind": "alias",
|
||||||
@@ -57,14 +43,14 @@
|
|||||||
"name": "OpenAPIClientError",
|
"name": "OpenAPIClientError",
|
||||||
"kind": "class",
|
"kind": "class",
|
||||||
"path": "openapi_first.client.OpenAPIClientError",
|
"path": "openapi_first.client.OpenAPIClientError",
|
||||||
"signature": "<bound method Class.signature of Class('OpenAPIClientError', 42, 45)>",
|
"signature": "<bound method Class.signature of Class('OpenAPIClientError', 43, 46)>",
|
||||||
"docstring": "Raised when an OpenAPI client operation fails."
|
"docstring": "Raised when an OpenAPI client operation fails."
|
||||||
},
|
},
|
||||||
"OpenAPIClient": {
|
"OpenAPIClient": {
|
||||||
"name": "OpenAPIClient",
|
"name": "OpenAPIClient",
|
||||||
"kind": "class",
|
"kind": "class",
|
||||||
"path": "openapi_first.client.OpenAPIClient",
|
"path": "openapi_first.client.OpenAPIClient",
|
||||||
"signature": "<bound method Class.signature of Class('OpenAPIClient', 48, 257)>",
|
"signature": "<bound method Class.signature of Class('OpenAPIClient', 49, 262)>",
|
||||||
"docstring": "OpenAPI-first HTTP client (`httpx`-based).\n\nNotes:\n **Responsibilities:**\n\n - This client derives all callable methods directly from an\n OpenAPI 3.x specification. Each `operationId` becomes a method\n on the client instance.\n\n **Guarantees:**\n\n - One callable per `operationId`.\n - Explicit parameters (path, query, headers, body).\n - No implicit schema inference or mutation.\n - Returns raw `httpx.Response` objects.\n - No response validation or deserialization.\n\nExample:\n ```python\n from openapi_first import loader, client\n\n spec = loader.load_openapi(\"openapi.yaml\")\n\n api = client.OpenAPIClient(\n spec=spec,\n base_url=\"http://localhost:8000\",\n )\n\n # Call operationId: getUser\n response = api.getUser(\n path_params={\"user_id\": 123}\n )\n\n print(response.status_code)\n print(response.json())\n ```",
|
"docstring": "OpenAPI-first HTTP client (`httpx`-based).\n\nNotes:\n **Responsibilities:**\n\n - This client derives all callable methods directly from an\n OpenAPI 3.x specification. Each `operationId` becomes a method\n on the client instance.\n\n **Guarantees:**\n\n - One callable per `operationId`.\n - Explicit parameters (path, query, headers, body).\n - No implicit schema inference or mutation.\n - Returns raw `httpx.Response` objects.\n - No response validation or deserialization.\n\nExample:\n ```python\n from openapi_first import loader, client\n\n spec = loader.load_openapi(\"openapi.yaml\")\n\n api = client.OpenAPIClient(\n spec=spec,\n base_url=\"http://localhost:8000\",\n )\n\n # Call operationId: getUser\n response = api.getUser(\n path_params={\"user_id\": 123}\n )\n\n print(response.status_code)\n print(response.json())\n ```",
|
||||||
"members": {
|
"members": {
|
||||||
"spec": {
|
"spec": {
|
||||||
@@ -92,7 +78,7 @@
|
|||||||
"name": "operations",
|
"name": "operations",
|
||||||
"kind": "function",
|
"kind": "function",
|
||||||
"path": "openapi_first.client.OpenAPIClient.operations",
|
"path": "openapi_first.client.OpenAPIClient.operations",
|
||||||
"signature": "<bound method Function.signature of Function('operations', 126, 127)>",
|
"signature": "<bound method Function.signature of Function('operations', 127, 128)>",
|
||||||
"docstring": null
|
"docstring": null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
51
docs/mcp/modules/openapi_first.codegen.json
Normal file
51
docs/mcp/modules/openapi_first.codegen.json
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"module": "openapi_first.codegen",
|
||||||
|
"content": {
|
||||||
|
"path": "openapi_first.codegen",
|
||||||
|
"docstring": "# Summary\n\nCore logic for generating Python source code from OpenAPI specifications.\n\nThis module provides reusable utilities for code generation, specifically\ngenerating Pydantic models and route handler stubs from OpenAPI 3.x schema\ndefinitions.",
|
||||||
|
"objects": {
|
||||||
|
"Path": {
|
||||||
|
"name": "Path",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.codegen.Path",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Path', 'pathlib.Path')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"InputFileType": {
|
||||||
|
"name": "InputFileType",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.codegen.InputFileType",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('InputFileType', 'datamodel_code_generator.InputFileType')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"PythonVersion": {
|
||||||
|
"name": "PythonVersion",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.codegen.PythonVersion",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('PythonVersion', 'datamodel_code_generator.PythonVersion')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"name": "generate",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.codegen.generate",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('generate', 'datamodel_code_generator.generate')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"generate_routes": {
|
||||||
|
"name": "generate_routes",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.codegen.generate_routes",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('generate_routes', 'openapi_first.codegen_routes.generate_routes')>",
|
||||||
|
"docstring": "Generate route handler stubs from an OpenAPI specification.\n\nCreates one ``<resource>.py`` file per resource in *output_dir*.\nResources are derived from the first path segment (e.g. ``/pets``\nand ``/pets/{id}`` both group under ``pets``).\n\nArgs:\n spec_path:\n Path to the OpenAPI specification file (YAML or JSON).\n output_dir:\n Directory where the generated route files are written.\n Created automatically if it does not exist.\n use_models:\n If ``True``, import Pydantic models from *models_module*\n for request-body schemas referenced via ``$ref``.\n models_module:\n Dotted Python module path from which to import models\n (e.g. ``\"models\"``, ``\"app.models\"``).\n\nReturns:\n list[Path]:\n Absolute paths of every generated route file.\n\nRaises:\n OpenAPISpecLoadError:\n If the spec cannot be loaded or validated.\n ValueError:\n If any operation is missing ``operationId``."
|
||||||
|
},
|
||||||
|
"generate_models": {
|
||||||
|
"name": "generate_models",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.codegen.generate_models",
|
||||||
|
"signature": "<bound method Function.signature of Function('generate_models', 22, 51)>",
|
||||||
|
"docstring": "Generate Pydantic models from an OpenAPI specification.\n\nArgs:\n spec_path (Path):\n Path to the OpenAPI specification file (YAML or JSON).\n output_path (Path):\n Path where the generated Python code should be written.\n pydantic_version (int, optional):\n The Pydantic version to target (1 or 2). Defaults to 2.\n\nNotes:\n **Reusability:**\n This function is designed to be used by the CLI and can be\n exposed as an MCP tool without modification."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
docs/mcp/modules/openapi_first.codegen_routes.json
Normal file
37
docs/mcp/modules/openapi_first.codegen_routes.json
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"module": "openapi_first.codegen_routes",
|
||||||
|
"content": {
|
||||||
|
"path": "openapi_first.codegen_routes",
|
||||||
|
"docstring": "Route handler code generation from OpenAPI specifications.\n\nThis module generates Python route handler stubs from an OpenAPI 3.x\nspecification. Each resource (derived from the first path segment)\ngets its own file under the output directory. Every OpenAPI operation\nmust define an ``operationId``, which becomes the handler function name.\n\nNotes:\n **Design constraints:**\n\n - ``operationId`` is required on every operation (matching\n ``binder.bind_routes``).\n - Handlers are stubs raising ``NotImplementedError``.\n - Sub-resources (e.g. ``/pets/{id}/photo``) are grouped with their\n parent resource (``pets``).\n - Parameter types and defaults are inferred from the spec.\n - ``response: Response`` is injected for non-200 success codes.",
|
||||||
|
"objects": {
|
||||||
|
"Path": {
|
||||||
|
"name": "Path",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.codegen_routes.Path",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Path', 'pathlib.Path')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"Any": {
|
||||||
|
"name": "Any",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.codegen_routes.Any",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Any', 'typing.Any')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"load_openapi": {
|
||||||
|
"name": "load_openapi",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.codegen_routes.load_openapi",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('load_openapi', 'openapi_first.loader.load_openapi')>",
|
||||||
|
"docstring": "Load and validate an OpenAPI 3.x specification from disk.\n\nArgs:\n path (str | Path):\n Filesystem path to an OpenAPI specification file. Supported\n extensions: `.json`, `.yaml`, `.yml`.\n\nReturns:\n dict[str, Any]:\n Parsed and validated OpenAPI specification.\n\nRaises:\n OpenAPISpecLoadError:\n If the file does not exist, cannot be parsed, or fails OpenAPI\n schema validation.\n\nNotes:\n **Guarantees:**\n\n - The specification is parsed based on file extension and validated\n using a strict OpenAPI schema validator.\n - Any error results in an immediate exception, preventing\n application startup."
|
||||||
|
},
|
||||||
|
"generate_routes": {
|
||||||
|
"name": "generate_routes",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.codegen_routes.generate_routes",
|
||||||
|
"signature": "<bound method Function.signature of Function('generate_routes', 27, 102)>",
|
||||||
|
"docstring": "Generate route handler stubs from an OpenAPI specification.\n\nCreates one ``<resource>.py`` file per resource in *output_dir*.\nResources are derived from the first path segment (e.g. ``/pets``\nand ``/pets/{id}`` both group under ``pets``).\n\nArgs:\n spec_path:\n Path to the OpenAPI specification file (YAML or JSON).\n output_dir:\n Directory where the generated route files are written.\n Created automatically if it does not exist.\n use_models:\n If ``True``, import Pydantic models from *models_module*\n for request-body schemas referenced via ``$ref``.\n models_module:\n Dotted Python module path from which to import models\n (e.g. ``\"models\"``, ``\"app.models\"``).\n\nReturns:\n list[Path]:\n Absolute paths of every generated route file.\n\nRaises:\n OpenAPISpecLoadError:\n If the spec cannot be loaded or validated.\n ValueError:\n If any operation is missing ``operationId``."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,22 +8,15 @@
|
|||||||
"name": "OpenAPIFirstError",
|
"name": "OpenAPIFirstError",
|
||||||
"kind": "class",
|
"kind": "class",
|
||||||
"path": "openapi_first.errors.OpenAPIFirstError",
|
"path": "openapi_first.errors.OpenAPIFirstError",
|
||||||
"signature": "<bound method Class.signature of Class('OpenAPIFirstError', 21, 34)>",
|
"signature": "<bound method Class.signature of Class('OpenAPIFirstError', 22, 36)>",
|
||||||
"docstring": "Base exception for all OpenAPI-first enforcement errors.\n\nNotes:\n **Responsibilities:**\n\n - This exception exists to allow callers, test suites, and CI\n pipelines to catch and distinguish OpenAPI contract violations\n from unrelated runtime errors.\n - All exceptions raised by the OpenAPI-first core should inherit\n from this type."
|
"docstring": "Base exception for all OpenAPI-first enforcement errors.\n\nNotes:\n **Responsibilities:**\n\n - This exception exists to allow callers, test suites, and CI\n pipelines to catch and distinguish OpenAPI contract violations\n from unrelated runtime errors.\n - All exceptions raised by the OpenAPI-first core should inherit\n from this type."
|
||||||
},
|
},
|
||||||
"MissingOperationHandler": {
|
"MissingOperationHandler": {
|
||||||
"name": "MissingOperationHandler",
|
"name": "MissingOperationHandler",
|
||||||
"kind": "class",
|
"kind": "class",
|
||||||
"path": "openapi_first.errors.MissingOperationHandler",
|
"path": "openapi_first.errors.MissingOperationHandler",
|
||||||
"signature": "<bound method Class.signature of Class('MissingOperationHandler', 37, 78)>",
|
"signature": "<bound method Class.signature of Class('MissingOperationHandler', 39, 77)>",
|
||||||
"docstring": "Raised when an OpenAPI operation cannot be resolved to a handler.\n\nNotes:\n **Scenarios:**\n\n - An OpenAPI operation does not define an `operationId`.\n - An `operationId` is defined but no matching function exists in\n the provided routes module.\n\n **Guarantees:**\n\n - This represents a violation of the OpenAPI-first contract and\n indicates that the specification and implementation are out of\n sync."
|
"docstring": "Raised when an OpenAPI operation cannot be resolved to a handler.\n\nNotes:\n **Scenarios:**\n\n - An OpenAPI operation does not define an `operationId`.\n - An `operationId` is defined but no matching function exists in\n the provided routes module.\n\n **Guarantees:**\n\n - This represents a violation of the OpenAPI-first contract and\n indicates that the specification and implementation are out of\n sync."
|
||||||
},
|
|
||||||
"Optional": {
|
|
||||||
"name": "Optional",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.errors.Optional",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('Optional', 'typing.Optional')>",
|
|
||||||
"docstring": null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
2830
docs/mcp/modules/openapi_first.json
Normal file
2830
docs/mcp/modules/openapi_first.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -50,29 +50,15 @@
|
|||||||
"name": "OpenAPISpecLoadError",
|
"name": "OpenAPISpecLoadError",
|
||||||
"kind": "class",
|
"kind": "class",
|
||||||
"path": "openapi_first.loader.OpenAPISpecLoadError",
|
"path": "openapi_first.loader.OpenAPISpecLoadError",
|
||||||
"signature": "<bound method Class.signature of Class('OpenAPISpecLoadError', 39, 49)>",
|
"signature": "<bound method Class.signature of Class('OpenAPISpecLoadError', 39, 50)>",
|
||||||
"docstring": "Raised when an OpenAPI specification cannot be loaded or validated.\n\nNotes:\n **Guarantees:**\n\n - This error indicates that the OpenAPI document is unreadable,\n malformed, or violates the OpenAPI 3.x specification."
|
"docstring": "Raised when an OpenAPI specification cannot be loaded or validated.\n\nNotes:\n **Guarantees:**\n\n - This error indicates that the OpenAPI document is unreadable,\n malformed, or violates the OpenAPI 3.x specification."
|
||||||
},
|
},
|
||||||
"load_openapi": {
|
"load_openapi": {
|
||||||
"name": "load_openapi",
|
"name": "load_openapi",
|
||||||
"kind": "function",
|
"kind": "function",
|
||||||
"path": "openapi_first.loader.load_openapi",
|
"path": "openapi_first.loader.load_openapi",
|
||||||
"signature": "<bound method Function.signature of Function('load_openapi', 52, 109)>",
|
"signature": "<bound method Function.signature of Function('load_openapi', 53, 110)>",
|
||||||
"docstring": "Load and validate an OpenAPI 3.x specification from disk.\n\nArgs:\n path (str | Path):\n Filesystem path to an OpenAPI specification file. Supported\n extensions: `.json`, `.yaml`, `.yml`.\n\nReturns:\n dict[str, Any]:\n Parsed and validated OpenAPI specification.\n\nRaises:\n OpenAPISpecLoadError:\n If the file does not exist, cannot be parsed, or fails OpenAPI\n schema validation.\n\nNotes:\n **Guarantees:**\n\n - The specification is parsed based on file extension and validated\n using a strict OpenAPI schema validator.\n - Any error results in an immediate exception, preventing\n application startup."
|
"docstring": "Load and validate an OpenAPI 3.x specification from disk.\n\nArgs:\n path (str | Path):\n Filesystem path to an OpenAPI specification file. Supported\n extensions: `.json`, `.yaml`, `.yml`.\n\nReturns:\n dict[str, Any]:\n Parsed and validated OpenAPI specification.\n\nRaises:\n OpenAPISpecLoadError:\n If the file does not exist, cannot be parsed, or fails OpenAPI\n schema validation.\n\nNotes:\n **Guarantees:**\n\n - The specification is parsed based on file extension and validated\n using a strict OpenAPI schema validator.\n - Any error results in an immediate exception, preventing\n application startup."
|
||||||
},
|
|
||||||
"Dict": {
|
|
||||||
"name": "Dict",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.loader.Dict",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('Dict', 'typing.Dict')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"Union": {
|
|
||||||
"name": "Union",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.loader.Union",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('Union', 'typing.Union')>",
|
|
||||||
"docstring": null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
93
docs/mcp/modules/openapi_first.security.json
Normal file
93
docs/mcp/modules/openapi_first.security.json
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
{
|
||||||
|
"module": "openapi_first.security",
|
||||||
|
"content": {
|
||||||
|
"path": "openapi_first.security",
|
||||||
|
"docstring": "OpenAPI security scheme parsing and auto-generated auth dependencies.\n\nReads `securitySchemes` and per-operation `security` from an OpenAPI spec,\nresolves `{ENV_VAR}` placeholders in `x-` extension fields, and generates\nFastAPI dependencies for token validation (e.g., Bearer JWT introspection).",
|
||||||
|
"objects": {
|
||||||
|
"os": {
|
||||||
|
"name": "os",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.security.os",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('os', 'os')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"re": {
|
||||||
|
"name": "re",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.security.re",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('re', 're')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"Callable": {
|
||||||
|
"name": "Callable",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.security.Callable",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Callable', 'collections.abc.Callable')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"Any": {
|
||||||
|
"name": "Any",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.security.Any",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Any', 'typing.Any')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"httpx": {
|
||||||
|
"name": "httpx",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.security.httpx",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('httpx', 'httpx')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"Depends": {
|
||||||
|
"name": "Depends",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.security.Depends",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Depends', 'fastapi.Depends')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"HTTPException": {
|
||||||
|
"name": "HTTPException",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.security.HTTPException",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('HTTPException', 'fastapi.HTTPException')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"Request": {
|
||||||
|
"name": "Request",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.security.Request",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Request', 'fastapi.Request')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"HTTPAuthorizationCredentials": {
|
||||||
|
"name": "HTTPAuthorizationCredentials",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.security.HTTPAuthorizationCredentials",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('HTTPAuthorizationCredentials', 'fastapi.security.HTTPAuthorizationCredentials')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"HTTPBearer": {
|
||||||
|
"name": "HTTPBearer",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.security.HTTPBearer",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('HTTPBearer', 'fastapi.security.HTTPBearer')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"parse_security_schemes": {
|
||||||
|
"name": "parse_security_schemes",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.security.parse_security_schemes",
|
||||||
|
"signature": "<bound method Function.signature of Function('parse_security_schemes', 43, 46)>",
|
||||||
|
"docstring": "Extract and resolve environment variables in security schemes."
|
||||||
|
},
|
||||||
|
"make_security_dependencies": {
|
||||||
|
"name": "make_security_dependencies",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.security.make_security_dependencies",
|
||||||
|
"signature": "<bound method Function.signature of Function('make_security_dependencies', 113, 150)>",
|
||||||
|
"docstring": "Build a mapping of ``METHOD:/path`` → list of ``Depends(...)``.\n\nThe effective security for each operation is resolved by:\n\n1. Using the operation-level ``security`` field if present.\n2. Falling back to the top-level ``security`` field.\n3. An empty list means *no auth required* for that operation."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
"kind": "class",
|
"kind": "class",
|
||||||
"path": "openapi_first.templates.crud_app.main.OpenAPIFirstApp",
|
"path": "openapi_first.templates.crud_app.main.OpenAPIFirstApp",
|
||||||
"signature": "<bound method Alias.signature of Alias('OpenAPIFirstApp', 'openapi_first.app.OpenAPIFirstApp')>",
|
"signature": "<bound method Alias.signature of Alias('OpenAPIFirstApp', 'openapi_first.app.OpenAPIFirstApp')>",
|
||||||
"docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each `operationId` is mapped to a Python function in the\n supplied routes module.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and `/openapi.json` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```",
|
"docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each ``operationId`` is mapped to a Python function in the\n supplied routes module.\n - Auth dependencies are auto-injected from the spec's\n ``securitySchemes`` and per-operation ``security`` fields.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and ``/openapi.json`` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n - Auth enforcement is driven entirely by the spec — no manual\n middleware or decorators required.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```",
|
||||||
"members": {
|
"members": {
|
||||||
"openapi": {
|
"openapi": {
|
||||||
"name": "openapi",
|
"name": "openapi",
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
"kind": "class",
|
"kind": "class",
|
||||||
"path": "openapi_first.templates.crud_app.main.OpenAPIFirstApp",
|
"path": "openapi_first.templates.crud_app.main.OpenAPIFirstApp",
|
||||||
"signature": "<bound method Alias.signature of Alias('OpenAPIFirstApp', 'openapi_first.app.OpenAPIFirstApp')>",
|
"signature": "<bound method Alias.signature of Alias('OpenAPIFirstApp', 'openapi_first.app.OpenAPIFirstApp')>",
|
||||||
"docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each `operationId` is mapped to a Python function in the\n supplied routes module.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and `/openapi.json` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```",
|
"docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each ``operationId`` is mapped to a Python function in the\n supplied routes module.\n - Auth dependencies are auto-injected from the spec's\n ``securitySchemes`` and per-operation ``security`` fields.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and ``/openapi.json`` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n - Auth enforcement is driven entirely by the spec — no manual\n middleware or decorators required.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```",
|
||||||
"members": {
|
"members": {
|
||||||
"openapi": {
|
"openapi": {
|
||||||
"name": "openapi",
|
"name": "openapi",
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
"kind": "class",
|
"kind": "class",
|
||||||
"path": "openapi_first.templates.health_app.main.OpenAPIFirstApp",
|
"path": "openapi_first.templates.health_app.main.OpenAPIFirstApp",
|
||||||
"signature": "<bound method Alias.signature of Alias('OpenAPIFirstApp', 'openapi_first.app.OpenAPIFirstApp')>",
|
"signature": "<bound method Alias.signature of Alias('OpenAPIFirstApp', 'openapi_first.app.OpenAPIFirstApp')>",
|
||||||
"docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each `operationId` is mapped to a Python function in the\n supplied routes module.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and `/openapi.json` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```",
|
"docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each ``operationId`` is mapped to a Python function in the\n supplied routes module.\n - Auth dependencies are auto-injected from the spec's\n ``securitySchemes`` and per-operation ``security`` fields.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and ``/openapi.json`` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n - Auth enforcement is driven entirely by the spec — no manual\n middleware or decorators required.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```",
|
||||||
"members": {
|
"members": {
|
||||||
"openapi": {
|
"openapi": {
|
||||||
"name": "openapi",
|
"name": "openapi",
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
"kind": "class",
|
"kind": "class",
|
||||||
"path": "openapi_first.templates.health_app.main.OpenAPIFirstApp",
|
"path": "openapi_first.templates.health_app.main.OpenAPIFirstApp",
|
||||||
"signature": "<bound method Alias.signature of Alias('OpenAPIFirstApp', 'openapi_first.app.OpenAPIFirstApp')>",
|
"signature": "<bound method Alias.signature of Alias('OpenAPIFirstApp', 'openapi_first.app.OpenAPIFirstApp')>",
|
||||||
"docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each `operationId` is mapped to a Python function in the\n supplied routes module.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and `/openapi.json` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```",
|
"docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each ``operationId`` is mapped to a Python function in the\n supplied routes module.\n - Auth dependencies are auto-injected from the spec's\n ``securitySchemes`` and per-operation ``security`` fields.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and ``/openapi.json`` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n - Auth enforcement is driven entirely by the spec — no manual\n middleware or decorators required.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```",
|
||||||
"members": {
|
"members": {
|
||||||
"openapi": {
|
"openapi": {
|
||||||
"name": "openapi",
|
"name": "openapi",
|
||||||
2274
docs/mcp/modules/openapi_first.templates.json
Normal file
2274
docs/mcp/modules/openapi_first.templates.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -81,7 +81,7 @@
|
|||||||
"kind": "class",
|
"kind": "class",
|
||||||
"path": "openapi_first.templates.model_app.main.OpenAPIFirstApp",
|
"path": "openapi_first.templates.model_app.main.OpenAPIFirstApp",
|
||||||
"signature": "<bound method Alias.signature of Alias('OpenAPIFirstApp', 'openapi_first.app.OpenAPIFirstApp')>",
|
"signature": "<bound method Alias.signature of Alias('OpenAPIFirstApp', 'openapi_first.app.OpenAPIFirstApp')>",
|
||||||
"docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each `operationId` is mapped to a Python function in the\n supplied routes module.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and `/openapi.json` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```",
|
"docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each ``operationId`` is mapped to a Python function in the\n supplied routes module.\n - Auth dependencies are auto-injected from the spec's\n ``securitySchemes`` and per-operation ``security`` fields.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and ``/openapi.json`` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n - Auth enforcement is driven entirely by the spec — no manual\n middleware or decorators required.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```",
|
||||||
"members": {
|
"members": {
|
||||||
"openapi": {
|
"openapi": {
|
||||||
"name": "openapi",
|
"name": "openapi",
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
"kind": "class",
|
"kind": "class",
|
||||||
"path": "openapi_first.templates.model_app.main.OpenAPIFirstApp",
|
"path": "openapi_first.templates.model_app.main.OpenAPIFirstApp",
|
||||||
"signature": "<bound method Alias.signature of Alias('OpenAPIFirstApp', 'openapi_first.app.OpenAPIFirstApp')>",
|
"signature": "<bound method Alias.signature of Alias('OpenAPIFirstApp', 'openapi_first.app.OpenAPIFirstApp')>",
|
||||||
"docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each `operationId` is mapped to a Python function in the\n supplied routes module.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and `/openapi.json` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```",
|
"docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each ``operationId`` is mapped to a Python function in the\n supplied routes module.\n - Auth dependencies are auto-injected from the spec's\n ``securitySchemes`` and per-operation ``security`` fields.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and ``/openapi.json`` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n - Auth enforcement is driven entirely by the spec — no manual\n middleware or decorators required.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```",
|
||||||
"members": {
|
"members": {
|
||||||
"openapi": {
|
"openapi": {
|
||||||
"name": "openapi",
|
"name": "openapi",
|
||||||
324
docs/mcp/modules/openapi_first.templates.vet_app.data.json
Normal file
324
docs/mcp/modules/openapi_first.templates.vet_app.data.json
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
{
|
||||||
|
"module": "openapi_first.templates.vet_app.data",
|
||||||
|
"content": {
|
||||||
|
"path": "openapi_first.templates.vet_app.data",
|
||||||
|
"docstring": "In-memory data store for the Veterinary Clinic example.\n\nThis module is NOT thread-safe and is intended for demos and scaffolds only.\n\nIt provides minimal, process-local data stores for the five veterinary\nclinic entities. Each store exposes standard CRUD operations backed by\na simple dictionary.\n\nThis module intentionally avoids:\n- persistence\n- concurrency guarantees\n- transactional semantics\n- validation beyond what Pydantic provides\n\nThis module is not part of the ``openapi_first`` library API surface.",
|
||||||
|
"objects": {
|
||||||
|
"date": {
|
||||||
|
"name": "date",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.date",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('date', 'datetime.date')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"datetime": {
|
||||||
|
"name": "datetime",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.datetime",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('datetime', 'datetime.datetime')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"timezone": {
|
||||||
|
"name": "timezone",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.timezone",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('timezone', 'datetime.timezone')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"Parent": {
|
||||||
|
"name": "Parent",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.Parent",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Parent', 'models.Parent')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"ParentCreate": {
|
||||||
|
"name": "ParentCreate",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.ParentCreate",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('ParentCreate', 'models.ParentCreate')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"Vet": {
|
||||||
|
"name": "Vet",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.Vet",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Vet', 'models.Vet')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"VetCreate": {
|
||||||
|
"name": "VetCreate",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.VetCreate",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('VetCreate', 'models.VetCreate')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"Treatment": {
|
||||||
|
"name": "Treatment",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.Treatment",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Treatment', 'models.Treatment')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"TreatmentCreate": {
|
||||||
|
"name": "TreatmentCreate",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.TreatmentCreate",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('TreatmentCreate', 'models.TreatmentCreate')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"Procedure": {
|
||||||
|
"name": "Procedure",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.Procedure",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Procedure', 'models.Procedure')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"BasicNote": {
|
||||||
|
"name": "BasicNote",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.BasicNote",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('BasicNote', 'models.BasicNote')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"HeartRateNote": {
|
||||||
|
"name": "HeartRateNote",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.HeartRateNote",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('HeartRateNote', 'models.HeartRateNote')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"DentalNote": {
|
||||||
|
"name": "DentalNote",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.DentalNote",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('DentalNote', 'models.DentalNote')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"VaccineNote": {
|
||||||
|
"name": "VaccineNote",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.VaccineNote",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('VaccineNote', 'models.VaccineNote')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"PreOpNote": {
|
||||||
|
"name": "PreOpNote",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.PreOpNote",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('PreOpNote', 'models.PreOpNote')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"SurgeryNote": {
|
||||||
|
"name": "SurgeryNote",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.SurgeryNote",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('SurgeryNote', 'models.SurgeryNote')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"Pet": {
|
||||||
|
"name": "Pet",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.Pet",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Pet', 'models.Pet')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"PetCreate": {
|
||||||
|
"name": "PetCreate",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.PetCreate",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('PetCreate', 'models.PetCreate')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"Appointment": {
|
||||||
|
"name": "Appointment",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.Appointment",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Appointment', 'models.Appointment')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"AppointmentCreate": {
|
||||||
|
"name": "AppointmentCreate",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.AppointmentCreate",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('AppointmentCreate', 'models.AppointmentCreate')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"list_parents": {
|
||||||
|
"name": "list_parents",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.list_parents",
|
||||||
|
"signature": "<bound method Function.signature of Function('list_parents', 45, 46)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"get_parent": {
|
||||||
|
"name": "get_parent",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.get_parent",
|
||||||
|
"signature": "<bound method Function.signature of Function('get_parent', 49, 50)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"create_parent": {
|
||||||
|
"name": "create_parent",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.create_parent",
|
||||||
|
"signature": "<bound method Function.signature of Function('create_parent', 53, 65)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"update_parent": {
|
||||||
|
"name": "update_parent",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.update_parent",
|
||||||
|
"signature": "<bound method Function.signature of Function('update_parent', 68, 81)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"delete_parent": {
|
||||||
|
"name": "delete_parent",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.delete_parent",
|
||||||
|
"signature": "<bound method Function.signature of Function('delete_parent', 84, 85)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"list_vets": {
|
||||||
|
"name": "list_vets",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.list_vets",
|
||||||
|
"signature": "<bound method Function.signature of Function('list_vets', 96, 97)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"get_vet": {
|
||||||
|
"name": "get_vet",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.get_vet",
|
||||||
|
"signature": "<bound method Function.signature of Function('get_vet', 100, 101)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"create_vet": {
|
||||||
|
"name": "create_vet",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.create_vet",
|
||||||
|
"signature": "<bound method Function.signature of Function('create_vet', 104, 117)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"update_vet": {
|
||||||
|
"name": "update_vet",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.update_vet",
|
||||||
|
"signature": "<bound method Function.signature of Function('update_vet', 120, 134)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"delete_vet": {
|
||||||
|
"name": "delete_vet",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.delete_vet",
|
||||||
|
"signature": "<bound method Function.signature of Function('delete_vet', 137, 138)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"list_treatments": {
|
||||||
|
"name": "list_treatments",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.list_treatments",
|
||||||
|
"signature": "<bound method Function.signature of Function('list_treatments', 149, 150)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"get_treatment": {
|
||||||
|
"name": "get_treatment",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.get_treatment",
|
||||||
|
"signature": "<bound method Function.signature of Function('get_treatment', 153, 154)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"create_treatment": {
|
||||||
|
"name": "create_treatment",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.create_treatment",
|
||||||
|
"signature": "<bound method Function.signature of Function('create_treatment', 157, 168)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"update_treatment": {
|
||||||
|
"name": "update_treatment",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.update_treatment",
|
||||||
|
"signature": "<bound method Function.signature of Function('update_treatment', 171, 183)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"delete_treatment": {
|
||||||
|
"name": "delete_treatment",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.delete_treatment",
|
||||||
|
"signature": "<bound method Function.signature of Function('delete_treatment', 186, 187)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"list_pets": {
|
||||||
|
"name": "list_pets",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.list_pets",
|
||||||
|
"signature": "<bound method Function.signature of Function('list_pets', 198, 199)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"get_pet": {
|
||||||
|
"name": "get_pet",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.get_pet",
|
||||||
|
"signature": "<bound method Function.signature of Function('get_pet', 202, 203)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"create_pet": {
|
||||||
|
"name": "create_pet",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.create_pet",
|
||||||
|
"signature": "<bound method Function.signature of Function('create_pet', 206, 223)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"update_pet": {
|
||||||
|
"name": "update_pet",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.update_pet",
|
||||||
|
"signature": "<bound method Function.signature of Function('update_pet', 226, 244)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"delete_pet": {
|
||||||
|
"name": "delete_pet",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.delete_pet",
|
||||||
|
"signature": "<bound method Function.signature of Function('delete_pet', 247, 248)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"list_appointments": {
|
||||||
|
"name": "list_appointments",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.list_appointments",
|
||||||
|
"signature": "<bound method Function.signature of Function('list_appointments', 259, 260)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"get_appointment": {
|
||||||
|
"name": "get_appointment",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.get_appointment",
|
||||||
|
"signature": "<bound method Function.signature of Function('get_appointment', 263, 264)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"create_appointment": {
|
||||||
|
"name": "create_appointment",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.create_appointment",
|
||||||
|
"signature": "<bound method Function.signature of Function('create_appointment', 267, 282)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"update_appointment": {
|
||||||
|
"name": "update_appointment",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.update_appointment",
|
||||||
|
"signature": "<bound method Function.signature of Function('update_appointment', 285, 301)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"delete_appointment": {
|
||||||
|
"name": "delete_appointment",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.data.delete_appointment",
|
||||||
|
"signature": "<bound method Function.signature of Function('delete_appointment', 304, 305)>",
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1572
docs/mcp/modules/openapi_first.templates.vet_app.json
Normal file
1572
docs/mcp/modules/openapi_first.templates.vet_app.json
Normal file
File diff suppressed because it is too large
Load Diff
60
docs/mcp/modules/openapi_first.templates.vet_app.main.json
Normal file
60
docs/mcp/modules/openapi_first.templates.vet_app.main.json
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"module": "openapi_first.templates.vet_app.main",
|
||||||
|
"content": {
|
||||||
|
"path": "openapi_first.templates.vet_app.main",
|
||||||
|
"docstring": "Application entry point for an OpenAPI-first Veterinary Clinic service.\n\nThis module constructs a FastAPI application exclusively from an\nOpenAPI specification and a handler namespace, without using\ndecorator-driven routing.\n\nAll HTTP routes, methods, request/response schemas, and operation\nbindings are defined in the OpenAPI document referenced by\n``openapi_path``. Python callables defined in the ``routes`` module are\nbound to OpenAPI operations strictly via ``operationId``.\n\nThis module contains no routing logic, persistence concerns, or\nframework configuration beyond application assembly.\n\nDesign guarantees:\n- OpenAPI is the single source of truth\n- No undocumented routes can exist\n- Every OpenAPI operationId must resolve to exactly one handler\n- All contract violations fail at application startup\n\nThis file is intended to be used as the ASGI entry point.\n\nExample:\n uvicorn main:app",
|
||||||
|
"objects": {
|
||||||
|
"asynccontextmanager": {
|
||||||
|
"name": "asynccontextmanager",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.main.asynccontextmanager",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('asynccontextmanager', 'contextlib.asynccontextmanager')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"CORSMiddleware": {
|
||||||
|
"name": "CORSMiddleware",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.main.CORSMiddleware",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('CORSMiddleware', 'starlette.middleware.cors.CORSMiddleware')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"OpenAPIFirstApp": {
|
||||||
|
"name": "OpenAPIFirstApp",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.main.OpenAPIFirstApp",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('OpenAPIFirstApp', 'openapi_first.app.OpenAPIFirstApp')>",
|
||||||
|
"docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each ``operationId`` is mapped to a Python function in the\n supplied routes module.\n - Auth dependencies are auto-injected from the spec's\n ``securitySchemes`` and per-operation ``security`` fields.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and ``/openapi.json`` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n - Auth enforcement is driven entirely by the spec — no manual\n middleware or decorators required.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```",
|
||||||
|
"members": {
|
||||||
|
"openapi": {
|
||||||
|
"name": "openapi",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.main.OpenAPIFirstApp.openapi",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('openapi', 'openapi_first.app.OpenAPIFirstApp.openapi')>",
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"routes": {
|
||||||
|
"name": "routes",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.main.routes",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('routes', 'routes')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"lifespan": {
|
||||||
|
"name": "lifespan",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.main.lifespan",
|
||||||
|
"signature": "<bound method Function.signature of Function('lifespan', 34, 39)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"app": {
|
||||||
|
"name": "app",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.main.app",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
681
docs/mcp/modules/openapi_first.templates.vet_app.models.json
Normal file
681
docs/mcp/modules/openapi_first.templates.vet_app.models.json
Normal file
@@ -0,0 +1,681 @@
|
|||||||
|
{
|
||||||
|
"module": "openapi_first.templates.vet_app.models",
|
||||||
|
"content": {
|
||||||
|
"path": "openapi_first.templates.vet_app.models",
|
||||||
|
"docstring": null,
|
||||||
|
"objects": {
|
||||||
|
"date": {
|
||||||
|
"name": "date",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.date",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('date', 'datetime.date')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"datetime": {
|
||||||
|
"name": "datetime",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.datetime",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('datetime', 'datetime.datetime')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"Annotated": {
|
||||||
|
"name": "Annotated",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Annotated",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Annotated', 'typing.Annotated')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"Literal": {
|
||||||
|
"name": "Literal",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Literal",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Literal', 'typing.Literal')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"BaseModel": {
|
||||||
|
"name": "BaseModel",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.BaseModel",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('BaseModel', 'pydantic.BaseModel')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"Field": {
|
||||||
|
"name": "Field",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Field",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Field', 'pydantic.Field')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"Metadata": {
|
||||||
|
"name": "Metadata",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Metadata",
|
||||||
|
"signature": "<bound method Class.signature of Class('Metadata', 6, 8)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"createdOn": {
|
||||||
|
"name": "createdOn",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Metadata.createdOn",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"updatedOn": {
|
||||||
|
"name": "updatedOn",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Metadata.updatedOn",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ParentBase": {
|
||||||
|
"name": "ParentBase",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.ParentBase",
|
||||||
|
"signature": "<bound method Class.signature of Class('ParentBase', 11, 15)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.ParentBase.name",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"name": "email",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.ParentBase.email",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"phone": {
|
||||||
|
"name": "phone",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.ParentBase.phone",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"name": "metadata",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.ParentBase.metadata",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ParentCreate": {
|
||||||
|
"name": "ParentCreate",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.ParentCreate",
|
||||||
|
"signature": "<bound method Class.signature of Class('ParentCreate', 18, 19)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"Parent": {
|
||||||
|
"name": "Parent",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Parent",
|
||||||
|
"signature": "<bound method Class.signature of Class('Parent', 22, 23)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Parent.id",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"VetBase": {
|
||||||
|
"name": "VetBase",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.VetBase",
|
||||||
|
"signature": "<bound method Class.signature of Class('VetBase', 26, 31)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.VetBase.name",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"specialty": {
|
||||||
|
"name": "specialty",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.VetBase.specialty",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"name": "email",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.VetBase.email",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"phone": {
|
||||||
|
"name": "phone",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.VetBase.phone",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"name": "metadata",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.VetBase.metadata",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"VetCreate": {
|
||||||
|
"name": "VetCreate",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.VetCreate",
|
||||||
|
"signature": "<bound method Class.signature of Class('VetCreate', 34, 35)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"Vet": {
|
||||||
|
"name": "Vet",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Vet",
|
||||||
|
"signature": "<bound method Class.signature of Class('Vet', 38, 39)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Vet.id",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ProcedureNoteBase": {
|
||||||
|
"name": "ProcedureNoteBase",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.ProcedureNoteBase",
|
||||||
|
"signature": "<bound method Class.signature of Class('ProcedureNoteBase', 42, 44)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"summary": {
|
||||||
|
"name": "summary",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.ProcedureNoteBase.summary",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"details": {
|
||||||
|
"name": "details",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.ProcedureNoteBase.details",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"BasicNote": {
|
||||||
|
"name": "BasicNote",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.BasicNote",
|
||||||
|
"signature": "<bound method Class.signature of Class('BasicNote', 47, 48)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"noteType": {
|
||||||
|
"name": "noteType",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.BasicNote.noteType",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"HeartRateNote": {
|
||||||
|
"name": "HeartRateNote",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.HeartRateNote",
|
||||||
|
"signature": "<bound method Class.signature of Class('HeartRateNote', 51, 53)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"noteType": {
|
||||||
|
"name": "noteType",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.HeartRateNote.noteType",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"bpm": {
|
||||||
|
"name": "bpm",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.HeartRateNote.bpm",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"DentalNote": {
|
||||||
|
"name": "DentalNote",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.DentalNote",
|
||||||
|
"signature": "<bound method Class.signature of Class('DentalNote', 56, 59)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"noteType": {
|
||||||
|
"name": "noteType",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.DentalNote.noteType",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"procedureType": {
|
||||||
|
"name": "procedureType",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.DentalNote.procedureType",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"teeth": {
|
||||||
|
"name": "teeth",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.DentalNote.teeth",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"VaccineNote": {
|
||||||
|
"name": "VaccineNote",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.VaccineNote",
|
||||||
|
"signature": "<bound method Class.signature of Class('VaccineNote', 62, 65)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"noteType": {
|
||||||
|
"name": "noteType",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.VaccineNote.noteType",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"medicine": {
|
||||||
|
"name": "medicine",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.VaccineNote.medicine",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"leg": {
|
||||||
|
"name": "leg",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.VaccineNote.leg",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"PreOpNote": {
|
||||||
|
"name": "PreOpNote",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.PreOpNote",
|
||||||
|
"signature": "<bound method Class.signature of Class('PreOpNote', 68, 71)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"noteType": {
|
||||||
|
"name": "noteType",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.PreOpNote.noteType",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"heartRate": {
|
||||||
|
"name": "heartRate",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.PreOpNote.heartRate",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"temperature": {
|
||||||
|
"name": "temperature",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.PreOpNote.temperature",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SurgeryNote": {
|
||||||
|
"name": "SurgeryNote",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.SurgeryNote",
|
||||||
|
"signature": "<bound method Class.signature of Class('SurgeryNote', 74, 77)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"noteType": {
|
||||||
|
"name": "noteType",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.SurgeryNote.noteType",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"surgeryType": {
|
||||||
|
"name": "surgeryType",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.SurgeryNote.surgeryType",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"complications": {
|
||||||
|
"name": "complications",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.SurgeryNote.complications",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ProcedureNotes": {
|
||||||
|
"name": "ProcedureNotes",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.ProcedureNotes",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"Procedure": {
|
||||||
|
"name": "Procedure",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Procedure",
|
||||||
|
"signature": "<bound method Class.signature of Class('Procedure', 86, 90)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Procedure.name",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Procedure.description",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"cost": {
|
||||||
|
"name": "cost",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Procedure.cost",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"notes": {
|
||||||
|
"name": "notes",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Procedure.notes",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"TreatmentBase": {
|
||||||
|
"name": "TreatmentBase",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.TreatmentBase",
|
||||||
|
"signature": "<bound method Class.signature of Class('TreatmentBase', 93, 96)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"label": {
|
||||||
|
"name": "label",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.TreatmentBase.label",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.TreatmentBase.description",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"name": "metadata",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.TreatmentBase.metadata",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"TreatmentCreate": {
|
||||||
|
"name": "TreatmentCreate",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.TreatmentCreate",
|
||||||
|
"signature": "<bound method Class.signature of Class('TreatmentCreate', 99, 100)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"Treatment": {
|
||||||
|
"name": "Treatment",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Treatment",
|
||||||
|
"signature": "<bound method Class.signature of Class('Treatment', 103, 104)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Treatment.id",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"PetBase": {
|
||||||
|
"name": "PetBase",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.PetBase",
|
||||||
|
"signature": "<bound method Class.signature of Class('PetBase', 107, 114)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.PetBase.name",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"species": {
|
||||||
|
"name": "species",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.PetBase.species",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"age": {
|
||||||
|
"name": "age",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.PetBase.age",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"weight": {
|
||||||
|
"name": "weight",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.PetBase.weight",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"birthDate": {
|
||||||
|
"name": "birthDate",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.PetBase.birthDate",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"photo": {
|
||||||
|
"name": "photo",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.PetBase.photo",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"name": "metadata",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.PetBase.metadata",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"PetCreate": {
|
||||||
|
"name": "PetCreate",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.PetCreate",
|
||||||
|
"signature": "<bound method Class.signature of Class('PetCreate', 117, 118)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"parent_ids": {
|
||||||
|
"name": "parent_ids",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.PetCreate.parent_ids",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Pet": {
|
||||||
|
"name": "Pet",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Pet",
|
||||||
|
"signature": "<bound method Class.signature of Class('Pet', 121, 123)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Pet.id",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"parents": {
|
||||||
|
"name": "parents",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Pet.parents",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AppointmentBase": {
|
||||||
|
"name": "AppointmentBase",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.AppointmentBase",
|
||||||
|
"signature": "<bound method Class.signature of Class('AppointmentBase', 126, 130)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"date": {
|
||||||
|
"name": "date",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.AppointmentBase.date",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"notes": {
|
||||||
|
"name": "notes",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.AppointmentBase.notes",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"procedures": {
|
||||||
|
"name": "procedures",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.AppointmentBase.procedures",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"name": "metadata",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.AppointmentBase.metadata",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AppointmentCreate": {
|
||||||
|
"name": "AppointmentCreate",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.AppointmentCreate",
|
||||||
|
"signature": "<bound method Class.signature of Class('AppointmentCreate', 133, 136)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"pet_id": {
|
||||||
|
"name": "pet_id",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.AppointmentCreate.pet_id",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"vet_id": {
|
||||||
|
"name": "vet_id",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.AppointmentCreate.vet_id",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"treatment_id": {
|
||||||
|
"name": "treatment_id",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.AppointmentCreate.treatment_id",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Appointment": {
|
||||||
|
"name": "Appointment",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Appointment",
|
||||||
|
"signature": "<bound method Class.signature of Class('Appointment', 139, 143)>",
|
||||||
|
"docstring": null,
|
||||||
|
"members": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Appointment.id",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"pet": {
|
||||||
|
"name": "pet",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Appointment.pet",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"vet": {
|
||||||
|
"name": "vet",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Appointment.vet",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"treatment": {
|
||||||
|
"name": "treatment",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.models.Appointment.treatment",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
275
docs/mcp/modules/openapi_first.templates.vet_app.routes.json
Normal file
275
docs/mcp/modules/openapi_first.templates.vet_app.routes.json
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
{
|
||||||
|
"module": "openapi_first.templates.vet_app.routes",
|
||||||
|
"content": {
|
||||||
|
"path": "openapi_first.templates.vet_app.routes",
|
||||||
|
"docstring": "Veterinary Clinic route handlers bound via OpenAPI operationId.\n\nHandlers explicitly control HTTP response status codes to ensure runtime\nbehavior matches the OpenAPI contract. Domain models defined using\nPydantic are used for request and response payloads.\n\nNo routing decorators, path definitions, or implicit framework behavior\nappear in this module. All routing, HTTP methods, and schemas are defined\nin the OpenAPI specification.",
|
||||||
|
"objects": {
|
||||||
|
"Response": {
|
||||||
|
"name": "Response",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.Response",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('Response', 'fastapi.Response')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"HTTPException": {
|
||||||
|
"name": "HTTPException",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.HTTPException",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('HTTPException', 'fastapi.HTTPException')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"UploadFile": {
|
||||||
|
"name": "UploadFile",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.UploadFile",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('UploadFile', 'fastapi.UploadFile')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"StreamingResponse": {
|
||||||
|
"name": "StreamingResponse",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.StreamingResponse",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('StreamingResponse', 'fastapi.responses.StreamingResponse')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"subscribe": {
|
||||||
|
"name": "subscribe",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.subscribe",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('subscribe', 'sse.subscribe')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"unsubscribe": {
|
||||||
|
"name": "unsubscribe",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.unsubscribe",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('unsubscribe', 'sse.unsubscribe')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"ParentCreate": {
|
||||||
|
"name": "ParentCreate",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.ParentCreate",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('ParentCreate', 'models.ParentCreate')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"VetCreate": {
|
||||||
|
"name": "VetCreate",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.VetCreate",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('VetCreate', 'models.VetCreate')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"TreatmentCreate": {
|
||||||
|
"name": "TreatmentCreate",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.TreatmentCreate",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('TreatmentCreate', 'models.TreatmentCreate')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"PetCreate": {
|
||||||
|
"name": "PetCreate",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.PetCreate",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('PetCreate', 'models.PetCreate')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"AppointmentCreate": {
|
||||||
|
"name": "AppointmentCreate",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.AppointmentCreate",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('AppointmentCreate', 'models.AppointmentCreate')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"list_parents": {
|
||||||
|
"name": "list_parents",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.list_parents",
|
||||||
|
"signature": "<bound method Function.signature of Function('list_parents', 60, 76)>",
|
||||||
|
"docstring": "List parents (paginated).\n\nParameters\n----------\nlimit : int\n Maximum number of records to return.\noffset : int\n Number of records to skip.\n\nReturns\n-------\ndict\n Paginated response with ``total`` and ``items``."
|
||||||
|
},
|
||||||
|
"create_parent": {
|
||||||
|
"name": "create_parent",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.create_parent",
|
||||||
|
"signature": "<bound method Function.signature of Function('create_parent', 79, 94)>",
|
||||||
|
"docstring": "Create a parent.\n\nParameters\n----------\npayload : ParentCreate\n Parent data excluding the ``id`` field.\n\nReturns\n-------\nParent\n The newly created parent."
|
||||||
|
},
|
||||||
|
"get_parent": {
|
||||||
|
"name": "get_parent",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.get_parent",
|
||||||
|
"signature": "<bound method Function.signature of Function('get_parent', 97, 118)>",
|
||||||
|
"docstring": "Retrieve a single parent by ID.\n\nParameters\n----------\nid : int\n Identifier of the parent.\n\nReturns\n-------\nParent\n The requested parent.\n\nRaises\n------\nHTTPException\n 404 if the parent does not exist."
|
||||||
|
},
|
||||||
|
"update_parent": {
|
||||||
|
"name": "update_parent",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.update_parent",
|
||||||
|
"signature": "<bound method Function.signature of Function('update_parent', 121, 144)>",
|
||||||
|
"docstring": "Update an existing parent.\n\nParameters\n----------\nid : int\n Identifier of the parent.\npayload : ParentCreate\n Updated parent data.\n\nReturns\n-------\nParent\n The updated parent.\n\nRaises\n------\nHTTPException\n 404 if the parent does not exist."
|
||||||
|
},
|
||||||
|
"delete_parent": {
|
||||||
|
"name": "delete_parent",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.delete_parent",
|
||||||
|
"signature": "<bound method Function.signature of Function('delete_parent', 147, 164)>",
|
||||||
|
"docstring": "Delete an existing parent.\n\nParameters\n----------\nid : int\n Identifier of the parent.\n\nRaises\n------\nHTTPException\n 404 if the parent does not exist."
|
||||||
|
},
|
||||||
|
"list_vets": {
|
||||||
|
"name": "list_vets",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.list_vets",
|
||||||
|
"signature": "<bound method Function.signature of Function('list_vets', 172, 175)>",
|
||||||
|
"docstring": "List vets (paginated)."
|
||||||
|
},
|
||||||
|
"create_vet": {
|
||||||
|
"name": "create_vet",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.create_vet",
|
||||||
|
"signature": "<bound method Function.signature of Function('create_vet', 178, 182)>",
|
||||||
|
"docstring": "Create a vet."
|
||||||
|
},
|
||||||
|
"get_vet": {
|
||||||
|
"name": "get_vet",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.get_vet",
|
||||||
|
"signature": "<bound method Function.signature of Function('get_vet', 185, 190)>",
|
||||||
|
"docstring": "Retrieve a single vet by ID."
|
||||||
|
},
|
||||||
|
"update_vet": {
|
||||||
|
"name": "update_vet",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.update_vet",
|
||||||
|
"signature": "<bound method Function.signature of Function('update_vet', 193, 198)>",
|
||||||
|
"docstring": "Update an existing vet."
|
||||||
|
},
|
||||||
|
"delete_vet": {
|
||||||
|
"name": "delete_vet",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.delete_vet",
|
||||||
|
"signature": "<bound method Function.signature of Function('delete_vet', 201, 207)>",
|
||||||
|
"docstring": "Delete an existing vet."
|
||||||
|
},
|
||||||
|
"list_treatments": {
|
||||||
|
"name": "list_treatments",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.list_treatments",
|
||||||
|
"signature": "<bound method Function.signature of Function('list_treatments', 215, 223)>",
|
||||||
|
"docstring": "List treatments (catalogue).\n\nReturns\n-------\nlist[Treatment]\n A list of treatment domain objects."
|
||||||
|
},
|
||||||
|
"create_treatment": {
|
||||||
|
"name": "create_treatment",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.create_treatment",
|
||||||
|
"signature": "<bound method Function.signature of Function('create_treatment', 226, 230)>",
|
||||||
|
"docstring": "Add a treatment (admin only)."
|
||||||
|
},
|
||||||
|
"get_treatment": {
|
||||||
|
"name": "get_treatment",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.get_treatment",
|
||||||
|
"signature": "<bound method Function.signature of Function('get_treatment', 233, 238)>",
|
||||||
|
"docstring": "Retrieve a single treatment by ID."
|
||||||
|
},
|
||||||
|
"update_treatment": {
|
||||||
|
"name": "update_treatment",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.update_treatment",
|
||||||
|
"signature": "<bound method Function.signature of Function('update_treatment', 241, 246)>",
|
||||||
|
"docstring": "Update an existing treatment."
|
||||||
|
},
|
||||||
|
"delete_treatment": {
|
||||||
|
"name": "delete_treatment",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.delete_treatment",
|
||||||
|
"signature": "<bound method Function.signature of Function('delete_treatment', 249, 255)>",
|
||||||
|
"docstring": "Delete an existing treatment."
|
||||||
|
},
|
||||||
|
"list_pets": {
|
||||||
|
"name": "list_pets",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.list_pets",
|
||||||
|
"signature": "<bound method Function.signature of Function('list_pets', 263, 266)>",
|
||||||
|
"docstring": "List pets (paginated)."
|
||||||
|
},
|
||||||
|
"create_pet": {
|
||||||
|
"name": "create_pet",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.create_pet",
|
||||||
|
"signature": "<bound method Function.signature of Function('create_pet', 269, 273)>",
|
||||||
|
"docstring": "Create a pet."
|
||||||
|
},
|
||||||
|
"get_pet": {
|
||||||
|
"name": "get_pet",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.get_pet",
|
||||||
|
"signature": "<bound method Function.signature of Function('get_pet', 276, 281)>",
|
||||||
|
"docstring": "Retrieve a single pet by ID."
|
||||||
|
},
|
||||||
|
"update_pet": {
|
||||||
|
"name": "update_pet",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.update_pet",
|
||||||
|
"signature": "<bound method Function.signature of Function('update_pet', 284, 289)>",
|
||||||
|
"docstring": "Update an existing pet."
|
||||||
|
},
|
||||||
|
"delete_pet": {
|
||||||
|
"name": "delete_pet",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.delete_pet",
|
||||||
|
"signature": "<bound method Function.signature of Function('delete_pet', 292, 298)>",
|
||||||
|
"docstring": "Delete an existing pet."
|
||||||
|
},
|
||||||
|
"upload_pet_photo": {
|
||||||
|
"name": "upload_pet_photo",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.upload_pet_photo",
|
||||||
|
"signature": "<bound method Function.signature of Function('upload_pet_photo', 301, 317)>",
|
||||||
|
"docstring": "Upload a pet photo.\n\nParameters\n----------\nid : int\n Identifier of the pet.\nfile : UploadFile\n Image file to upload.\n\nReturns\n-------\ndict\n A confirmation with the pet ID."
|
||||||
|
},
|
||||||
|
"list_appointments": {
|
||||||
|
"name": "list_appointments",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.list_appointments",
|
||||||
|
"signature": "<bound method Function.signature of Function('list_appointments', 325, 337)>",
|
||||||
|
"docstring": "List appointments (paginated, filterable)."
|
||||||
|
},
|
||||||
|
"create_appointment": {
|
||||||
|
"name": "create_appointment",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.create_appointment",
|
||||||
|
"signature": "<bound method Function.signature of Function('create_appointment', 340, 344)>",
|
||||||
|
"docstring": "Create an appointment."
|
||||||
|
},
|
||||||
|
"get_appointment": {
|
||||||
|
"name": "get_appointment",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.get_appointment",
|
||||||
|
"signature": "<bound method Function.signature of Function('get_appointment', 347, 352)>",
|
||||||
|
"docstring": "Retrieve a single appointment by ID."
|
||||||
|
},
|
||||||
|
"update_appointment": {
|
||||||
|
"name": "update_appointment",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.update_appointment",
|
||||||
|
"signature": "<bound method Function.signature of Function('update_appointment', 355, 360)>",
|
||||||
|
"docstring": "Update an existing appointment."
|
||||||
|
},
|
||||||
|
"delete_appointment": {
|
||||||
|
"name": "delete_appointment",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.delete_appointment",
|
||||||
|
"signature": "<bound method Function.signature of Function('delete_appointment', 363, 369)>",
|
||||||
|
"docstring": "Delete an existing appointment."
|
||||||
|
},
|
||||||
|
"stream_actions": {
|
||||||
|
"name": "stream_actions",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.routes.stream_actions",
|
||||||
|
"signature": "<bound method Function.signature of Function('stream_actions', 372, 389)>",
|
||||||
|
"docstring": "Stream animal actions via SSE, scoped to a pet's species."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
docs/mcp/modules/openapi_first.templates.vet_app.sse.json
Normal file
44
docs/mcp/modules/openapi_first.templates.vet_app.sse.json
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"module": "openapi_first.templates.vet_app.sse",
|
||||||
|
"content": {
|
||||||
|
"path": "openapi_first.templates.vet_app.sse",
|
||||||
|
"docstring": null,
|
||||||
|
"objects": {
|
||||||
|
"asyncio": {
|
||||||
|
"name": "asyncio",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.sse.asyncio",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('asyncio', 'asyncio')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"random": {
|
||||||
|
"name": "random",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.sse.random",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('random', 'random')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"json": {
|
||||||
|
"name": "json",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.sse.json",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('json', 'json')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"subscribe": {
|
||||||
|
"name": "subscribe",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.sse.subscribe",
|
||||||
|
"signature": "<bound method Function.signature of Function('subscribe', 33, 37)>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"unsubscribe": {
|
||||||
|
"name": "unsubscribe",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.sse.unsubscribe",
|
||||||
|
"signature": "<bound method Function.signature of Function('unsubscribe', 40, 48)>",
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
{
|
||||||
|
"module": "openapi_first.templates.vet_app.test_vet_app",
|
||||||
|
"content": {
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app",
|
||||||
|
"docstring": "End-to-end tests for the OpenAPI-first Veterinary Clinic example app.\n\nThese tests validate that all CRUD operations behave correctly\nagainst the in-memory mock data store using Pydantic models.",
|
||||||
|
"objects": {
|
||||||
|
"TestClient": {
|
||||||
|
"name": "TestClient",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.TestClient",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('TestClient', 'fastapi.testclient.TestClient')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"app": {
|
||||||
|
"name": "app",
|
||||||
|
"kind": "alias",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.app",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('app', 'main.app')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"load_openapi": {
|
||||||
|
"name": "load_openapi",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.load_openapi",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('load_openapi', 'openapi_first.loader.load_openapi')>",
|
||||||
|
"docstring": "Load and validate an OpenAPI 3.x specification from disk.\n\nArgs:\n path (str | Path):\n Filesystem path to an OpenAPI specification file. Supported\n extensions: `.json`, `.yaml`, `.yml`.\n\nReturns:\n dict[str, Any]:\n Parsed and validated OpenAPI specification.\n\nRaises:\n OpenAPISpecLoadError:\n If the file does not exist, cannot be parsed, or fails OpenAPI\n schema validation.\n\nNotes:\n **Guarantees:**\n\n - The specification is parsed based on file extension and validated\n using a strict OpenAPI schema validator.\n - Any error results in an immediate exception, preventing\n application startup."
|
||||||
|
},
|
||||||
|
"OpenAPIClient": {
|
||||||
|
"name": "OpenAPIClient",
|
||||||
|
"kind": "class",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.OpenAPIClient",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('OpenAPIClient', 'openapi_first.client.OpenAPIClient')>",
|
||||||
|
"docstring": "OpenAPI-first HTTP client (`httpx`-based).\n\nNotes:\n **Responsibilities:**\n\n - This client derives all callable methods directly from an\n OpenAPI 3.x specification. Each `operationId` becomes a method\n on the client instance.\n\n **Guarantees:**\n\n - One callable per `operationId`.\n - Explicit parameters (path, query, headers, body).\n - No implicit schema inference or mutation.\n - Returns raw `httpx.Response` objects.\n - No response validation or deserialization.\n\nExample:\n ```python\n from openapi_first import loader, client\n\n spec = loader.load_openapi(\"openapi.yaml\")\n\n api = client.OpenAPIClient(\n spec=spec,\n base_url=\"http://localhost:8000\",\n )\n\n # Call operationId: getUser\n response = api.getUser(\n path_params={\"user_id\": 123}\n )\n\n print(response.status_code)\n print(response.json())\n ```",
|
||||||
|
"members": {
|
||||||
|
"spec": {
|
||||||
|
"name": "spec",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.OpenAPIClient.spec",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('spec', 'openapi_first.client.OpenAPIClient.spec')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"base_url": {
|
||||||
|
"name": "base_url",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.OpenAPIClient.base_url",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('base_url', 'openapi_first.client.OpenAPIClient.base_url')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"name": "client",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.OpenAPIClient.client",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('client', 'openapi_first.client.OpenAPIClient.client')>",
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"operations": {
|
||||||
|
"name": "operations",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.OpenAPIClient.operations",
|
||||||
|
"signature": "<bound method Alias.signature of Alias('operations', 'openapi_first.client.OpenAPIClient.operations')>",
|
||||||
|
"docstring": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test_client": {
|
||||||
|
"name": "test_client",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.test_client",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"name": "spec",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.spec",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"name": "client",
|
||||||
|
"kind": "attribute",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.client",
|
||||||
|
"signature": null,
|
||||||
|
"docstring": null
|
||||||
|
},
|
||||||
|
"test_list_parents": {
|
||||||
|
"name": "test_list_parents",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.test_list_parents",
|
||||||
|
"signature": "<bound method Function.signature of Function('test_list_parents', 24, 30)>",
|
||||||
|
"docstring": "List parents returns paginated response."
|
||||||
|
},
|
||||||
|
"test_create_parent": {
|
||||||
|
"name": "test_create_parent",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.test_create_parent",
|
||||||
|
"signature": "<bound method Function.signature of Function('test_create_parent', 33, 40)>",
|
||||||
|
"docstring": "Creating a parent returns 201 with the created entity."
|
||||||
|
},
|
||||||
|
"test_get_parent": {
|
||||||
|
"name": "test_get_parent",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.test_get_parent",
|
||||||
|
"signature": "<bound method Function.signature of Function('test_get_parent', 43, 48)>",
|
||||||
|
"docstring": "Get parent by ID returns the entity."
|
||||||
|
},
|
||||||
|
"test_update_parent": {
|
||||||
|
"name": "test_update_parent",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.test_update_parent",
|
||||||
|
"signature": "<bound method Function.signature of Function('test_update_parent', 51, 57)>",
|
||||||
|
"docstring": "Update parent replaces its values."
|
||||||
|
},
|
||||||
|
"test_delete_parent": {
|
||||||
|
"name": "test_delete_parent",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.test_delete_parent",
|
||||||
|
"signature": "<bound method Function.signature of Function('test_delete_parent', 60, 64)>",
|
||||||
|
"docstring": "Delete parent returns 204 and removes the entity."
|
||||||
|
},
|
||||||
|
"test_list_vets": {
|
||||||
|
"name": "test_list_vets",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.test_list_vets",
|
||||||
|
"signature": "<bound method Function.signature of Function('test_list_vets', 67, 73)>",
|
||||||
|
"docstring": "List vets returns paginated response."
|
||||||
|
},
|
||||||
|
"test_create_vet": {
|
||||||
|
"name": "test_create_vet",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.test_create_vet",
|
||||||
|
"signature": "<bound method Function.signature of Function('test_create_vet', 76, 81)>",
|
||||||
|
"docstring": "Creating a vet returns 201."
|
||||||
|
},
|
||||||
|
"test_list_treatments": {
|
||||||
|
"name": "test_list_treatments",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.test_list_treatments",
|
||||||
|
"signature": "<bound method Function.signature of Function('test_list_treatments', 84, 88)>",
|
||||||
|
"docstring": "List treatments returns an array."
|
||||||
|
},
|
||||||
|
"test_create_treatment": {
|
||||||
|
"name": "test_create_treatment",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.test_create_treatment",
|
||||||
|
"signature": "<bound method Function.signature of Function('test_create_treatment', 91, 96)>",
|
||||||
|
"docstring": "Creating a treatment returns 201."
|
||||||
|
},
|
||||||
|
"test_create_pet": {
|
||||||
|
"name": "test_create_pet",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.test_create_pet",
|
||||||
|
"signature": "<bound method Function.signature of Function('test_create_pet', 99, 105)>",
|
||||||
|
"docstring": "Creating a pet links FK references."
|
||||||
|
},
|
||||||
|
"test_upload_pet_photo": {
|
||||||
|
"name": "test_upload_pet_photo",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.test_upload_pet_photo",
|
||||||
|
"signature": "<bound method Function.signature of Function('test_upload_pet_photo', 108, 115)>",
|
||||||
|
"docstring": "Upload pet photo returns 200."
|
||||||
|
},
|
||||||
|
"test_list_appointments": {
|
||||||
|
"name": "test_list_appointments",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.test_list_appointments",
|
||||||
|
"signature": "<bound method Function.signature of Function('test_list_appointments', 118, 124)>",
|
||||||
|
"docstring": "List appointments returns paginated response with filter params."
|
||||||
|
},
|
||||||
|
"test_full_appointment_lifecycle": {
|
||||||
|
"name": "test_full_appointment_lifecycle",
|
||||||
|
"kind": "function",
|
||||||
|
"path": "openapi_first.templates.vet_app.test_vet_app.test_full_appointment_lifecycle",
|
||||||
|
"signature": "<bound method Function.signature of Function('test_full_appointment_lifecycle', 127, 151)>",
|
||||||
|
"docstring": "Create a parent, vet, treatment, pet, then an appointment."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,14 @@
|
|||||||
"module": "openapi_first.client",
|
"module": "openapi_first.client",
|
||||||
"resource": "doc://modules/openapi_first.client"
|
"resource": "doc://modules/openapi_first.client"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"module": "openapi_first.codegen",
|
||||||
|
"resource": "doc://modules/openapi_first.codegen"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module": "openapi_first.codegen_routes",
|
||||||
|
"resource": "doc://modules/openapi_first.codegen_routes"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"module": "openapi_first.errors",
|
"module": "openapi_first.errors",
|
||||||
"resource": "doc://modules/openapi_first.errors"
|
"resource": "doc://modules/openapi_first.errors"
|
||||||
@@ -27,6 +35,10 @@
|
|||||||
"module": "openapi_first.loader",
|
"module": "openapi_first.loader",
|
||||||
"resource": "doc://modules/openapi_first.loader"
|
"resource": "doc://modules/openapi_first.loader"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"module": "openapi_first.security",
|
||||||
|
"resource": "doc://modules/openapi_first.security"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"module": "openapi_first.templates",
|
"module": "openapi_first.templates",
|
||||||
"resource": "doc://modules/openapi_first.templates"
|
"resource": "doc://modules/openapi_first.templates"
|
||||||
@@ -86,5 +98,33 @@
|
|||||||
{
|
{
|
||||||
"module": "openapi_first.templates.model_app.test_model_app",
|
"module": "openapi_first.templates.model_app.test_model_app",
|
||||||
"resource": "doc://modules/openapi_first.templates.model_app.test_model_app"
|
"resource": "doc://modules/openapi_first.templates.model_app.test_model_app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module": "openapi_first.templates.vet_app",
|
||||||
|
"resource": "doc://modules/openapi_first.templates.vet_app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module": "openapi_first.templates.vet_app.data",
|
||||||
|
"resource": "doc://modules/openapi_first.templates.vet_app.data"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module": "openapi_first.templates.vet_app.main",
|
||||||
|
"resource": "doc://modules/openapi_first.templates.vet_app.main"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module": "openapi_first.templates.vet_app.models",
|
||||||
|
"resource": "doc://modules/openapi_first.templates.vet_app.models"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module": "openapi_first.templates.vet_app.routes",
|
||||||
|
"resource": "doc://modules/openapi_first.templates.vet_app.routes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module": "openapi_first.templates.vet_app.sse",
|
||||||
|
"resource": "doc://modules/openapi_first.templates.vet_app.sse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module": "openapi_first.templates.vet_app.test_vet_app",
|
||||||
|
"resource": "doc://modules/openapi_first.templates.vet_app.test_vet_app"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,702 +0,0 @@
|
|||||||
{
|
|
||||||
"module": "openapi_first.templates",
|
|
||||||
"content": {
|
|
||||||
"path": "openapi_first.templates",
|
|
||||||
"docstring": "Application templates for FastAPI OpenAPI First.\n\nThis package contains example and scaffolding templates intended to be\ncopied into user projects via the ``openapi-first`` CLI.\n\nTemplates in this package are:\n- Reference implementations of OpenAPI-first services\n- Not part of the ``openapi_first`` public or internal API\n- Not intended to be imported as runtime dependencies\n\nThe presence of this file exists solely to:\n- Mark the directory as an explicit Python package\n- Enable deterministic tooling behavior (documentation, packaging)\n- Avoid accidental traversal of non-package directories\n\nNo code in this package should be imported by library consumers.",
|
|
||||||
"objects": {
|
|
||||||
"crud_app": {
|
|
||||||
"name": "crud_app",
|
|
||||||
"kind": "module",
|
|
||||||
"path": "openapi_first.templates.crud_app",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": "OpenAPI-first CRUD application template.\n\nThis package contains a complete, minimal example of an OpenAPI-first\nCRUD service built using the ``openapi_first`` library.\n\nThe application is assembled exclusively from:\n- an OpenAPI specification (``openapi.yaml``)\n- a handler namespace implementing CRUD operations (``routes``)\n- an in-memory mock data store (``data``)\n\nAll HTTP routes, methods, schemas, and operation bindings are defined\nin the OpenAPI specification and enforced at application startup.\nNo decorator-driven routing or implicit framework behavior is used.\n\nThis template demonstrates:\n- operationId-driven server-side route binding\n- explicit HTTP status code control in handlers\n- operationId-driven client usage against the same OpenAPI contract\n- end-to-end validation using in-memory data and tests\n\n----------------------------------------------------------------------\nScaffolding via CLI\n----------------------------------------------------------------------\n\nCreate a new CRUD example service using the bundled template:\n\n openapi-first crud_app\n\nCreate the service in a custom directory:\n\n openapi-first crud_app my-crud-service\n\nList all available application templates:\n\n openapi-first --list\n\nThe CLI copies template files verbatim into the target directory.\nNo code is generated or modified beyond the copied scaffold.\n\n----------------------------------------------------------------------\nClient Usage Example\n----------------------------------------------------------------------\n\nThe same OpenAPI specification used by the server can be used to\nconstruct a strict, operationId-driven HTTP client.\n\nExample client calls for CRUD operations:\n\n from openapi_first.loader import load_openapi\n from openapi_first.client import OpenAPIClient\n\n spec = load_openapi(\"openapi.yaml\")\n client = OpenAPIClient(spec)\n\n # List items\n response = client.list_items()\n\n # Get item by ID\n response = client.get_item(\n path_params={\"item_id\": 1}\n )\n\n # Create item\n response = client.create_item(\n body={\"name\": \"Orange\", \"price\": 0.8}\n )\n\n # Update item\n response = client.update_item(\n path_params={\"item_id\": 1},\n body={\"name\": \"Green Apple\", \"price\": 0.6},\n )\n\n # Delete item\n response = client.delete_item(\n path_params={\"item_id\": 1}\n )\n\nClient guarantees:\n- One callable per OpenAPI ``operationId``\n- No hardcoded URLs or HTTP methods in user code\n- Path and request parameters must match the OpenAPI specification\n- Invalid or incomplete OpenAPI specs fail at client construction time\n\n----------------------------------------------------------------------\nNon-Goals\n----------------------------------------------------------------------\n\nThis template is intentionally minimal and is NOT:\n- production-ready\n- persistent or concurrency-safe\n- a reference architecture for data storage\n\nIt exists solely as a copyable example for learning, testing, and\nbootstrapping OpenAPI-first services.\n\nThis package is not part of the ``openapi_first`` library API surface.",
|
|
||||||
"members": {
|
|
||||||
"data": {
|
|
||||||
"name": "data",
|
|
||||||
"kind": "module",
|
|
||||||
"path": "openapi_first.templates.crud_app.data",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": "In-memory mock data store for CRUD example.\n\nThis module intentionally avoids persistence and concurrency guarantees.\nIt is suitable for demos, tests, and scaffolding only.\n\nIt intentionally avoids\n- persistence\n- concurrency guarantees\n- validation\n- error handling\n\nThe implementation is suitable for:\n- demonstrations\n- tests\n- scaffolding and example services\n\nIt is explicitly NOT suitable for production use.\n\nThis module is not part of the ``openapi_first`` library API surface.",
|
|
||||||
"members": {
|
|
||||||
"Dict": {
|
|
||||||
"name": "Dict",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.templates.crud_app.data.Dict",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('Dict', 'typing.Dict')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"list_items": {
|
|
||||||
"name": "list_items",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.crud_app.data.list_items",
|
|
||||||
"signature": "<bound method Function.signature of Function('list_items', 36, 48)>",
|
|
||||||
"docstring": "Return all items in the data store.\n\nThis function performs no filtering, pagination, or sorting.\nThe returned collection reflects the current in-memory state.\n\nReturns\n-------\nlist[dict]\n A list of item representations."
|
|
||||||
},
|
|
||||||
"get_item": {
|
|
||||||
"name": "get_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.crud_app.data.get_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('get_item', 51, 68)>",
|
|
||||||
"docstring": "Retrieve a single item by ID.\n\nThis function assumes the item exists and will raise ``KeyError``\nif the ID is not present in the store.\n\nParameters\n----------\nitem_id : int\n Identifier of the item to retrieve.\n\nReturns\n-------\ndict\n The stored item representation."
|
|
||||||
},
|
|
||||||
"create_item": {
|
|
||||||
"name": "create_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.crud_app.data.create_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('create_item', 71, 92)>",
|
|
||||||
"docstring": "Create a new item in the data store.\n\nA new integer ID is assigned automatically. No validation is\nperformed on the provided payload.\n\nParameters\n----------\npayload : dict\n Item attributes excluding the ``id`` field.\n\nReturns\n-------\ndict\n The newly created item, including its assigned ID."
|
|
||||||
},
|
|
||||||
"update_item": {
|
|
||||||
"name": "update_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.crud_app.data.update_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('update_item', 95, 117)>",
|
|
||||||
"docstring": "Replace an existing item in the data store.\n\nThis function overwrites the existing item entirely and does not\nperform partial updates or validation. If the item does not exist,\nit will be created implicitly.\n\nParameters\n----------\nitem_id : int\n Identifier of the item to update.\npayload : dict\n Item attributes excluding the ``id`` field.\n\nReturns\n-------\ndict\n The updated item representation."
|
|
||||||
},
|
|
||||||
"delete_item": {
|
|
||||||
"name": "delete_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.crud_app.data.delete_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('delete_item', 120, 132)>",
|
|
||||||
"docstring": "Remove an item from the data store.\n\nThis function assumes the item exists and will raise ``KeyError``\nif the ID is not present.\n\nParameters\n----------\nitem_id : int\n Identifier of the item to delete."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"main": {
|
|
||||||
"name": "main",
|
|
||||||
"kind": "module",
|
|
||||||
"path": "openapi_first.templates.crud_app.main",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": "Application entry point for an OpenAPI-first CRUD example service.\n\nThis module constructs a FastAPI application exclusively from an\nOpenAPI specification and a handler namespace, without using\ndecorator-driven routing.\n\nAll HTTP routes, methods, request/response schemas, and operation\nbindings are defined in the OpenAPI document referenced by\n``openapi_path``. Python callables defined in the ``routes`` module are\nbound to OpenAPI operations strictly via ``operationId``.\n\nThis module contains no routing logic, persistence concerns, or\nframework configuration beyond application assembly.\n\nDesign guarantees:\n- OpenAPI is the single source of truth\n- No undocumented routes can exist\n- Every OpenAPI operationId must resolve to exactly one handler\n- All contract violations fail at application startup\n\nThis file is intended to be used as the ASGI entry point.\n\nExample:\n uvicorn main:app",
|
|
||||||
"members": {
|
|
||||||
"OpenAPIFirstApp": {
|
|
||||||
"name": "OpenAPIFirstApp",
|
|
||||||
"kind": "class",
|
|
||||||
"path": "openapi_first.templates.crud_app.main.OpenAPIFirstApp",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('OpenAPIFirstApp', 'openapi_first.app.OpenAPIFirstApp')>",
|
|
||||||
"docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each `operationId` is mapped to a Python function in the\n supplied routes module.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and `/openapi.json` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```",
|
|
||||||
"members": {
|
|
||||||
"openapi": {
|
|
||||||
"name": "openapi",
|
|
||||||
"kind": "attribute",
|
|
||||||
"path": "openapi_first.templates.crud_app.main.OpenAPIFirstApp.openapi",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('openapi', 'openapi_first.app.OpenAPIFirstApp.openapi')>",
|
|
||||||
"docstring": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"routes": {
|
|
||||||
"name": "routes",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.templates.crud_app.main.routes",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('routes', 'routes')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"app": {
|
|
||||||
"name": "app",
|
|
||||||
"kind": "attribute",
|
|
||||||
"path": "openapi_first.templates.crud_app.main.app",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"routes": {
|
|
||||||
"name": "routes",
|
|
||||||
"kind": "module",
|
|
||||||
"path": "openapi_first.templates.crud_app.routes",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": "CRUD route handlers bound via OpenAPI operationId.\n\nThese handlers explicitly control HTTP status codes to ensure\nruntime behavior matches the OpenAPI contract.\n\nThis module defines OpenAPI-bound operation handlers for a simple CRUD\nservice. Functions in this module are bound to HTTP routes exclusively\nvia OpenAPI ``operationId`` values.\n\nHandlers explicitly control HTTP response status codes to ensure runtime\nbehavior matches the OpenAPI contract. Error conditions are translated\ninto explicit HTTP responses rather than relying on implicit framework\nbehavior.\n\nNo routing decorators or path definitions appear in this module. All\nrouting, HTTP methods, and schemas are defined in the OpenAPI\nspecification.",
|
|
||||||
"members": {
|
|
||||||
"Response": {
|
|
||||||
"name": "Response",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.templates.crud_app.routes.Response",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('Response', 'fastapi.Response')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"HTTPException": {
|
|
||||||
"name": "HTTPException",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.templates.crud_app.routes.HTTPException",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('HTTPException', 'fastapi.HTTPException')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"list_items": {
|
|
||||||
"name": "list_items",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.crud_app.routes.list_items",
|
|
||||||
"signature": "<bound method Function.signature of Function('list_items', 32, 44)>",
|
|
||||||
"docstring": "List all items.\n\nImplements the OpenAPI operation identified by\n``operationId: list_items``.\n\nReturns\n-------\nlist[dict]\n A list of item representations."
|
|
||||||
},
|
|
||||||
"get_item": {
|
|
||||||
"name": "get_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.crud_app.routes.get_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('get_item', 47, 72)>",
|
|
||||||
"docstring": "Retrieve a single item by ID.\n\nImplements the OpenAPI operation identified by\n``operationId: get_item``.\n\nParameters\n----------\nitem_id : int\n Identifier of the item to retrieve.\n\nReturns\n-------\ndict\n The requested item.\n\nRaises\n------\nHTTPException\n 404 if the item does not exist."
|
|
||||||
},
|
|
||||||
"create_item": {
|
|
||||||
"name": "create_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.crud_app.routes.create_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('create_item', 75, 96)>",
|
|
||||||
"docstring": "Create a new item.\n\nImplements the OpenAPI operation identified by\n``operationId: create_item``.\n\nParameters\n----------\npayload : dict\n Item attributes excluding the ``id`` field.\nresponse : fastapi.Response\n Response object used to set the HTTP status code.\n\nReturns\n-------\ndict\n The newly created item."
|
|
||||||
},
|
|
||||||
"update_item": {
|
|
||||||
"name": "update_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.crud_app.routes.update_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('update_item', 99, 126)>",
|
|
||||||
"docstring": "Update an existing item.\n\nImplements the OpenAPI operation identified by\n``operationId: update_item``.\n\nParameters\n----------\nitem_id : int\n Identifier of the item to update.\npayload : dict\n Item attributes excluding the ``id`` field.\n\nReturns\n-------\ndict\n The updated item.\n\nRaises\n------\nHTTPException\n 404 if the item does not exist."
|
|
||||||
},
|
|
||||||
"delete_item": {
|
|
||||||
"name": "delete_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.crud_app.routes.delete_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('delete_item', 129, 158)>",
|
|
||||||
"docstring": "Delete an existing item.\n\nImplements the OpenAPI operation identified by\n``operationId: delete_item``.\n\nParameters\n----------\nitem_id : int\n Identifier of the item to delete.\nresponse : fastapi.Response\n Response object used to set the HTTP status code.\n\nReturns\n-------\nNone\n No content.\n\nRaises\n------\nHTTPException\n 404 if the item does not exist."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"test_crud_app": {
|
|
||||||
"name": "test_crud_app",
|
|
||||||
"kind": "module",
|
|
||||||
"path": "openapi_first.templates.crud_app.test_crud_app",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": "End-to-end tests for the OpenAPI-first CRUD example app.\n\nThese tests validate that all CRUD operations behave correctly\nagainst the in-memory mock data store.\n- OpenAPI specification loading\n- OperationId-driven route binding on the server\n- OperationId-driven client invocation\n- Correct HTTP status codes and response payloads\n\nThe tests exercise all CRUD operations against an in-memory mock data\nstore and assume deterministic behavior within a single process.\n\nThe tests assume:\n- OpenAPI-first route binding\n- In-memory storage (no persistence guarantees)\n- Deterministic behavior in a single process\n- One-to-one correspondence between OpenAPI operationId values and\n server/client callables",
|
|
||||||
"members": {
|
|
||||||
"TestClient": {
|
|
||||||
"name": "TestClient",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.templates.crud_app.test_crud_app.TestClient",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('TestClient', 'fastapi.testclient.TestClient')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"app": {
|
|
||||||
"name": "app",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.templates.crud_app.test_crud_app.app",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('app', 'main.app')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"load_openapi": {
|
|
||||||
"name": "load_openapi",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.crud_app.test_crud_app.load_openapi",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('load_openapi', 'openapi_first.loader.load_openapi')>",
|
|
||||||
"docstring": "Load and validate an OpenAPI 3.x specification from disk.\n\nArgs:\n path (str | Path):\n Filesystem path to an OpenAPI specification file. Supported\n extensions: `.json`, `.yaml`, `.yml`.\n\nReturns:\n dict[str, Any]:\n Parsed and validated OpenAPI specification.\n\nRaises:\n OpenAPISpecLoadError:\n If the file does not exist, cannot be parsed, or fails OpenAPI\n schema validation.\n\nNotes:\n **Guarantees:**\n\n - The specification is parsed based on file extension and validated\n using a strict OpenAPI schema validator.\n - Any error results in an immediate exception, preventing\n application startup."
|
|
||||||
},
|
|
||||||
"OpenAPIClient": {
|
|
||||||
"name": "OpenAPIClient",
|
|
||||||
"kind": "class",
|
|
||||||
"path": "openapi_first.templates.crud_app.test_crud_app.OpenAPIClient",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('OpenAPIClient', 'openapi_first.client.OpenAPIClient')>",
|
|
||||||
"docstring": "OpenAPI-first HTTP client (`httpx`-based).\n\nNotes:\n **Responsibilities:**\n\n - This client derives all callable methods directly from an\n OpenAPI 3.x specification. Each `operationId` becomes a method\n on the client instance.\n\n **Guarantees:**\n\n - One callable per `operationId`.\n - Explicit parameters (path, query, headers, body).\n - No implicit schema inference or mutation.\n - Returns raw `httpx.Response` objects.\n - No response validation or deserialization.\n\nExample:\n ```python\n from openapi_first import loader, client\n\n spec = loader.load_openapi(\"openapi.yaml\")\n\n api = client.OpenAPIClient(\n spec=spec,\n base_url=\"http://localhost:8000\",\n )\n\n # Call operationId: getUser\n response = api.getUser(\n path_params={\"user_id\": 123}\n )\n\n print(response.status_code)\n print(response.json())\n ```",
|
|
||||||
"members": {
|
|
||||||
"spec": {
|
|
||||||
"name": "spec",
|
|
||||||
"kind": "attribute",
|
|
||||||
"path": "openapi_first.templates.crud_app.test_crud_app.OpenAPIClient.spec",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('spec', 'openapi_first.client.OpenAPIClient.spec')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"base_url": {
|
|
||||||
"name": "base_url",
|
|
||||||
"kind": "attribute",
|
|
||||||
"path": "openapi_first.templates.crud_app.test_crud_app.OpenAPIClient.base_url",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('base_url', 'openapi_first.client.OpenAPIClient.base_url')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"client": {
|
|
||||||
"name": "client",
|
|
||||||
"kind": "attribute",
|
|
||||||
"path": "openapi_first.templates.crud_app.test_crud_app.OpenAPIClient.client",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('client', 'openapi_first.client.OpenAPIClient.client')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"operations": {
|
|
||||||
"name": "operations",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.crud_app.test_crud_app.OpenAPIClient.operations",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('operations', 'openapi_first.client.OpenAPIClient.operations')>",
|
|
||||||
"docstring": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"client": {
|
|
||||||
"name": "client",
|
|
||||||
"kind": "attribute",
|
|
||||||
"path": "openapi_first.templates.crud_app.test_crud_app.client",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"spec": {
|
|
||||||
"name": "spec",
|
|
||||||
"kind": "attribute",
|
|
||||||
"path": "openapi_first.templates.crud_app.test_crud_app.spec",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"test_list_items_initial": {
|
|
||||||
"name": "test_list_items_initial",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.crud_app.test_crud_app.test_list_items_initial",
|
|
||||||
"signature": "<bound method Function.signature of Function('test_list_items_initial', 38, 49)>",
|
|
||||||
"docstring": "Initial items should be present."
|
|
||||||
},
|
|
||||||
"test_get_item": {
|
|
||||||
"name": "test_get_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.crud_app.test_crud_app.test_get_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('test_get_item', 52, 62)>",
|
|
||||||
"docstring": "Existing item should be retrievable by ID."
|
|
||||||
},
|
|
||||||
"test_create_item": {
|
|
||||||
"name": "test_create_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.crud_app.test_crud_app.test_create_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('test_create_item', 65, 85)>",
|
|
||||||
"docstring": "Creating a new item should return the created entity."
|
|
||||||
},
|
|
||||||
"test_update_item": {
|
|
||||||
"name": "test_update_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.crud_app.test_crud_app.test_update_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('test_update_item', 88, 112)>",
|
|
||||||
"docstring": "Updating an item should replace its values."
|
|
||||||
},
|
|
||||||
"test_delete_item": {
|
|
||||||
"name": "test_delete_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.crud_app.test_crud_app.test_delete_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('test_delete_item', 115, 125)>",
|
|
||||||
"docstring": "Deleting an item should remove it from the store."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"health_app": {
|
|
||||||
"name": "health_app",
|
|
||||||
"kind": "module",
|
|
||||||
"path": "openapi_first.templates.health_app",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": "OpenAPI-first FastAPI application template.\n\nThis package contains a minimal, fully working example of an\nOpenAPI-first FastAPI service built using the ``openapi_first`` library.\n\nThe application is assembled exclusively from:\n- an OpenAPI specification (``openapi.yaml``)\n- a handler namespace (``routes``)\n\nNo routing decorators, implicit behavior, or framework-specific\nconvenience abstractions are used. All HTTP routes, methods, and\noperation bindings are defined in OpenAPI and enforced at application\nstartup.\n\nThis package is intended to be copied as a starting point for new\nservices via the ``openapi-first`` CLI. It is not part of the\n``openapi_first`` library API surface.\n\n----------------------------------------------------------------------\nScaffolding via CLI\n----------------------------------------------------------------------\n\nCreate a new OpenAPI-first health check service using the bundled\ntemplate:\n\n openapi-first health_app\n\nCreate the service in a custom directory:\n\n openapi-first health_app my-health-service\n\nList all available application templates:\n\n openapi-first --list\n\nThe CLI copies template files verbatim into the target directory.\nNo code is generated or modified beyond the copied scaffold.\n\n----------------------------------------------------------------------\nClient Usage Example\n----------------------------------------------------------------------\n\nThe same OpenAPI specification used by the server can be used to\nconstruct a strict, operationId-driven HTTP client.\n\nExample client call for the ``get_health`` operation:\n\n from openapi_first.loader import load_openapi\n from openapi_first.client import OpenAPIClient\n\n spec = load_openapi(\"openapi.yaml\")\n client = OpenAPIClient(spec)\n\n response = client.get_health()\n\n assert response.status_code == 200\n assert response.json() == {\"status\": \"ok\"}\n\nClient guarantees:\n- One callable per OpenAPI ``operationId``\n- No hardcoded URLs or HTTP methods in user code\n- Path and request parameters must match the OpenAPI specification\n- Invalid or incomplete OpenAPI specs fail at client construction time",
|
|
||||||
"members": {
|
|
||||||
"main": {
|
|
||||||
"name": "main",
|
|
||||||
"kind": "module",
|
|
||||||
"path": "openapi_first.templates.health_app.main",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": "Application entry point for an OpenAPI-first FastAPI service.\n\nThis module constructs a FastAPI application exclusively from an\nOpenAPI specification and a handler namespace, without using\ndecorator-driven routing.\n\nAll HTTP routes, methods, and operation bindings are defined in the\nOpenAPI document referenced by ``openapi_path``. Python callables\ndefined in the ``routes`` module are bound to OpenAPI operations\nstrictly via ``operationId``.\n\nThis module contains no routing logic, request handling, or framework\nconfiguration beyond application assembly.\n\nDesign guarantees:\n- OpenAPI is the single source of truth\n- No undocumented routes can exist\n- Every OpenAPI operationId must resolve to exactly one handler\n- All contract violations fail at application startup\n\nThis file is intended to be used as the ASGI entry point.\n\nExample:\n uvicorn main:app",
|
|
||||||
"members": {
|
|
||||||
"OpenAPIFirstApp": {
|
|
||||||
"name": "OpenAPIFirstApp",
|
|
||||||
"kind": "class",
|
|
||||||
"path": "openapi_first.templates.health_app.main.OpenAPIFirstApp",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('OpenAPIFirstApp', 'openapi_first.app.OpenAPIFirstApp')>",
|
|
||||||
"docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each `operationId` is mapped to a Python function in the\n supplied routes module.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and `/openapi.json` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```",
|
|
||||||
"members": {
|
|
||||||
"openapi": {
|
|
||||||
"name": "openapi",
|
|
||||||
"kind": "attribute",
|
|
||||||
"path": "openapi_first.templates.health_app.main.OpenAPIFirstApp.openapi",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('openapi', 'openapi_first.app.OpenAPIFirstApp.openapi')>",
|
|
||||||
"docstring": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"routes": {
|
|
||||||
"name": "routes",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.templates.health_app.main.routes",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('routes', 'routes')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"app": {
|
|
||||||
"name": "app",
|
|
||||||
"kind": "attribute",
|
|
||||||
"path": "openapi_first.templates.health_app.main.app",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"routes": {
|
|
||||||
"name": "routes",
|
|
||||||
"kind": "module",
|
|
||||||
"path": "openapi_first.templates.health_app.routes",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": "OpenAPI operation handlers.\n\nThis module defines pure Python callables that implement OpenAPI\noperations for this service. Functions in this module are bound to HTTP\nroutes exclusively via OpenAPI ``operationId`` values.\n\nNo routing decorators, HTTP metadata, or framework-specific logic\nshould appear here. All request/response semantics are defined in the\nOpenAPI specification.\n\nThis module serves solely as an operationId namespace.",
|
|
||||||
"members": {
|
|
||||||
"get_health": {
|
|
||||||
"name": "get_health",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.health_app.routes.get_health",
|
|
||||||
"signature": "<bound method Function.signature of Function('get_health', 16, 32)>",
|
|
||||||
"docstring": "Health check operation handler.\n\nThis function implements the OpenAPI operation identified by\n``operationId: get_health``.\n\nIt contains no routing metadata or framework-specific logic.\nRequest binding, HTTP method, and response semantics are defined\nexclusively by the OpenAPI specification.\n\nReturns\n-------\ndict\n A minimal liveness payload indicating service health."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"model_app": {
|
|
||||||
"name": "model_app",
|
|
||||||
"kind": "module",
|
|
||||||
"path": "openapi_first.templates.model_app",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": "OpenAPI-first model-based CRUD application template.\n\nThis package contains a complete, minimal example of an OpenAPI-first\nCRUD service that uses explicit Pydantic domain models for request and\nresponse schemas.\n\nThe application is assembled exclusively from:\n- an OpenAPI specification (``openapi.yaml``)\n- a handler namespace implementing CRUD operations (``routes``)\n- Pydantic domain models (``models``)\n- an in-memory mock data store (``data``)\n\nAll HTTP routes, methods, schemas, and operation bindings are defined\nin the OpenAPI specification and enforced at application startup.\nNo decorator-driven routing or implicit framework behavior is used.\n\nThis template demonstrates:\n- operationId-driven server-side route binding\n- explicit request and response modeling with Pydantic\n- explicit HTTP status code control in handlers\n- operationId-driven client usage against the same OpenAPI contract\n- end-to-end validation using in-memory data and tests\n\n----------------------------------------------------------------------\nScaffolding via CLI\n----------------------------------------------------------------------\n\nCreate a new model-based CRUD example service using the bundled template:\n\n openapi-first model_app\n\nCreate the service in a custom directory:\n\n openapi-first model_app my-model-service\n\nList all available application templates:\n\n openapi-first --list\n\nThe CLI copies template files verbatim into the target directory.\nNo code is generated or modified beyond the copied scaffold.\n\n----------------------------------------------------------------------\nClient Usage Example\n----------------------------------------------------------------------\n\nThe same OpenAPI specification used by the server can be used to\nconstruct a strict, operationId-driven HTTP client.\n\nExample client calls for model-based CRUD operations:\n\n from openapi_first.loader import load_openapi\n from openapi_first.client import OpenAPIClient\n\n spec = load_openapi(\"openapi.yaml\")\n client = OpenAPIClient(spec)\n\n # List items\n response = client.list_items()\n\n # Get item by ID\n response = client.get_item(\n path_params={\"item_id\": 1}\n )\n\n # Create item\n response = client.create_item(\n body={\"name\": \"Orange\", \"price\": 0.8}\n )\n\n # Update item\n response = client.update_item(\n path_params={\"item_id\": 1},\n body={\"name\": \"Green Apple\", \"price\": 0.6},\n )\n\n # Delete item\n response = client.delete_item(\n path_params={\"item_id\": 1}\n )\n\nClient guarantees:\n- One callable per OpenAPI ``operationId``\n- No hardcoded URLs or HTTP methods in user code\n- Request and response payloads conform to Pydantic models\n- Invalid or incomplete OpenAPI specs fail at client construction time\n\n----------------------------------------------------------------------\nNon-Goals\n----------------------------------------------------------------------\n\nThis template is intentionally minimal and is NOT:\n- production-ready\n- persistent or concurrency-safe\n- a reference architecture for data storage\n\nIt exists solely as a copyable example for learning, testing, and\nbootstrapping OpenAPI-first services.\n\nThis package is not part of the ``openapi_first`` library API surface.",
|
|
||||||
"members": {
|
|
||||||
"data": {
|
|
||||||
"name": "data",
|
|
||||||
"kind": "module",
|
|
||||||
"path": "openapi_first.templates.model_app.data",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": "In-memory data store using Pydantic models.\n\nThis module is NOT thread-safe and is intended for demos and scaffolds only.\nThis module provides a minimal, process-local data store for the\nmodel-based CRUD example application. It stores and returns domain\nobjects defined using Pydantic models and is intended solely for\ndemonstration and scaffolding purposes.\n\nThe implementation intentionally avoids:\n- persistence\n- concurrency guarantees\n- transactional semantics\n- validation beyond what Pydantic provides\n\nIt is not part of the ``openapi_first`` library API surface.",
|
|
||||||
"members": {
|
|
||||||
"Dict": {
|
|
||||||
"name": "Dict",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.templates.model_app.data.Dict",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('Dict', 'typing.Dict')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"Item": {
|
|
||||||
"name": "Item",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.templates.model_app.data.Item",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('Item', 'models.Item')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"ItemCreate": {
|
|
||||||
"name": "ItemCreate",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.templates.model_app.data.ItemCreate",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('ItemCreate', 'models.ItemCreate')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"list_items": {
|
|
||||||
"name": "list_items",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.model_app.data.list_items",
|
|
||||||
"signature": "<bound method Function.signature of Function('list_items', 34, 43)>",
|
|
||||||
"docstring": "Return all items in the data store.\n\nReturns\n-------\nlist[Item]\n A list of item domain objects."
|
|
||||||
},
|
|
||||||
"get_item": {
|
|
||||||
"name": "get_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.model_app.data.get_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('get_item', 46, 65)>",
|
|
||||||
"docstring": "Retrieve a single item by ID.\n\nParameters\n----------\nitem_id : int\n Identifier of the item to retrieve.\n\nReturns\n-------\nItem\n The requested item.\n\nRaises\n------\nKeyError\n If the item does not exist."
|
|
||||||
},
|
|
||||||
"create_item": {
|
|
||||||
"name": "create_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.model_app.data.create_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('create_item', 68, 89)>",
|
|
||||||
"docstring": "Create a new item in the data store.\n\nA new identifier is assigned automatically. No additional validation\nis performed beyond Pydantic model validation.\n\nParameters\n----------\npayload : ItemCreate\n Data required to create a new item.\n\nReturns\n-------\nItem\n The newly created item."
|
|
||||||
},
|
|
||||||
"update_item": {
|
|
||||||
"name": "update_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.model_app.data.update_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('update_item', 92, 120)>",
|
|
||||||
"docstring": "Replace an existing item in the data store.\n\nThis function performs a full replacement of the stored item.\nPartial updates are not supported.\n\nParameters\n----------\nitem_id : int\n Identifier of the item to update.\npayload : ItemCreate\n New item data.\n\nReturns\n-------\nItem\n The updated item.\n\nRaises\n------\nKeyError\n If the item does not exist."
|
|
||||||
},
|
|
||||||
"delete_item": {
|
|
||||||
"name": "delete_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.model_app.data.delete_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('delete_item', 123, 137)>",
|
|
||||||
"docstring": "Remove an item from the data store.\n\nParameters\n----------\nitem_id : int\n Identifier of the item to delete.\n\nRaises\n------\nKeyError\n If the item does not exist."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"main": {
|
|
||||||
"name": "main",
|
|
||||||
"kind": "module",
|
|
||||||
"path": "openapi_first.templates.model_app.main",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": "Application entry point for an OpenAPI-first model-based CRUD example service.\n\nThis module constructs a FastAPI application exclusively from an\nOpenAPI specification and a handler namespace, without using\ndecorator-driven routing.\n\nAll HTTP routes, methods, request/response schemas, and operation\nbindings are defined in the OpenAPI document referenced by\n``openapi_path``. Python callables defined in the ``routes`` module are\nbound to OpenAPI operations strictly via ``operationId``.\n\nThis module contains no routing logic, persistence concerns, or\nframework configuration beyond application assembly.\n\nDesign guarantees:\n- OpenAPI is the single source of truth\n- No undocumented routes can exist\n- Every OpenAPI operationId must resolve to exactly one handler\n- All contract violations fail at application startup\n\nThis file is intended to be used as the ASGI entry point.\n\nExample:\n uvicorn main:app",
|
|
||||||
"members": {
|
|
||||||
"OpenAPIFirstApp": {
|
|
||||||
"name": "OpenAPIFirstApp",
|
|
||||||
"kind": "class",
|
|
||||||
"path": "openapi_first.templates.model_app.main.OpenAPIFirstApp",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('OpenAPIFirstApp', 'openapi_first.app.OpenAPIFirstApp')>",
|
|
||||||
"docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each `operationId` is mapped to a Python function in the\n supplied routes module.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and `/openapi.json` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```",
|
|
||||||
"members": {
|
|
||||||
"openapi": {
|
|
||||||
"name": "openapi",
|
|
||||||
"kind": "attribute",
|
|
||||||
"path": "openapi_first.templates.model_app.main.OpenAPIFirstApp.openapi",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('openapi', 'openapi_first.app.OpenAPIFirstApp.openapi')>",
|
|
||||||
"docstring": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"routes": {
|
|
||||||
"name": "routes",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.templates.model_app.main.routes",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('routes', 'routes')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"app": {
|
|
||||||
"name": "app",
|
|
||||||
"kind": "attribute",
|
|
||||||
"path": "openapi_first.templates.model_app.main.app",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"models": {
|
|
||||||
"name": "models",
|
|
||||||
"kind": "module",
|
|
||||||
"path": "openapi_first.templates.model_app.models",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": "Pydantic domain models for the CRUD example.\n\nThis module defines Pydantic models that represent the domain entities\nused by the service. These models are referenced by the OpenAPI\nspecification for request and response schemas.\n\nThe models are declarative and framework-agnostic. They contain no\npersistence logic, validation beyond type constraints, or business\nbehavior.\n\nThis module is not part of the ``openapi_first`` library API surface.\nIt exists solely to support the example application template.",
|
|
||||||
"members": {
|
|
||||||
"BaseModel": {
|
|
||||||
"name": "BaseModel",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.templates.model_app.models.BaseModel",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('BaseModel', 'pydantic.BaseModel')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"ItemBase": {
|
|
||||||
"name": "ItemBase",
|
|
||||||
"kind": "class",
|
|
||||||
"path": "openapi_first.templates.model_app.models.ItemBase",
|
|
||||||
"signature": "<bound method Class.signature of Class('ItemBase', 19, 27)>",
|
|
||||||
"docstring": "Base domain model for an item.\n\nDefines fields common to all item representations.",
|
|
||||||
"members": {
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"kind": "attribute",
|
|
||||||
"path": "openapi_first.templates.model_app.models.ItemBase.name",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"price": {
|
|
||||||
"name": "price",
|
|
||||||
"kind": "attribute",
|
|
||||||
"path": "openapi_first.templates.model_app.models.ItemBase.price",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ItemCreate": {
|
|
||||||
"name": "ItemCreate",
|
|
||||||
"kind": "class",
|
|
||||||
"path": "openapi_first.templates.model_app.models.ItemCreate",
|
|
||||||
"signature": "<bound method Class.signature of Class('ItemCreate', 30, 39)>",
|
|
||||||
"docstring": "Domain model for item creation requests.\n\nThis model is used for request bodies when creating new items.\nIt intentionally excludes the ``id`` field, which is assigned\nby the service."
|
|
||||||
},
|
|
||||||
"Item": {
|
|
||||||
"name": "Item",
|
|
||||||
"kind": "class",
|
|
||||||
"path": "openapi_first.templates.model_app.models.Item",
|
|
||||||
"signature": "<bound method Class.signature of Class('Item', 42, 50)>",
|
|
||||||
"docstring": "Domain model for a persisted item.\n\nThis model represents the full item state returned in responses,\nincluding the server-assigned identifier.",
|
|
||||||
"members": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"kind": "attribute",
|
|
||||||
"path": "openapi_first.templates.model_app.models.Item.id",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"routes": {
|
|
||||||
"name": "routes",
|
|
||||||
"kind": "module",
|
|
||||||
"path": "openapi_first.templates.model_app.routes",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": "CRUD route handlers bound via OpenAPI operationId.\n\nThis module defines OpenAPI-bound operation handlers for a model-based\nCRUD service. Functions in this module are bound to HTTP routes\nexclusively via OpenAPI ``operationId`` values.\n\nHandlers explicitly control HTTP response status codes to ensure runtime\nbehavior matches the OpenAPI contract. Domain models defined using\nPydantic are used for request and response payloads.\n\nNo routing decorators, path definitions, or implicit framework behavior\nappear in this module. All routing, HTTP methods, and schemas are defined\nin the OpenAPI specification.",
|
|
||||||
"members": {
|
|
||||||
"Response": {
|
|
||||||
"name": "Response",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.templates.model_app.routes.Response",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('Response', 'fastapi.Response')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"HTTPException": {
|
|
||||||
"name": "HTTPException",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.templates.model_app.routes.HTTPException",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('HTTPException', 'fastapi.HTTPException')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"ItemCreate": {
|
|
||||||
"name": "ItemCreate",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.templates.model_app.routes.ItemCreate",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('ItemCreate', 'models.ItemCreate')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"list_items": {
|
|
||||||
"name": "list_items",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.model_app.routes.list_items",
|
|
||||||
"signature": "<bound method Function.signature of Function('list_items', 29, 41)>",
|
|
||||||
"docstring": "List all items.\n\nImplements the OpenAPI operation identified by\n``operationId: list_items``.\n\nReturns\n-------\nlist[Item]\n A list of item domain objects."
|
|
||||||
},
|
|
||||||
"get_item": {
|
|
||||||
"name": "get_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.model_app.routes.get_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('get_item', 44, 69)>",
|
|
||||||
"docstring": "Retrieve a single item by ID.\n\nImplements the OpenAPI operation identified by\n``operationId: get_item``.\n\nParameters\n----------\nitem_id : int\n Identifier of the item to retrieve.\n\nReturns\n-------\nItem\n The requested item.\n\nRaises\n------\nHTTPException\n 404 if the item does not exist."
|
|
||||||
},
|
|
||||||
"create_item": {
|
|
||||||
"name": "create_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.model_app.routes.create_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('create_item', 72, 93)>",
|
|
||||||
"docstring": "Create a new item.\n\nImplements the OpenAPI operation identified by\n``operationId: create_item``.\n\nParameters\n----------\npayload : ItemCreate\n Request body describing the item to create.\nresponse : fastapi.Response\n Response object used to set the HTTP status code.\n\nReturns\n-------\nItem\n The newly created item."
|
|
||||||
},
|
|
||||||
"update_item": {
|
|
||||||
"name": "update_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.model_app.routes.update_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('update_item', 96, 123)>",
|
|
||||||
"docstring": "Update an existing item.\n\nImplements the OpenAPI operation identified by\n``operationId: update_item``.\n\nParameters\n----------\nitem_id : int\n Identifier of the item to update.\npayload : ItemCreate\n New item data.\n\nReturns\n-------\nItem\n The updated item.\n\nRaises\n------\nHTTPException\n 404 if the item does not exist."
|
|
||||||
},
|
|
||||||
"delete_item": {
|
|
||||||
"name": "delete_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.model_app.routes.delete_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('delete_item', 126, 156)>",
|
|
||||||
"docstring": "Delete an existing item.\n\nImplements the OpenAPI operation identified by\n``operationId: delete_item``.\n\nParameters\n----------\nitem_id : int\n Identifier of the item to delete.\nresponse : fastapi.Response\n Response object used to set the HTTP status code.\n\nReturns\n-------\nNone\n No content.\n\nRaises\n------\nHTTPException\n 404 if the item does not exist."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"test_model_app": {
|
|
||||||
"name": "test_model_app",
|
|
||||||
"kind": "module",
|
|
||||||
"path": "openapi_first.templates.model_app.test_model_app",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": "End-to-end tests for the OpenAPI-first model CRUD example app.\n\nThese tests validate that all CRUD operations behave correctly\nagainst the in-memory mock data store using Pydantic models.\n- OpenAPI specification loading\n- OperationId-driven route binding on the server\n- OperationId-driven client invocation\n- Pydantic model-based request and response handling\n\nAll CRUD operations are exercised against an in-memory mock data store\nbacked by Pydantic domain models.\n\nThe tests assume:\n- OpenAPI-first route binding\n- Pydantic model validation\n- In-memory storage (no persistence guarantees)\n- Deterministic behavior in a single process",
|
|
||||||
"members": {
|
|
||||||
"TestClient": {
|
|
||||||
"name": "TestClient",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.templates.model_app.test_model_app.TestClient",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('TestClient', 'fastapi.testclient.TestClient')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"app": {
|
|
||||||
"name": "app",
|
|
||||||
"kind": "alias",
|
|
||||||
"path": "openapi_first.templates.model_app.test_model_app.app",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('app', 'main.app')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"load_openapi": {
|
|
||||||
"name": "load_openapi",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.model_app.test_model_app.load_openapi",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('load_openapi', 'openapi_first.loader.load_openapi')>",
|
|
||||||
"docstring": "Load and validate an OpenAPI 3.x specification from disk.\n\nArgs:\n path (str | Path):\n Filesystem path to an OpenAPI specification file. Supported\n extensions: `.json`, `.yaml`, `.yml`.\n\nReturns:\n dict[str, Any]:\n Parsed and validated OpenAPI specification.\n\nRaises:\n OpenAPISpecLoadError:\n If the file does not exist, cannot be parsed, or fails OpenAPI\n schema validation.\n\nNotes:\n **Guarantees:**\n\n - The specification is parsed based on file extension and validated\n using a strict OpenAPI schema validator.\n - Any error results in an immediate exception, preventing\n application startup."
|
|
||||||
},
|
|
||||||
"OpenAPIClient": {
|
|
||||||
"name": "OpenAPIClient",
|
|
||||||
"kind": "class",
|
|
||||||
"path": "openapi_first.templates.model_app.test_model_app.OpenAPIClient",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('OpenAPIClient', 'openapi_first.client.OpenAPIClient')>",
|
|
||||||
"docstring": "OpenAPI-first HTTP client (`httpx`-based).\n\nNotes:\n **Responsibilities:**\n\n - This client derives all callable methods directly from an\n OpenAPI 3.x specification. Each `operationId` becomes a method\n on the client instance.\n\n **Guarantees:**\n\n - One callable per `operationId`.\n - Explicit parameters (path, query, headers, body).\n - No implicit schema inference or mutation.\n - Returns raw `httpx.Response` objects.\n - No response validation or deserialization.\n\nExample:\n ```python\n from openapi_first import loader, client\n\n spec = loader.load_openapi(\"openapi.yaml\")\n\n api = client.OpenAPIClient(\n spec=spec,\n base_url=\"http://localhost:8000\",\n )\n\n # Call operationId: getUser\n response = api.getUser(\n path_params={\"user_id\": 123}\n )\n\n print(response.status_code)\n print(response.json())\n ```",
|
|
||||||
"members": {
|
|
||||||
"spec": {
|
|
||||||
"name": "spec",
|
|
||||||
"kind": "attribute",
|
|
||||||
"path": "openapi_first.templates.model_app.test_model_app.OpenAPIClient.spec",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('spec', 'openapi_first.client.OpenAPIClient.spec')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"base_url": {
|
|
||||||
"name": "base_url",
|
|
||||||
"kind": "attribute",
|
|
||||||
"path": "openapi_first.templates.model_app.test_model_app.OpenAPIClient.base_url",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('base_url', 'openapi_first.client.OpenAPIClient.base_url')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"client": {
|
|
||||||
"name": "client",
|
|
||||||
"kind": "attribute",
|
|
||||||
"path": "openapi_first.templates.model_app.test_model_app.OpenAPIClient.client",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('client', 'openapi_first.client.OpenAPIClient.client')>",
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"operations": {
|
|
||||||
"name": "operations",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.model_app.test_model_app.OpenAPIClient.operations",
|
|
||||||
"signature": "<bound method Alias.signature of Alias('operations', 'openapi_first.client.OpenAPIClient.operations')>",
|
|
||||||
"docstring": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"client": {
|
|
||||||
"name": "client",
|
|
||||||
"kind": "attribute",
|
|
||||||
"path": "openapi_first.templates.model_app.test_model_app.client",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"spec": {
|
|
||||||
"name": "spec",
|
|
||||||
"kind": "attribute",
|
|
||||||
"path": "openapi_first.templates.model_app.test_model_app.spec",
|
|
||||||
"signature": null,
|
|
||||||
"docstring": null
|
|
||||||
},
|
|
||||||
"test_list_items_initial": {
|
|
||||||
"name": "test_list_items_initial",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.model_app.test_model_app.test_list_items_initial",
|
|
||||||
"signature": "<bound method Function.signature of Function('test_list_items_initial', 37, 48)>",
|
|
||||||
"docstring": "Initial items should be present."
|
|
||||||
},
|
|
||||||
"test_get_item": {
|
|
||||||
"name": "test_get_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.model_app.test_model_app.test_get_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('test_get_item', 51, 61)>",
|
|
||||||
"docstring": "Existing item should be retrievable by ID."
|
|
||||||
},
|
|
||||||
"test_create_item": {
|
|
||||||
"name": "test_create_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.model_app.test_model_app.test_create_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('test_create_item', 64, 84)>",
|
|
||||||
"docstring": "Creating a new item should return the created entity."
|
|
||||||
},
|
|
||||||
"test_update_item": {
|
|
||||||
"name": "test_update_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.model_app.test_model_app.test_update_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('test_update_item', 87, 111)>",
|
|
||||||
"docstring": "Updating an item should replace its values."
|
|
||||||
},
|
|
||||||
"test_delete_item": {
|
|
||||||
"name": "test_delete_item",
|
|
||||||
"kind": "function",
|
|
||||||
"path": "openapi_first.templates.model_app.test_model_app.test_delete_item",
|
|
||||||
"signature": "<bound method Function.signature of Function('test_delete_item', 114, 124)>",
|
|
||||||
"docstring": "Deleting an item should remove it from the store."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
59
mkdocs.yml
59
mkdocs.yml
@@ -21,6 +21,31 @@ theme:
|
|||||||
- search.highlight
|
- search.highlight
|
||||||
- search.share
|
- search.share
|
||||||
- search.suggest
|
- search.suggest
|
||||||
|
icon:
|
||||||
|
logo: material/api
|
||||||
|
repo: fontawesome/brands/github
|
||||||
|
markdown_extensions:
|
||||||
|
- pymdownx.superfences
|
||||||
|
- pymdownx.inlinehilite
|
||||||
|
- pymdownx.snippets
|
||||||
|
- admonition
|
||||||
|
- pymdownx.details
|
||||||
|
- pymdownx.highlight:
|
||||||
|
linenums: true
|
||||||
|
anchor_linenums: true
|
||||||
|
line_spans: __span
|
||||||
|
pygments_lang_class: true
|
||||||
|
- pymdownx.tabbed:
|
||||||
|
alternate_style: true
|
||||||
|
- pymdownx.tasklist:
|
||||||
|
custom_checkbox: true
|
||||||
|
- tables
|
||||||
|
- footnotes
|
||||||
|
- pymdownx.caret
|
||||||
|
- pymdownx.tilde
|
||||||
|
- pymdownx.mark
|
||||||
|
extra_css:
|
||||||
|
- https://unpkg.com/dracula-prism/dist/css/dracula-prism.css
|
||||||
plugins:
|
plugins:
|
||||||
- search
|
- search
|
||||||
- mkdocstrings:
|
- mkdocstrings:
|
||||||
@@ -41,35 +66,15 @@ plugins:
|
|||||||
show_category_heading: true
|
show_category_heading: true
|
||||||
show_object_full_path: false
|
show_object_full_path: false
|
||||||
show_symbol_type_heading: true
|
show_symbol_type_heading: true
|
||||||
markdown_extensions:
|
|
||||||
- pymdownx.superfences
|
|
||||||
- pymdownx.inlinehilite
|
|
||||||
- pymdownx.snippets
|
|
||||||
- admonition
|
|
||||||
- pymdownx.details
|
|
||||||
- pymdownx.superfences
|
|
||||||
- pymdownx.highlight:
|
|
||||||
linenums: true
|
|
||||||
anchor_linenums: true
|
|
||||||
line_spans: __span
|
|
||||||
pygments_lang_class: true
|
|
||||||
- pymdownx.tabbed:
|
|
||||||
alternate_style: true
|
|
||||||
- pymdownx.tasklist:
|
|
||||||
custom_checkbox: true
|
|
||||||
- tables
|
|
||||||
- footnotes
|
|
||||||
- pymdownx.caret
|
|
||||||
- pymdownx.tilde
|
|
||||||
- pymdownx.mark
|
|
||||||
site_name: openapi_first
|
site_name: openapi_first
|
||||||
|
docs_dir: docs
|
||||||
nav:
|
nav:
|
||||||
- Home: index.md
|
- Home: lib/index.md
|
||||||
- Application Bootstrap:
|
- Application Bootstrap:
|
||||||
- app.md
|
- lib/app.md
|
||||||
- binder.md
|
- lib/binder.md
|
||||||
- Core Utilities:
|
- Core Utilities:
|
||||||
- loader.md
|
- lib/loader.md
|
||||||
- errors.md
|
- lib/errors.md
|
||||||
- OpenAPI Client:
|
- OpenAPI Client:
|
||||||
- client.md
|
- lib/client.md
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ from . import client
|
|||||||
from . import errors
|
from . import errors
|
||||||
from . import codegen
|
from . import codegen
|
||||||
from . import codegen_routes
|
from . import codegen_routes
|
||||||
|
from . import security
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"app",
|
"app",
|
||||||
@@ -111,4 +112,5 @@ __all__ = [
|
|||||||
"errors",
|
"errors",
|
||||||
"codegen",
|
"codegen",
|
||||||
"codegen_routes",
|
"codegen_routes",
|
||||||
|
"security",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
from . import app as app
|
from . import app as app
|
||||||
from . import binder as binder
|
from . import binder as binder
|
||||||
from . import loader as loader
|
|
||||||
from . import client as client
|
from . import client as client
|
||||||
from . import errors as errors
|
|
||||||
from . import codegen as codegen
|
from . import codegen as codegen
|
||||||
from . import codegen_routes as codegen_routes
|
from . import codegen_routes as codegen_routes
|
||||||
|
from . import errors as errors
|
||||||
|
from . import loader as loader
|
||||||
|
|
||||||
__all__ = ["app", "binder", "loader", "client", "errors", "codegen", "codegen_routes"]
|
__all__ = ["app", "binder", "loader", "client", "errors", "codegen", "codegen_routes"]
|
||||||
|
|||||||
@@ -32,10 +32,36 @@ Notes:
|
|||||||
- Alter FastAPI dependency injection semantics.
|
- Alter FastAPI dependency injection semantics.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
|
||||||
from .loader import load_openapi
|
|
||||||
from .binder import bind_routes
|
from .binder import bind_routes
|
||||||
|
from .loader import load_openapi
|
||||||
|
from .security import make_security_dependencies, parse_security_schemes
|
||||||
|
|
||||||
|
_env_pattern = re.compile(r"\{(\w+)\}")
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_env(value: str) -> str:
|
||||||
|
"""Replace {ENV_VAR} placeholders with values from os.environ."""
|
||||||
|
|
||||||
|
def _replace(m: re.Match) -> str:
|
||||||
|
return os.environ.get(m.group(1), m.group(0))
|
||||||
|
|
||||||
|
return _env_pattern.sub(_replace, value)
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_spec_env_vars(obj):
|
||||||
|
"""Recursively resolve {ENV_VAR} in all string values of the spec."""
|
||||||
|
if isinstance(obj, str):
|
||||||
|
return _resolve_env(obj)
|
||||||
|
if isinstance(obj, dict):
|
||||||
|
return {k: _resolve_spec_env_vars(v) for k, v in obj.items()}
|
||||||
|
if isinstance(obj, list):
|
||||||
|
return [_resolve_spec_env_vars(v) for v in obj]
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
class OpenAPIFirstApp(FastAPI):
|
class OpenAPIFirstApp(FastAPI):
|
||||||
@@ -48,15 +74,19 @@ class OpenAPIFirstApp(FastAPI):
|
|||||||
- `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route
|
- `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route
|
||||||
registration with OpenAPI-driven binding.
|
registration with OpenAPI-driven binding.
|
||||||
- All routes are derived from the provided OpenAPI specification,
|
- All routes are derived from the provided OpenAPI specification,
|
||||||
and each `operationId` is mapped to a Python function in the
|
and each ``operationId`` is mapped to a Python function in the
|
||||||
supplied routes module.
|
supplied routes module.
|
||||||
|
- Auth dependencies are auto-injected from the spec's
|
||||||
|
``securitySchemes`` and per-operation ``security`` fields.
|
||||||
|
|
||||||
**Guarantees:**
|
**Guarantees:**
|
||||||
|
|
||||||
- No route can exist without an OpenAPI declaration.
|
- No route can exist without an OpenAPI declaration.
|
||||||
- No OpenAPI operation can exist without a handler.
|
- No OpenAPI operation can exist without a handler.
|
||||||
- Swagger UI and `/openapi.json` always reflect the provided spec.
|
- Swagger UI and ``/openapi.json`` always reflect the provided spec.
|
||||||
- Handler functions remain framework-agnostic and testable.
|
- Handler functions remain framework-agnostic and testable.
|
||||||
|
- Auth enforcement is driven entirely by the spec — no manual
|
||||||
|
middleware or decorators required.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```python
|
```python
|
||||||
@@ -87,28 +117,33 @@ class OpenAPIFirstApp(FastAPI):
|
|||||||
specification is treated as the authoritative API contract.
|
specification is treated as the authoritative API contract.
|
||||||
routes_module (module):
|
routes_module (module):
|
||||||
Python module containing handler functions whose names correspond
|
Python module containing handler functions whose names correspond
|
||||||
exactly to OpenAPI `operationId` values.
|
exactly to OpenAPI ``operationId`` values.
|
||||||
**fastapi_kwargs (Any):
|
**fastapi_kwargs (Any):
|
||||||
Additional keyword arguments passed directly to
|
Additional keyword arguments passed directly to
|
||||||
`fastapi.FastAPI` (e.g., title, version, middleware, lifespan
|
``fastapi.FastAPI`` (e.g., title, version, middleware, lifespan
|
||||||
handlers).
|
handlers).
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
OpenAPIFirstError:
|
OpenAPIFirstError:
|
||||||
If the OpenAPI specification is invalid, or if any declared
|
If the OpenAPI specification is invalid, or if any declared
|
||||||
`operationId` does not have a corresponding handler function.
|
``operationId`` does not have a corresponding handler function.
|
||||||
"""
|
"""
|
||||||
# Initialize FastAPI normally
|
# Initialize FastAPI normally
|
||||||
super().__init__(**fastapi_kwargs)
|
super().__init__(**fastapi_kwargs)
|
||||||
|
|
||||||
# Load and validate OpenAPI specification
|
# Load and validate OpenAPI specification
|
||||||
self._openapi_spec = load_openapi(openapi_path)
|
self._openapi_spec = _resolve_spec_env_vars(load_openapi(openapi_path))
|
||||||
|
|
||||||
# Bind routes strictly from OpenAPI spec
|
# Parse security schemes and build per-route dependencies
|
||||||
|
security_schemes = parse_security_schemes(self._openapi_spec)
|
||||||
|
security_deps = make_security_dependencies(self._openapi_spec, security_schemes)
|
||||||
|
|
||||||
|
# Bind routes strictly from OpenAPI spec (with security deps)
|
||||||
bind_routes(
|
bind_routes(
|
||||||
app=self,
|
app=self,
|
||||||
spec=self._openapi_spec,
|
spec=self._openapi_spec,
|
||||||
routes_module=routes_module,
|
routes_module=routes_module,
|
||||||
|
security_deps=security_deps,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Override FastAPI's OpenAPI generation
|
# Override FastAPI's OpenAPI generation
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
from fastapi import FastAPI
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
class OpenAPIFirstApp(FastAPI):
|
class OpenAPIFirstApp(FastAPI):
|
||||||
def __init__(self, *, openapi_path: str, routes_module: Any, **fastapi_kwargs: Any) -> None: ...
|
def __init__(
|
||||||
|
self, *, openapi_path: str, routes_module: Any, **fastapi_kwargs: Any
|
||||||
|
) -> None: ...
|
||||||
|
|||||||
@@ -32,12 +32,19 @@ Notes:
|
|||||||
- Interpret OpenAPI semantics beyond routing metadata.
|
- Interpret OpenAPI semantics beyond routing metadata.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from fastapi.routing import APIRoute
|
from fastapi.routing import APIRoute
|
||||||
|
|
||||||
from .errors import MissingOperationHandler
|
from .errors import MissingOperationHandler
|
||||||
|
|
||||||
|
|
||||||
def bind_routes(app, spec: dict, routes_module) -> None:
|
def bind_routes(
|
||||||
|
app,
|
||||||
|
spec: dict,
|
||||||
|
routes_module,
|
||||||
|
security_deps: dict[str, list[Any]] | None = None,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Bind OpenAPI operations to FastAPI routes.
|
Bind OpenAPI operations to FastAPI routes.
|
||||||
|
|
||||||
@@ -49,18 +56,24 @@ def bind_routes(app, spec: dict, routes_module) -> None:
|
|||||||
routes_module (module):
|
routes_module (module):
|
||||||
Python module containing handler functions. Each handler's name MUST
|
Python module containing handler functions. Each handler's name MUST
|
||||||
exactly match an OpenAPI `operationId`.
|
exactly match an OpenAPI `operationId`.
|
||||||
|
security_deps (dict | None):
|
||||||
|
Optional mapping of ``METHOD:/path`` → ``list[Depends(...)]``
|
||||||
|
generated from the spec's ``securitySchemes`` and per-operation
|
||||||
|
``security`` fields.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
MissingOperationHandler:
|
MissingOperationHandler:
|
||||||
If an `operationId` is missing from the spec or if no corresponding
|
If an ``operationId`` is missing from the spec or if no corresponding
|
||||||
handler function exists in the routes module.
|
handler function exists in the routes module.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
**Responsibilities:**
|
**Responsibilities:**
|
||||||
|
|
||||||
- Iterates through the OpenAPI specification paths and methods.
|
- Iterates through the OpenAPI specification paths and methods.
|
||||||
- Resolves each `operationId` to a handler function, and registers
|
- Resolves each ``operationId`` to a handler function, and registers
|
||||||
a corresponding `APIRoute` on the FastAPI application.
|
a corresponding ``APIRoute`` on the FastAPI application.
|
||||||
|
- Injects FastAPI ``Depends()`` for each security requirement found
|
||||||
|
on the operation or inherited from the top-level ``security`` field.
|
||||||
|
|
||||||
**Guarantees:**
|
**Guarantees:**
|
||||||
|
|
||||||
@@ -70,9 +83,16 @@ def bind_routes(app, spec: dict, routes_module) -> None:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
paths = spec.get("paths", {})
|
paths = spec.get("paths", {})
|
||||||
|
security_deps = security_deps or {}
|
||||||
|
|
||||||
|
http_methods = {"get", "put", "post", "delete", "options", "head", "patch", "trace"}
|
||||||
|
|
||||||
for path, methods in paths.items():
|
for path, methods in paths.items():
|
||||||
for http_method, operation in methods.items():
|
for http_method, operation in methods.items():
|
||||||
|
if http_method.lower() not in http_methods or not isinstance(
|
||||||
|
operation, dict
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
operation_id = operation.get("operationId")
|
operation_id = operation.get("operationId")
|
||||||
if not operation_id:
|
if not operation_id:
|
||||||
@@ -88,12 +108,16 @@ def bind_routes(app, spec: dict, routes_module) -> None:
|
|||||||
path=path,
|
path=path,
|
||||||
method=http_method,
|
method=http_method,
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
)
|
) from None
|
||||||
|
|
||||||
|
key = f"{http_method.upper()}:{path}"
|
||||||
|
deps = security_deps.get(key, [])
|
||||||
|
|
||||||
route = APIRoute(
|
route = APIRoute(
|
||||||
path=path,
|
path=path,
|
||||||
endpoint=endpoint,
|
endpoint=endpoint,
|
||||||
methods=[http_method.upper()],
|
methods=[http_method.upper()],
|
||||||
|
dependencies=deps,
|
||||||
name=operation_id,
|
name=operation_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from typing import Any, Dict
|
from typing import Any
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
|
||||||
def bind_routes(app: FastAPI, spec: Dict[str, Any], routes_module: Any) -> None: ...
|
def bind_routes(app: FastAPI, spec: dict[str, Any], routes_module: Any) -> None: ...
|
||||||
|
|||||||
@@ -11,9 +11,8 @@ bundled templates packaged with the library.
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import shutil
|
import shutil
|
||||||
from pathlib import Path
|
|
||||||
from importlib import resources
|
from importlib import resources
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
DEFAULT_TEMPLATE = "health_app"
|
DEFAULT_TEMPLATE = "health_app"
|
||||||
|
|
||||||
@@ -52,14 +51,15 @@ def copy_template(template: str, target_dir: Path) -> None:
|
|||||||
target_dir.mkdir(parents=True, exist_ok=True)
|
target_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
root = resources.files("openapi_first.templates")
|
root = resources.files("openapi_first.templates")
|
||||||
src = root / template
|
|
||||||
|
|
||||||
if not src.exists():
|
if template not in available_templates():
|
||||||
raise FileNotFoundError(
|
raise FileNotFoundError(
|
||||||
f"Template '{template}' not found. "
|
f"Template '{template}' not found. "
|
||||||
f"Available templates: {', '.join(available_templates())}"
|
f"Available templates: {', '.join(available_templates())}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
src = root / template
|
||||||
|
|
||||||
with resources.as_file(src) as path:
|
with resources.as_file(src) as path:
|
||||||
shutil.copytree(path, target_dir, dirs_exist_ok=True)
|
shutil.copytree(path, target_dir, dirs_exist_ok=True)
|
||||||
|
|
||||||
@@ -168,11 +168,11 @@ def main() -> None:
|
|||||||
# Handle the case where someone uses the old CLI style (openapi-first template path)
|
# Handle the case where someone uses the old CLI style (openapi-first template path)
|
||||||
# argparse with subparsers might not automatically handle this if positional args are passed
|
# argparse with subparsers might not automatically handle this if positional args are passed
|
||||||
# but let's assume we want to encourage the new style.
|
# but let's assume we want to encourage the new style.
|
||||||
|
|
||||||
# If no command was provided but positional args were, they might be for scaffolding
|
# If no command was provided but positional args were, they might be for scaffolding
|
||||||
# This is a bit tricky with argparse subparsers.
|
# This is a bit tricky with argparse subparsers.
|
||||||
# For simplicity, let's just support the new explicit commands.
|
# For simplicity, let's just support the new explicit commands.
|
||||||
|
|
||||||
if args.command is None and not any(vars(args).values()):
|
if args.command is None and not any(vars(args).values()):
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ Notes:
|
|||||||
- This module intentionally does NOT: Generate client code, validate request/response schemas, deserialize responses, retry requests, implement authentication helpers, or assume non-2xx responses are failures.
|
- This module intentionally does NOT: Generate client code, validate request/response schemas, deserialize responses, retry requests, implement authentication helpers, or assume non-2xx responses are failures.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Any, Callable, Dict, Optional
|
from collections.abc import Callable
|
||||||
|
from typing import Any
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
@@ -88,8 +89,8 @@ class OpenAPIClient:
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
spec: dict[str, Any],
|
spec: dict[str, Any],
|
||||||
base_url: Optional[str] = None,
|
base_url: str | None = None,
|
||||||
client: Optional[httpx.Client] = None,
|
client: httpx.Client | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Initialize the OpenAPI client.
|
Initialize the OpenAPI client.
|
||||||
@@ -110,7 +111,7 @@ class OpenAPIClient:
|
|||||||
self.base_url = base_url or self._resolve_base_url(spec)
|
self.base_url = base_url or self._resolve_base_url(spec)
|
||||||
self.client = client or httpx.Client(base_url=self.base_url)
|
self.client = client or httpx.Client(base_url=self.base_url)
|
||||||
|
|
||||||
self._operations: Dict[str, Callable[..., httpx.Response]] = {}
|
self._operations: dict[str, Callable[..., httpx.Response]] = {}
|
||||||
self._build_operations()
|
self._build_operations()
|
||||||
|
|
||||||
# ------------------------------------------------------------------ #
|
# ------------------------------------------------------------------ #
|
||||||
@@ -123,7 +124,7 @@ class OpenAPIClient:
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
raise AttributeError(f"No such operationId: {name}") from None
|
raise AttributeError(f"No such operationId: {name}") from None
|
||||||
|
|
||||||
def operations(self) -> Dict[str, Callable[..., httpx.Response]]:
|
def operations(self) -> dict[str, Callable[..., httpx.Response]]:
|
||||||
return dict(self._operations)
|
return dict(self._operations)
|
||||||
|
|
||||||
# ------------------------------------------------------------------ #
|
# ------------------------------------------------------------------ #
|
||||||
@@ -149,7 +150,13 @@ class OpenAPIClient:
|
|||||||
for path, path_item in paths.items():
|
for path, path_item in paths.items():
|
||||||
for method, operation in path_item.items():
|
for method, operation in path_item.items():
|
||||||
if method.lower() not in {
|
if method.lower() not in {
|
||||||
"get", "post", "put", "patch", "delete", "head", "options"
|
"get",
|
||||||
|
"post",
|
||||||
|
"put",
|
||||||
|
"patch",
|
||||||
|
"delete",
|
||||||
|
"head",
|
||||||
|
"options",
|
||||||
}:
|
}:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -181,11 +188,11 @@ class OpenAPIClient:
|
|||||||
|
|
||||||
def call(
|
def call(
|
||||||
*,
|
*,
|
||||||
path_params: Optional[dict[str, Any]] = None,
|
path_params: dict[str, Any] | None = None,
|
||||||
query: Optional[dict[str, Any]] = None,
|
query: dict[str, Any] | None = None,
|
||||||
headers: Optional[dict[str, str]] = None,
|
headers: dict[str, str] | None = None,
|
||||||
body: Optional[Any] = None,
|
body: Any | None = None,
|
||||||
timeout: Optional[float] = None,
|
timeout: float | None = None,
|
||||||
) -> httpx.Response:
|
) -> httpx.Response:
|
||||||
url = self._build_url(path, path_params or {})
|
url = self._build_url(path, path_params or {})
|
||||||
|
|
||||||
@@ -228,9 +235,7 @@ class OpenAPIClient:
|
|||||||
try:
|
try:
|
||||||
formatted_path = path.format(**path_params)
|
formatted_path = path.format(**path_params)
|
||||||
except KeyError as exc:
|
except KeyError as exc:
|
||||||
raise OpenAPIClientError(
|
raise OpenAPIClientError(f"Missing path parameter: {exc.args[0]}") from exc
|
||||||
f"Missing path parameter: {exc.args[0]}"
|
|
||||||
) from exc
|
|
||||||
|
|
||||||
return urljoin(self.base_url, formatted_path.lstrip("/"))
|
return urljoin(self.base_url, formatted_path.lstrip("/"))
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
from typing import Any, Callable, Dict, Optional
|
from collections.abc import Callable
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
from .errors import OpenAPIFirstError
|
from .errors import OpenAPIFirstError
|
||||||
|
|
||||||
class OpenAPIClientError(OpenAPIFirstError): ...
|
class OpenAPIClientError(OpenAPIFirstError): ...
|
||||||
|
|
||||||
class OpenAPIClient:
|
class OpenAPIClient:
|
||||||
spec: Dict[str, Any]
|
spec: dict[str, Any]
|
||||||
base_url: str
|
base_url: str
|
||||||
client: httpx.Client
|
client: httpx.Client
|
||||||
def __init__(self, spec: Dict[str, Any], base_url: Optional[str] = ..., client: Optional[httpx.Client] = ...) -> None: ...
|
def __init__(
|
||||||
|
self,
|
||||||
|
spec: dict[str, Any],
|
||||||
|
base_url: str | None = ...,
|
||||||
|
client: httpx.Client | None = ...,
|
||||||
|
) -> None: ...
|
||||||
def __getattr__(self, name: str) -> Callable[..., httpx.Response]: ...
|
def __getattr__(self, name: str) -> Callable[..., httpx.Response]: ...
|
||||||
def operations(self) -> Dict[str, Callable[..., httpx.Response]]: ...
|
def operations(self) -> dict[str, Callable[..., httpx.Response]]: ...
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ definitions.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from datamodel_code_generator import (
|
from datamodel_code_generator import (
|
||||||
InputFileType,
|
InputFileType,
|
||||||
PythonVersion,
|
PythonVersion,
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ def generate_routes(
|
|||||||
output_dir.mkdir(parents=True, exist_ok=True)
|
output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# Group paths by resource (first non-param path segment)
|
# Group paths by resource (first non-param path segment)
|
||||||
resources: dict[str, list[tuple[str, str, dict]]] = {}
|
resources_map: dict[str, list[tuple[str, str, dict[str, Any]]]] = {}
|
||||||
|
|
||||||
paths = spec.get("paths", {})
|
paths = spec.get("paths", {})
|
||||||
for path, methods in paths.items():
|
for path, methods in paths.items():
|
||||||
@@ -74,18 +74,18 @@ def generate_routes(
|
|||||||
if not segments:
|
if not segments:
|
||||||
continue
|
continue
|
||||||
resource = segments[0]
|
resource = segments[0]
|
||||||
if resource not in resources:
|
if resource not in resources_map:
|
||||||
resources[resource] = []
|
resources_map[resource] = []
|
||||||
|
|
||||||
for http_method, operation in methods.items():
|
for http_method, operation in methods.items():
|
||||||
if http_method.startswith("x-"):
|
if http_method.startswith("x-"):
|
||||||
continue
|
continue
|
||||||
resources[resource].append((path, http_method, operation))
|
resources_map[resource].append((path, http_method, operation))
|
||||||
|
|
||||||
generated_files: list[Path] = []
|
generated_files: list[Path] = []
|
||||||
|
|
||||||
for resource in sorted(resources):
|
for resource in sorted(resources_map):
|
||||||
operations = resources[resource]
|
operations = resources_map[resource]
|
||||||
_validate_operations(resource, operations)
|
_validate_operations(resource, operations)
|
||||||
|
|
||||||
file_path = output_dir / f"{resource}.py"
|
file_path = output_dir / f"{resource}.py"
|
||||||
@@ -116,7 +116,9 @@ _TYPE_MAP: dict[str, str] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _validate_operations(resource: str, operations: list[tuple[str, str, dict]]) -> None:
|
def _validate_operations(
|
||||||
|
resource: str, operations: list[tuple[str, str, dict[str, Any]]]
|
||||||
|
) -> None:
|
||||||
"""Ensure every operation has an operationId."""
|
"""Ensure every operation has an operationId."""
|
||||||
for path, http_method, operation in operations:
|
for path, http_method, operation in operations:
|
||||||
if not operation.get("operationId"):
|
if not operation.get("operationId"):
|
||||||
@@ -144,7 +146,7 @@ def _get_request_body_schema(operation: dict[str, Any]) -> str | None:
|
|||||||
schema = media_info.get("schema", {})
|
schema = media_info.get("schema", {})
|
||||||
ref = schema.get("$ref", "")
|
ref = schema.get("$ref", "")
|
||||||
if ref:
|
if ref:
|
||||||
return ref.rsplit("/", 1)[-1]
|
return str(ref.rsplit("/", 1)[-1])
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -159,7 +161,7 @@ def _get_success_status(operation: dict[str, Any]) -> str:
|
|||||||
return "200"
|
return "200"
|
||||||
|
|
||||||
|
|
||||||
def _needs_any(operations: list[tuple[str, str, dict]]) -> bool:
|
def _needs_any(operations: list[tuple[str, str, dict[str, Any]]]) -> bool:
|
||||||
"""Check if any operation uses a type that requires `from typing import Any`."""
|
"""Check if any operation uses a type that requires `from typing import Any`."""
|
||||||
for _, _, op in operations:
|
for _, _, op in operations:
|
||||||
for param in op.get("parameters", []):
|
for param in op.get("parameters", []):
|
||||||
@@ -176,7 +178,7 @@ def _needs_any(operations: list[tuple[str, str, dict]]) -> bool:
|
|||||||
|
|
||||||
def _generate_resource_file(
|
def _generate_resource_file(
|
||||||
resource: str,
|
resource: str,
|
||||||
operations: list[tuple[str, str, dict]],
|
operations: list[tuple[str, str, dict[str, Any]]],
|
||||||
spec_path: str,
|
spec_path: str,
|
||||||
use_models: bool,
|
use_models: bool,
|
||||||
models_module: str,
|
models_module: str,
|
||||||
@@ -205,7 +207,9 @@ def _generate_resource_file(
|
|||||||
if schema:
|
if schema:
|
||||||
schemas_needed.add(schema)
|
schemas_needed.add(schema)
|
||||||
if schemas_needed:
|
if schemas_needed:
|
||||||
lines.append(f"from {models_module} import {', '.join(sorted(schemas_needed))}")
|
lines.append(
|
||||||
|
f"from {models_module} import {', '.join(sorted(schemas_needed))}"
|
||||||
|
)
|
||||||
|
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
@@ -237,7 +241,6 @@ def _generate_handler(
|
|||||||
schema: dict[str, Any] = param.get("schema", {})
|
schema: dict[str, Any] = param.get("schema", {})
|
||||||
param_type: str = _resolve_type(schema)
|
param_type: str = _resolve_type(schema)
|
||||||
required: bool = param.get("required", False)
|
required: bool = param.get("required", False)
|
||||||
description: str = schema.get("description", schema.get("x-description", ""))
|
|
||||||
default_raw = schema.get("default")
|
default_raw = schema.get("default")
|
||||||
|
|
||||||
if param_in == "path":
|
if param_in == "path":
|
||||||
@@ -277,13 +280,15 @@ def _generate_handler(
|
|||||||
# Document parameters
|
# Document parameters
|
||||||
doc_params: list[tuple[str, str, str]] = []
|
doc_params: list[tuple[str, str, str]] = []
|
||||||
for param in operation.get("parameters", []):
|
for param in operation.get("parameters", []):
|
||||||
name: str = param.get("name", "")
|
doc_name: str = param.get("name", "")
|
||||||
param_in: str = param.get("in", "")
|
doc_in: str = param.get("in", "")
|
||||||
schema: dict[str, Any] = param.get("schema", {})
|
doc_schema: dict[str, Any] = param.get("schema", {})
|
||||||
param_type: str = _resolve_type(schema)
|
doc_type: str = _resolve_type(doc_schema)
|
||||||
description: str = param.get("description", schema.get("x-description", ""))
|
doc_description: str = param.get(
|
||||||
if param_in in ("path", "query"):
|
"description", doc_schema.get("x-description", "")
|
||||||
doc_params.append((name, param_type, description))
|
)
|
||||||
|
if doc_in in ("path", "query"):
|
||||||
|
doc_params.append((doc_name, doc_type, doc_description))
|
||||||
|
|
||||||
if doc_params:
|
if doc_params:
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ Notes:
|
|||||||
These errors should normally cause immediate application failure.
|
These errors should normally cause immediate application failure.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class OpenAPIFirstError(Exception):
|
class OpenAPIFirstError(Exception):
|
||||||
"""
|
"""
|
||||||
Base exception for all OpenAPI-first enforcement errors.
|
Base exception for all OpenAPI-first enforcement errors.
|
||||||
@@ -31,6 +32,7 @@ class OpenAPIFirstError(Exception):
|
|||||||
- All exceptions raised by the OpenAPI-first core should inherit
|
- All exceptions raised by the OpenAPI-first core should inherit
|
||||||
from this type.
|
from this type.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -70,9 +72,6 @@ class MissingOperationHandler(OpenAPIFirstError):
|
|||||||
f"({method.upper()} {path})"
|
f"({method.upper()} {path})"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
message = (
|
message = f"Missing operationId for operation " f"({method.upper()} {path})"
|
||||||
f"Missing operationId for operation "
|
|
||||||
f"({method.upper()} {path})"
|
|
||||||
)
|
|
||||||
|
|
||||||
super().__init__(message)
|
super().__init__(message)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from typing import Optional
|
|
||||||
|
|
||||||
class OpenAPIFirstError(Exception): ...
|
class OpenAPIFirstError(Exception): ...
|
||||||
|
|
||||||
class MissingOperationHandler(OpenAPIFirstError):
|
class MissingOperationHandler(OpenAPIFirstError):
|
||||||
def __init__(self, *, path: str, method: str, operation_id: Optional[str] = ...) -> None: ...
|
def __init__(
|
||||||
|
self, *, path: str, method: str, operation_id: str | None = ...
|
||||||
|
) -> None: ...
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class OpenAPISpecLoadError(OpenAPIFirstError):
|
|||||||
- This error indicates that the OpenAPI document is unreadable,
|
- This error indicates that the OpenAPI document is unreadable,
|
||||||
malformed, or violates the OpenAPI 3.x specification.
|
malformed, or violates the OpenAPI 3.x specification.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, Union
|
from typing import Any
|
||||||
|
|
||||||
from .errors import OpenAPIFirstError
|
from .errors import OpenAPIFirstError
|
||||||
|
|
||||||
class OpenAPISpecLoadError(OpenAPIFirstError): ...
|
class OpenAPISpecLoadError(OpenAPIFirstError): ...
|
||||||
|
|
||||||
def load_openapi(path: Union[str, Path]) -> Dict[str, Any]: ...
|
def load_openapi(path: str | Path) -> dict[str, Any]: ...
|
||||||
|
|||||||
0
openapi_first/py.typed
Normal file
0
openapi_first/py.typed
Normal file
150
openapi_first/security.py
Normal file
150
openapi_first/security.py
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
"""
|
||||||
|
OpenAPI security scheme parsing and auto-generated auth dependencies.
|
||||||
|
|
||||||
|
Reads `securitySchemes` and per-operation `security` from an OpenAPI spec,
|
||||||
|
resolves `{ENV_VAR}` placeholders in `x-` extension fields, and generates
|
||||||
|
FastAPI dependencies for token validation (e.g., Bearer JWT introspection).
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from collections.abc import Callable
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
from fastapi import Depends, HTTPException, Request
|
||||||
|
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||||||
|
|
||||||
|
_env_pattern = re.compile(r"\{(\w+)\}")
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_env(value: str) -> str:
|
||||||
|
"""Replace {ENV_VAR} placeholders with values from os.environ."""
|
||||||
|
|
||||||
|
def _replace(m: re.Match[str]) -> str:
|
||||||
|
return os.environ.get(m.group(1), "")
|
||||||
|
|
||||||
|
return _env_pattern.sub(_replace, value)
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_scheme(scheme: dict[str, Any]) -> dict[str, Any]:
|
||||||
|
"""Recursively resolve env vars in all string-valued fields of a scheme."""
|
||||||
|
resolved: dict[str, Any] = {}
|
||||||
|
for key, value in scheme.items():
|
||||||
|
if isinstance(value, str):
|
||||||
|
resolved[key] = _resolve_env(value)
|
||||||
|
elif isinstance(value, dict):
|
||||||
|
resolved[key] = _resolve_scheme(value)
|
||||||
|
else:
|
||||||
|
resolved[key] = value
|
||||||
|
return resolved
|
||||||
|
|
||||||
|
|
||||||
|
def parse_security_schemes(spec: dict[str, Any]) -> dict[str, dict[str, Any]]:
|
||||||
|
"""Extract and resolve environment variables in security schemes."""
|
||||||
|
raw = spec.get("components", {}).get("securitySchemes", {})
|
||||||
|
return {name: _resolve_scheme(scheme) for name, scheme in raw.items()}
|
||||||
|
|
||||||
|
|
||||||
|
def _make_bearer_dependency(introspect_url: str | None) -> Callable[..., Any]:
|
||||||
|
"""
|
||||||
|
Create a FastAPI dependency that validates a Bearer JWT.
|
||||||
|
|
||||||
|
If *introspect_url* is provided the dependency calls that endpoint
|
||||||
|
with ``{"token": "<token>"}`` and expects ``{"active": true, "user": …}``
|
||||||
|
back. The resolved user dict is stored on ``request.state.user``.
|
||||||
|
|
||||||
|
Without an introspection URL the dependency only extracts the raw token
|
||||||
|
and stores it on ``request.state.token`` — no remote validation.
|
||||||
|
"""
|
||||||
|
bearer = HTTPBearer(auto_error=False)
|
||||||
|
|
||||||
|
async def _bearer_dep(
|
||||||
|
request: Request,
|
||||||
|
credentials: HTTPAuthorizationCredentials | None = Depends(bearer),
|
||||||
|
) -> None:
|
||||||
|
if credentials is None:
|
||||||
|
raise HTTPException(status_code=401, detail="Not authenticated")
|
||||||
|
|
||||||
|
token = credentials.credentials
|
||||||
|
|
||||||
|
if introspect_url:
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient(timeout=5) as client:
|
||||||
|
resp = await client.post(
|
||||||
|
introspect_url,
|
||||||
|
json={"token": token},
|
||||||
|
)
|
||||||
|
except httpx.RequestError:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=503,
|
||||||
|
detail="Authentication service unavailable",
|
||||||
|
) from None
|
||||||
|
|
||||||
|
if resp.status_code != 200:
|
||||||
|
raise HTTPException(status_code=401, detail="Invalid or expired token")
|
||||||
|
|
||||||
|
body = resp.json()
|
||||||
|
if not body.get("active", False):
|
||||||
|
raise HTTPException(status_code=401, detail="Invalid or expired token")
|
||||||
|
|
||||||
|
request.state.user = body.get("user")
|
||||||
|
else:
|
||||||
|
request.state.token = token
|
||||||
|
|
||||||
|
return _bearer_dep
|
||||||
|
|
||||||
|
|
||||||
|
def _build_dependency(
|
||||||
|
scheme_name: str, scheme: dict[str, Any]
|
||||||
|
) -> Callable[..., Any] | None:
|
||||||
|
"""Return a FastAPI dependency callable for *scheme*, or ``None``."""
|
||||||
|
scheme_type = scheme.get("type")
|
||||||
|
|
||||||
|
if scheme_type == "http" and scheme.get("scheme") == "bearer":
|
||||||
|
server_url = scheme.get("x-server-url", "")
|
||||||
|
introspect_path = scheme.get("x-introspect-path", "/introspect")
|
||||||
|
introspect_url = server_url + introspect_path if server_url else None
|
||||||
|
return _make_bearer_dependency(introspect_url)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def make_security_dependencies(
|
||||||
|
spec: dict[str, Any],
|
||||||
|
security_schemes: dict[str, dict[str, Any]],
|
||||||
|
) -> dict[str, list[Any]]:
|
||||||
|
"""
|
||||||
|
Build a mapping of ``METHOD:/path`` → list of ``Depends(...)``.
|
||||||
|
|
||||||
|
The effective security for each operation is resolved by:
|
||||||
|
|
||||||
|
1. Using the operation-level ``security`` field if present.
|
||||||
|
2. Falling back to the top-level ``security`` field.
|
||||||
|
3. An empty list means *no auth required* for that operation.
|
||||||
|
"""
|
||||||
|
global_security = spec.get("security", [])
|
||||||
|
paths = spec.get("paths", {})
|
||||||
|
|
||||||
|
result: dict[str, list[Any]] = {}
|
||||||
|
|
||||||
|
for path, methods in paths.items():
|
||||||
|
for http_method, operation in methods.items():
|
||||||
|
if http_method.startswith("x-"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
op_security = operation.get("security", global_security)
|
||||||
|
|
||||||
|
depends_list: list[Any] = []
|
||||||
|
for sec_req in op_security:
|
||||||
|
for scheme_name in sec_req:
|
||||||
|
scheme = security_schemes.get(scheme_name)
|
||||||
|
if scheme:
|
||||||
|
dep = _build_dependency(scheme_name, scheme)
|
||||||
|
if dep:
|
||||||
|
depends_list.append(Depends(dep))
|
||||||
|
|
||||||
|
key = f"{http_method.upper()}:{path}"
|
||||||
|
result[key] = depends_list
|
||||||
|
|
||||||
|
return result
|
||||||
107
pyproject.toml
107
pyproject.toml
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "openapi-first"
|
name = "openapi-first"
|
||||||
version = "0.0.5"
|
version = "0.0.6"
|
||||||
description = "Strict OpenAPI-first application bootstrap for FastAPI."
|
description = "Strict OpenAPI-first application bootstrap for FastAPI."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
@@ -37,9 +37,11 @@ classifiers = [
|
|||||||
"Programming Language :: Python :: 3.10",
|
"Programming Language :: Python :: 3.10",
|
||||||
"Programming Language :: Python :: 3.11",
|
"Programming Language :: Python :: 3.11",
|
||||||
"Programming Language :: Python :: 3.12",
|
"Programming Language :: Python :: 3.12",
|
||||||
|
"Programming Language :: Python :: 3.13",
|
||||||
"Framework :: FastAPI",
|
"Framework :: FastAPI",
|
||||||
"Topic :: Software Development :: Libraries",
|
"Topic :: Software Development :: Libraries",
|
||||||
"Topic :: Internet :: WWW/HTTP :: HTTP Servers",
|
"Topic :: Internet :: WWW/HTTP :: HTTP Servers",
|
||||||
|
"Typing :: Typed",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -55,6 +57,9 @@ dependencies = [
|
|||||||
|
|
||||||
# Code generation
|
# Code generation
|
||||||
"datamodel-code-generator>=0.25.0",
|
"datamodel-code-generator>=0.25.0",
|
||||||
|
|
||||||
|
# HTTP client for service-to-service (token introspection)
|
||||||
|
"httpx>=0.27.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
@@ -63,9 +68,15 @@ openapi-first = "openapi_first.cli:main"
|
|||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
dev = [
|
dev = [
|
||||||
"pytest>=8.0.0",
|
"pytest>=8.0.0",
|
||||||
|
"pytest-asyncio>=0.21.0",
|
||||||
"pytest-cov>=4.1.0",
|
"pytest-cov>=4.1.0",
|
||||||
|
"black>=23.0.0",
|
||||||
"ruff>=0.3.0",
|
"ruff>=0.3.0",
|
||||||
"mypy>=1.8.0",
|
"mypy>=1.8.0",
|
||||||
|
"build>=1.0.0",
|
||||||
|
"twine>=4.0.0",
|
||||||
|
"pre-commit>=3.4.0",
|
||||||
|
"doc-forge[mcp,mkdocs]>=0.0.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
docs = [
|
docs = [
|
||||||
@@ -74,6 +85,10 @@ docs = [
|
|||||||
"mkdocstrings[python]>=0.24.0",
|
"mkdocstrings[python]>=0.24.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
all = [
|
||||||
|
"openapi-first[dev,docs]",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
Homepage = "https://git.aetoskia.com/aetos/openapi-first"
|
Homepage = "https://git.aetoskia.com/aetos/openapi-first"
|
||||||
@@ -87,15 +102,101 @@ Versions = "https://git.aetoskia.com/aetos/openapi-first/tags"
|
|||||||
packages = { find = { include = ["openapi_first*"] } }
|
packages = { find = { include = ["openapi_first*"] } }
|
||||||
|
|
||||||
[tool.setuptools.package-data]
|
[tool.setuptools.package-data]
|
||||||
openapi_first = ["templates/**/*"]
|
openapi_first = ["py.typed", "templates/**/*"]
|
||||||
|
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
testpaths = ["tests"]
|
||||||
|
asyncio_mode = "auto"
|
||||||
|
python_files = ["test_*.py"]
|
||||||
|
python_classes = ["Test*"]
|
||||||
|
python_functions = ["test_*"]
|
||||||
|
addopts = [
|
||||||
|
"--strict-markers",
|
||||||
|
"--strict-config",
|
||||||
|
"--cov=openapi_first",
|
||||||
|
"--cov-report=term-missing",
|
||||||
|
"--cov-report=html",
|
||||||
|
"--cov-report=xml",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
[tool.black]
|
||||||
|
line-length = 88
|
||||||
|
target-version = ["py310", "py311", "py312", "py313"]
|
||||||
|
include = '\.pyi?$'
|
||||||
|
extend-exclude = '''
|
||||||
|
/(
|
||||||
|
\.eggs
|
||||||
|
| \.git
|
||||||
|
| \.hg
|
||||||
|
| \.mypy_cache
|
||||||
|
| \.tox
|
||||||
|
| \.venv
|
||||||
|
| build
|
||||||
|
| dist
|
||||||
|
| openapi_first/templates
|
||||||
|
)/
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
line-length = 100
|
line-length = 88
|
||||||
target-version = "py310"
|
target-version = "py310"
|
||||||
|
exclude = ["openapi_first/templates"]
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
|
select = [
|
||||||
|
"E",
|
||||||
|
"W",
|
||||||
|
"F",
|
||||||
|
"I",
|
||||||
|
"B",
|
||||||
|
"C4",
|
||||||
|
"UP",
|
||||||
|
]
|
||||||
|
ignore = [
|
||||||
|
"E501",
|
||||||
|
"B008",
|
||||||
|
"C901",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.ruff.lint.per-file-ignores]
|
||||||
|
"__init__.py" = ["F401", "I001"]
|
||||||
|
"tests/*" = ["B008"]
|
||||||
|
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
python_version = "3.10"
|
python_version = "3.10"
|
||||||
strict = true
|
strict = true
|
||||||
|
exclude = [
|
||||||
|
"openapi_first/templates/",
|
||||||
|
"tests/",
|
||||||
|
]
|
||||||
|
files = ["openapi_first"]
|
||||||
|
|
||||||
|
[[tool.mypy.overrides]]
|
||||||
|
module = [
|
||||||
|
"openapi_spec_validator.*",
|
||||||
|
"datamodel_code_generator.*",
|
||||||
|
]
|
||||||
ignore_missing_imports = true
|
ignore_missing_imports = true
|
||||||
|
|
||||||
|
|
||||||
|
[tool.coverage.run]
|
||||||
|
source = ["openapi_first"]
|
||||||
|
omit = [
|
||||||
|
"*/tests/*",
|
||||||
|
"*/test_*.py",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.coverage.report]
|
||||||
|
exclude_lines = [
|
||||||
|
"pragma: no cover",
|
||||||
|
"def __repr__",
|
||||||
|
"if __name__ == .__main__.:",
|
||||||
|
"raise AssertionError",
|
||||||
|
"raise NotImplementedError",
|
||||||
|
"if TYPE_CHECKING:",
|
||||||
|
"@abstractmethod",
|
||||||
|
]
|
||||||
43
tests/conftest.py
Normal file
43
tests/conftest.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
"""Shared fixtures for the openapi-first smoke test suite."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
MINIMAL_SPEC = {
|
||||||
|
"openapi": "3.0.3",
|
||||||
|
"info": {"title": "Smoke Test API", "version": "1.0.0"},
|
||||||
|
"servers": [{"url": "https://api.example.com/v1"}],
|
||||||
|
"paths": {
|
||||||
|
"/health": {
|
||||||
|
"get": {
|
||||||
|
"operationId": "get_health",
|
||||||
|
"responses": {"200": {"description": "OK"}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def minimal_spec() -> dict:
|
||||||
|
"""Return a minimal valid OpenAPI 3.0.3 specification."""
|
||||||
|
return json.loads(json.dumps(MINIMAL_SPEC))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def spec_file(tmp_path, minimal_spec):
|
||||||
|
"""Write the minimal spec to a temp JSON file and return its path."""
|
||||||
|
|
||||||
|
def _write(name: str, spec: dict | None = None) -> str:
|
||||||
|
path = tmp_path / name
|
||||||
|
payload = spec if spec is not None else minimal_spec
|
||||||
|
if name.endswith(".json"):
|
||||||
|
path.write_text(json.dumps(payload), encoding="utf-8")
|
||||||
|
else:
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
path.write_text(yaml.safe_dump(payload), encoding="utf-8")
|
||||||
|
return str(path)
|
||||||
|
|
||||||
|
return _write
|
||||||
89
tests/test_app.py
Normal file
89
tests/test_app.py
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
"""Smoke tests for openapi_first.app."""
|
||||||
|
|
||||||
|
import types
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
|
from openapi_first.app import OpenAPIFirstApp
|
||||||
|
from openapi_first.errors import MissingOperationHandler
|
||||||
|
|
||||||
|
SPEC = {
|
||||||
|
"openapi": "3.0.3",
|
||||||
|
"info": {"title": "App API", "version": "1.0.0"},
|
||||||
|
"servers": [{"url": "https://api.example.com/v1"}],
|
||||||
|
"paths": {
|
||||||
|
"/health": {
|
||||||
|
"get": {
|
||||||
|
"operationId": "get_health",
|
||||||
|
"responses": {"200": {"description": "OK"}},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/greet/{name}": {
|
||||||
|
"get": {
|
||||||
|
"operationId": "get_greet",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"in": "path",
|
||||||
|
"required": True,
|
||||||
|
"schema": {"type": "string"},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {"200": {"description": "OK"}},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _routes_module():
|
||||||
|
module = types.ModuleType("smoke_routes")
|
||||||
|
|
||||||
|
def get_health():
|
||||||
|
return {"status": "ok"}
|
||||||
|
|
||||||
|
def get_greet(name: str):
|
||||||
|
return {"greeting": f"Hello, {name}"}
|
||||||
|
|
||||||
|
module.get_health = get_health
|
||||||
|
module.get_greet = get_greet
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def spec_file(tmp_path):
|
||||||
|
import json
|
||||||
|
|
||||||
|
path = tmp_path / "openapi.json"
|
||||||
|
path.write_text(json.dumps(SPEC), encoding="utf-8")
|
||||||
|
return str(path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_app_routes_served(spec_file):
|
||||||
|
app = OpenAPIFirstApp(openapi_path=spec_file, routes_module=_routes_module())
|
||||||
|
client = TestClient(app)
|
||||||
|
response = client.get("/health")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == {"status": "ok"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_app_path_param_route(spec_file):
|
||||||
|
app = OpenAPIFirstApp(openapi_path=spec_file, routes_module=_routes_module())
|
||||||
|
client = TestClient(app)
|
||||||
|
response = client.get("/greet/Ada")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == {"greeting": "Hello, Ada"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_app_overrides_openapi_schema(spec_file):
|
||||||
|
app = OpenAPIFirstApp(openapi_path=spec_file, routes_module=_routes_module())
|
||||||
|
openapi = app.openapi()
|
||||||
|
assert openapi["info"]["title"] == "App API"
|
||||||
|
assert "/health" in openapi["paths"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_app_missing_handler_fails_at_startup(spec_file):
|
||||||
|
empty = types.ModuleType("empty_routes")
|
||||||
|
with pytest.raises(MissingOperationHandler):
|
||||||
|
OpenAPIFirstApp(openapi_path=spec_file, routes_module=empty)
|
||||||
72
tests/test_binder.py
Normal file
72
tests/test_binder.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
"""Smoke tests for openapi_first.binder."""
|
||||||
|
|
||||||
|
import types
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
from openapi_first.binder import bind_routes
|
||||||
|
from openapi_first.errors import MissingOperationHandler
|
||||||
|
|
||||||
|
|
||||||
|
def _routes_module():
|
||||||
|
module = types.ModuleType("smoke_routes")
|
||||||
|
|
||||||
|
def get_health():
|
||||||
|
return {"status": "ok"}
|
||||||
|
|
||||||
|
module.get_health = get_health
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def test_bind_routes_registers_matching_routes(minimal_spec):
|
||||||
|
from fastapi.routing import APIRoute
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
bind_routes(app, minimal_spec, _routes_module())
|
||||||
|
bound = [r for r in app.router.routes if isinstance(r, APIRoute)]
|
||||||
|
assert len(bound) == 1
|
||||||
|
assert bound[0].name == "get_health"
|
||||||
|
assert bound[0].path == "/health"
|
||||||
|
assert "GET" in bound[0].methods
|
||||||
|
|
||||||
|
|
||||||
|
def test_bind_routes_missing_handler(minimal_spec):
|
||||||
|
app = FastAPI()
|
||||||
|
module = types.ModuleType("empty_routes")
|
||||||
|
with pytest.raises(MissingOperationHandler):
|
||||||
|
bind_routes(app, minimal_spec, module)
|
||||||
|
|
||||||
|
|
||||||
|
def test_bind_routes_missing_operation_id(spec_file):
|
||||||
|
spec = {
|
||||||
|
"openapi": "3.0.3",
|
||||||
|
"info": {"title": "No OpId", "version": "1.0.0"},
|
||||||
|
"paths": {"/health": {"get": {"responses": {"200": {"description": "OK"}}}}},
|
||||||
|
}
|
||||||
|
app = FastAPI()
|
||||||
|
with pytest.raises(MissingOperationHandler):
|
||||||
|
bind_routes(app, spec, _routes_module())
|
||||||
|
|
||||||
|
|
||||||
|
def test_bind_routes_ignores_spec_only_paths():
|
||||||
|
from fastapi.routing import APIRoute
|
||||||
|
|
||||||
|
spec = {
|
||||||
|
"openapi": "3.0.3",
|
||||||
|
"info": {"title": "Path Item", "version": "1.0.0"},
|
||||||
|
"paths": {
|
||||||
|
"/health": {
|
||||||
|
"parameters": [{"name": "x-trace", "in": "header"}],
|
||||||
|
"get": {
|
||||||
|
"operationId": "get_health",
|
||||||
|
"responses": {"200": {"description": "OK"}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
app = FastAPI()
|
||||||
|
bind_routes(app, spec, _routes_module())
|
||||||
|
bound = [r for r in app.router.routes if isinstance(r, APIRoute)]
|
||||||
|
assert len(bound) == 1
|
||||||
|
assert bound[0].name == "get_health"
|
||||||
130
tests/test_client.py
Normal file
130
tests/test_client.py
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
"""Smoke tests for openapi_first.client."""
|
||||||
|
|
||||||
|
import types
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from openapi_first.client import OpenAPIClient, OpenAPIClientError
|
||||||
|
|
||||||
|
SPEC = {
|
||||||
|
"openapi": "3.0.3",
|
||||||
|
"info": {"title": "Client API", "version": "1.0.0"},
|
||||||
|
"servers": [{"url": "https://api.example.com/v1"}],
|
||||||
|
"paths": {
|
||||||
|
"/users/{user_id}": {
|
||||||
|
"get": {
|
||||||
|
"operationId": "get_user",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "user_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": True,
|
||||||
|
"schema": {"type": "integer"},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {"200": {"description": "OK"}},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/users": {
|
||||||
|
"post": {
|
||||||
|
"operationId": "create_user",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {"application/json": {"schema": {"type": "object"}}}
|
||||||
|
},
|
||||||
|
"responses": {"201": {"description": "Created"}},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class _RecordingClient:
|
||||||
|
"""Minimal httpx.Client stand-in that records and routes requests."""
|
||||||
|
|
||||||
|
def __init__(self, base_url=None):
|
||||||
|
self.base_url = base_url
|
||||||
|
self.requests = []
|
||||||
|
|
||||||
|
def request(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
params=None,
|
||||||
|
headers=None,
|
||||||
|
json=None,
|
||||||
|
content=None,
|
||||||
|
timeout=None,
|
||||||
|
):
|
||||||
|
self.requests.append(
|
||||||
|
{
|
||||||
|
"method": method,
|
||||||
|
"url": url,
|
||||||
|
"params": params,
|
||||||
|
"headers": headers,
|
||||||
|
"json": json,
|
||||||
|
"content": content,
|
||||||
|
"timeout": timeout,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
response = types.SimpleNamespace(status_code=200, json=lambda: {"ok": True})
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_base_url_from_servers():
|
||||||
|
client = OpenAPIClient(SPEC)
|
||||||
|
assert client.base_url == "https://api.example.com/v1/"
|
||||||
|
|
||||||
|
|
||||||
|
def test_operation_methods_are_generated(minimal_spec):
|
||||||
|
client = OpenAPIClient(minimal_spec, base_url="http://test")
|
||||||
|
assert "get_health" in client.operations()
|
||||||
|
assert callable(client.get_health)
|
||||||
|
|
||||||
|
|
||||||
|
def test_unknown_operation_raises_attribute_error(minimal_spec):
|
||||||
|
client = OpenAPIClient(minimal_spec, base_url="http://test")
|
||||||
|
with pytest.raises(AttributeError):
|
||||||
|
client.does_not_exist()
|
||||||
|
|
||||||
|
|
||||||
|
def test_path_param_formatting_and_urljoin():
|
||||||
|
rec = _RecordingClient()
|
||||||
|
client = OpenAPIClient(SPEC, base_url="https://host", client=rec)
|
||||||
|
client.get_user(path_params={"user_id": 42})
|
||||||
|
assert rec.requests[0]["url"] == "https://host/users/42"
|
||||||
|
|
||||||
|
|
||||||
|
def test_missing_required_path_param():
|
||||||
|
rec = _RecordingClient()
|
||||||
|
client = OpenAPIClient(SPEC, base_url="https://host", client=rec)
|
||||||
|
with pytest.raises(OpenAPIClientError):
|
||||||
|
client.get_user(path_params={})
|
||||||
|
|
||||||
|
|
||||||
|
def test_json_body_defaults_content_type():
|
||||||
|
rec = _RecordingClient()
|
||||||
|
client = OpenAPIClient(SPEC, base_url="https://host", client=rec)
|
||||||
|
client.create_user(body={"name": "Ada"})
|
||||||
|
req = rec.requests[0]
|
||||||
|
assert req["json"] == {"name": "Ada"}
|
||||||
|
assert req["headers"].get("Content-Type") == "application/json"
|
||||||
|
|
||||||
|
|
||||||
|
def test_missing_server_raises():
|
||||||
|
spec = dict(SPEC)
|
||||||
|
spec.pop("servers")
|
||||||
|
with pytest.raises(OpenAPIClientError):
|
||||||
|
OpenAPIClient(spec)
|
||||||
|
|
||||||
|
|
||||||
|
def test_duplicate_operation_id_raises():
|
||||||
|
spec = dict(SPEC)
|
||||||
|
spec["paths"]["/extra"] = {
|
||||||
|
"get": {
|
||||||
|
"operationId": "get_user",
|
||||||
|
"responses": {"200": {"description": "OK"}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(OpenAPIClientError):
|
||||||
|
OpenAPIClient(spec)
|
||||||
63
tests/test_loader.py
Normal file
63
tests/test_loader.py
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
"""Smoke tests for openapi_first.loader."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from openapi_first.loader import OpenAPISpecLoadError, load_openapi
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_openapi_json(spec_file, minimal_spec):
|
||||||
|
path = spec_file("openapi.json")
|
||||||
|
spec = load_openapi(path)
|
||||||
|
assert spec == minimal_spec
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_openapi_yaml(spec_file, minimal_spec):
|
||||||
|
path = spec_file("openapi.yaml")
|
||||||
|
spec = load_openapi(path)
|
||||||
|
assert spec == minimal_spec
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_missing_file(tmp_path):
|
||||||
|
with pytest.raises(OpenAPISpecLoadError):
|
||||||
|
load_openapi(tmp_path / "missing.json")
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_unsupported_extension(tmp_path):
|
||||||
|
path = tmp_path / "openapi.txt"
|
||||||
|
path.write_text("{}", encoding="utf-8")
|
||||||
|
with pytest.raises(OpenAPISpecLoadError):
|
||||||
|
load_openapi(path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_invalid_spec(tmp_path):
|
||||||
|
path = tmp_path / "invalid.yaml"
|
||||||
|
path.write_text("not: [valid: openapi", encoding="utf-8")
|
||||||
|
with pytest.raises(OpenAPISpecLoadError):
|
||||||
|
load_openapi(path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_validation_failure(tmp_path):
|
||||||
|
path = tmp_path / "bad.yaml"
|
||||||
|
path.write_text("openapi: 3.0.3\ninfo: {}\n", encoding="utf-8")
|
||||||
|
with pytest.raises(OpenAPISpecLoadError):
|
||||||
|
load_openapi(path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_env_var_resolution(spec_file, monkeypatch):
|
||||||
|
monkeypatch.setenv("AUTH_HOST", "http://auth.internal")
|
||||||
|
spec = {
|
||||||
|
"openapi": "3.0.3",
|
||||||
|
"info": {"title": "Env API", "version": "1.0.0"},
|
||||||
|
"servers": [{"url": "{AUTH_HOST}/v1"}],
|
||||||
|
"paths": {
|
||||||
|
"/ping": {
|
||||||
|
"get": {
|
||||||
|
"operationId": "get_ping",
|
||||||
|
"responses": {"200": {"description": "OK"}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
path = spec_file("env.yaml", spec)
|
||||||
|
loaded = load_openapi(path)
|
||||||
|
assert loaded["servers"][0]["url"] == "{AUTH_HOST}/v1"
|
||||||
Reference in New Issue
Block a user