openfeature-provider-flagd 0.1.5__py3-none-any.whl → 0.2.1__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.
Files changed (44) hide show
  1. openfeature/.gitignore +2 -0
  2. openfeature/contrib/provider/flagd/config.py +214 -23
  3. openfeature/contrib/provider/flagd/provider.py +88 -12
  4. openfeature/contrib/provider/flagd/resolvers/__init__.py +1 -47
  5. openfeature/contrib/provider/flagd/resolvers/grpc.py +229 -17
  6. openfeature/contrib/provider/flagd/resolvers/in_process.py +40 -31
  7. openfeature/contrib/provider/flagd/resolvers/process/connector/__init__.py +11 -0
  8. openfeature/contrib/provider/flagd/resolvers/process/connector/file_watcher.py +107 -0
  9. openfeature/contrib/provider/flagd/resolvers/process/connector/grpc_watcher.py +218 -0
  10. openfeature/contrib/provider/flagd/resolvers/process/custom_ops.py +58 -19
  11. openfeature/contrib/provider/flagd/resolvers/process/flags.py +50 -6
  12. openfeature/contrib/provider/flagd/resolvers/process/targeting.py +35 -0
  13. openfeature/contrib/provider/flagd/resolvers/protocol.py +47 -0
  14. openfeature/contrib/provider/flagd/sync_metadata_hook.py +14 -0
  15. openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2.py +72 -0
  16. openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2.pyi +450 -0
  17. openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2_grpc.py +358 -0
  18. openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2_grpc.pyi +155 -0
  19. openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2.py +50 -0
  20. openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2.pyi +148 -0
  21. openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2_grpc.py +186 -0
  22. openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2_grpc.pyi +86 -0
  23. openfeature/schemas/protobuf/schema/v1/schema_pb2.py +72 -0
  24. openfeature/schemas/protobuf/schema/v1/schema_pb2.pyi +451 -0
  25. openfeature/schemas/protobuf/schema/v1/schema_pb2_grpc.py +358 -0
  26. openfeature/schemas/protobuf/schema/v1/schema_pb2_grpc.pyi +156 -0
  27. openfeature/schemas/protobuf/sync/v1/sync_service_pb2.py +47 -0
  28. openfeature/schemas/protobuf/sync/v1/sync_service_pb2.pyi +174 -0
  29. openfeature/schemas/protobuf/sync/v1/sync_service_pb2_grpc.py +143 -0
  30. openfeature/schemas/protobuf/sync/v1/sync_service_pb2_grpc.pyi +70 -0
  31. {openfeature_provider_flagd-0.1.5.dist-info → openfeature_provider_flagd-0.2.1.dist-info}/METADATA +116 -15
  32. openfeature_provider_flagd-0.2.1.dist-info/RECORD +36 -0
  33. {openfeature_provider_flagd-0.1.5.dist-info → openfeature_provider_flagd-0.2.1.dist-info}/WHEEL +1 -1
  34. {openfeature_provider_flagd-0.1.5.dist-info → openfeature_provider_flagd-0.2.1.dist-info}/licenses/LICENSE +1 -1
  35. openfeature/contrib/provider/flagd/proto/flagd/evaluation/v1/evaluation_pb2.py +0 -62
  36. openfeature/contrib/provider/flagd/proto/flagd/evaluation/v1/evaluation_pb2_grpc.py +0 -267
  37. openfeature/contrib/provider/flagd/proto/flagd/sync/v1/sync_pb2.py +0 -40
  38. openfeature/contrib/provider/flagd/proto/flagd/sync/v1/sync_pb2_grpc.py +0 -135
  39. openfeature/contrib/provider/flagd/proto/schema/v1/schema_pb2.py +0 -62
  40. openfeature/contrib/provider/flagd/proto/schema/v1/schema_pb2_grpc.py +0 -267
  41. openfeature/contrib/provider/flagd/proto/sync/v1/sync_service_pb2.py +0 -37
  42. openfeature/contrib/provider/flagd/proto/sync/v1/sync_service_pb2_grpc.py +0 -102
  43. openfeature/contrib/provider/flagd/resolvers/process/file_watcher.py +0 -89
  44. openfeature_provider_flagd-0.1.5.dist-info/RECORD +0 -22
