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.
Files changed (42) hide show
  1. openfeature/.gitignore +2 -0
  2. openfeature/contrib/provider/flagd/config.py +202 -8
  3. openfeature/contrib/provider/flagd/provider.py +89 -97
  4. openfeature/contrib/provider/flagd/resolvers/__init__.py +5 -0
  5. openfeature/contrib/provider/flagd/resolvers/grpc.py +354 -0
  6. openfeature/contrib/provider/flagd/resolvers/in_process.py +131 -0
  7. openfeature/contrib/provider/flagd/resolvers/process/connector/__init__.py +11 -0
  8. openfeature/contrib/provider/flagd/resolvers/process/connector/file_watcher.py +106 -0
  9. openfeature/contrib/provider/flagd/resolvers/process/connector/grpc_watcher.py +192 -0
  10. openfeature/contrib/provider/flagd/resolvers/process/custom_ops.py +165 -0
  11. openfeature/contrib/provider/flagd/resolvers/process/flags.py +95 -0
  12. openfeature/contrib/provider/flagd/resolvers/process/targeting.py +35 -0
  13. openfeature/contrib/provider/flagd/resolvers/protocol.py +47 -0
  14. openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2.py +72 -0
  15. openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2.pyi +450 -0
  16. openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2_grpc.py +358 -0
  17. openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2_grpc.pyi +155 -0
  18. openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2.py +50 -0
  19. openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2.pyi +148 -0
  20. openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2_grpc.py +186 -0
  21. openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2_grpc.pyi +86 -0
  22. openfeature/schemas/protobuf/schema/v1/schema_pb2.py +72 -0
  23. openfeature/schemas/protobuf/schema/v1/schema_pb2.pyi +451 -0
  24. openfeature/schemas/protobuf/schema/v1/schema_pb2_grpc.py +358 -0
  25. openfeature/schemas/protobuf/schema/v1/schema_pb2_grpc.pyi +156 -0
  26. openfeature/schemas/protobuf/sync/v1/sync_service_pb2.py +47 -0
  27. openfeature/schemas/protobuf/sync/v1/sync_service_pb2.pyi +174 -0
  28. openfeature/schemas/protobuf/sync/v1/sync_service_pb2_grpc.py +143 -0
  29. openfeature/schemas/protobuf/sync/v1/sync_service_pb2_grpc.pyi +70 -0
  30. {openfeature_provider_flagd-0.1.4.dist-info → openfeature_provider_flagd-0.2.0.dist-info}/METADATA +132 -14
  31. openfeature_provider_flagd-0.2.0.dist-info/RECORD +35 -0
  32. {openfeature_provider_flagd-0.1.4.dist-info → openfeature_provider_flagd-0.2.0.dist-info}/WHEEL +1 -1
  33. {openfeature_provider_flagd-0.1.4.dist-info → openfeature_provider_flagd-0.2.0.dist-info}/licenses/LICENSE +1 -1
  34. openfeature/contrib/provider/flagd/proto/flagd/evaluation/v1/evaluation_pb2.py +0 -62
  35. openfeature/contrib/provider/flagd/proto/flagd/evaluation/v1/evaluation_pb2_grpc.py +0 -267
  36. openfeature/contrib/provider/flagd/proto/flagd/sync/v1/sync_pb2.py +0 -40
  37. openfeature/contrib/provider/flagd/proto/flagd/sync/v1/sync_pb2_grpc.py +0 -135
  38. openfeature/contrib/provider/flagd/proto/schema/v1/schema_pb2.py +0 -62
  39. openfeature/contrib/provider/flagd/proto/schema/v1/schema_pb2_grpc.py +0 -267
  40. openfeature/contrib/provider/flagd/proto/sync/v1/sync_service_pb2.py +0 -37
  41. openfeature/contrib/provider/flagd/proto/sync/v1/sync_service_pb2_grpc.py +0 -102
  42. openfeature_provider_flagd-0.1.4.dist-info/RECORD +0 -16
