azure-functions-openapi 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- azure_functions_openapi/__init__.py +3 -0
- azure_functions_openapi/decorator.py +143 -0
- azure_functions_openapi/openapi.py +99 -0
- azure_functions_openapi/swagger_ui.py +28 -0
- azure_functions_openapi/utils.py +20 -0
- azure_functions_openapi-0.4.0.dist-info/METADATA +244 -0
- azure_functions_openapi-0.4.0.dist-info/RECORD +9 -0
- azure_functions_openapi-0.4.0.dist-info/WHEEL +4 -0
- azure_functions_openapi-0.4.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# src/azure_functions_openapi/decorator.py
|
|
2
|
+
from typing import Any, Callable, Dict, List, Optional, Type, TypeVar
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
# Define a generic type variable for functions
|
|
7
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
|
8
|
+
|
|
9
|
+
# Global registry to hold OpenAPI metadata for each function
|
|
10
|
+
_openapi_registry: Dict[str, Dict[str, Any]] = {}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def openapi(
|
|
14
|
+
# ── basic metadata ───────────────────────────────────────────
|
|
15
|
+
summary: str = "",
|
|
16
|
+
description: str = "",
|
|
17
|
+
tags: Optional[List[str]] = None,
|
|
18
|
+
operation_id: Optional[str] = None,
|
|
19
|
+
# ── routing information ─────────────────────────────────────
|
|
20
|
+
route: Optional[str] = None,
|
|
21
|
+
method: Optional[str] = None,
|
|
22
|
+
parameters: Optional[List[Dict[str, Any]]] = None,
|
|
23
|
+
# ── request / response schema ───────────────────────────────
|
|
24
|
+
request_model: Optional[Type[BaseModel]] = None,
|
|
25
|
+
request_body: Optional[Dict[str, Any]] = None,
|
|
26
|
+
response_model: Optional[Type[BaseModel]] = None,
|
|
27
|
+
response: Optional[Dict[int, Dict[str, Any]]] = None,
|
|
28
|
+
) -> Callable[[F], F]:
|
|
29
|
+
"""
|
|
30
|
+
Decorator that attaches OpenAPI metadata to an Azure Functions handler.
|
|
31
|
+
|
|
32
|
+
Examples
|
|
33
|
+
--------
|
|
34
|
+
### 1 · Minimal “Hello World”
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
@app.route(route="hello")
|
|
38
|
+
@openapi(summary="Hello", description="Returns plain text.")
|
|
39
|
+
def hello(req: func.HttpRequest) -> func.HttpResponse:
|
|
40
|
+
return func.HttpResponse("Hello, world!", status_code=200)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 2 · Pydantic-powered JSON API
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from pydantic import BaseModel
|
|
47
|
+
|
|
48
|
+
class TodoRequest(BaseModel):
|
|
49
|
+
title: str
|
|
50
|
+
done: bool = False
|
|
51
|
+
|
|
52
|
+
class TodoResponse(BaseModel):
|
|
53
|
+
id: int
|
|
54
|
+
title: str
|
|
55
|
+
done: bool
|
|
56
|
+
|
|
57
|
+
@app.route(route="todos/{id}", method="put")
|
|
58
|
+
@openapi(
|
|
59
|
+
summary="Update a todo item",
|
|
60
|
+
description=\"""Update a todo and return the updated document.\""",
|
|
61
|
+
tags=["Todo"],
|
|
62
|
+
parameters=[{"name": "id", "in": "path", "required": True, "schema": {"type": "integer"}}],
|
|
63
|
+
request_model=TodoRequest,
|
|
64
|
+
response_model=TodoResponse,
|
|
65
|
+
operation_id="updateTodo",
|
|
66
|
+
)
|
|
67
|
+
def update_todo(req: func.HttpRequest) -> func.HttpResponse:
|
|
68
|
+
# ... business logic ...
|
|
69
|
+
body = TodoRequest.model_validate_json(req.get_body())
|
|
70
|
+
todo = TodoResponse(id=1, **body.model_dump())
|
|
71
|
+
return func.HttpResponse(
|
|
72
|
+
todo.model_dump_json(),
|
|
73
|
+
status_code=200,
|
|
74
|
+
mimetype="application/json",
|
|
75
|
+
)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
After starting the Function App you get:
|
|
79
|
+
|
|
80
|
+
* **Swagger UI** → `http://localhost:7071/api/docs`
|
|
81
|
+
* **Raw JSON spec** → `http://localhost:7071/api/openapi.json`
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
summary:
|
|
86
|
+
Short description shown in Swagger UI.
|
|
87
|
+
description:
|
|
88
|
+
Longer Markdown-enabled description.
|
|
89
|
+
tags:
|
|
90
|
+
List of group tags.
|
|
91
|
+
operation_id:
|
|
92
|
+
Custom operationId (defaults to function name).
|
|
93
|
+
route:
|
|
94
|
+
Override for the HTTP route path (e.g. "/items/{id}").
|
|
95
|
+
method:
|
|
96
|
+
Explicit HTTP method if not inferrable.
|
|
97
|
+
parameters:
|
|
98
|
+
List of param objects (query/path/header/cookie).
|
|
99
|
+
request_model:
|
|
100
|
+
Pydantic model used to derive requestBody schema.
|
|
101
|
+
request_body:
|
|
102
|
+
Raw requestBody schema (if you don’t use Pydantic).
|
|
103
|
+
response_model:
|
|
104
|
+
Pydantic model used to derive 200-response schema.
|
|
105
|
+
response:
|
|
106
|
+
Manual responses dict keyed by status code.
|
|
107
|
+
|
|
108
|
+
Returns
|
|
109
|
+
-------
|
|
110
|
+
Callable
|
|
111
|
+
The original function, with its name stored in `_openapi_registry`.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
def decorator(func: F) -> F:
|
|
115
|
+
_openapi_registry[func.__name__] = {
|
|
116
|
+
# ── basic metadata ────────────────────────────────
|
|
117
|
+
"summary": summary,
|
|
118
|
+
"description": description,
|
|
119
|
+
"tags": tags or ["default"],
|
|
120
|
+
"operation_id": operation_id,
|
|
121
|
+
# ── routing info ─────────────────────────────────
|
|
122
|
+
"route": route,
|
|
123
|
+
"method": method,
|
|
124
|
+
"parameters": parameters or [],
|
|
125
|
+
# ── request / response schema ────────────────────
|
|
126
|
+
"request_model": request_model,
|
|
127
|
+
"request_body": request_body,
|
|
128
|
+
"response_model": response_model,
|
|
129
|
+
"response": response or {},
|
|
130
|
+
}
|
|
131
|
+
return func
|
|
132
|
+
|
|
133
|
+
return decorator
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def get_openapi_registry() -> Dict[str, Dict[str, Any]]:
|
|
137
|
+
"""
|
|
138
|
+
Retrieve OpenAPI metadata for all registered functions.
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
A dictionary where each key is a function name and value is its OpenAPI metadata.
|
|
142
|
+
"""
|
|
143
|
+
return _openapi_registry
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# src/azure_functions_openapi/openapi.py
|
|
2
|
+
import json
|
|
3
|
+
from typing import Any, Dict, List
|
|
4
|
+
|
|
5
|
+
import yaml
|
|
6
|
+
|
|
7
|
+
from azure_functions_openapi.decorator import get_openapi_registry
|
|
8
|
+
from azure_functions_openapi.utils import model_to_schema
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def generate_openapi_spec(title: str = "API", version: str = "1.0.0") -> Dict[str, Any]:
|
|
12
|
+
"""
|
|
13
|
+
Compile an OpenAPI-3 specification from the registry.
|
|
14
|
+
No base-path is added; `route=` is used exactly as provided.
|
|
15
|
+
"""
|
|
16
|
+
registry = get_openapi_registry()
|
|
17
|
+
paths: Dict[str, Dict[str, Any]] = {}
|
|
18
|
+
|
|
19
|
+
for func_name, meta in registry.items():
|
|
20
|
+
# route & method --------------------------------------------------
|
|
21
|
+
path = meta.get("route") or f"/{func_name}"
|
|
22
|
+
method = (meta.get("method") or "get").lower()
|
|
23
|
+
|
|
24
|
+
# responses -------------------------------------------------------
|
|
25
|
+
responses: Dict[str, Any] = {}
|
|
26
|
+
for status, detail in meta.get("response", {}).items():
|
|
27
|
+
resp = {"description": detail.get("description", "")}
|
|
28
|
+
if "content" in detail:
|
|
29
|
+
resp["content"] = detail["content"]
|
|
30
|
+
responses[str(status)] = resp
|
|
31
|
+
|
|
32
|
+
if meta.get("response_model"):
|
|
33
|
+
responses["200"] = {
|
|
34
|
+
"description": "Successful Response",
|
|
35
|
+
"content": {
|
|
36
|
+
"application/json": {"schema": model_to_schema(meta["response_model"])}
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# operation object ------------------------------------------------
|
|
41
|
+
op: Dict[str, Any] = {
|
|
42
|
+
"summary": meta.get("summary", ""),
|
|
43
|
+
"description": meta.get("description", ""),
|
|
44
|
+
"operationId": meta.get("operation_id") or f"{method}_{func_name}",
|
|
45
|
+
"tags": meta.get("tags") or ["default"],
|
|
46
|
+
"responses": responses,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# parameters ------------------------------------------------------
|
|
50
|
+
parameters: List[Dict[str, Any]] = meta.get("parameters", [])
|
|
51
|
+
if parameters:
|
|
52
|
+
op["parameters"] = parameters
|
|
53
|
+
|
|
54
|
+
# requestBody (POST/PUT/PATCH) ------------------------------------
|
|
55
|
+
if method in {"post", "put", "patch"}:
|
|
56
|
+
if meta.get("request_body"):
|
|
57
|
+
op["requestBody"] = {
|
|
58
|
+
"required": True,
|
|
59
|
+
"content": {"application/json": {"schema": meta["request_body"]}},
|
|
60
|
+
}
|
|
61
|
+
elif meta.get("request_model"):
|
|
62
|
+
op["requestBody"] = {
|
|
63
|
+
"required": True,
|
|
64
|
+
"content": {
|
|
65
|
+
"application/json": {"schema": model_to_schema(meta["request_model"])}
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# merge into paths (support multiple methods per route) ----------
|
|
70
|
+
paths.setdefault(path, {})[method] = op
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
"openapi": "3.0.0",
|
|
74
|
+
"info": {
|
|
75
|
+
"title": title,
|
|
76
|
+
"version": version,
|
|
77
|
+
"description": (
|
|
78
|
+
"Auto-generated OpenAPI documentation. "
|
|
79
|
+
"Markdown supported in descriptions (CommonMark)."
|
|
80
|
+
),
|
|
81
|
+
},
|
|
82
|
+
"paths": paths,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def get_openapi_json() -> str:
|
|
87
|
+
"""Return the spec as pretty-printed JSON (UTF-8).
|
|
88
|
+
Returns:
|
|
89
|
+
str: OpenAPI spec in JSON format.
|
|
90
|
+
"""
|
|
91
|
+
return json.dumps(generate_openapi_spec(), indent=2, ensure_ascii=False)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def get_openapi_yaml() -> str:
|
|
95
|
+
"""Return the spec as YAML.
|
|
96
|
+
Returns:
|
|
97
|
+
str: OpenAPI spec in YAML format.
|
|
98
|
+
"""
|
|
99
|
+
return yaml.safe_dump(generate_openapi_spec(), sort_keys=False, allow_unicode=True)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from azure.functions import HttpResponse
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def render_swagger_ui() -> HttpResponse:
|
|
5
|
+
html_content = """
|
|
6
|
+
<!DOCTYPE html>
|
|
7
|
+
<html>
|
|
8
|
+
<head>
|
|
9
|
+
<title>Swagger UI</title>
|
|
10
|
+
<link rel="stylesheet"
|
|
11
|
+
type="text/css"
|
|
12
|
+
href="https://cdn.jsdelivr.net/npm/swagger-ui-dist/swagger-ui.css" />
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<div id="swagger-ui"></div>
|
|
16
|
+
<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist/swagger-ui-bundle.js"></script>
|
|
17
|
+
<script>
|
|
18
|
+
const ui = SwaggerUIBundle({
|
|
19
|
+
url: '/api/openapi.json',
|
|
20
|
+
dom_id: '#swagger-ui',
|
|
21
|
+
presets: [SwaggerUIBundle.presets.apis],
|
|
22
|
+
layout: 'BaseLayout'
|
|
23
|
+
});
|
|
24
|
+
</script>
|
|
25
|
+
</body>
|
|
26
|
+
</html>
|
|
27
|
+
"""
|
|
28
|
+
return HttpResponse(html_content, mimetype="text/html")
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# src/azure_functions_openapi/utils.py
|
|
2
|
+
from typing import Any, Dict, cast
|
|
3
|
+
|
|
4
|
+
from packaging import version
|
|
5
|
+
import pydantic
|
|
6
|
+
|
|
7
|
+
PYDANTIC_V2 = version.parse(pydantic.__version__) >= version.parse("2.0.0")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def model_to_schema(model_cls: Any) -> Dict[str, Any]:
|
|
11
|
+
"""Return JSON schema from a Pydantic model class.
|
|
12
|
+
Parameters:
|
|
13
|
+
model_cls: Pydantic model class.
|
|
14
|
+
Returns:
|
|
15
|
+
Dict[str, Any]: JSON schema representation of the model.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
if PYDANTIC_V2:
|
|
19
|
+
return cast(Dict[str, Any], model_cls.model_json_schema())
|
|
20
|
+
return cast(Dict[str, Any], model_cls.schema())
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: azure-functions-openapi
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: OpenAPI (Swagger) integration for Python-based Azure Functions
|
|
5
|
+
Author-email: Yeongseon Choe <yeongseon.choe@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Python: <3.13,>=3.9
|
|
9
|
+
Requires-Dist: azure-functions
|
|
10
|
+
Requires-Dist: pydantic<3.0,>=1.10
|
|
11
|
+
Requires-Dist: pyyaml
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: bandit; extra == 'dev'
|
|
14
|
+
Requires-Dist: black; extra == 'dev'
|
|
15
|
+
Requires-Dist: git-changelog; extra == 'dev'
|
|
16
|
+
Requires-Dist: hatch; extra == 'dev'
|
|
17
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
18
|
+
Requires-Dist: pre-commit; extra == 'dev'
|
|
19
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
20
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
21
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
22
|
+
Requires-Dist: types-pyyaml; extra == 'dev'
|
|
23
|
+
Provides-Extra: docs
|
|
24
|
+
Requires-Dist: mkdocs; extra == 'docs'
|
|
25
|
+
Requires-Dist: mkdocs-material; extra == 'docs'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# azure-functions-openapi
|
|
29
|
+
|
|
30
|
+
[](https://pypi.org/project/azure-functions-openapi/)
|
|
31
|
+
[](https://pypi.org/project/azure-functions-openapi/)
|
|
32
|
+
[](https://github.com/yeongseon/azure-functions-openapi/actions/workflows/test.yml)
|
|
33
|
+
[](https://codecov.io/gh/yeongseon/azure-functions-openapi)
|
|
34
|
+
[](https://pre-commit.com/)
|
|
35
|
+
[](https://yeongseon.github.io/azure-functions-openapi/)
|
|
36
|
+
[](LICENSE)
|
|
37
|
+
|
|
38
|
+
> Effortless OpenAPI (Swagger) documentation & Swagger‑UI for **Python Azure Functions**.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
- `@openapi` decorator — annotate once, generate full spec
|
|
45
|
+
- Serves `/openapi.json`, `/openapi.yaml`, and `/docs` (Swagger UI)
|
|
46
|
+
- Supports query/path/header parameters, requestBody, responses, tags
|
|
47
|
+
- Optional Pydantic integration (supports both v1 and v2)
|
|
48
|
+
- Zero hard dependency on Pydantic
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install azure-functions-openapi
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
For local development:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
git clone https://github.com/yeongseon/azure-functions-openapi.git
|
|
62
|
+
cd azure-functions-openapi
|
|
63
|
+
pip install -e .[dev]
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Quick Start
|
|
69
|
+
|
|
70
|
+
> Create a minimal HTTP-triggered Azure Function with auto Swagger documentation.
|
|
71
|
+
|
|
72
|
+
1. Set up environment
|
|
73
|
+
```bash
|
|
74
|
+
python -m venv .venv
|
|
75
|
+
source .venv/bin/activate
|
|
76
|
+
pip install azure-functions azure-functions-worker azure-functions-openapi
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
2. Initialize Azure Functions project
|
|
80
|
+
```bash
|
|
81
|
+
func init hello_openapi --python
|
|
82
|
+
cd hello_openapi
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
3. Add `function_app.py` with OpenAPI-decorated function and endpoints:
|
|
86
|
+
```python
|
|
87
|
+
# hello_openapi/function_app.py
|
|
88
|
+
|
|
89
|
+
import json
|
|
90
|
+
import azure.functions as func
|
|
91
|
+
from azure_functions_openapi.decorator import openapi
|
|
92
|
+
from azure_functions_openapi.openapi import get_openapi_json, get_openapi_yaml
|
|
93
|
+
from azure_functions_openapi.swagger_ui import render_swagger_ui
|
|
94
|
+
|
|
95
|
+
app = func.FunctionApp()
|
|
96
|
+
|
|
97
|
+
@openapi(
|
|
98
|
+
summary="Greet user",
|
|
99
|
+
route="/api/http_trigger",
|
|
100
|
+
request_model={"name": "string"},
|
|
101
|
+
response_model={"message": "string"},
|
|
102
|
+
tags=["Example"]
|
|
103
|
+
)
|
|
104
|
+
@app.function_name(name="http_trigger")
|
|
105
|
+
@app.route(route="/api/http_trigger", auth_level=func.AuthLevel.ANONYMOUS, methods=["POST"])
|
|
106
|
+
def main(req: func.HttpRequest) -> func.HttpResponse:
|
|
107
|
+
try:
|
|
108
|
+
data = req.get_json()
|
|
109
|
+
name = data.get("name", "world")
|
|
110
|
+
return func.HttpResponse(
|
|
111
|
+
json.dumps({"message": f"Hello, {name}!"}),
|
|
112
|
+
mimetype="application/json"
|
|
113
|
+
)
|
|
114
|
+
except Exception as e:
|
|
115
|
+
return func.HttpResponse(f"Error: {str(e)}", status_code=400)
|
|
116
|
+
|
|
117
|
+
@app.function_name(name="openapi_json")
|
|
118
|
+
@app.route(route="/api/openapi.json", auth_level=func.AuthLevel.ANONYMOUS, methods=["GET"])
|
|
119
|
+
def openapi_json(req: func.HttpRequest) -> func.HttpResponse:
|
|
120
|
+
return get_openapi_json()
|
|
121
|
+
|
|
122
|
+
@app.function_name(name="openapi_yaml")
|
|
123
|
+
@app.route(route="/api/openapi.yaml", auth_level=func.AuthLevel.ANONYMOUS, methods=["GET"])
|
|
124
|
+
def openapi_yaml(req: func.HttpRequest) -> func.HttpResponse:
|
|
125
|
+
return get_openapi_yaml()
|
|
126
|
+
|
|
127
|
+
@app.function_name(name="swagger_ui")
|
|
128
|
+
@app.route(route="/api/docs", auth_level=func.AuthLevel.ANONYMOUS, methods=["GET"])
|
|
129
|
+
def swagger_ui(req: func.HttpRequest) -> func.HttpResponse:
|
|
130
|
+
return render_swagger_ui()
|
|
131
|
+
```
|
|
132
|
+
> Swagger UI (`/docs`) is now supported via `render_swagger_ui()` helper.
|
|
133
|
+
|
|
134
|
+
4. Run locally:
|
|
135
|
+
```bash
|
|
136
|
+
func start
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
- OpenAPI JSON: http://localhost:7071/api/openapi.json
|
|
140
|
+
- Swagger UI: http://localhost:7071/api/docs
|
|
141
|
+
|
|
142
|
+
5. Deploy:
|
|
143
|
+
```bash
|
|
144
|
+
func azure functionapp publish <FUNCTION-APP-NAME> --python
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
- OpenAPI JSON: https://<FUNCTION-APP-NAME>.azurewebsites.net/api/openapi.json
|
|
148
|
+
|
|
149
|
+
A partial example of the generated `/api/openapi.json`:
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"openapi": "3.0.0",
|
|
154
|
+
"info": {
|
|
155
|
+
"title": "API",
|
|
156
|
+
"version": "1.0.0",
|
|
157
|
+
"description": "Auto-generated OpenAPI documentation. Markdown supported in descriptions (CommonMark)."
|
|
158
|
+
},
|
|
159
|
+
"paths": {
|
|
160
|
+
"/api/http_trigger": {
|
|
161
|
+
"get": {
|
|
162
|
+
"summary": "HTTP Trigger with name parameter",
|
|
163
|
+
"description": "Returns a greeting using the **name** from query or body.",
|
|
164
|
+
"parameters": [
|
|
165
|
+
{
|
|
166
|
+
"name": "name",
|
|
167
|
+
"in": "query",
|
|
168
|
+
"required": true,
|
|
169
|
+
"schema": { "type": "string" },
|
|
170
|
+
"description": "Name to greet"
|
|
171
|
+
}
|
|
172
|
+
],
|
|
173
|
+
"responses": {
|
|
174
|
+
"200": {
|
|
175
|
+
"description": "Successful response with greeting",
|
|
176
|
+
"content": {
|
|
177
|
+
"application/json": {
|
|
178
|
+
"examples": {
|
|
179
|
+
"sample": {
|
|
180
|
+
"summary": "Example greeting",
|
|
181
|
+
"value": {
|
|
182
|
+
"message": "Hello, Azure!"
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
"400": { "description": "Invalid request" }
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
- Swagger UI: https://<FUNCTION-APP-NAME>.azurewebsites.net/api/docs
|
|
198
|
+
|
|
199
|
+
Swagger UI will look once you set up the routes:
|
|
200
|
+
|
|
201
|
+

|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Example with Pydantic
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
from pydantic import BaseModel
|
|
209
|
+
from azure_functions_openapi.decorator import openapi
|
|
210
|
+
|
|
211
|
+
class RequestModel(BaseModel):
|
|
212
|
+
name: str
|
|
213
|
+
|
|
214
|
+
class ResponseModel(BaseModel):
|
|
215
|
+
message: str
|
|
216
|
+
|
|
217
|
+
@openapi(
|
|
218
|
+
summary="Greet user (Pydantic)",
|
|
219
|
+
route="/api/http_trigger",
|
|
220
|
+
request_model=RequestModel,
|
|
221
|
+
response_model=ResponseModel,
|
|
222
|
+
tags=["Example"]
|
|
223
|
+
)
|
|
224
|
+
def http_trigger(req: func.HttpRequest) -> func.HttpResponse:
|
|
225
|
+
...
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
> Supports both Pydantic v1 and v2.
|
|
229
|
+
Schema inference will work automatically with either version.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Documentation
|
|
234
|
+
|
|
235
|
+
- Full docs: [yeongseon.github.io/azure-functions-openapi](https://yeongseon.github.io/azure-functions-openapi/)
|
|
236
|
+
- [Quickstart](docs/usage.md)
|
|
237
|
+
- [Development Guide](docs/development.md)
|
|
238
|
+
- [Contribution Guide](docs/contributing.md)
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## License
|
|
243
|
+
|
|
244
|
+
MIT © 2025 Yeongseon Choe
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
azure_functions_openapi/__init__.py,sha256=3espqW3_xVQoBqi0ha08IP22z55rFwElswG-a0ao18c,65
|
|
2
|
+
azure_functions_openapi/decorator.py,sha256=7rDj-MD-PKSvZHSjCFF-OIRo2sLygyNeF2hA2T58O0g,5009
|
|
3
|
+
azure_functions_openapi/openapi.py,sha256=0X35QpYDRxT2FblaMiQyhKHRCrpmK4bgQNcIvXBOCxA,3558
|
|
4
|
+
azure_functions_openapi/swagger_ui.py,sha256=AGB4oAlQA3ayMREk5UskNwfahifOcPh_jzfTyK5z3ok,834
|
|
5
|
+
azure_functions_openapi/utils.py,sha256=xzZn7-DfC_OktsuS3WTFiDruTBeNGOwxnKhYvIBsqps,593
|
|
6
|
+
azure_functions_openapi-0.4.0.dist-info/METADATA,sha256=G5ZCaw72tMl1VTfj_mDhgNuludQX4nQXI6sECP0iGSg,7245
|
|
7
|
+
azure_functions_openapi-0.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
8
|
+
azure_functions_openapi-0.4.0.dist-info/licenses/LICENSE,sha256=gBTzk1NFKuyZKqa5-8leVY816k7POZUMpw2_PKl2MsY,1071
|
|
9
|
+
azure_functions_openapi-0.4.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Yeongseon Choe
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|