@@ -0,0 +1,218 @@
1
+ import json
2
+ import logging
3
+ import threading
4
+ import time
5
+ import typing
6
+
7
+ import grpc
8
+ from google.protobuf.json_format import MessageToDict
9
+
10
+ from openfeature.evaluation_context import EvaluationContext
11
+ from openfeature.event import ProviderEventDetails
12
+ from openfeature.exception import ErrorCode, ParseError, ProviderNotReadyError
13
+ from openfeature.schemas.protobuf.flagd.sync.v1 import (
14
+ sync_pb2,
15
+ sync_pb2_grpc,
16
+ )
17
+
18
+ from ....config import Config
19
+ from ..connector import FlagStateConnector
20
+ from ..flags import FlagStore
21
+
22
+ logger = logging.getLogger("openfeature.contrib")
23
+
24
+
25
+ class GrpcWatcher(FlagStateConnector):
26
+ def __init__(
27
+ self,
28
+ config: Config,
29
+ flag_store: FlagStore,
30
+ emit_provider_ready: typing.Callable[[ProviderEventDetails, dict], None],
31
+ emit_provider_error: typing.Callable[[ProviderEventDetails], None],
32
+ emit_provider_stale: typing.Callable[[ProviderEventDetails], None],
33
+ ):
34
+ self.flag_store = flag_store
35
+ self.config = config
36
+
37
+ self.channel = self._generate_channel(config)
38
+ self.stub = sync_pb2_grpc.FlagSyncServiceStub(self.channel)
39
+ self.retry_backoff_seconds = config.retry_backoff_ms * 0.001
40
+ self.retry_backoff_max_seconds = config.retry_backoff_ms * 0.001
41
+ self.retry_grace_period = config.retry_grace_period
42
+ self.streamline_deadline_seconds = config.stream_deadline_ms * 0.001
43
+ self.deadline = config.deadline_ms * 0.001
44
+ self.selector = config.selector
45
+ self.provider_id = config.provider_id
46
+ self.emit_provider_ready = emit_provider_ready
47
+ self.emit_provider_error = emit_provider_error
48
+ self.emit_provider_stale = emit_provider_stale
49
+
50
+ self.connected = False
51
+ self.thread: typing.Optional[threading.Thread] = None
52
+ self.timer: typing.Optional[threading.Timer] = None
53
+
54
+ self.start_time = time.time()
55
+
56
+ def _generate_channel(self, config: Config) -> grpc.Channel:
57
+ target = f"{config.host}:{config.port}"
58
+ # Create the channel with the service config
59
+ options: list[tuple[str, typing.Any]] = [
60
+ ("grpc.keepalive_time_ms", config.keep_alive_time),
61
+ ("grpc.initial_reconnect_backoff_ms", config.retry_backoff_ms),
62
+ ("grpc.max_reconnect_backoff_ms", config.retry_backoff_max_ms),
63
+ ("grpc.min_reconnect_backoff_ms", config.stream_deadline_ms),
64
+ ]
65
+ if config.default_authority is not None:
66
+ options.append(("grpc.default_authority", config.default_authority))
67
+
68
+ if config.channel_credentials is not None:
69
+ channel_args = {
70
+ "options": options,
71
+ "credentials": config.channel_credentials,
72
+ }
73
+ channel = grpc.secure_channel(target, **channel_args)
74
+
75
+ elif config.tls:
76
+ channel_args = {
77
+ "options": options,
78
+ "credentials": grpc.ssl_channel_credentials(),
79
+ }
80
+ if config.cert_path:
81
+ with open(config.cert_path, "rb") as f:
82
+ channel_args["credentials"] = grpc.ssl_channel_credentials(f.read())
83
+
84
+ channel = grpc.secure_channel(target, **channel_args)
85
+
86
+ else:
87
+ channel = grpc.insecure_channel(
88
+ target,
89
+ options=options,
90
+ )
91
+
92
+ return channel
93
+
94
+ def initialize(self, context: EvaluationContext) -> None:
95
+ self.connect()
96
+
97
+ def connect(self) -> None:
98
+ self.active = True
99
+
100
+ # Run monitoring in a separate thread
101
+ self.monitor_thread = threading.Thread(
102
+ target=self.monitor, daemon=True, name="FlagdGrpcSyncServiceMonitorThread"
103
+ )
104
+ self.monitor_thread.start()
105
+ ## block until ready or deadline reached
106
+ timeout = self.deadline + time.time()
107
+ while not self.connected and time.time() < timeout:
108
+ time.sleep(0.05)
109
+ logger.debug("Finished blocking gRPC state initialization")
110
+
111
+ if not self.connected:
112
+ raise ProviderNotReadyError(
113
+ "Blocking init finished before data synced. Consider increasing startup deadline to avoid inconsistent evaluations."
114
+ )
115
+
116
+ def monitor(self) -> None:
117
+ self.channel.subscribe(self._state_change_callback, try_to_connect=True)
118
+
119
+ def _state_change_callback(self, new_state: grpc.ChannelConnectivity) -> None:
120
+ logger.debug(f"gRPC state change: {new_state}")
121
+ if (
122
+ new_state == grpc.ChannelConnectivity.READY
123
+ or new_state == grpc.ChannelConnectivity.IDLE
124
+ ):
125
+ if not self.thread or not self.thread.is_alive():
126
+ self.thread = threading.Thread(
127
+ target=self.listen,
128
+ daemon=True,
129
+ name="FlagdGrpcSyncWorkerThread",
130
+ )
131
+ self.thread.start()
132
+
133
+ if self.timer and self.timer.is_alive():
134
+ logger.debug("gRPC error timer expired")
135
+ self.timer.cancel()
136
+
137
+ elif new_state == grpc.ChannelConnectivity.TRANSIENT_FAILURE:
138
+ # this is the failed reconnect attempt so we are going into stale
139
+ self.emit_provider_stale(
140
+ ProviderEventDetails(
141
+ message="gRPC sync disconnected, reconnecting",
142
+ )
143
+ )
144
+ self.start_time = time.time()
145
+ # adding a timer, so we can emit the error event after time
146
+ self.timer = threading.Timer(self.retry_grace_period, self.emit_error)
147
+
148
+ logger.debug("gRPC error timer started")
149
+ self.timer.start()
150
+ self.connected = False
151
+
152
+ def emit_error(self) -> None:
153
+ logger.debug("gRPC error emitted")
154
+ self.emit_provider_error(
155
+ ProviderEventDetails(
156
+ message="gRPC sync disconnected, reconnecting",
157
+ error_code=ErrorCode.GENERAL,
158
+ )
159
+ )
160
+
161
+ def shutdown(self) -> None:
162
+ self.active = False
163
+ self.channel.close()
164
+
165
+ def listen(self) -> None:
166
+ call_args = (
167
+ {"timeout": self.streamline_deadline_seconds}
168
+ if self.streamline_deadline_seconds > 0
169
+ else {}
170
+ )
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
176
+
177
+ while self.active:
178
+ try:
179
+ context_values_request = sync_pb2.GetMetadataRequest()
180
+ context_values_response: sync_pb2.GetMetadataResponse = (
181
+ self.stub.GetMetadata(context_values_request, wait_for_ready=True)
182
+ )
183
+ context_values = MessageToDict(context_values_response)
184
+
185
+ request = sync_pb2.SyncFlagsRequest(**request_args)
186
+
187
+ logger.debug("Setting up gRPC sync flags connection")
188
+ for flag_rsp in self.stub.SyncFlags(
189
+ request, wait_for_ready=True, **call_args
190
+ ):
191
+ flag_str = flag_rsp.flag_configuration
192
+ logger.debug(
193
+ f"Received flag configuration - {abs(hash(flag_str)) % (10**8)}"
194
+ )
195
+ self.flag_store.update(json.loads(flag_str))
196
+
197
+ if not self.connected:
198
+ self.emit_provider_ready(
199
+ ProviderEventDetails(
200
+ message="gRPC sync connection established"
201
+ ),
202
+ context_values["metadata"],
203
+ )
204
+ self.connected = True
205
+
206
+ if not self.active:
207
+ logger.debug("Terminating gRPC sync thread")
208
+ return
209
+ except grpc.RpcError as e: # noqa: PERF203
210
+ logger.error(f"SyncFlags stream error, {e.code()=} {e.details()=}")
211
+ except json.JSONDecodeError:
212
+ logger.exception(
213
+ f"Could not parse JSON flag data from SyncFlags endpoint: {flag_str=}"
214
+ )
215
+ except ParseError:
216
+ logger.exception(
217
+ f"Could not parse flag data using flagd syntax: {flag_str=}"
218
+ )
@@ -1,5 +1,6 @@
1
1
  import logging
