|
| 1 | +# FeatBit OpenFeature Provider for Python Server SDK |
| 2 | + |
| 3 | +Thin OpenFeature provider backed by an existing FeatBit Python Server SDK client. |
| 4 | +The provider converts OpenFeature evaluation contexts and resolution details; feature |
| 5 | +flag synchronization, local evaluation, and insight events remain owned by |
| 6 | +`fb-python-sdk`. |
| 7 | + |
| 8 | +## Installation |
| 9 | + |
| 10 | +```shell |
| 11 | +pip install featbit-openfeature-server |
| 12 | +``` |
| 13 | + |
| 14 | +The provider requires `fb-python-sdk>=1.1.8`, which exposes variation IDs and |
| 15 | +data-update status listeners. |
| 16 | + |
| 17 | +## Usage |
| 18 | + |
| 19 | +```python |
| 20 | +from fbclient.client import FBClient |
| 21 | +from fbclient.config import Config |
| 22 | +from openfeature import api |
| 23 | +from openfeature.evaluation_context import EvaluationContext |
| 24 | + |
| 25 | +from featbit_openfeature import FeatBitProvider |
| 26 | + |
| 27 | +fb_client = FBClient( |
| 28 | + Config( |
| 29 | + "server-side-environment-secret", |
| 30 | + event_url="https://app-eval.featbit.co", |
| 31 | + streaming_url="wss://app-eval.featbit.co", |
| 32 | + ), |
| 33 | + start_wait=0, |
| 34 | +) |
| 35 | + |
| 36 | +api.set_provider_and_wait(FeatBitProvider(fb_client)) |
| 37 | +client = api.get_client() |
| 38 | + |
| 39 | +details = client.get_boolean_details( |
| 40 | + "new-checkout", |
| 41 | + False, |
| 42 | + EvaluationContext( |
| 43 | + targeting_key="user-123", |
| 44 | + attributes={"name": "Alice", "plan": "beta"}, |
| 45 | + ), |
| 46 | +) |
| 47 | + |
| 48 | +print(details.value, details.reason, details.variant) |
| 49 | + |
| 50 | +api.shutdown() |
| 51 | +``` |
| 52 | + |
| 53 | +The provider owns the injected `FBClient`. Calling `api.shutdown()` removes |
| 54 | +provider listeners and stops the FeatBit client so queued events are flushed |
| 55 | +and background resources are released. |
| 56 | + |
| 57 | +## Supported mappings |
| 58 | + |
| 59 | +- Boolean, string, integer, float, and object resolution. |
| 60 | +- OpenFeature targeting key and scalar attributes to a FeatBit user dictionary. |
| 61 | +- FeatBit reasons and failures to OpenFeature `Reason` and `ErrorCode`. |
| 62 | +- FeatBit variation ID to OpenFeature `variant`. |
| 63 | +- FeatBit flag changes to `PROVIDER_CONFIGURATION_CHANGED`. |
| 64 | +- FeatBit runtime data-source status to OpenFeature provider status. |
| 65 | +- OpenFeature tracking value to `FBClient.track_metric()`. |
| 66 | + |
| 67 | +Structured evaluation-context attributes and tracking custom attributes are not |
| 68 | +supported by the current FeatBit user and metric schemas. They are ignored rather |
| 69 | +than represented inaccurately. |
| 70 | + |
| 71 | +## Provider status |
| 72 | + |
| 73 | +Applications can read the status through the standard OpenFeature client: |
| 74 | + |
| 75 | +```python |
| 76 | +from openfeature.provider import ProviderStatus |
| 77 | + |
| 78 | +status = client.get_provider_status() |
| 79 | +assert status in { |
| 80 | + ProviderStatus.NOT_READY, |
| 81 | + ProviderStatus.READY, |
| 82 | + ProviderStatus.STALE, |
| 83 | + ProviderStatus.ERROR, |
| 84 | + ProviderStatus.FATAL, |
| 85 | +} |
| 86 | +``` |
| 87 | + |
| 88 | +FeatBit states are mapped as follows: |
| 89 | + |
| 90 | +| FeatBit state | OpenFeature status | |
| 91 | +| --- | --- | |
| 92 | +| initializing | `NOT_READY` during provider initialization | |
| 93 | +| OK | `READY` | |
| 94 | +| interrupted | `STALE` | |
| 95 | +| off observed while provider is active | `FATAL` | |
| 96 | + |
| 97 | +After `api.shutdown()`, OpenFeature removes this provider and installs its default |
| 98 | +no-op provider. Status after that point belongs to the OpenFeature SDK rather than |
| 99 | +to FeatBit. |
| 100 | + |
| 101 | +The included `py.typed` marker declares this package as PEP 561 typed, allowing |
| 102 | +type checkers to use the annotations shipped with the provider after install. |
| 103 | + |
| 104 | +## Development tests |
| 105 | + |
| 106 | +Unit tests use mocks, and the integration test uses FeatBit offline data. Neither |
| 107 | +requires a FeatBit account. |
| 108 | + |
| 109 | +```shell |
| 110 | +python -m pytest -q |
| 111 | +python -m flake8 featbit_openfeature tests |
| 112 | +python -m build |
| 113 | +``` |
| 114 | + |
| 115 | +## Release |
| 116 | + |
| 117 | +Publishing is handled by `.github/workflows/release.yml` when a GitHub Release |
| 118 | +is published. The build and publish jobs are separated so package build code |
| 119 | +never receives an OpenID Connect token. The publish job uses PyPI Trusted |
| 120 | +Publishing and does not store a long-lived PyPI password in GitHub. |
| 121 | + |
| 122 | +Before the first release, configure the PyPI trusted publisher with owner |
| 123 | +`featbit`, repository `openfeature-provider-python-server`, workflow |
| 124 | +`release.yml`, and GitHub environment `pypi`. |
0 commit comments