diff --git a/README.md b/README.md index e478947..1da633a 100644 --- a/README.md +++ b/README.md @@ -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)` | diff --git a/docs/METADATA_SPEC.md b/docs/METADATA_SPEC.md index baac284..931f19e 100644 --- a/docs/METADATA_SPEC.md +++ b/docs/METADATA_SPEC.md @@ -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 diff --git a/pyproject.toml b/pyproject.toml index d1dde82..b987f3a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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", diff --git a/tests/test_openapi_consumer_contract.py b/tests/test_openapi_consumer_contract.py new file mode 100644 index 0000000..5eb2af6 --- /dev/null +++ b/tests/test_openapi_consumer_contract.py @@ -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"]