2
2
  import typing
3
+ from dataclasses import dataclass
3
4
 
4
5
  import mmh3
5
6
  import semver
@@ -10,6 +11,12 @@ JsonLogicArg = typing.Union[JsonPrimitive, typing.Sequence[JsonPrimitive]]
10
11
  logger = logging.getLogger("openfeature.contrib")
11
12
 
12
13
 
14
+ @dataclass
15
+ class Fraction:
16
+ variant: str
17
+ weight: int = 1
18
+
19
+
13
20
  def fractional(data: dict, *args: JsonLogicArg) -> typing.Optional[str]:
14
21
  if not args:
15
22
  logger.error("No arguments provided to fractional operator.")
@@ -32,28 +39,52 @@ def fractional(data: dict, *args: JsonLogicArg) -> typing.Optional[str]:
32
39
  return None
33
40
 
34
41
  hash_ratio = abs(mmh3.hash(bucket_by)) / (2**31 - 1)
35
- bucket = int(hash_ratio * 100)
36
-
37
- for arg in args:
38
- if (
39
- not isinstance(arg, (tuple, list))
40
- or len(arg) != 2
41
- or not isinstance(arg[0], str)
42
- or not isinstance(arg[1], int)
43
- ):
44
- logger.error("Fractional variant weights must be (str, int) tuple")
45
- return None
46
- variant_weights: typing.Tuple[typing.Tuple[str, int]] = args # type: ignore[assignment]
42
+ bucket = hash_ratio * 100
47
43
 