@@ -0,0 +1,192 @@
1
+ import json
2
+ import logging
3
+ import threading
4
+ import time
5
+ import typing
6
+
7
+ import grpc
8
+
9
+ from openfeature.evaluation_context import EvaluationContext
10
+ from openfeature.event import ProviderEventDetails
11
+ from openfeature.exception import ErrorCode, ParseError, ProviderNotReadyError
12
+ from openfeature.schemas.protobuf.flagd.sync.v1 import (
13
+ sync_pb2,
14
+ sync_pb2_grpc,
15
+ )
16
+
17
+ from ....config import Config
18
+ from ..connector import FlagStateConnector
19
+ from ..flags import FlagStore
20
+
21
+ logger = logging.getLogger("openfeature.contrib")
22
+
23
+
24
+ class GrpcWatcher(FlagStateConnector):
25
+ def __init__(
26
+ self,
27
+ config: Config,
28
+ flag_store: FlagStore,
29
+ emit_provider_ready: typing.Callable[[ProviderEventDetails], None],
30
+ emit_provider_error: typing.Callable[[ProviderEventDetails], None],
31
+ emit_provider_stale: typing.Callable[[ProviderEventDetails], None],
32
+ ):
33
+ self.flag_store = flag_store
34
+ self.config = config
35
+
36
+ self.channel = self._generate_channel(config)
37
+ self.stub = sync_pb2_grpc.FlagSyncServiceStub(self.channel)
38
+ self.retry_backoff_seconds = config.retry_backoff_ms * 0.001
39
+ self.retry_backoff_max_seconds = config.retry_backoff_ms * 0.001
40
+ self.retry_grace_period = config.retry_grace_period
41
+ self.streamline_deadline_seconds = config.stream_deadline_ms * 0.001
42
+ self.deadline = config.deadline_ms * 0.001
43
+ self.selector = config.selector
44
+ self.emit_provider_ready = emit_provider_ready
45
+ self.emit_provider_error = emit_provider_error
46
+ self.emit_provider_stale = emit_provider_stale
47
+
48
+ self.connected = False
49
+ self.thread: typing.Optional[threading.Thread] = None
50
+ self.timer: typing.Optional[threading.Timer] = None
51
+
52
+ self.start_time = time.time()
53
+
54
+ def _generate_channel(self, config: Config) -> grpc.Channel:
55
+ target = f"{config.host}:{config.port}"
56
+ # Create the channel with the service config
57
+ options = [
58
+ ("grpc.keepalive_time_ms", config.keep_alive_time),
59
+ ("grpc.initial_reconnect_backoff_ms", config.retry_backoff_ms),
60
+ ("grpc.max_reconnect_backoff_ms", config.retry_backoff_max_ms),
61
+ ("grpc.min_reconnect_backoff_ms", config.stream_deadline_ms),
62
+ ]
63
+ if config.tls:
64
+ channel_args = {
65
+ "options": options,
66
+ "credentials": grpc.ssl_channel_credentials(),
67
+ }
68
+ if config.cert_path:
69
+ with open(config.cert_path, "rb") as f:
70
+ channel_args["credentials"] = grpc.ssl_channel_credentials(f.read())
71
+
72
+ channel = grpc.secure_channel(target, **channel_args)
73
+
74
+ else:
75
+ channel = grpc.insecure_channel(
76
+ target,
77
+ options=options,
78
+ )
79
+
80
+ return channel
81
+
82
+ def initialize(self, context: EvaluationContext) -> None:
83
+ self.connect()
84
+
85
+ def connect(self) -> None:
86
+ self.active = True
87
+
88
+ # Run monitoring in a separate thread
89
+ self.monitor_thread = threading.Thread(
90
+ target=self.monitor, daemon=True, name="FlagdGrpcSyncServiceMonitorThread"
91
+ )
92
+ self.monitor_thread.start()
93
+ ## block until ready or deadline reached
94
+ timeout = self.deadline + time.time()
95
+ while not self.connected and time.time() < timeout:
96
+ time.sleep(0.05)
97
+ logger.debug("Finished blocking gRPC state initialization")
98
+
99
+ if not self.connected:
100
+ raise ProviderNotReadyError(
101
+ "Blocking init finished before data synced. Consider increasing startup deadline to avoid inconsistent evaluations."
102
+ )
103
+
104
+ def monitor(self) -> None:
105
+ self.channel.subscribe(self._state_change_callback, try_to_connect=True)
106
+
107
+ def _state_change_callback(self, new_state: grpc.ChannelConnectivity) -> None:
108
+ logger.debug(f"gRPC state change: {new_state}")
109
+ if new_state == grpc.ChannelConnectivity.READY:
110
+ if not self.thread or not self.thread.is_alive():
111
+ self.thread = threading.Thread(
112
+ target=self.listen,
113
+ daemon=True,
114
+ name="FlagdGrpcSyncWorkerThread",
115
+ )
116
+ self.thread.start()
117
+
118
+ if self.timer and self.timer.is_alive():
119
+ logger.debug("gRPC error timer expired")
120
+ self.timer.cancel()
121
+
122
+ elif new_state == grpc.ChannelConnectivity.TRANSIENT_FAILURE:
123
+ # this is the failed reconnect attempt so we are going into stale
124
+ self.emit_provider_stale(
125
+ ProviderEventDetails(
126
+ message="gRPC sync disconnected, reconnecting",
127
+ )
128
+ )
129
+ self.start_time = time.time()
130
+ # adding a timer, so we can emit the error event after time
131
+ self.timer = threading.Timer(self.retry_grace_period, self.emit_error)
132
+
133
+ logger.debug("gRPC error timer started")
134
+ self.timer.start()
135
+ self.connected = False
136
+
137
+ def emit_error(self) -> None:
138
+ logger.debug("gRPC error emitted")
139
+ self.emit_provider_error(
140
+ ProviderEventDetails(
141
+ message="gRPC sync disconnected, reconnecting",
142
+ error_code=ErrorCode.GENERAL,
143
+ )
144
+ )
145
+
146
+ def shutdown(self) -> None:
147
+ self.active = False
148
+ self.channel.close()
149
+
150
+ def listen(self) -> None:
151
+ call_args = (
152
+ {"timeout": self.streamline_deadline_seconds}
153
+ if self.streamline_deadline_seconds > 0
154
+ else {}
155
+ )
156
+ request_args = {"selector": self.selector} if self.selector is not None else {}
157
+
158
+ while self.active:
159
+ try:
160
+ request = sync_pb2.SyncFlagsRequest(**request_args)
161
+
162
+ logger.debug("Setting up gRPC sync flags connection")
163
+ for flag_rsp in self.stub.SyncFlags(
164
+ request, wait_for_ready=True, **call_args
165
+ ):
166
+ flag_str = flag_rsp.flag_configuration
167
+ logger.debug(
168
+ f"Received flag configuration - {abs(hash(flag_str)) % (10**8)}"
169
+ )
170
+ self.flag_store.update(json.loads(flag_str))
171
+
172
+ if not self.connected:
173
+ self.emit_provider_ready(
174
+ ProviderEventDetails(
175
+ message="gRPC sync connection established"
176
+ )
177
+ )
178
+ self.connected = True
179
+
180
+ if not self.active:
181
+ logger.debug("Terminating gRPC sync thread")
182
+ return
183
+ except grpc.RpcError as e: # noqa: PERF203
184
+ logger.error(f"SyncFlags stream error, {e.code()=} {e.details()=}")
185
+ except json.JSONDecodeError:
186
+ logger.exception(
187
+ f"Could not parse JSON flag data from SyncFlags endpoint: {flag_str=}"
188
+ )
189
+ except ParseError:
190
+ logger.exception(
191
+ f"Could not parse flag data using flagd syntax: {flag_str=}"
192
+ )
@@ -0,0 +1,165 @@
1
+ import logging
2
+ import typing
3
+ from dataclasses import dataclass
4
+
5
+ import mmh3
6
+ import semver
7
+
8
+ JsonPrimitive = typing.Union[str, bool, float, int]
9
+ JsonLogicArg = typing.Union[JsonPrimitive, typing.Sequence[JsonPrimitive]]
10
+
11
+ logger = logging.getLogger("openfeature.contrib")
12
+
13
+
14
+ @dataclass
15
+ class Fraction:
16
+ variant: str
17
+ weight: int = 1
18
+
19
+
20
+ def fractional(data: dict, *args: JsonLogicArg) -> typing.Optional[str]:
21
+ if not args:
22
+ logger.error("No arguments provided to fractional operator.")
23
+ return None
24
+
25
+ bucket_by = None
26
+ if isinstance(args[0], str):
27
+ bucket_by = args[0]
28
+ args = args[1:]
29
+ else:
30
+ seed = data.get("$flagd", {}).get("flagKey", "")
31
+ targeting_key = data.get("targetingKey")
32
+ if not targeting_key:
33
+ logger.error("No targetingKey provided for fractional shorthand syntax.")
34
+ return None
35
+ bucket_by = seed + targeting_key
36
+
37
+ if not bucket_by:
38
+ logger.error("No hashKey value resolved")
39
+ return None
40
+
41
+ hash_ratio = abs(mmh3.hash(bucket_by)) / (2**31 - 1)
42
+ bucket = hash_ratio * 100
43
+
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
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
62
+ return None
63
+
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
+
88
+ def starts_with(data: dict, *args: JsonLogicArg) -> typing.Optional[bool]:
89
+ def f(s1: str, s2: str) -> bool:
90
+ return s1.startswith(s2)
91
+
92
+ return string_comp(f, data, *args)
93
+
94
+
95
+ def ends_with(data: dict, *args: JsonLogicArg) -> typing.Optional[bool]:
96
+ def f(s1: str, s2: str) -> bool:
97
+ return s1.endswith(s2)
98
+
99
+ return string_comp(f, data, *args)
100
+
101
+
102
+ def string_comp(
103
+ comparator: typing.Callable[[str, str], bool], data: dict, *args: JsonLogicArg
104
+ ) -> typing.Optional[bool]:
105
+ if not args:
106
+ logger.error("No arguments provided to string_comp operator.")
107
+ return None
108
+ if len(args) != 2:
109
+ logger.error("Exactly 2 args expected for string_comp operator.")
110
+ return None
111
+ arg1, arg2 = args
112
+ if not isinstance(arg1, str):
113
+ logger.debug(f"incorrect argument for first argument, expected string: {arg1}")
114
+ return False
115
+ if not isinstance(arg2, str):
116
+ logger.debug(f"incorrect argument for second argument, expected string: {arg2}")
117
+ return False
118
+
119
+ return comparator(arg1, arg2)
120
+
121
+
122
+ def sem_ver(data: dict, *args: JsonLogicArg) -> typing.Optional[bool]: # noqa: C901
123
+ if not args:
124
+ logger.error("No arguments provided to sem_ver operator.")
125
+ return None
126
+ if len(args) != 3:
127
+ logger.error("Exactly 3 args expected for sem_ver operator.")
128
+ return None
129
+
130
+ arg1, op, arg2 = args
131
+
132
+ try:
133
+ v1 = parse_version(arg1)
134
+ v2 = parse_version(arg2)
135
+ except ValueError as e:
136
+ logger.exception(e)
137
+ return None
138
+
139
+ if op == "=":
140
+ return v1 == v2
141
+ elif op == "!=":
142
+ return v1 != v2
143
+ elif op == "<":
144
+ return v1 < v2
145
+ elif op == "<=":
146
+ return v1 <= v2
147
+ elif op == ">":
148
+ return v1 > v2
149
+ elif op == ">=":
150
+ return v1 >= v2
151
+ elif op == "^":
152
+ return v1.major == v2.major
153
+ elif op == "~":
154
+ return v1.major == v2.major and v1.minor == v2.minor
155
+ else:
156
+ logger.error(f"Op not supported by sem_ver: {op}")
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)
@@ -0,0 +1,95 @@
1
+ import json
2
+ import re
3
+ import typing
4
+ from dataclasses import dataclass
5
+
6
+ from openfeature.event import ProviderEventDetails
7
+ from openfeature.exception import ParseError
8
+
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
+
43
+ @dataclass
44
+ class Flag:
45
+ key: str
46
+ state: str
47
+ variants: typing.Mapping[str, typing.Any]
48
+ default_variant: typing.Union[bool, str]
49
+ targeting: typing.Optional[dict] = None
50
+
51
+ def __post_init__(self) -> None:
52
+ if not self.state or not isinstance(self.state, str):
53
+ raise ParseError("Incorrect 'state' value provided in flag config")
54
+
55
+ if not self.variants or not isinstance(self.variants, dict):
56
+ raise ParseError("Incorrect 'variants' value provided in flag config")
57
+
58
+ if not self.default_variant or not isinstance(
59
+ self.default_variant, (str, bool)
60
+ ):
61
+ raise ParseError("Incorrect 'defaultVariant' value provided in flag config")
62
+
63
+ if self.targeting and not isinstance(self.targeting, dict):
64
+ raise ParseError("Incorrect 'targeting' value provided in flag config")
65
+
66
+ if self.default_variant not in self.variants:
67
+ raise ParseError("Default variant does not match set of variants")
68
+
69
+ @classmethod
70
+ def from_dict(cls, key: str, data: dict) -> "Flag":
71
+ if "defaultVariant" in data:
72
+ data["default_variant"] = data["defaultVariant"]
73
+ del data["defaultVariant"]
74
+
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
84
+
85
+ @property
86
+ def default(self) -> tuple[str, typing.Any]:
87
+ return self.get_variant(self.default_variant)
88
+
89
+ def get_variant(
90
+ self, variant_key: typing.Union[str, bool]
91
+ ) -> tuple[str, typing.Any]:
92
+ if isinstance(variant_key, bool):
93
+ variant_key = str(variant_key).lower()
94
+
95
+ return variant_key, self.variants.get(variant_key)
@@ -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,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)