openfeature-provider-flagd 0.2.1__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/CHANGELOG.md +14 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/PKG-INFO +1 -1
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/pyproject.toml +2 -2
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/contrib/provider/flagd/config.py +9 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/contrib/provider/flagd/provider.py +2 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/contrib/provider/flagd/resolvers/in_process.py +1 -1
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/contrib/provider/flagd/resolvers/process/connector/grpc_watcher.py +22 -9
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/contrib/provider/flagd/resolvers/process/flags.py +2 -4
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2_grpc.py +1 -1
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2_grpc.py +1 -1
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/schemas/protobuf/schema/v1/schema_pb2_grpc.py +1 -1
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/schemas/protobuf/sync/v1/sync_service_pb2_grpc.py +1 -1
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/step/event_steps.py +4 -2
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/step/provider_steps.py +3 -1
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/testfilter.py +3 -1
- openfeature_provider_flagd-0.2.2/tests/test_in_process.py +218 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/.gitignore +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/LICENSE +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/README.md +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/pytest.ini +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/contrib/provider/flagd/__init__.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/contrib/provider/flagd/flag_type.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/contrib/provider/flagd/resolvers/__init__.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/contrib/provider/flagd/resolvers/grpc.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/contrib/provider/flagd/resolvers/process/connector/__init__.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/contrib/provider/flagd/resolvers/process/connector/file_watcher.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/contrib/provider/flagd/resolvers/process/custom_ops.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/contrib/provider/flagd/resolvers/process/targeting.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/contrib/provider/flagd/resolvers/protocol.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/contrib/provider/flagd/sync_metadata_hook.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2.pyi +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2_grpc.pyi +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2.pyi +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2_grpc.pyi +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/schemas/protobuf/schema/v1/schema_pb2.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/schemas/protobuf/schema/v1/schema_pb2.pyi +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/schemas/protobuf/schema/v1/schema_pb2_grpc.pyi +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/schemas/protobuf/sync/v1/sync_service_pb2.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/schemas/protobuf/sync/v1/sync_service_pb2.pyi +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/src/openfeature/schemas/protobuf/sync/v1/sync_service_pb2_grpc.pyi +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/__init__.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/conftest.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/__init__.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/conftest.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/file/__init__.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/file/conftest.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/file/test_flaqd.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/flagd_container.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/inprocess/__init__.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/inprocess/conftest.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/inprocess/test_flaqd.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/parsers.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/paths.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/rpc/__init__.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/rpc/conftest.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/rpc/test_flaqd.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/step/_utils.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/step/config_steps.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/step/context_steps.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/step/flag_step.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/basic-flag-broken-default.json +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/basic-flag-broken-state.json +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/basic-flag-broken-targeting.json +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/basic-flag-broken-variants.json +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/basic-flag-disabled.json +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/basic-flag-invalid.not-json +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/basic-flag-no-state.json +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/basic-flag-wrong-structure.json +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/basic-flag-wrong-variant.json +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/basic-flag.json +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/basic-flag.yaml +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/invalid-fractional-args-wrong-content.json +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/invalid-fractional-args.json +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/invalid-fractional-weights-strings.json +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/invalid-fractional-weights.json +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/invalid-semver-args.json +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/invalid-semver-op.json +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/invalid-stringcomp-args.json +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/test_config.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/test_errors.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/test_file_store.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/test_flagd.py +0 -0
- {openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/test_targeting.py +0 -0
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.2](https://github.com/open-feature/python-sdk-contrib/compare/openfeature-provider-flagd/v0.2.1...openfeature-provider-flagd/v0.2.2) (2025-03-18)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### 🐛 Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **flagd:** handle falsy target values correctly ([#214](https://github.com/open-feature/python-sdk-contrib/issues/214)) ([fafd099](https://github.com/open-feature/python-sdk-contrib/commit/fafd099f07365a7d0032e8215477b51bfe90c01a))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### 🧹 Chore
|
|
12
|
+
|
|
13
|
+
* **deps:** update dependency grpcio-health-checking to v1.71.0 ([#209](https://github.com/open-feature/python-sdk-contrib/issues/209)) ([345e793](https://github.com/open-feature/python-sdk-contrib/commit/345e7934b9de3879d3aff45c8213ece1a98e3711))
|
|
14
|
+
* **deps:** update pre-commit hook astral-sh/ruff-pre-commit to v0.11.0 ([#212](https://github.com/open-feature/python-sdk-contrib/issues/212)) ([1b9b5f1](https://github.com/open-feature/python-sdk-contrib/commit/1b9b5f128a7fe08ffbf84cbc7de2986f95dc01f5))
|
|
15
|
+
* **flagd:** Add sync metadata disabled ([#211](https://github.com/open-feature/python-sdk-contrib/issues/211)) ([2f85057](https://github.com/open-feature/python-sdk-contrib/commit/2f850574943cc92d55d198c8ccd91e80583a2ee6))
|
|
16
|
+
|
|
3
17
|
## [0.2.1](https://github.com/open-feature/python-sdk-contrib/compare/openfeature-provider-flagd/v0.2.0...openfeature-provider-flagd/v0.2.1) (2025-03-10)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openfeature-provider-flagd
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: OpenFeature provider for the flagd flag evaluation engine
|
|
5
5
|
Project-URL: Homepage, https://github.com/open-feature/python-sdk-contrib
|
|
6
6
|
Author-email: OpenFeature <openfeature-core@groups.io>
|
|
@@ -5,7 +5,7 @@ build-backend = "hatchling.build"
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "openfeature-provider-flagd"
|
|
8
|
-
version = "0.2.
|
|
8
|
+
version = "0.2.2"
|
|
9
9
|
description = "OpenFeature provider for the flagd flag evaluation engine"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
authors = [{ name = "OpenFeature", email = "openfeature-core@groups.io" }]
|
|
@@ -40,7 +40,7 @@ dependencies = [
|
|
|
40
40
|
"pytest-bdd",
|
|
41
41
|
"testcontainers",
|
|
42
42
|
"asserts",
|
|
43
|
-
"grpcio-health-checking==1.
|
|
43
|
+
"grpcio-health-checking==1.71.0",
|
|
44
44
|
]
|
|
45
45
|
pre-install-commands = [
|
|
46
46
|
"hatch build",
|
|
@@ -100,6 +100,7 @@ class Config:
|
|
|
100
100
|
cert_path: typing.Optional[str] = None,
|
|
101
101
|
default_authority: typing.Optional[str] = None,
|
|
102
102
|
channel_credentials: typing.Optional[grpc.ChannelCredentials] = None,
|
|
103
|
+
sync_metadata_disabled: typing.Optional[bool] = None,
|
|
103
104
|
):
|
|
104
105
|
self.host = env_or_default(ENV_VAR_HOST, DEFAULT_HOST) if host is None else host
|
|
105
106
|
|
|
@@ -248,3 +249,11 @@ class Config:
|
|
|
248
249
|
)
|
|
249
250
|
|
|
250
251
|
self.channel_credentials = channel_credentials
|
|
252
|
+
|
|
253
|
+
# TODO: remove the metadata call entirely after https://github.com/open-feature/flagd/issues/1584
|
|
254
|
+
# This is a temporary stop-gap solutions to support servers that don't implement sync.GetMetadata
|
|
255
|
+
# (see: https://buf.build/open-feature/flagd/docs/main:flagd.sync.v1#flagd.sync.v1.FlagSyncService.GetMetadata).
|
|
256
|
+
# Using this option disables call to sync.GetMetadata
|
|
257
|
+
# Disabling will prevent static context from flagd being used in evaluations.
|
|
258
|
+
# GetMetadata and this option will be removed.
|
|
259
|
+
self.sync_metadata_disabled = sync_metadata_disabled
|
|
@@ -64,6 +64,7 @@ class FlagdProvider(AbstractProvider):
|
|
|
64
64
|
cert_path: typing.Optional[str] = None,
|
|
65
65
|
default_authority: typing.Optional[str] = None,
|
|
66
66
|
channel_credentials: typing.Optional[grpc.ChannelCredentials] = None,
|
|
67
|
+
sync_metadata_disabled: typing.Optional[bool] = None,
|
|
67
68
|
):
|
|
68
69
|
"""
|
|
69
70
|
Create an instance of the FlagdProvider
|
|
@@ -106,6 +107,7 @@ class FlagdProvider(AbstractProvider):
|
|
|
106
107
|
cert_path=cert_path,
|
|
107
108
|
default_authority=default_authority,
|
|
108
109
|
channel_credentials=channel_credentials,
|
|
110
|
+
sync_metadata_disabled=sync_metadata_disabled,
|
|
109
111
|
)
|
|
110
112
|
self.enriched_context: dict = {}
|
|
111
113
|
|
|
@@ -6,6 +6,7 @@ import typing
|
|
|
6
6
|
|
|
7
7
|
import grpc
|
|
8
8
|
from google.protobuf.json_format import MessageToDict
|
|
9
|
+
from google.protobuf.struct_pb2 import Struct
|
|
9
10
|
|
|
10
11
|
from openfeature.evaluation_context import EvaluationContext
|
|
11
12
|
from openfeature.event import ProviderEventDetails
|
|
@@ -162,24 +163,36 @@ class GrpcWatcher(FlagStateConnector):
|
|
|
162
163
|
self.active = False
|
|
163
164
|
self.channel.close()
|
|
164
165
|
|
|
166
|
+
def _create_request_args(self) -> dict:
|
|
167
|
+
request_args = {}
|
|
168
|
+
if self.selector is not None:
|
|
169
|
+
request_args["selector"] = self.selector
|
|
170
|
+
if self.provider_id is not None:
|
|
171
|
+
request_args["provider_id"] = self.provider_id
|
|
172
|
+
|
|
173
|
+
return request_args
|
|
174
|
+
|
|
165
175
|
def listen(self) -> None:
|
|
166
176
|
call_args = (
|
|
167
177
|
{"timeout": self.streamline_deadline_seconds}
|
|
168
178
|
if self.streamline_deadline_seconds > 0
|
|
169
179
|
else {}
|
|
170
180
|
)
|
|
171
|
-
request_args =
|
|
172
|
-
if self.selector is not None:
|
|
173
|
-
request_args["selector"] = self.selector
|
|
174
|
-
if self.provider_id is not None:
|
|
175
|
-
request_args["provider_id"] = self.provider_id
|
|
181
|
+
request_args = self._create_request_args()
|
|
176
182
|
|
|
177
183
|
while self.active:
|
|
178
184
|
try:
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
185
|
+
context_values_response: sync_pb2.GetMetadataResponse
|
|
186
|
+
if self.config.sync_metadata_disabled:
|
|
187
|
+
context_values_response = sync_pb2.GetMetadataResponse(
|
|
188
|
+
metadata=Struct()
|
|
189
|
+
)
|
|
190
|
+
else:
|
|
191
|
+
context_values_request = sync_pb2.GetMetadataRequest()
|
|
192
|
+
context_values_response = self.stub.GetMetadata(
|
|
193
|
+
context_values_request, wait_for_ready=True
|
|
194
|
+
)
|
|
195
|
+
|
|
183
196
|
context_values = MessageToDict(context_values_response)
|
|
184
197
|
|
|
185
198
|
request = sync_pb2.SyncFlagsRequest(**request_args)
|
|
@@ -72,10 +72,8 @@ class Flag:
|
|
|
72
72
|
data["default_variant"] = data["defaultVariant"]
|
|
73
73
|
del data["defaultVariant"]
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if "selector" in data:
|
|
78
|
-
del data["selector"]
|
|
75
|
+
data.pop("source", None)
|
|
76
|
+
data.pop("selector", None)
|
|
79
77
|
try:
|
|
80
78
|
flag = cls(key=key, **data)
|
|
81
79
|
return flag
|
|
@@ -5,7 +5,7 @@ import warnings
|
|
|
5
5
|
|
|
6
6
|
from openfeature.schemas.protobuf.flagd.evaluation.v1 import evaluation_pb2 as openfeature_dot_schemas_dot_protobuf_dot_flagd_dot_evaluation_dot_v1_dot_evaluation__pb2
|
|
7
7
|
|
|
8
|
-
GRPC_GENERATED_VERSION = '1.
|
|
8
|
+
GRPC_GENERATED_VERSION = '1.71.0'
|
|
9
9
|
GRPC_VERSION = grpc.__version__
|
|
10
10
|
_version_not_supported = False
|
|
11
11
|
|
|
@@ -5,7 +5,7 @@ import warnings
|
|
|
5
5
|
|
|
6
6
|
from openfeature.schemas.protobuf.flagd.sync.v1 import sync_pb2 as openfeature_dot_schemas_dot_protobuf_dot_flagd_dot_sync_dot_v1_dot_sync__pb2
|
|
7
7
|
|
|
8
|
-
GRPC_GENERATED_VERSION = '1.
|
|
8
|
+
GRPC_GENERATED_VERSION = '1.71.0'
|
|
9
9
|
GRPC_VERSION = grpc.__version__
|
|
10
10
|
_version_not_supported = False
|
|
11
11
|
|
|
@@ -5,7 +5,7 @@ import warnings
|
|
|
5
5
|
|
|
6
6
|
from openfeature.schemas.protobuf.schema.v1 import schema_pb2 as openfeature_dot_schemas_dot_protobuf_dot_schema_dot_v1_dot_schema__pb2
|
|
7
7
|
|
|
8
|
-
GRPC_GENERATED_VERSION = '1.
|
|
8
|
+
GRPC_GENERATED_VERSION = '1.71.0'
|
|
9
9
|
GRPC_VERSION = grpc.__version__
|
|
10
10
|
_version_not_supported = False
|
|
11
11
|
|
|
@@ -5,7 +5,7 @@ import warnings
|
|
|
5
5
|
|
|
6
6
|
from openfeature.schemas.protobuf.sync.v1 import sync_service_pb2 as openfeature_dot_schemas_dot_protobuf_dot_sync_dot_v1_dot_sync__service__pb2
|
|
7
7
|
|
|
8
|
-
GRPC_GENERATED_VERSION = '1.
|
|
8
|
+
GRPC_GENERATED_VERSION = '1.71.0'
|
|
9
9
|
GRPC_VERSION = grpc.__version__
|
|
10
10
|
_version_not_supported = False
|
|
11
11
|
|
{openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/step/event_steps.py
RENAMED
|
@@ -8,6 +8,8 @@ from pytest_bdd import given, parsers, then, when
|
|
|
8
8
|
from openfeature.client import OpenFeatureClient
|
|
9
9
|
from openfeature.event import ProviderEvent
|
|
10
10
|
|
|
11
|
+
logger = logging.getLogger("openfeature.contrib.tests")
|
|
12
|
+
|
|
11
13
|
events = {
|
|
12
14
|
"ready": ProviderEvent.PROVIDER_READY,
|
|
13
15
|
"error": ProviderEvent.PROVIDER_ERROR,
|
|
@@ -28,7 +30,7 @@ def event_handles() -> list:
|
|
|
28
30
|
)
|
|
29
31
|
def add_event_handler(client: OpenFeatureClient, event_type: str, event_handles: list):
|
|
30
32
|
def handler(event):
|
|
31
|
-
|
|
33
|
+
logger.warning((event_type, event))
|
|
32
34
|
event_handles.append(
|
|
33
35
|
{
|
|
34
36
|
"type": event_type,
|
|
@@ -38,7 +40,7 @@ def add_event_handler(client: OpenFeatureClient, event_type: str, event_handles:
|
|
|
38
40
|
|
|
39
41
|
client.add_handler(events[event_type], handler)
|
|
40
42
|
|
|
41
|
-
|
|
43
|
+
logger.warning(("handler added", event_type))
|
|
42
44
|
|
|
43
45
|
|
|
44
46
|
def assert_handlers(handles, event_type: str, max_wait: int = 2):
|
|
@@ -22,6 +22,8 @@ KEY_FLAGS = "flags"
|
|
|
22
22
|
|
|
23
23
|
MERGED_FILE = "merged_file"
|
|
24
24
|
|
|
25
|
+
logger = logging.getLogger("openfeature.contrib.tests")
|
|
26
|
+
|
|
25
27
|
|
|
26
28
|
class TestProviderType(Enum):
|
|
27
29
|
UNAVAILABLE = "unavailable"
|
|
@@ -133,7 +135,7 @@ def container(request):
|
|
|
133
135
|
try:
|
|
134
136
|
container.stop()
|
|
135
137
|
except: # noqa: E722 - we want to ensure all containers are stopped, even if we do have an exception here
|
|
136
|
-
|
|
138
|
+
logger.debug("container was not running anymore")
|
|
137
139
|
|
|
138
140
|
# Teardown code
|
|
139
141
|
request.addfinalizer(fin)
|
{openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/testfilter.py
RENAMED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
3
|
|
|
4
|
+
logger = logging.getLogger("openfeature.contrib.tests")
|
|
5
|
+
|
|
4
6
|
|
|
5
7
|
class TestFilter:
|
|
6
8
|
def __init__(self, config, feature_list=None, resolver=None, base_path=None):
|
|
@@ -40,7 +42,7 @@ class TestFilter:
|
|
|
40
42
|
all_tags = self._get_item_tags(item)
|
|
41
43
|
|
|
42
44
|
# Debug: Print collected tags for each item
|
|
43
|
-
|
|
45
|
+
logger.debug(f"Item: {item.nodeid}, Tags: {all_tags}")
|
|
44
46
|
|
|
45
47
|
# Include-only logic: Skip items that do not match include_tags
|
|
46
48
|
if (
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
from unittest.mock import Mock, create_autospec
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from openfeature.contrib.provider.flagd.config import Config
|
|
6
|
+
from openfeature.contrib.provider.flagd.resolvers.in_process import InProcessResolver
|
|
7
|
+
from openfeature.contrib.provider.flagd.resolvers.process.flags import Flag, FlagStore
|
|
8
|
+
from openfeature.evaluation_context import EvaluationContext
|
|
9
|
+
from openfeature.exception import FlagNotFoundError, ParseError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def targeting():
|
|
13
|
+
return {
|
|
14
|
+
"if": [
|
|
15
|
+
{"==": [{"var": "targetingKey"}, "target_variant"]},
|
|
16
|
+
"target_variant",
|
|
17
|
+
None,
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def context(targeting_key):
|
|
23
|
+
return EvaluationContext(targeting_key=targeting_key)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@pytest.fixture
|
|
27
|
+
def config():
|
|
28
|
+
return create_autospec(Config)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.fixture
|
|
32
|
+
def flag_store():
|
|
33
|
+
return create_autospec(FlagStore)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@pytest.fixture
|
|
37
|
+
def flag():
|
|
38
|
+
return Flag(
|
|
39
|
+
key="flag",
|
|
40
|
+
state="ENABLED",
|
|
41
|
+
variants={"default_variant": False, "target_variant": True},
|
|
42
|
+
default_variant="default_variant",
|
|
43
|
+
targeting=targeting(),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@pytest.fixture
|
|
48
|
+
def resolver(config):
|
|
49
|
+
config.offline_flag_source_path = "flag.json"
|
|
50
|
+
config.deadline_ms = 100
|
|
51
|
+
return InProcessResolver(
|
|
52
|
+
config=config,
|
|
53
|
+
emit_provider_ready=Mock(),
|
|
54
|
+
emit_provider_error=Mock(),
|
|
55
|
+
emit_provider_stale=Mock(),
|
|
56
|
+
emit_provider_configuration_changed=Mock(),
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_resolve_boolean_details_flag_not_found(resolver):
|
|
61
|
+
resolver.flag_store.get_flag = Mock(return_value=None)
|
|
62
|
+
with pytest.raises(FlagNotFoundError):
|
|
63
|
+
resolver.resolve_boolean_details("nonexistent_flag", False)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_resolve_boolean_details_disabled_flag(flag, resolver):
|
|
67
|
+
flag.state = "DISABLED"
|
|
68
|
+
resolver.flag_store.get_flag = Mock(return_value=flag)
|
|
69
|
+
|
|
70
|
+
result = resolver.resolve_boolean_details("disabled_flag", False)
|
|
71
|
+
|
|
72
|
+
assert result.reason == "DISABLED"
|
|
73
|
+
assert result.variant is None
|
|
74
|
+
assert not result.value
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_resolve_boolean_details_invalid_variant(resolver, flag):
|
|
78
|
+
flag.targeting = {"var": ["targetingKey", "invalid_variant"]}
|
|
79
|
+
|
|
80
|
+
resolver.flag_store.get_flag = Mock(return_value=flag)
|
|
81
|
+
|
|
82
|
+
with pytest.raises(ParseError):
|
|
83
|
+
resolver.resolve_boolean_details("flag", False)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@pytest.mark.parametrize(
|
|
87
|
+
"input_config, resolve_config, expected",
|
|
88
|
+
[
|
|
89
|
+
(
|
|
90
|
+
{
|
|
91
|
+
"variants": {"default_variant": False, "target_variant": True},
|
|
92
|
+
"targeting": None,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"context": None,
|
|
96
|
+
"method": "resolve_boolean_details",
|
|
97
|
+
"default_value": False,
|
|
98
|
+
},
|
|
99
|
+
{"reason": "STATIC", "variant": "default_variant", "value": False},
|
|
100
|
+
),
|
|
101
|
+
(
|
|
102
|
+
{
|
|
103
|
+
"variants": {"default_variant": False, "target_variant": True},
|
|
104
|
+
"targeting": targeting(),
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"context": context("no_target_variant"),
|
|
108
|
+
"method": "resolve_boolean_details",
|
|
109
|
+
"default_value": False,
|
|
110
|
+
},
|
|
111
|
+
{"reason": "DEFAULT", "variant": "default_variant", "value": False},
|
|
112
|
+
),
|
|
113
|
+
(
|
|
114
|
+
{
|
|
115
|
+
"variants": {"default_variant": False, "target_variant": True},
|
|
116
|
+
"targeting": targeting(),
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"context": context("target_variant"),
|
|
120
|
+
"method": "resolve_boolean_details",
|
|
121
|
+
"default_value": False,
|
|
122
|
+
},
|
|
123
|
+
{"reason": "TARGETING_MATCH", "variant": "target_variant", "value": True},
|
|
124
|
+
),
|
|
125
|
+
(
|
|
126
|
+
{
|
|
127
|
+
"variants": {"default_variant": "default", "target_variant": "target"},
|
|
128
|
+
"targeting": targeting(),
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"context": context("target_variant"),
|
|
132
|
+
"method": "resolve_string_details",
|
|
133
|
+
"default_value": "placeholder",
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"reason": "TARGETING_MATCH",
|
|
137
|
+
"variant": "target_variant",
|
|
138
|
+
"value": "target",
|
|
139
|
+
},
|
|
140
|
+
),
|
|
141
|
+
(
|
|
142
|
+
{
|
|
143
|
+
"variants": {"default_variant": 1.0, "target_variant": 2.0},
|
|
144
|
+
"targeting": targeting(),
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"context": context("target_variant"),
|
|
148
|
+
"method": "resolve_float_details",
|
|
149
|
+
"default_value": 0.0,
|
|
150
|
+
},
|
|
151
|
+
{"reason": "TARGETING_MATCH", "variant": "target_variant", "value": 2.0},
|
|
152
|
+
),
|
|
153
|
+
(
|
|
154
|
+
{
|
|
155
|
+
"variants": {"default_variant": True, "target_variant": False},
|
|
156
|
+
"targeting": targeting(),
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"context": context("target_variant"),
|
|
160
|
+
"method": "resolve_boolean_details",
|
|
161
|
+
"default_value": True,
|
|
162
|
+
},
|
|
163
|
+
{"reason": "TARGETING_MATCH", "variant": "target_variant", "value": False},
|
|
164
|
+
),
|
|
165
|
+
(
|
|
166
|
+
{
|
|
167
|
+
"variants": {"default_variant": 10, "target_variant": 0},
|
|
168
|
+
"targeting": targeting(),
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"context": context("target_variant"),
|
|
172
|
+
"method": "resolve_integer_details",
|
|
173
|
+
"default_value": 1,
|
|
174
|
+
},
|
|
175
|
+
{"reason": "TARGETING_MATCH", "variant": "target_variant", "value": 0},
|
|
176
|
+
),
|
|
177
|
+
(
|
|
178
|
+
{
|
|
179
|
+
"variants": {"default_variant": {}, "target_variant": {}},
|
|
180
|
+
"targeting": targeting(),
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"context": context("target_variant"),
|
|
184
|
+
"method": "resolve_object_details",
|
|
185
|
+
"default_value": {},
|
|
186
|
+
},
|
|
187
|
+
{"reason": "TARGETING_MATCH", "variant": "target_variant", "value": {}},
|
|
188
|
+
),
|
|
189
|
+
],
|
|
190
|
+
ids=[
|
|
191
|
+
"static_flag",
|
|
192
|
+
"boolean_default_fallback",
|
|
193
|
+
"boolean_targeting_match",
|
|
194
|
+
"string_targeting_match",
|
|
195
|
+
"float_targeting_match",
|
|
196
|
+
"boolean_falsy_target",
|
|
197
|
+
"integer_falsy_target",
|
|
198
|
+
"object_falsy_target",
|
|
199
|
+
],
|
|
200
|
+
)
|
|
201
|
+
def test_resolver_details(
|
|
202
|
+
resolver,
|
|
203
|
+
flag,
|
|
204
|
+
input_config,
|
|
205
|
+
resolve_config,
|
|
206
|
+
expected,
|
|
207
|
+
):
|
|
208
|
+
flag.variants = input_config["variants"]
|
|
209
|
+
flag.targeting = input_config["targeting"]
|
|
210
|
+
resolver.flag_store.get_flag = Mock(return_value=flag)
|
|
211
|
+
|
|
212
|
+
result = getattr(resolver, resolve_config["method"])(
|
|
213
|
+
"flag", resolve_config["default_value"], resolve_config["context"]
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
assert result.reason == expected["reason"]
|
|
217
|
+
assert result.variant == expected["variant"]
|
|
218
|
+
assert result.value == expected["value"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/file/__init__.py
RENAMED
|
File without changes
|
{openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/file/conftest.py
RENAMED
|
File without changes
|
{openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/file/test_flaqd.py
RENAMED
|
File without changes
|
{openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/flagd_container.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/rpc/__init__.py
RENAMED
|
File without changes
|
{openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/rpc/conftest.py
RENAMED
|
File without changes
|
{openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/rpc/test_flaqd.py
RENAMED
|
File without changes
|
{openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/step/_utils.py
RENAMED
|
File without changes
|
{openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/step/config_steps.py
RENAMED
|
File without changes
|
|
File without changes
|
{openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/e2e/step/flag_step.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/basic-flag.json
RENAMED
|
File without changes
|
{openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/flags/basic-flag.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/test_file_store.py
RENAMED
|
File without changes
|
|
File without changes
|
{openfeature_provider_flagd-0.2.1 → openfeature_provider_flagd-0.2.2}/tests/test_targeting.py
RENAMED
|
File without changes
|