48
- range_end = 0
49
- for variant, weight in variant_weights:
50
- range_end += weight
51
- if bucket < range_end:
52
- return variant
44
+ total_weight = 0
45
+ fractions = []
46
+ try:
47
+ for arg in args:
48
+ fraction = _parse_fraction(arg)
49
+ if fraction:
50
+ fractions.append(fraction)
51
+ total_weight += fraction.weight
52
+
53
+ except ValueError:
54
+ logger.debug(f"Invalid {args} configuration")
55
+ return None
53
56
 
57
+ range_end: float = 0
58
+ for fraction in fractions:
59
+ range_end += fraction.weight * 100 / total_weight
60
+ if bucket < range_end:
61
+ return fraction.variant
54
62
  return None
55
63
 
56
64
 
65
+ def _parse_fraction(arg: JsonLogicArg) -> Fraction:
66
+ if not isinstance(arg, (tuple, list)) or not arg or len(arg) > 2:
67
+ raise ValueError(
68
+ "Fractional variant weights must be (str, int) tuple or [str] list"
69
+ )
70
+
71
+ if not isinstance(arg[0], str):
72
+ raise ValueError(
73
+ "Fractional variant identifier (first element) isn't of type 'str'"
74
+ )
75
+
76
+ if len(arg) >= 2 and not isinstance(arg[1], int):
77
+ raise ValueError(
78
+ "Fractional variant weight value (second element) isn't of type 'int'"
79
+ )
80
+
81
+ fraction = Fraction(variant=arg[0])
82
+ if len(arg) >= 2:
83
+ fraction.weight = arg[1]
84
+
85
+ return fraction
86
+
87
+
57
88
  def starts_with(data: dict, *args: JsonLogicArg) -> typing.Optional[bool]:
58
89
  def f(s1: str, s2: str) -> bool:
59
90
  return s1.startswith(s2)
@@ -99,8 +130,8 @@ def sem_ver(data: dict, *args: JsonLogicArg) -> typing.Optional[bool]: # noqa:
99
130
  arg1, op, arg2 = args
100
131
 
101
132
  try:
102
- v1 = semver.Version.parse(str(arg1))
103
- v2 = semver.Version.parse(str(arg2))
133
+ v1 = parse_version(arg1)
134
+ v2 = parse_version(arg2)
104
135
  except ValueError as e:
105
136
  logger.exception(e)
106
137
  return None
