|
7 | 7 | from openfeature.exception import ErrorCode, GeneralError, ParseError, OpenFeatureError, TargetingKeyMissingError |
8 | 8 | from openfeature.flag_evaluation import Reason, FlagResolutionDetails |
9 | 9 | from openfeature.provider import AbstractProvider, Metadata |
10 | | -from split_openfeature_provider.split_client_wrapper import SplitClientWrapper |
| 10 | +from openfeature.event import ProviderEventDetails |
| 11 | +from split_openfeature_provider.split_client_wrapper import SplitClientWrapper, SPLIT_EVENT_BUR_TIMEOUT |
11 | 12 |
|
12 | 13 | _LOGGER = logging.getLogger(__name__) |
13 | 14 |
|
| 15 | +try: |
| 16 | + from splitio.models.events import SdkEvent |
| 17 | +except ImportError: |
| 18 | + SdkEvent = None # type: ignore |
| 19 | + |
| 20 | + |
| 21 | +def _flags_changed_from_sdk_update(event_metadata): |
| 22 | + """ |
| 23 | + Extract list of updated flag/split names from Split SDK_UPDATE event metadata. |
| 24 | + OpenFeature expects flags_changed: list[str] for PROVIDER_CONFIGURATION_CHANGED. |
| 25 | + Handles: dict with "names", object with .metadata, or object with get_names() (Split EventsMetadata). |
| 26 | + """ |
| 27 | + if event_metadata is None: |
| 28 | + return None |
| 29 | + if hasattr(event_metadata, "metadata") and getattr(event_metadata, "metadata", None) is not None: |
| 30 | + event_metadata = getattr(event_metadata, "metadata") |
| 31 | + if isinstance(event_metadata, dict): |
| 32 | + val = event_metadata.get("names") |
| 33 | + if isinstance(val, list): |
| 34 | + return [str(x) for x in val if x is not None] |
| 35 | + return None |
| 36 | + if hasattr(event_metadata, "get_names"): |
| 37 | + names = event_metadata.get_names() |
| 38 | + if names is not None: |
| 39 | + return [str(x) for x in names if x is not None] |
| 40 | + return None |
| 41 | + |
| 42 | + |
| 43 | +def _metadata_from_split(split_event, event_metadata): |
| 44 | + """Build OpenFeature event metadata dict from Split event (and optional Split metadata).""" |
| 45 | + meta = {"split_event": getattr(split_event, "value", str(split_event))} |
| 46 | + if event_metadata is not None and isinstance(event_metadata, dict): |
| 47 | + for k, v in event_metadata.items(): |
| 48 | + if isinstance(v, (bool, str, int, float)): |
| 49 | + meta["split_%s" % k] = v |
| 50 | + # Split may pass an object with get_type/get_names (e.g. EventsMetadata) |
| 51 | + if event_metadata is not None and hasattr(event_metadata, "get_type"): |
| 52 | + t = event_metadata.get_type() |
| 53 | + meta["split_type"] = getattr(t, "value", str(t)) |
| 54 | + if event_metadata is not None and hasattr(event_metadata, "get_names"): |
| 55 | + names = event_metadata.get_names() |
| 56 | + meta["split_names"] = list(names) if names is not None else [] |
| 57 | + return meta |
| 58 | + |
| 59 | + |
14 | 60 | class SplitProviderBase(AbstractProvider): |
15 | 61 |
|
16 | 62 | def get_metadata(self) -> Metadata: |
17 | 63 | return Metadata("Split") |
18 | 64 |
|
| 65 | + def attach(self, on_emit): |
| 66 | + super().attach(on_emit) |
| 67 | + self._split_client_wrapper.set_event_receiver(self) |
| 68 | + self._split_client_wrapper.register_for_split_events() |
| 69 | + |
| 70 | + def detach(self): |
| 71 | + self._split_client_wrapper.unregister_for_split_events() |
| 72 | + super().detach() |
| 73 | + |
| 74 | + def _on_split_event(self, split_event, event_metadata): |
| 75 | + """Map Split SDK events to OpenFeature provider events with OpenFeature-friendly details.""" |
| 76 | + _LOGGER.debug("SplitProvider: _on_split_event received %s", split_event) |
| 77 | + if split_event == SPLIT_EVENT_BUR_TIMEOUT: |
| 78 | + self.emit_provider_error(ProviderEventDetails( |
| 79 | + message="Block until ready timed out", |
| 80 | + error_code=ErrorCode.PROVIDER_NOT_READY, |
| 81 | + metadata=_metadata_from_split(split_event, event_metadata), |
| 82 | + )) |
| 83 | + return |
| 84 | + if SdkEvent is None: |
| 85 | + return |
| 86 | + if split_event == SdkEvent.SDK_READY: |
| 87 | + self.emit_provider_ready(ProviderEventDetails( |
| 88 | + metadata=_metadata_from_split(split_event, event_metadata), |
| 89 | + )) |
| 90 | + elif split_event == SdkEvent.SDK_UPDATE: |
| 91 | + flags_changed = _flags_changed_from_sdk_update(event_metadata) |
| 92 | + details = ProviderEventDetails( |
| 93 | + flags_changed=flags_changed, |
| 94 | + metadata=_metadata_from_split(split_event, event_metadata), |
| 95 | + ) |
| 96 | + _LOGGER.info("SplitProvider: emitting PROVIDER_CONFIGURATION_CHANGED flags_changed=%s", flags_changed) |
| 97 | + self.emit_provider_configuration_changed(details) |
| 98 | + |
19 | 99 | def get_provider_hooks(self) -> typing.List[Hook]: |
20 | 100 | return [] |
21 | 101 |
|
|
0 commit comments