-
Notifications
You must be signed in to change notification settings - Fork 30
feat: implement provider events #278
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
289b47b
feat: implement provider events
federicobond e31c1ac
feat: add error_code field to EventDetails and ProviderEventDetails
federicobond 47d7e1a
fix: replace strings with postponed evaluation of annotations
federicobond aca435f
feat: run handlers immediately if provider already in associated state
federicobond faa1751
feat: remove unused _provider from openfeature.api
federicobond ba56db1
test: add some comments to test cases
federicobond 2dabd81
test: add provider event late binding test cases
federicobond 3324a6c
fix: fix status handlers running immediately if provider already in a…
federicobond 349da5c
refactor: reuse provider property in OpenFeatureClient
federicobond 7997eef
refactor: move _provider_status_to_event to ProviderEvent.from_provid…
federicobond e49d375
refactor: move EventSupport class to an internal module
federicobond cda7245
refactor: replace EventSupport class with module-level functions
federicobond d4420f3
style: fix code style
federicobond File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from collections import defaultdict | ||
| from typing import TYPE_CHECKING, Dict, List | ||
|
|
||
| from openfeature.event import ( | ||
| EventDetails, | ||
| EventHandler, | ||
| ProviderEvent, | ||
| ProviderEventDetails, | ||
| ) | ||
| from openfeature.provider import FeatureProvider | ||
|
|
||
| if TYPE_CHECKING: | ||
| from openfeature.client import OpenFeatureClient | ||
|
|
||
|
|
||
| _global_handlers: Dict[ProviderEvent, List[EventHandler]] = defaultdict(list) | ||
| _client_handlers: Dict[OpenFeatureClient, Dict[ProviderEvent, List[EventHandler]]] = ( | ||
| defaultdict(lambda: defaultdict(list)) | ||
| ) | ||
|
|
||
|
|
||
| def run_client_handlers( | ||
| client: OpenFeatureClient, event: ProviderEvent, details: EventDetails | ||
| ) -> None: | ||
| for handler in _client_handlers[client][event]: | ||
| handler(details) | ||
|
|
||
|
|
||
| def run_global_handlers(event: ProviderEvent, details: EventDetails) -> None: | ||
| for handler in _global_handlers[event]: | ||
| handler(details) | ||
|
|
||
|
|
||
| def add_client_handler( | ||
| client: OpenFeatureClient, event: ProviderEvent, handler: EventHandler | ||
| ) -> None: | ||
| handlers = _client_handlers[client][event] | ||
| handlers.append(handler) | ||
|
|
||
| _run_immediate_handler(client, event, handler) | ||
|
|
||
|
|
||
| def remove_client_handler( | ||
| client: OpenFeatureClient, event: ProviderEvent, handler: EventHandler | ||
| ) -> None: | ||
| handlers = _client_handlers[client][event] | ||
| handlers.remove(handler) | ||
gruebel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| def add_global_handler(event: ProviderEvent, handler: EventHandler) -> None: | ||
| _global_handlers[event].append(handler) | ||
|
|
||
| from openfeature.api import get_client | ||
|
|
||
| _run_immediate_handler(get_client(), event, handler) | ||
|
|
||
|
|
||
| def remove_global_handler(event: ProviderEvent, handler: EventHandler) -> None: | ||
| _global_handlers[event].remove(handler) | ||
gruebel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| def run_handlers_for_provider( | ||
| provider: FeatureProvider, | ||
| event: ProviderEvent, | ||
| provider_details: ProviderEventDetails, | ||
| ) -> None: | ||
| details = EventDetails.from_provider_event_details( | ||
| provider.get_metadata().name, provider_details | ||
| ) | ||
| # run the global handlers | ||
| run_global_handlers(event, details) | ||
| # run the handlers for clients associated to this provider | ||
| for client in _client_handlers: | ||
| if client.provider == provider: | ||
| run_client_handlers(client, event, details) | ||
|
|
||
|
|
||
| def _run_immediate_handler( | ||
| client: OpenFeatureClient, event: ProviderEvent, handler: EventHandler | ||
| ) -> None: | ||
| if event == ProviderEvent.from_provider_status(client.get_provider_status()): | ||
| handler(EventDetails(provider_name=client.provider.get_metadata().name)) | ||
|
|
||
|
|
||
| def clear() -> None: | ||
| _global_handlers.clear() | ||
| _client_handlers.clear() | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from dataclasses import dataclass, field | ||
| from enum import Enum | ||
| from typing import Callable, ClassVar, Dict, List, Optional, Union | ||
|
|
||
| from openfeature.exception import ErrorCode | ||
| from openfeature.provider import ProviderStatus | ||
|
|
||
|
|
||
| class ProviderEvent(Enum): | ||
| PROVIDER_READY = "PROVIDER_READY" | ||
| PROVIDER_CONFIGURATION_CHANGED = "PROVIDER_CONFIGURATION_CHANGED" | ||
| PROVIDER_ERROR = "PROVIDER_ERROR" | ||
| PROVIDER_FATAL = "PROVIDER_FATAL" | ||
| PROVIDER_STALE = "PROVIDER_STALE" | ||
|
|
||
| __status__: ClassVar[Dict[ProviderStatus, str]] = { | ||
gruebel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ProviderStatus.READY: PROVIDER_READY, | ||
| ProviderStatus.ERROR: PROVIDER_ERROR, | ||
| ProviderStatus.FATAL: PROVIDER_FATAL, | ||
| ProviderStatus.STALE: PROVIDER_STALE, | ||
| } | ||
|
|
||
| @classmethod | ||
| def from_provider_status(cls, status: ProviderStatus) -> Optional[ProviderEvent]: | ||
| value = ProviderEvent.__status__.get(status) | ||
| return ProviderEvent[value] if value else None | ||
|
|
||
|
|
||
| @dataclass | ||
| class ProviderEventDetails: | ||
| flags_changed: Optional[List[str]] = None | ||
| message: Optional[str] = None | ||
| error_code: Optional[ErrorCode] = None | ||
| metadata: Dict[str, Union[bool, str, int, float]] = field(default_factory=dict) | ||
|
|
||
|
|
||
| @dataclass | ||
| class EventDetails(ProviderEventDetails): | ||
federicobond marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| provider_name: str = "" | ||
| flags_changed: Optional[List[str]] = None | ||
| message: Optional[str] = None | ||
| error_code: Optional[ErrorCode] = None | ||
| metadata: Dict[str, Union[bool, str, int, float]] = field(default_factory=dict) | ||
|
|
||
| @classmethod | ||
| def from_provider_event_details( | ||
| cls, provider_name: str, details: ProviderEventDetails | ||
| ) -> EventDetails: | ||
| return cls( | ||
| provider_name=provider_name, | ||
| flags_changed=details.flags_changed, | ||
| message=details.message, | ||
| error_code=details.error_code, | ||
| metadata=details.metadata, | ||
| ) | ||
|
|
||
|
|
||
| EventHandler = Callable[[EventDetails], None] | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.