Skip to content

Feat/grpc sync addition #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 13 commits into from
Closed
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
12 changes: 6 additions & 6 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
package:
- "hooks/openfeature-hooks-opentelemetry"
- "providers/openfeature-provider-flagd"
Expand All @@ -50,15 +50,15 @@ jobs:
working-directory: ${{ matrix.package }}

- name: Type checking
if: matrix.python-version == '3.11'
if: matrix.python-version == '3.13'
working-directory: ${{ matrix.package }}
run: hatch run mypy:run

- name: Test with pytest
run: hatch test -c
working-directory: ${{ matrix.package }}

- if: matrix.python-version == '3.11'
- if: matrix.python-version == '3.13'
name: Upload coverage to Codecov
uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # v5.3.1
with:
Expand All @@ -75,7 +75,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5
with:
python-version: "3.11"
python-version: "3.13"
cache: "pip"

- name: Run pre-commit
Expand All @@ -91,10 +91,10 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4

- name: Initialize CodeQL
uses: github/codeql-action/init@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3
uses: github/codeql-action/init@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3
with:
languages: python
config-file: ./.github/codeql-config.yml

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3
uses: github/codeql-action/analyze@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[submodule "providers/openfeature-provider-flagd/test-harness"]
path = providers/openfeature-provider-flagd/openfeature/test-harness
url = git@github.com:open-feature/flagd-testbed.git
branch = v0.5.21
branch = v2.2.0
[submodule "providers/openfeature-provider-flagd/spec"]
path = providers/openfeature-provider-flagd/openfeature/spec
url = https://github.com/open-feature/spec
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
default_stages: [commit]
default_stages: [pre-commit]
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.4
rev: v0.9.6
hooks:
- id: ruff
args: [--fix]
Expand Down
24 changes: 19 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

### System Requirements

Python 3.8 and above are required.
Python 3.9 and above are required.

### Target version(s)

Python 3.8 and above are supported by the SDK.
Python 3.9 and above are supported by the SDK.

### Installation and Dependencies

Expand All @@ -30,7 +30,13 @@ We use `pytest` for our unit testing, making use of `parametrized` to inject cas

### Integration tests

