openfeature-provider-flagd 0.1.4__py3-none-any.whl → 0.2.0__py3-none-any.whl
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/.gitignore +2 -0
- openfeature/contrib/provider/flagd/config.py +202 -8
- openfeature/contrib/provider/flagd/provider.py +89 -97
- openfeature/contrib/provider/flagd/resolvers/__init__.py +5 -0
- openfeature/contrib/provider/flagd/resolvers/grpc.py +354 -0
- openfeature/contrib/provider/flagd/resolvers/in_process.py +131 -0
- openfeature/contrib/provider/flagd/resolvers/process/connector/__init__.py +11 -0
- openfeature/contrib/provider/flagd/resolvers/process/connector/file_watcher.py +106 -0
- openfeature/contrib/provider/flagd/resolvers/process/connector/grpc_watcher.py +192 -0
- openfeature/contrib/provider/flagd/resolvers/process/custom_ops.py +165 -0
- openfeature/contrib/provider/flagd/resolvers/process/flags.py +95 -0
- openfeature/contrib/provider/flagd/resolvers/process/targeting.py +35 -0
- openfeature/contrib/provider/flagd/resolvers/protocol.py +47 -0
- openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2.py +72 -0
- openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2.pyi +450 -0
- openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2_grpc.py +358 -0
- openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2_grpc.pyi +155 -0
- openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2.py +50 -0
- openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2.pyi +148 -0
- openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2_grpc.py +186 -0
- openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2_grpc.pyi +86 -0
- openfeature/schemas/protobuf/schema/v1/schema_pb2.py +72 -0
- openfeature/schemas/protobuf/schema/v1/schema_pb2.pyi +451 -0
- openfeature/schemas/protobuf/schema/v1/schema_pb2_grpc.py +358 -0
- openfeature/schemas/protobuf/schema/v1/schema_pb2_grpc.pyi +156 -0
- openfeature/schemas/protobuf/sync/v1/sync_service_pb2.py +47 -0
- openfeature/schemas/protobuf/sync/v1/sync_service_pb2.pyi +174 -0
- openfeature/schemas/protobuf/sync/v1/sync_service_pb2_grpc.py +143 -0
- openfeature/schemas/protobuf/sync/v1/sync_service_pb2_grpc.pyi +70 -0
- {openfeature_provider_flagd-0.1.4.dist-info → openfeature_provider_flagd-0.2.0.dist-info}/METADATA +132 -14
- openfeature_provider_flagd-0.2.0.dist-info/RECORD +35 -0
- {openfeature_provider_flagd-0.1.4.dist-info → openfeature_provider_flagd-0.2.0.dist-info}/WHEEL +1 -1
- {openfeature_provider_flagd-0.1.4.dist-info → openfeature_provider_flagd-0.2.0.dist-info}/licenses/LICENSE +1 -1
- openfeature/contrib/provider/flagd/proto/flagd/evaluation/v1/evaluation_pb2.py +0 -62
- openfeature/contrib/provider/flagd/proto/flagd/evaluation/v1/evaluation_pb2_grpc.py +0 -267
- openfeature/contrib/provider/flagd/proto/flagd/sync/v1/sync_pb2.py +0 -40
- openfeature/contrib/provider/flagd/proto/flagd/sync/v1/sync_pb2_grpc.py +0 -135
- openfeature/contrib/provider/flagd/proto/schema/v1/schema_pb2.py +0 -62
- openfeature/contrib/provider/flagd/proto/schema/v1/schema_pb2_grpc.py +0 -267
- openfeature/contrib/provider/flagd/proto/sync/v1/sync_service_pb2.py +0 -37
- openfeature/contrib/provider/flagd/proto/sync/v1/sync_service_pb2_grpc.py +0 -102
- openfeature_provider_flagd-0.1.4.dist-info/RECORD +0 -16
openfeature/.gitignore
ADDED
|
@@ -1,5 +1,53 @@
|
|
|
1
|
+
import dataclasses
|
|
1
2
|
import os
|
|
2
3
|
import typing
|
|
4
|
+
from enum import Enum
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ResolverType(Enum):
|
|
8
|
+
RPC = "rpc"
|
|
9
|
+
IN_PROCESS = "in-process"
|
|
10
|
+
FILE = "file"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CacheType(Enum):
|
|
14
|
+
LRU = "lru"
|
|
15
|
+
DISABLED = "disabled"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
DEFAULT_CACHE = CacheType.LRU
|
|
19
|
+
DEFAULT_CACHE_SIZE = 1000
|
|
20
|
+
DEFAULT_DEADLINE = 500
|
|
21
|
+
DEFAULT_HOST = "localhost"
|
|
22
|
+
DEFAULT_KEEP_ALIVE = 0
|
|
23
|
+
DEFAULT_OFFLINE_SOURCE_PATH: typing.Optional[str] = None
|
|
24
|
+
DEFAULT_OFFLINE_POLL_MS = 5000
|
|
25
|
+
DEFAULT_PORT_IN_PROCESS = 8015
|
|
26
|
+
DEFAULT_PORT_RPC = 8013
|
|
27
|
+
DEFAULT_RESOLVER_TYPE = ResolverType.RPC
|
|
28
|
+
DEFAULT_RETRY_BACKOFF = 1000
|
|
29
|
+
DEFAULT_RETRY_BACKOFF_MAX = 120000
|
|
30
|
+
DEFAULT_RETRY_GRACE_PERIOD_SECONDS = 5
|
|
31
|
+
DEFAULT_STREAM_DEADLINE = 600000
|
|
32
|
+
DEFAULT_TLS = False
|
|
33
|
+
DEFAULT_TLS_CERT: typing.Optional[str] = None
|
|
34
|
+
|
|
35
|
+
ENV_VAR_CACHE_SIZE = "FLAGD_MAX_CACHE_SIZE"
|
|
36
|
+
ENV_VAR_CACHE_TYPE = "FLAGD_CACHE"
|
|
37
|
+
ENV_VAR_DEADLINE_MS = "FLAGD_DEADLINE_MS"
|
|
38
|
+
ENV_VAR_HOST = "FLAGD_HOST"
|
|
39
|
+
ENV_VAR_KEEP_ALIVE_TIME_MS = "FLAGD_KEEP_ALIVE_TIME_MS"
|
|
40
|
+
ENV_VAR_OFFLINE_FLAG_SOURCE_PATH = "FLAGD_OFFLINE_FLAG_SOURCE_PATH"
|
|
41
|
+
ENV_VAR_OFFLINE_POLL_MS = "FLAGD_OFFLINE_POLL_MS"
|
|
42
|
+
ENV_VAR_PORT = "FLAGD_PORT"
|
|
43
|
+
ENV_VAR_RESOLVER_TYPE = "FLAGD_RESOLVER"
|
|
44
|
+
ENV_VAR_RETRY_BACKOFF_MS = "FLAGD_RETRY_BACKOFF_MS"
|
|
45
|
+
ENV_VAR_RETRY_BACKOFF_MAX_MS = "FLAGD_RETRY_BACKOFF_MAX_MS"
|
|
46
|
+
ENV_VAR_RETRY_GRACE_PERIOD_SECONDS = "FLAGD_RETRY_GRACE_PERIOD"
|
|
47
|
+
ENV_VAR_SELECTOR = "FLAGD_SOURCE_SELECTOR"
|
|
48
|
+
ENV_VAR_STREAM_DEADLINE_MS = "FLAGD_STREAM_DEADLINE_MS"
|
|
49
|
+
ENV_VAR_TLS = "FLAGD_TLS"
|
|
50
|
+
ENV_VAR_TLS_CERT = "FLAGD_SERVER_CERT_PATH"
|
|
3
51
|
|
|
4
52
|
T = typing.TypeVar("T")
|
|
5
53
|
|
|
@@ -8,6 +56,14 @@ def str_to_bool(val: str) -> bool:
|
|
|
8
56
|
return val.lower() == "true"
|
|
9
57
|
|
|
10
58
|
|
|
59
|
+
def convert_resolver_type(val: typing.Union[str, ResolverType]) -> ResolverType:
|
|
60
|
+
if isinstance(val, str):
|
|
61
|
+
v = val.lower()
|
|
62
|
+
return ResolverType(v)
|
|
63
|
+
else:
|
|
64
|
+
return ResolverType(val)
|
|
65
|
+
|
|
66
|
+
|
|
11
67
|
def env_or_default(
|
|
12
68
|
env_var: str, default: T, cast: typing.Optional[typing.Callable[[str], T]] = None
|
|
13
69
|
) -> typing.Union[str, T]:
|
|
@@ -17,19 +73,157 @@ def env_or_default(
|
|
|
17
73
|
return val if cast is None else cast(val)
|
|
18
74
|
|
|
19
75
|
|
|
76
|
+
@dataclasses.dataclass
|
|
20
77
|
class Config:
|
|
21
|
-
def __init__(
|
|
78
|
+
def __init__( # noqa: PLR0913
|
|
22
79
|
self,
|
|
23
80
|
host: typing.Optional[str] = None,
|
|
24
81
|
port: typing.Optional[int] = None,
|
|
25
82
|
tls: typing.Optional[bool] = None,
|
|
26
|
-
|
|
83
|
+
selector: typing.Optional[str] = None,
|
|
84
|
+
resolver: typing.Optional[ResolverType] = None,
|
|
85
|
+
offline_flag_source_path: typing.Optional[str] = None,
|
|
86
|
+
offline_poll_interval_ms: typing.Optional[int] = None,
|
|
87
|
+
retry_backoff_ms: typing.Optional[int] = None,
|
|
88
|
+
retry_backoff_max_ms: typing.Optional[int] = None,
|
|
89
|
+
retry_grace_period: typing.Optional[int] = None,
|
|
90
|
+
deadline_ms: typing.Optional[int] = None,
|
|
91
|
+
stream_deadline_ms: typing.Optional[int] = None,
|
|
92
|
+
keep_alive_time: typing.Optional[int] = None,
|
|
93
|
+
cache: typing.Optional[CacheType] = None,
|
|
94
|
+
max_cache_size: typing.Optional[int] = None,
|
|
95
|
+
cert_path: typing.Optional[str] = None,
|
|
27
96
|
):
|
|
28
|
-
self.host = env_or_default(
|
|
29
|
-
|
|
30
|
-
env_or_default("FLAGD_PORT", 8013, cast=int) if port is None else port
|
|
31
|
-
)
|
|
97
|
+
self.host = env_or_default(ENV_VAR_HOST, DEFAULT_HOST) if host is None else host
|
|
98
|
+
|
|
32
99
|
self.tls = (
|
|
33
|
-
env_or_default(
|
|
100
|
+
env_or_default(ENV_VAR_TLS, DEFAULT_TLS, cast=str_to_bool)
|
|
101
|
+
if tls is None
|
|
102
|
+
else tls
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
self.retry_backoff_ms: int = (
|
|
106
|
+
int(
|
|
107
|
+
env_or_default(
|
|
108
|
+
ENV_VAR_RETRY_BACKOFF_MS, DEFAULT_RETRY_BACKOFF, cast=int
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
if retry_backoff_ms is None
|
|
112
|
+
else retry_backoff_ms
|
|
113
|
+
)
|
|
114
|
+
self.retry_backoff_max_ms: int = (
|
|
115
|
+
int(
|
|
116
|
+
env_or_default(
|
|
117
|
+
ENV_VAR_RETRY_BACKOFF_MAX_MS, DEFAULT_RETRY_BACKOFF_MAX, cast=int
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
if retry_backoff_max_ms is None
|
|
121
|
+
else retry_backoff_max_ms
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
self.retry_grace_period: int = (
|
|
125
|
+
int(
|
|
126
|
+
env_or_default(
|
|
127
|
+
ENV_VAR_RETRY_GRACE_PERIOD_SECONDS,
|
|
128
|
+
DEFAULT_RETRY_GRACE_PERIOD_SECONDS,
|
|
129
|
+
cast=int,
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
if retry_grace_period is None
|
|
133
|
+
else retry_grace_period
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
self.resolver = (
|
|
137
|
+
env_or_default(
|
|
138
|
+
ENV_VAR_RESOLVER_TYPE, DEFAULT_RESOLVER_TYPE, cast=convert_resolver_type
|
|
139
|
+
)
|
|
140
|
+
if resolver is None
|
|
141
|
+
else resolver
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
default_port = (
|
|
145
|
+
DEFAULT_PORT_RPC
|
|
146
|
+
if self.resolver is ResolverType.RPC
|
|
147
|
+
else DEFAULT_PORT_IN_PROCESS
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
self.port: int = (
|
|
151
|
+
int(env_or_default(ENV_VAR_PORT, default_port, cast=int))
|
|
152
|
+
if port is None
|
|
153
|
+
else port
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
self.offline_flag_source_path = (
|
|
157
|
+
env_or_default(
|
|
158
|
+
ENV_VAR_OFFLINE_FLAG_SOURCE_PATH, DEFAULT_OFFLINE_SOURCE_PATH
|
|
159
|
+
)
|
|
160
|
+
if offline_flag_source_path is None
|
|
161
|
+
else offline_flag_source_path
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
if (
|
|
165
|
+
self.offline_flag_source_path is not None
|
|
166
|
+
and self.resolver is ResolverType.IN_PROCESS
|
|
167
|
+
):
|
|
168
|
+
self.resolver = ResolverType.FILE
|
|
169
|
+
|
|
170
|
+
if self.resolver is ResolverType.FILE and self.offline_flag_source_path is None:
|
|
171
|
+
raise AttributeError(
|
|
172
|
+
"Resolver Type 'FILE' requires a offlineFlagSourcePath"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
self.offline_poll_interval_ms: int = (
|
|
176
|
+
int(
|
|
177
|
+
env_or_default(
|
|
178
|
+
ENV_VAR_OFFLINE_POLL_MS, DEFAULT_OFFLINE_POLL_MS, cast=int
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
if offline_poll_interval_ms is None
|
|
182
|
+
else offline_poll_interval_ms
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
self.deadline_ms: int = (
|
|
186
|
+
int(env_or_default(ENV_VAR_DEADLINE_MS, DEFAULT_DEADLINE, cast=int))
|
|
187
|
+
if deadline_ms is None
|
|
188
|
+
else deadline_ms
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
self.stream_deadline_ms: int = (
|
|
192
|
+
int(
|
|
193
|
+
env_or_default(
|
|
194
|
+
ENV_VAR_STREAM_DEADLINE_MS, DEFAULT_STREAM_DEADLINE, cast=int
|
|
195
|
+
)
|
|
196
|
+
)
|
|
197
|
+
if stream_deadline_ms is None
|
|
198
|
+
else stream_deadline_ms
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
self.keep_alive_time: int = (
|
|
202
|
+
int(
|
|
203
|
+
env_or_default(ENV_VAR_KEEP_ALIVE_TIME_MS, DEFAULT_KEEP_ALIVE, cast=int)
|
|
204
|
+
)
|
|
205
|
+
if keep_alive_time is None
|
|
206
|
+
else keep_alive_time
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
self.cache = (
|
|
210
|
+
CacheType(env_or_default(ENV_VAR_CACHE_TYPE, DEFAULT_CACHE))
|
|
211
|
+
if cache is None
|
|
212
|
+
else cache
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
self.max_cache_size: int = (
|
|
216
|
+
int(env_or_default(ENV_VAR_CACHE_SIZE, DEFAULT_CACHE_SIZE, cast=int))
|
|
217
|
+
if max_cache_size is None
|
|
218
|
+
else max_cache_size
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
self.cert_path = (
|
|
222
|
+
env_or_default(ENV_VAR_TLS_CERT, DEFAULT_TLS_CERT)
|
|
223
|
+
if cert_path is None
|
|
224
|
+
else cert_path
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
self.selector = (
|
|
228
|
+
env_or_default(ENV_VAR_SELECTOR, None) if selector is None else selector
|
|
34
229
|
)
|
|
35
|
-
self.timeout = 5 if timeout is None else timeout
|
|
@@ -22,25 +22,15 @@
|
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
24
|
import typing
|
|
25
|
-
|
|
26
|
-
import grpc
|
|
27
|
-
from google.protobuf.struct_pb2 import Struct
|
|
25
|
+
import warnings
|
|
28
26
|
|
|
29
27
|
from openfeature.evaluation_context import EvaluationContext
|
|
30
|
-
from openfeature.exception import (
|
|
31
|
-
FlagNotFoundError,
|
|
32
|
-
GeneralError,
|
|
33
|
-
InvalidContextError,
|
|
34
|
-
ParseError,
|
|
35
|
-
TypeMismatchError,
|
|
36
|
-
)
|
|
37
28
|
from openfeature.flag_evaluation import FlagResolutionDetails
|
|
29
|
+
from openfeature.provider import AbstractProvider
|
|
38
30
|
from openfeature.provider.metadata import Metadata
|
|
39
|
-
from openfeature.provider.provider import AbstractProvider
|
|
40
31
|
|
|
41
|
-
from .config import Config
|
|
42
|
-
from .
|
|
43
|
-
from .proto.schema.v1 import schema_pb2, schema_pb2_grpc
|
|
32
|
+
from .config import CacheType, Config, ResolverType
|
|
33
|
+
from .resolvers import AbstractResolver, GrpcResolver, InProcessResolver
|
|
44
34
|
|
|
45
35
|
T = typing.TypeVar("T")
|
|
46
36
|
|
|
@@ -48,12 +38,24 @@ T = typing.TypeVar("T")
|
|
|
48
38
|
class FlagdProvider(AbstractProvider):
|
|
49
39
|
"""Flagd OpenFeature Provider"""
|
|
50
40
|
|
|
51
|
-
def __init__(
|
|
41
|
+
def __init__( # noqa: PLR0913
|
|
52
42
|
self,
|
|
53
43
|
host: typing.Optional[str] = None,
|
|
54
44
|
port: typing.Optional[int] = None,
|
|
55
45
|
tls: typing.Optional[bool] = None,
|
|
46
|
+
deadline_ms: typing.Optional[int] = None,
|
|
56
47
|
timeout: typing.Optional[int] = None,
|
|
48
|
+
retry_backoff_ms: typing.Optional[int] = None,
|
|
49
|
+
selector: typing.Optional[str] = None,
|
|
50
|
+
resolver_type: typing.Optional[ResolverType] = None,
|
|
51
|
+
offline_flag_source_path: typing.Optional[str] = None,
|
|
52
|
+
stream_deadline_ms: typing.Optional[int] = None,
|
|
53
|
+
keep_alive_time: typing.Optional[int] = None,
|
|
54
|
+
cache: typing.Optional[CacheType] = None,
|
|
55
|
+
max_cache_size: typing.Optional[int] = None,
|
|
56
|
+
retry_backoff_max_ms: typing.Optional[int] = None,
|
|
57
|
+
retry_grace_period: typing.Optional[int] = None,
|
|
58
|
+
cert_path: typing.Optional[str] = None,
|
|
57
59
|
):
|
|
58
60
|
"""
|
|
59
61
|
Create an instance of the FlagdProvider
|
|
@@ -61,21 +63,73 @@ class FlagdProvider(AbstractProvider):
|
|
|
61
63
|
:param host: the host to make requests to
|
|
62
64
|
:param port: the port the flagd service is available on
|
|
63
65
|
:param tls: enable/disable secure TLS connectivity
|
|
64
|
-
:param
|
|
66
|
+
:param deadline_ms: the maximum to wait before a request times out
|
|
67
|
+
:param timeout: the maximum time to wait before a request times out
|
|
68
|
+
:param retry_backoff_ms: the number of milliseconds to backoff
|
|
69
|
+
:param offline_flag_source_path: the path to the flag source file
|
|
70
|
+
:param stream_deadline_ms: the maximum time to wait before a request times out
|
|
71
|
+
:param keep_alive_time: the number of milliseconds to keep alive
|
|
72
|
+
:param resolver_type: the type of resolver to use
|
|
65
73
|
"""
|
|
74
|
+
if deadline_ms is None and timeout is not None:
|
|
75
|
+
deadline_ms = timeout * 1000
|
|
76
|
+
warnings.warn(
|
|
77
|
+
"'timeout' property is deprecated, please use 'deadline' instead, be aware that 'deadline' is in milliseconds",
|
|
78
|
+
DeprecationWarning,
|
|
79
|
+
stacklevel=2,
|
|
80
|
+
)
|
|
81
|
+
|
|
66
82
|
self.config = Config(
|
|
67
83
|
host=host,
|
|
68
84
|
port=port,
|
|
69
85
|
tls=tls,
|
|
70
|
-
|
|
86
|
+
deadline_ms=deadline_ms,
|
|
87
|
+
retry_backoff_ms=retry_backoff_ms,
|
|
88
|
+
retry_backoff_max_ms=retry_backoff_max_ms,
|
|
89
|
+
retry_grace_period=retry_grace_period,
|
|
90
|
+
selector=selector,
|
|
91
|
+
resolver=resolver_type,
|
|
92
|
+
offline_flag_source_path=offline_flag_source_path,
|
|
93
|
+
stream_deadline_ms=stream_deadline_ms,
|
|
94
|
+
keep_alive_time=keep_alive_time,
|
|
95
|
+
cache=cache,
|
|
96
|
+
max_cache_size=max_cache_size,
|
|
97
|
+
cert_path=cert_path,
|
|
71
98
|
)
|
|
72
99
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
100
|
+
self.resolver = self.setup_resolver()
|
|
101
|
+
|
|
102
|
+
def setup_resolver(self) -> AbstractResolver:
|
|
103
|
+
if self.config.resolver == ResolverType.RPC:
|
|
104
|
+
return GrpcResolver(
|
|
105
|
+
self.config,
|
|
106
|
+
self.emit_provider_ready,
|
|
107
|
+
self.emit_provider_error,
|
|
108
|
+
self.emit_provider_stale,
|
|
109
|
+
self.emit_provider_configuration_changed,
|
|
110
|
+
)
|
|
111
|
+
elif (
|
|
112
|
+
self.config.resolver == ResolverType.IN_PROCESS
|
|
113
|
+
or self.config.resolver == ResolverType.FILE
|
|
114
|
+
):
|
|
115
|
+
return InProcessResolver(
|
|
116
|
+
self.config,
|
|
117
|
+
self.emit_provider_ready,
|
|
118
|
+
self.emit_provider_error,
|
|
119
|
+
self.emit_provider_stale,
|
|
120
|
+
self.emit_provider_configuration_changed,
|
|
121
|
+
)
|
|
122
|
+
else:
|
|
123
|
+
raise ValueError(
|
|
124
|
+
f"`resolver_type` parameter invalid: {self.config.resolver}"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
def initialize(self, evaluation_context: EvaluationContext) -> None:
|
|
128
|
+
self.resolver.initialize(evaluation_context)
|
|
76
129
|
|
|
77
130
|
def shutdown(self) -> None:
|
|
78
|
-
self.
|
|
131
|
+
if self.resolver:
|
|
132
|
+
self.resolver.shutdown()
|
|
79
133
|
|
|
80
134
|
def get_metadata(self) -> Metadata:
|
|
81
135
|
"""Returns provider metadata"""
|
|
@@ -87,7 +141,9 @@ class FlagdProvider(AbstractProvider):
|
|
|
87
141
|
default_value: bool,
|
|
88
142
|
evaluation_context: typing.Optional[EvaluationContext] = None,
|
|
89
143
|
) -> FlagResolutionDetails[bool]:
|
|
90
|
-
return self.
|
|
144
|
+
return self.resolver.resolve_boolean_details(
|
|
145
|
+
key, default_value, evaluation_context
|
|
146
|
+
)
|
|
91
147
|
|
|
92
148
|
def resolve_string_details(
|
|
93
149
|
self,
|
|
@@ -95,7 +151,9 @@ class FlagdProvider(AbstractProvider):
|
|
|
95
151
|
default_value: str,
|
|
96
152
|
evaluation_context: typing.Optional[EvaluationContext] = None,
|
|
97
153
|
) -> FlagResolutionDetails[str]:
|
|
98
|
-
return self.
|
|
154
|
+
return self.resolver.resolve_string_details(
|
|
155
|
+
key, default_value, evaluation_context
|
|
156
|
+
)
|
|
99
157
|
|
|
100
158
|
def resolve_float_details(
|
|
101
159
|
self,
|
|
@@ -103,7 +161,9 @@ class FlagdProvider(AbstractProvider):
|
|
|
103
161
|
default_value: float,
|
|
104
162
|
evaluation_context: typing.Optional[EvaluationContext] = None,
|
|
105
163
|
) -> FlagResolutionDetails[float]:
|
|
106
|
-
return self.
|
|
164
|
+
return self.resolver.resolve_float_details(
|
|
165
|
+
key, default_value, evaluation_context
|
|
166
|
+
)
|
|
107
167
|
|
|
108
168
|
def resolve_integer_details(
|
|
109
169
|
self,
|
|
@@ -111,7 +171,9 @@ class FlagdProvider(AbstractProvider):
|
|
|
111
171
|
default_value: int,
|
|
112
172
|
evaluation_context: typing.Optional[EvaluationContext] = None,
|
|
113
173
|
) -> FlagResolutionDetails[int]:
|
|
114
|
-
return self.
|
|
174
|
+
return self.resolver.resolve_integer_details(
|
|
175
|
+
key, default_value, evaluation_context
|
|
176
|
+
)
|
|
115
177
|
|
|
116
178
|
def resolve_object_details(
|
|
117
179
|
self,
|
|
@@ -119,76 +181,6 @@ class FlagdProvider(AbstractProvider):
|
|
|
119
181
|
default_value: typing.Union[dict, list],
|
|
120
182
|
evaluation_context: typing.Optional[EvaluationContext] = None,
|
|
121
183
|
) -> FlagResolutionDetails[typing.Union[dict, list]]:
|
|
122
|
-
return self.
|
|
123
|
-
|
|
124
|
-
def _resolve(
|
|
125
|
-
self,
|
|
126
|
-
flag_key: str,
|
|
127
|
-
flag_type: FlagType,
|
|
128
|
-
default_value: T,
|
|
129
|
-
evaluation_context: typing.Optional[EvaluationContext],
|
|
130
|
-
) -> FlagResolutionDetails[T]:
|
|
131
|
-
context = self._convert_context(evaluation_context)
|
|
132
|
-
call_args = {"timeout": self.config.timeout}
|
|
133
|
-
try:
|
|
134
|
-
if flag_type == FlagType.BOOLEAN:
|
|
135
|
-
request = schema_pb2.ResolveBooleanRequest( # type:ignore[attr-defined]
|
|
136
|
-
flag_key=flag_key, context=context
|
|
137
|
-
)
|
|
138
|
-
response = self.stub.ResolveBoolean(request, **call_args)
|
|
139
|
-
elif flag_type == FlagType.STRING:
|
|
140
|
-
request = schema_pb2.ResolveStringRequest( # type:ignore[attr-defined]
|
|
141
|
-
flag_key=flag_key, context=context
|
|
142
|
-
)
|
|
143
|
-
response = self.stub.ResolveString(request, **call_args)
|
|
144
|
-
elif flag_type == FlagType.OBJECT:
|
|
145
|
-
request = schema_pb2.ResolveObjectRequest( # type:ignore[attr-defined]
|
|
146
|
-
flag_key=flag_key, context=context
|
|
147
|
-
)
|
|
148
|
-
response = self.stub.ResolveObject(request, **call_args)
|
|
149
|
-
elif flag_type == FlagType.FLOAT:
|
|
150
|
-
request = schema_pb2.ResolveFloatRequest( # type:ignore[attr-defined]
|
|
151
|
-
flag_key=flag_key, context=context
|
|
152
|
-
)
|
|
153
|
-
response = self.stub.ResolveFloat(request, **call_args)
|
|
154
|
-
elif flag_type == FlagType.INTEGER:
|
|
155
|
-
request = schema_pb2.ResolveIntRequest( # type:ignore[attr-defined]
|
|
156
|
-
flag_key=flag_key, context=context
|
|
157
|
-
)
|
|
158
|
-
response = self.stub.ResolveInt(request, **call_args)
|
|
159
|
-
else:
|
|
160
|
-
raise ValueError(f"Unknown flag type: {flag_type}")
|
|
161
|
-
|
|
162
|
-
except grpc.RpcError as e:
|
|
163
|
-
code = e.code()
|
|
164
|
-
message = f"received grpc status code {code}"
|
|
165
|
-
|
|
166
|
-
if code == grpc.StatusCode.NOT_FOUND:
|
|
167
|
-
raise FlagNotFoundError(message) from e
|
|
168
|
-
elif code == grpc.StatusCode.INVALID_ARGUMENT:
|
|
169
|
-
raise TypeMismatchError(message) from e
|
|
170
|
-
elif code == grpc.StatusCode.DATA_LOSS:
|
|
171
|
-
raise ParseError(message) from e
|
|
172
|
-
raise GeneralError(message) from e
|
|
173
|
-
|
|
174
|
-
# Got a valid flag and valid type. Return it.
|
|
175
|
-
return FlagResolutionDetails(
|
|
176
|
-
value=response.value,
|
|
177
|
-
reason=response.reason,
|
|
178
|
-
variant=response.variant,
|
|
184
|
+
return self.resolver.resolve_object_details(
|
|
185
|
+
key, default_value, evaluation_context
|
|
179
186
|
)
|
|
180
|
-
|
|
181
|
-
def _convert_context(
|
|
182
|
-
self, evaluation_context: typing.Optional[EvaluationContext]
|
|
183
|
-
) -> Struct:
|
|
184
|
-
s = Struct()
|
|
185
|
-
if evaluation_context:
|
|
186
|
-
try:
|
|
187
|
-
s["targetingKey"] = evaluation_context.targeting_key
|
|
188
|
-
s.update(evaluation_context.attributes)
|
|
189
|
-
except ValueError as exc:
|
|
190
|
-
message = (
|
|
191
|
-
"could not serialize evaluation context to google.protobuf.Struct"
|
|
192
|
-
)
|
|
193
|
-
raise InvalidContextError(message) from exc
|
|
194
|
-
return s
|