@@ -124,3 +155,11 @@ def sem_ver(data: dict, *args: JsonLogicArg) -> typing.Optional[bool]: # noqa:
124
155
  else:
125
156
  logger.error(f"Op not supported by sem_ver: {op}")
126
157
  return None
158
+
159
+
160
+ def parse_version(arg: typing.Any) -> semver.Version:
161
+ version = str(arg)
162
+ if version.startswith(("v", "V")):
163
+ version = version[1:]
164
+
165
+ return semver.Version.parse(version)
@@ -1,9 +1,45 @@
1
+ import json
2
+ import re
1
3
  import typing
2
4
  from dataclasses import dataclass
3
5
 
6
+ from openfeature.event import ProviderEventDetails
4
7
  from openfeature.exception import ParseError
5
8
 
6
9
 
10
+ class FlagStore:
11
+ def __init__(
12
+ self,
13
+ emit_provider_configuration_changed: typing.Callable[
14
+ [ProviderEventDetails], None
15
+ ],
16
+ ):
17
+ self.emit_provider_configuration_changed = emit_provider_configuration_changed
18
+ self.flags: typing.Mapping[str, Flag] = {}
19
+
20
+ def get_flag(self, key: str) -> typing.Optional["Flag"]:
21
+ return self.flags.get(key)
22
+
23
+ def update(self, flags_data: dict) -> None:
24
+ flags = flags_data.get("flags", {})
25
+ evaluators: typing.Optional[dict] = flags_data.get("$evaluators")
26
+ if evaluators:
27
+ transposed = json.dumps(flags)
28
+ for name, rule in evaluators.items():
29
+ transposed = re.sub(
30
+ rf"{{\s*\"\$ref\":\s*\"{name}\"\s*}}", json.dumps(rule), transposed
31
+ )
32
+ flags = json.loads(transposed)
33
+
34
+ if not isinstance(flags, dict):
35
+ raise ParseError("`flags` key of configuration must be a dictionary")
36
+ self.flags = {key: Flag.from_dict(key, data) for key, data in flags.items()}
37
+
38
+ self.emit_provider_configuration_changed(
39
+ ProviderEventDetails(flags_changed=list(self.flags.keys()))
40
+ )
41
+
42
+
7
43
  @dataclass
8
44
  class Flag:
9
45
  key: str
@@ -32,19 +68,27 @@ class Flag:
32
68
 
33
69
  @classmethod
34
70
  def from_dict(cls, key: str, data: dict) -> "Flag":
35
- data["default_variant"] = data["defaultVariant"]
36
- del data["defaultVariant"]
37
- flag = cls(key=key, **data)
71
+ if "defaultVariant" in data:
72
+ data["default_variant"] = data["defaultVariant"]
73
+ del data["defaultVariant"]
38
74
 
39
- return flag
75
+ if "source" in data:
76
+ del data["source"]
77
+ if "selector" in data:
78
+ del data["selector"]
79
+ try:
80
+ flag = cls(key=key, **data)
81
+ return flag
82
+ except Exception as err:
83
+ raise ParseError from err
40
84
 
41
85
  @property
42
- def default(self) -> typing.Tuple[str, typing.Any]:
86
+ def default(self) -> tuple[str, typing.Any]:
43
87
  return self.get_variant(self.default_variant)
44
88
 
45
89
  def get_variant(
46
90
  self, variant_key: typing.Union[str, bool]
47
- ) -> typing.Tuple[str, typing.Any]:
91
+ ) -> tuple[str, typing.Any]:
48
92
  if isinstance(variant_key, bool):
49
93
  variant_key = str(variant_key).lower()
50
94
 