These are planned once the SDK has been stabilized and a Flagd provider implemented. At that point, we will utilize the [gherkin integration tests](https://github.com/open-feature/test-harness/blob/main/features/evaluation.feature) to validate against a live, seeded Flagd instance.
The Flagd provider utilizes the [gherkin integration tests](https://github.com/open-feature/test-harness/blob/main/features/evaluation.feature) to validate against a live, seeded Flagd instance.

To run the integration tests you need to have a container runtime, like docker, ranger, etc. installed.

```bash
hatch run test
```

### Type checking

Expand All @@ -52,6 +58,13 @@ Navigate to the repository folder
cd python-sdk-contrib
```

Checkout submodules

```bash
git submodule update --init --recursive
```


Add your fork as an origin

```bash
Expand All @@ -62,15 +75,16 @@ Ensure your development environment is all set up by building and testing

```bash
cd <package>
hatch run test
hatch build
hatch test
```

To start working on a new feature or bugfix, create a new branch and start working on it.

```bash
git checkout -b feat/NAME_OF_FEATURE
# Make your changes
git commit
git commit -s -m "feat: my feature"
git push fork feat/NAME_OF_FEATURE
```

Expand Down
4 changes: 2 additions & 2 deletions hooks/openfeature-hooks-opentelemetry/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ dependencies = [
"openfeature-sdk>=0.6.0",
"opentelemetry-api",
]
requires-python = ">=3.8"
requires-python = ">=3.9"

[project.urls]
Homepage = "https://github.com/open-feature/python-sdk-contrib"
Expand Down Expand Up @@ -70,7 +70,7 @@ packages = ["src/openfeature"]
mypy_path = "src"
files = "src"

python_version = "3.8" # should be identical to the minimum supported version
python_version = "3.9" # should be identical to the minimum supported version
namespace_packages = true
explicit_package_bases = true
local_partial_types = true
Expand Down
35 changes: 33 additions & 2 deletions providers/openfeature-provider-flagd/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# flagd Provider for OpenFeature

This provider is designed to use flagd's [evaluation protocol](https://github.com/open-feature/schemas/blob/main/protobuf/schema/v1/schema.proto).
This provider is designed to use flagd's [evaluation protocol](https://github.com/open-feature/schemas/blob/main/protobuf/schema/v1/schema.proto), or locally evaluate flags defined in a flagd [flag definition](https://github.com/open-feature/schemas/blob/main/json/flagd-definitions.json) via the OpenFeature Python SDK.

## Installation

Expand Down Expand Up @@ -29,7 +29,9 @@ api.set_provider(FlagdProvider())

### In-process resolver

This mode performs flag evaluations locally (in-process).
This mode performs flag evaluations locally (in-process). Flag configurations for evaluation are obtained via gRPC protocol using [sync protobuf schema](https://buf.build/open-feature/flagd/file/main:sync/v1/sync_service.proto) service definition.

Consider the following example to create a `FlagdProvider` with in-process evaluations,

```python
from openfeature import api
Expand All @@ -38,10 +40,39 @@ from openfeature.contrib.provider.flagd.config import ResolverType

api.set_provider(FlagdProvider(
resolver_type=ResolverType.IN_PROCESS,
))
```

In the above example, in-process handlers attempt to connect to a sync service on address `localhost:8013` to obtain [flag definitions](https://github.com/open-feature/schemas/blob/main/json/flags.json).

<!--
#### Sync-metadata

To support the injection of contextual data configured in flagd for in-process evaluation, the provider exposes a `getSyncMetadata` accessor which provides the most recent value returned by the [GetMetadata RPC](https://buf.build/open-feature/flagd/docs/main:flagd.sync.v1#flagd.sync.v1.FlagSyncService.GetMetadata).
The value is updated with every (re)connection to the sync implementation.
This can be used to enrich evaluations with such data.
If the `in-process` mode is not used, and before the provider is ready, the `getSyncMetadata` returns an empty map.
-->
### File mode

In-process resolvers can also work in an offline mode.
To enable this mode, you should provide a valid flag configuration file with the option `offlineFlagSourcePath`.

```python
from openfeature import api
from openfeature.contrib.provider.flagd import FlagdProvider
from openfeature.contrib.provider.flagd.config import ResolverType

api.set_provider(FlagdProvider(
resolver_type=ResolverType.FILE,
offline_flag_source_path="my-flag.json",
))
```

Provider will attempt to detect file changes using polling.
Polling happens at 5 second intervals and this is currently unconfigurable.
This mode is useful for local development, tests and offline applications.

### Configuration options

The default options can be defined in the FlagdProvider constructor.
Expand Down
8 changes: 4 additions & 4 deletions providers/openfeature-provider-flagd/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ classifiers = [
keywords = []
dependencies = [
"openfeature-sdk>=0.6.0",
"grpcio>=1.68.0",
"protobuf>=4.25.2",
"grpcio>=1.68.1",
"protobuf>=4.29.2",
"mmh3>=4.1.0",
"panzi-json-logic>=1.0.1",
"semver>=3,<4",
"pyyaml>=6.0.1",
"cachebox"
]
requires-python = ">=3.8"
requires-python = ">=3.9"

[project.urls]
Homepage = "https://github.com/open-feature/python-sdk-contrib"
Expand Down Expand Up @@ -113,7 +113,7 @@ omit = [
mypy_path = "src"
files = "src"

python_version = "3.8" # should be identical to the minimum supported version
python_version = "3.9" # should be identical to the minimum supported version
namespace_packages = true
explicit_package_bases = true
local_partial_types = true
Expand Down
14 changes: 13 additions & 1 deletion providers/openfeature-provider-flagd/pytest.ini
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
[pytest]
markers =
rpc: tests for rpc mode.
in-process: tests for rpc mode.
in-process: tests for in-process mode.
file: tests for file mode.
unavailable: tests for unavailable providers.
customCert: Supports custom certs.
unixsocket: Supports unixsockets.
targetURI: Supports targetURI.
grace: Supports grace attempts.
targeting: Supports targeting.
fractional: Supports fractional.
string: Supports string.
semver: Supports semver.
reconnect: Supports reconnect.
events: Supports events.
sync: Supports sync.
caching: Supports caching.
offline: Supports offline.
os.linux: linux mark.
stream: Supports streams.
bdd_features_base_dir = tests/features
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
class ResolverType(Enum):
RPC = "rpc"
IN_PROCESS = "in-process"
FILE = "file"


class CacheType(Enum):
Expand Down Expand Up @@ -43,6 +44,7 @@ class CacheType(Enum):
ENV_VAR_RETRY_BACKOFF_MS = "FLAGD_RETRY_BACKOFF_MS"
ENV_VAR_RETRY_BACKOFF_MAX_MS = "FLAGD_RETRY_BACKOFF_MAX_MS"
ENV_VAR_RETRY_GRACE_PERIOD_SECONDS = "FLAGD_RETRY_GRACE_PERIOD"
ENV_VAR_SELECTOR = "FLAGD_SOURCE_SELECTOR"
ENV_VAR_STREAM_DEADLINE_MS = "FLAGD_STREAM_DEADLINE_MS"
ENV_VAR_TLS = "FLAGD_TLS"
ENV_VAR_TLS_CERT = "FLAGD_SERVER_CERT_PATH"
Expand Down Expand Up @@ -78,6 +80,7 @@ def __init__( # noqa: PLR0913
host: typing.Optional[str] = None,
port: typing.Optional[int] = None,
tls: typing.Optional[bool] = None,
selector: typing.Optional[str] = None,
resolver: typing.Optional[ResolverType] = None,
offline_flag_source_path: typing.Optional[str] = None,
offline_poll_interval_ms: typing.Optional[int] = None,
Expand Down Expand Up @@ -158,6 +161,17 @@ def __init__( # noqa: PLR0913
else offline_flag_source_path
)

if (
self.offline_flag_source_path is not None
and self.resolver is ResolverType.IN_PROCESS
):
self.resolver = ResolverType.FILE

if self.resolver is ResolverType.FILE and self.offline_flag_source_path is None:
raise AttributeError(
"Resolver Type 'FILE' requires a offlineFlagSourcePath"
)

self.offline_poll_interval_ms: int = (
int(
env_or_default(
Expand Down Expand Up @@ -209,3 +223,7 @@ def __init__( # noqa: PLR0913
if cert_path is None
else cert_path
)

self.selector = (
env_or_default(ENV_VAR_SELECTOR, None) if selector is None else selector
)
Loading
Loading