fastapi.py
 1  """
 2  FastAPI dependency helpers for feature flag gating.
 3  
 4  These helpers wrap the OpenFeature client for use as FastAPI ``Depends``
 5  injectors. They are framework-specific and live here rather than in
 6  ``core.py`` to keep the core module framework-agnostic.
 7  
 8  Usage::
 9  
10      from solace_agent_mesh.common.features.fastapi import (
11          require_feature,
12          get_feature_value,
13      )
14  
15      # Hard-gate an entire endpoint — returns 404 when flag is off:
16      @router.get("/my-endpoint")
17      async def my_endpoint(_: None = Depends(require_feature("my_flag"))):
18          ...
19  
20      # Inject the flag value for soft behaviour changes:
21      @router.get("/my-endpoint")
22      async def my_endpoint(flag: bool = Depends(get_feature_value("my_flag"))):
23          if flag:
24              ...
25  """
26  
27  from __future__ import annotations
28  
29  from collections.abc import Callable
30  
31  from fastapi import HTTPException, status
32  from openfeature import api as openfeature_api
33  
34  
35  def require_feature(key: str) -> Callable:
36      """
37      FastAPI dependency factory that hard-gates an endpoint on a feature flag.
38  
39      Raises HTTP 404 when the flag is disabled so that the endpoint appears
40      non-existent to callers. Use when the entire endpoint should be
41      unavailable while the flag is off.
42  
43      For soft behaviour changes based on a flag, use :func:`get_feature_value`.
44      """
45      def _check() -> None:
46          if not openfeature_api.get_client().get_boolean_value(key, False):
47              raise HTTPException(
48                  status_code=status.HTTP_404_NOT_FOUND,
49                  detail=f"Feature '{key}' is not enabled.",
50              )
51      return _check
52  
53  
54  def get_feature_value(key: str) -> Callable:
55      """
56      FastAPI dependency factory that injects the current boolean value of a
57      feature flag into an endpoint without raising on disabled.
58  
59      Use when the endpoint should remain accessible but needs to vary its
60      behaviour based on whether the flag is on or off.
61      """
62      def _resolve() -> bool:
63          return openfeature_api.get_client().get_boolean_value(key, False)
64      return _resolve