@@ -0,0 +1,35 @@
1
+ import time
2
+ import typing
3
+
4
+ from json_logic import builtins, jsonLogic
5
+ from json_logic.types import JsonValue
6
+
7
+ from openfeature.evaluation_context import EvaluationContext
8
+
9
+ from .custom_ops import (
10
+ ends_with,
11
+ fractional,
12
+ sem_ver,
13
+ starts_with,
14
+ )
15
+
16
+ OPERATORS = {
17
+ **builtins.BUILTINS,
18
+ "fractional": fractional,
19
+ "starts_with": starts_with,
20
+ "ends_with": ends_with,
21
+ "sem_ver": sem_ver,
22
+ }
23
+
24
+
25
+ def targeting(
26
+ key: str,
27
+ targeting: dict,
28
+ evaluation_context: typing.Optional[EvaluationContext] = None,
29
+ ) -> JsonValue:
30
+ json_logic_context = evaluation_context.attributes if evaluation_context else {}
31
+ json_logic_context["$flagd"] = {"flagKey": key, "timestamp": int(time.time())}
32
+ json_logic_context["targetingKey"] = (
33
+ evaluation_context.targeting_key if evaluation_context else None
34
+ )
35
+ return jsonLogic(targeting, json_logic_context, OPERATORS)
@@ -0,0 +1,47 @@
1
+ import typing
2
+
3
+ from typing_extensions import Protocol
4
+
5
+ from openfeature.evaluation_context import EvaluationContext
6
+ from openfeature.flag_evaluation import FlagResolutionDetails
7
+
8
+
9
+ class AbstractResolver(Protocol):
10
+ def initialize(self, evaluation_context: EvaluationContext) -> None: ...
11
+
12
+ def shutdown(self) -> None: ...
13
+
14
+ def resolve_boolean_details(
15
+ self,
16
+ key: str,
17
+ default_value: bool,
18
+ evaluation_context: typing.Optional[EvaluationContext] = None,
19
+ ) -> FlagResolutionDetails[bool]: ...
20
+
21
+ def resolve_string_details(
22
+ self,
23
+ key: str,
24
+ default_value: str,
25
+ evaluation_context: typing.Optional[EvaluationContext] = None,
26
+ ) -> FlagResolutionDetails[str]: ...
27
+
28
+ def resolve_float_details(
29
+ self,
30
+ key: str,
31
+ default_value: float,
32
+ evaluation_context: typing.Optional[EvaluationContext] = None,
33
+ ) -> FlagResolutionDetails[float]: ...
34
+
35
+ def resolve_integer_details(
36
+ self,
37
+ key: str,
38
+ default_value: int,
39
+ evaluation_context: typing.Optional[EvaluationContext] = None,
40
+ ) -> FlagResolutionDetails[int]: ...
41
+
42
+ def resolve_object_details(
43
+ self,
44
+ key: str,
45
+ default_value: typing.Union[dict, list],
46
+ evaluation_context: typing.Optional[EvaluationContext] = None,
47
+ ) -> FlagResolutionDetails[typing.Union[dict, list]]: ...
@@ -0,0 +1,14 @@
1
+ import typing
2
+
3
+ from openfeature.evaluation_context import EvaluationContext
4
+ from openfeature.hook import Hook, HookContext, HookHints
5
+
6
+
7
+ class SyncMetadataHook(Hook):
8
+ def __init__(self, context_supplier: typing.Callable[[], EvaluationContext]):
9
+ self.context_supplier = context_supplier
10
+
11
+ def before(
12
+ self, hook_context: HookContext, hints: HookHints
13
+ ) -> typing.Optional[EvaluationContext]:
14
+ return self.context_supplier()
@@ -0,0 +1,72 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # NO CHECKED-IN PROTOBUF GENCODE
4
+ # source: openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation.proto
5
+ # Protobuf Python Version: 5.29.0
6
+ """Generated protocol buffer code."""
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import descriptor_pool as _descriptor_pool
9
+ from google.protobuf import runtime_version as _runtime_version
10
+ from google.protobuf import symbol_database as _symbol_database
11
+ from google.protobuf.internal import builder as _builder
12
+ _runtime_version.ValidateProtobufRuntimeVersion(
13
+ _runtime_version.Domain.PUBLIC,
14
+ 5,
15
+ 29,
16
+ 0,
17
+ '',
18
+ 'openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation.proto'
19
+ )
20
+ # @@protoc_insertion_point(imports)
21
+
22
+ _sym_db = _symbol_database.Default()
23
+
24
+
25
+ from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2
26
+
27
+
28
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\nAopenfeature/schemas/protobuf/flagd/evaluation/v1/evaluation.proto\x12\x13\x66lagd.evaluation.v1\x1a\x1cgoogle/protobuf/struct.proto\"=\n\x11ResolveAllRequest\x12(\n\x07\x63ontext\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\"\xa3\x01\n\x12ResolveAllResponse\x12\x41\n\x05\x66lags\x18\x01 \x03(\x0b\x32\x32.flagd.evaluation.v1.ResolveAllResponse.FlagsEntry\x1aJ\n\nFlagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12+\n\x05value\x18\x02 \x01(\x0b\x32\x1c.flagd.evaluation.v1.AnyFlag:\x02\x38\x01\"\xaa\x01\n\x07\x41nyFlag\x12\x0e\n\x06reason\x18\x01 \x01(\t\x12\x0f\n\x07variant\x18\x02 \x01(\t\x12\x14\n\nbool_value\x18\x03 \x01(\x08H\x00\x12\x16\n\x0cstring_value\x18\x04 \x01(\tH\x00\x12\x16\n\x0c\x64ouble_value\x18\x05 \x01(\x01H\x00\x12/\n\x0cobject_value\x18\x06 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x42\x07\n\x05value\"S\n\x15ResolveBooleanRequest\x12\x10\n\x08\x66lag_key\x18\x01 \x01(\t\x12(\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"s\n\x16ResolveBooleanResponse\x12\r\n\x05value\x18\x01 \x01(\x08\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x0f\n\x07variant\x18\x03 \x01(\t\x12)\n\x08metadata\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\"R\n\x14ResolveStringRequest\x12\x10\n\x08\x66lag_key\x18\x01 \x01(\t\x12(\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"r\n\x15ResolveStringResponse\x12\r\n\x05value\x18\x01 \x01(\t\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x0f\n\x07variant\x18\x03 \x01(\t\x12)\n\x08metadata\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\"Q\n\x13ResolveFloatRequest\x12\x10\n\x08\x66lag_key\x18\x01 \x01(\t\x12(\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"q\n\x14ResolveFloatResponse\x12\r\n\x05value\x18\x01 \x01(\x01\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x0f\n\x07variant\x18\x03 \x01(\t\x12)\n\x08metadata\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\"O\n\x11ResolveIntRequest\x12\x10\n\x08\x66lag_key\x18\x01 \x01(\t\x12(\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"o\n\x12ResolveIntResponse\x12\r\n\x05value\x18\x01 \x01(\x03\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x0f\n\x07variant\x18\x03 \x01(\t\x12)\n\x08metadata\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\"R\n\x14ResolveObjectRequest\x12\x10\n\x08\x66lag_key\x18\x01 \x01(\t\x12(\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\x8b\x01\n\x15ResolveObjectResponse\x12&\n\x05value\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x0f\n\x07variant\x18\x03 \x01(\t\x12)\n\x08metadata\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\"J\n\x13\x45ventStreamResponse\x12\x0c\n\x04type\x18\x01 \x01(\t\x12%\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\x14\n\x12\x45ventStreamRequest2\xd9\x05\n\x07Service\x12_\n\nResolveAll\x12&.flagd.evaluation.v1.ResolveAllRequest\x1a\'.flagd.evaluation.v1.ResolveAllResponse\"\x00\x12k\n\x0eResolveBoolean\x12*.flagd.evaluation.v1.ResolveBooleanRequest\x1a+.flagd.evaluation.v1.ResolveBooleanResponse\"\x00\x12h\n\rResolveString\x12).flagd.evaluation.v1.ResolveStringRequest\x1a*.flagd.evaluation.v1.ResolveStringResponse\"\x00\x12\x65\n\x0cResolveFloat\x12(.flagd.evaluation.v1.ResolveFloatRequest\x1a).flagd.evaluation.v1.ResolveFloatResponse\"\x00\x12_\n\nResolveInt\x12&.flagd.evaluation.v1.ResolveIntRequest\x1a\'.flagd.evaluation.v1.ResolveIntResponse\"\x00\x12h\n\rResolveObject\x12).flagd.evaluation.v1.ResolveObjectRequest\x1a*.flagd.evaluation.v1.ResolveObjectResponse\"\x00\x12\x64\n\x0b\x45ventStream\x12\'.flagd.evaluation.v1.EventStreamRequest\x1a(.flagd.evaluation.v1.EventStreamResponse\"\x00\x30\x01\x42\xc6\x01\n%dev.openfeature.flagd.grpc.evaluationZ\x13\x66lagd/evaluation/v1\xaa\x02!OpenFeature.Flagd.Grpc.Evaluation\xca\x02\x32OpenFeature\\Providers\\Flagd\\Schema\\Grpc\\Evaluation\xea\x02.OpenFeature::Flagd::Provider::Grpc::Evaluationb\x06proto3')
29
+
30
+ _globals = globals()
31
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
32
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'openfeature.schemas.protobuf.flagd.evaluation.v1.evaluation_pb2', _globals)
33
+ if not _descriptor._USE_C_DESCRIPTORS:
34
+ _globals['DESCRIPTOR']._loaded_options = None
35
+ _globals['DESCRIPTOR']._serialized_options = b'\n%dev.openfeature.flagd.grpc.evaluationZ\023flagd/evaluation/v1\252\002!OpenFeature.Flagd.Grpc.Evaluation\312\0022OpenFeature\\Providers\\Flagd\\Schema\\Grpc\\Evaluation\352\002.OpenFeature::Flagd::Provider::Grpc::Evaluation'
36
+ _globals['_RESOLVEALLRESPONSE_FLAGSENTRY']._loaded_options = None
37
+ _globals['_RESOLVEALLRESPONSE_FLAGSENTRY']._serialized_options = b'8\001'
38
+ _globals['_RESOLVEALLREQUEST']._serialized_start=120
39
+ _globals['_RESOLVEALLREQUEST']._serialized_end=181
40
+ _globals['_RESOLVEALLRESPONSE']._serialized_start=184
41
+ _globals['_RESOLVEALLRESPONSE']._serialized_end=347
42
+ _globals['_RESOLVEALLRESPONSE_FLAGSENTRY']._serialized_start=273
43
+ _globals['_RESOLVEALLRESPONSE_FLAGSENTRY']._serialized_end=347
44
+ _globals['_ANYFLAG']._serialized_start=350
45
+ _globals['_ANYFLAG']._serialized_end=520
46
+ _globals['_RESOLVEBOOLEANREQUEST']._serialized_start=522
47
+ _globals['_RESOLVEBOOLEANREQUEST']._serialized_end=605
48
+ _globals['_RESOLVEBOOLEANRESPONSE']._serialized_start=607
49
+ _globals['_RESOLVEBOOLEANRESPONSE']._serialized_end=722
50
+ _globals['_RESOLVESTRINGREQUEST']._serialized_start=724
51
+ _globals['_RESOLVESTRINGREQUEST']._serialized_end=806
52
+ _globals['_RESOLVESTRINGRESPONSE']._serialized_start=808
53
+ _globals['_RESOLVESTRINGRESPONSE']._serialized_end=922
54
+ _globals['_RESOLVEFLOATREQUEST']._serialized_start=924
55
+ _globals['_RESOLVEFLOATREQUEST']._serialized_end=1005
56
+ _globals['_RESOLVEFLOATRESPONSE']._serialized_start=1007
57
+ _globals['_RESOLVEFLOATRESPONSE']._serialized_end=1120
58
+ _globals['_RESOLVEINTREQUEST']._serialized_start=1122
59
+ _globals['_RESOLVEINTREQUEST']._serialized_end=1201
60
+ _globals['_RESOLVEINTRESPONSE']._serialized_start=1203
61
+ _globals['_RESOLVEINTRESPONSE']._serialized_end=1314
62
+ _globals['_RESOLVEOBJECTREQUEST']._serialized_start=1316
63
+ _globals['_RESOLVEOBJECTREQUEST']._serialized_end=1398
64
+ _globals['_RESOLVEOBJECTRESPONSE']._serialized_start=1401
65
+ _globals['_RESOLVEOBJECTRESPONSE']._serialized_end=1540
66
+ _globals['_EVENTSTREAMRESPONSE']._serialized_start=1542
67
+ _globals['_EVENTSTREAMRESPONSE']._serialized_end=1616
68
+ _globals['_EVENTSTREAMREQUEST']._serialized_start=1618
69
+ _globals['_EVENTSTREAMREQUEST']._serialized_end=1638
70
+ _globals['_SERVICE']._serialized_start=1641
71
+ _globals['_SERVICE']._serialized_end=2370
72
+ # @@protoc_insertion_point(module_scope)