Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,13 @@ In short: raw Pydantic validates *one* handler; `@validate_http` standardizes *e

## FastAPI comparison

Choose native `FunctionApp` decorators when Functions bindings, worker-visible
signatures and the host's routing/auth model are your primary application model.
Choose FastAPI hosted by Azure Functions `AsgiFunctionApp` when you want FastAPI's
router, dependency injection, middleware and OpenAPI lifecycle as one ASGI app.
This library targets native HTTP handlers, not replacement validation inside a
FastAPI application. Host authentication remains host-owned in either approach.

| Feature | FastAPI | azure-functions-validation |
|---------|---------|---------------------------|
| Request body parsing | Built-in via type hints | `@validate_http(body=Model)` |
Expand Down
17 changes: 17 additions & 0 deletions docs/METADATA_SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,23 @@ its own local conformance tests against the versioned dict convention.

## Distribution & sync

The versioned JSON-compatible namespace remains the integration boundary; no new
shared runtime distribution is warranted. The local schema is a producer-side
conformance artifact, not a second runtime dependency. The OpenAPI consumer reads
`endpoint` directly, validates its version, copies it and translates response
schemas to OpenAPI response content. It does not need Validation model classes.
`tests/test_openapi_consumer_contract.py` runs the real published OpenAPI bridge
against this producer in normal CI, detecting consumer/producer drift rather than
assuming matching schema file names imply compatibility. OpenAPI is a development
test dependency only; no sibling repository is modified or coordinated here.

For version 1, preserve existing field meanings and JSON-compatible values;
breaking field changes require a new namespace payload version and compatibility
tests. An unknown version must remain observable rather than silently treated as
the supported one. The `logging` namespace is independently owned by Logging;
Validation only detects its presence to warn about correlation-context decorator
order. It neither imports Logging nor consumes its private payload structure.

- The schema ships as package data inside
`azure_functions_validation.schemas`, loaded via `importlib.resources`. No
new runtime dependency is introduced; `jsonschema` is required only to
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Issues = "https://github.com/yeongseon/azure-functions-validation-python/issues"

[project.optional-dependencies]
dev = [
"azure-functions-openapi>=0.25,<1",
"bandit==1.9.4",
"build",
"coverage",
Expand Down
27 changes: 27 additions & 0 deletions tests/test_openapi_consumer_contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import azure.functions as func
from azure_functions_openapi.bridge import scan_endpoint_metadata
from azure_functions_openapi.registry import OpenAPIRegistry
from pydantic import BaseModel

from azure_functions_validation import validate_http


def test_real_openapi_consumer_accepts_validation_endpoint_contract() -> None:
class Payload(BaseModel):
name: str

app = func.FunctionApp()

@app.route(route="contract", methods=["POST"])
@validate_http(body=Payload, response_model=Payload)
def handler(req: func.HttpRequest, body: Payload) -> Payload:
return body

registry = OpenAPIRegistry()
scan_endpoint_metadata(app, registry=registry)
entry = registry.get("post::/api/contract")
assert entry is not None
assert entry["request_body"]["properties"]["name"]["type"] == "string"
assert entry["request_body_required"] is True
assert 200 in entry["response"]
assert 422 in entry["response"]
Loading