Skip to content
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

feat: add events #182

Merged
merged 24 commits into from
May 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 100 additions & 2 deletions specification.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
{
"id": "Requirement 1.1.5",
"machine_id": "requirement_1_1_5",
"content": "The API MUST provide a function for retrieving the metadata field of the configured `provider`.",
"content": "The `API` MUST provide a function for retrieving the metadata field of the configured `provider`.",
toddbaert marked this conversation as resolved.
Show resolved Hide resolved
"RFC 2119 keyword": "MUST",
"children": []
},
Expand Down Expand Up @@ -230,7 +230,7 @@
{
"id": "Requirement 1.6.1",
"machine_id": "requirement_1_6_1",
"content": "The API MUST define a `shutdown` function, which, when called, must call the respective `shutdown` function on the active provider.",
"content": "The API MUST define a `shutdown` function which, when called, must call the respective `shutdown` function on the active provider.",
"RFC 2119 keyword": "MUST",
"children": []
},
Expand Down Expand Up @@ -629,6 +629,104 @@
"content": "The hook MUST NOT alter the `hook hints` structure.",
"RFC 2119 keyword": "MUST NOT",
"children": []
},
{
"id": "Requirement 5.1.1",
"machine_id": "requirement_5_1_1",
"content": "The `provider` MAY define a mechanism for signaling the occurrence of one of a set of events, including `PROVIDER_READY`, `PROVIDER_ERROR`, `PROVIDER_CONFIGURATION_CHANGED` and `PROVIDER_STALE`, with a `provider event details` payload.",
"RFC 2119 keyword": "MAY",
"children": []
},
{
"id": "Requirement 5.1.2",
"machine_id": "requirement_5_1_2",
"content": "When a `provider` signals the occurrence of a particular `event`, the associated `client` and `API` event handlers MUST run.",
"RFC 2119 keyword": "MUST",
"children": []
},
{
"id": "Requirement 5.1.3",
"machine_id": "requirement_5_1_3",
"content": "When a `provider` signals the occurrence of a particular `event`, event handlers on clients which are not associated with that provider MUST NOT run.",
"RFC 2119 keyword": "MUST NOT",
"children": []
},
{
"id": "Requirement 5.1.4",
"machine_id": "requirement_5_1_4",
"content": "`PROVIDER_ERROR` events SHOULD populate the `provider event details`'s `error message` field.",
"RFC 2119 keyword": "SHOULD",
"children": []
},
{
"id": "Requirement 5.2.1",
"machine_id": "requirement_5_2_1",
"content": "The `client` MUST provide a function for associating `handler functions` with a particular `provider event type`.",
"RFC 2119 keyword": "MUST",
"children": []
},
{
"id": "Requirement 5.2.2",
"machine_id": "requirement_5_2_2",
"content": "The `API` MUST provide a function for associating `handler functions` with a particular `provider event type`.",
"RFC 2119 keyword": "MUST",
"children": []
},
{
"id": "Requirement 5.2.3",
"machine_id": "requirement_5_2_3",
"content": "The `event details` MUST contain the `client name` associated with the event.",
"RFC 2119 keyword": "MUST",
"children": []
},
{
"id": "Requirement 5.2.4",
"machine_id": "requirement_5_2_4",
"content": "The `handler function` MUST accept a `event details` parameter.",
"RFC 2119 keyword": "MUST",
"children": []
},
{
"id": "Requirement 5.2.5",
"machine_id": "requirement_5_2_5",
"content": "If a `handler function` terminates abnormally, other `handler functions` MUST run.",
"RFC 2119 keyword": "MUST",
"children": []
},
{
"id": "Requirement 5.2.6",
"machine_id": "requirement_5_2_6",
"content": "Event handlers MUST persist across `provider` changes.",
"RFC 2119 keyword": "MUST",
"children": []
},
{
"id": "Requirement 5.2.7",
"machine_id": "requirement_5_2_7",
"content": "The `API` and `client` MUST provide a function allowing the removal of event handlers.",
"RFC 2119 keyword": "MUST",
"children": []
},
{
"id": "Requirement 5.3.1",
"machine_id": "requirement_5_3_1",
"content": "If the provider's `initialize` function terminates normally, `PROVIDER_READY` handlers MUST run.",
"RFC 2119 keyword": "MUST",
"children": []
},
{
"id": "Requirement 5.3.2",
"machine_id": "requirement_5_3_2",
"content": "If the provider's `initialize` function terminates abnormally, `PROVIDER_ERROR` handlers MUST run.",
"RFC 2119 keyword": "MUST",
"children": []
},
{
"id": "Requirement 5.3.3",
"machine_id": "requirement_5_3_3",
"content": "`PROVIDER_READY` handlers attached after the provider is already in a ready state MUST run immediately.",
"RFC 2119 keyword": "MUST",
"children": []
}
]
}
4 changes: 2 additions & 2 deletions specification/sections/01-flag-evaluation.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ See [hooks](./04-hooks.md) for details.

#### Requirement 1.1.5

> The API **MUST** provide a function for retrieving the metadata field of the configured `provider`.
> The `API` **MUST** provide a function for retrieving the metadata field of the configured `provider`.

```typescript
// example provider accessor
Expand Down Expand Up @@ -274,7 +274,7 @@ See [hooks](./04-hooks.md) for details.

#### Requirement 1.6.1

> The API **MUST** define a `shutdown` function, which, when called, must call the respective `shutdown` function on the active provider.
> The API **MUST** define a `shutdown` function which, when called, must call the respective `shutdown` function on the active provider.

The precise name of this function is not prescribed by this specification, but should be defined be the SDK.
Relevant language idioms should be considered when choosing the name for this function, in accordance with the resource-disposal semantics of the language in question.
toddbaert marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
2 changes: 1 addition & 1 deletion specification/sections/04-hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ val = client.get_boolean_value('my-key', False, evaluation_options={
})
```

See: [Flag evaluation options](./01-flag-evaluation.md#)
see: [Flag evaluation options](./01-flag-evaluation.md#)

#### Requirement 4.5.1

Expand Down
139 changes: 139 additions & 0 deletions specification/sections/05-events.md
toddbaert marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
---
title: Events Extensions
description: Specification defining event semantics
toc_max_heading_level: 4
---

# 5. Events

[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)

## Overview

`Events` allow consumers (_application integrator_, _application author_, _integration author_) to react to state changes in the provider or underlying flag management system, such as flag definition changes, provider readiness, or error conditions. A provider may emit events or run a callback indicating that it received a certain event, optionally providing data associated with that event. Handlers registered on the `client` or the global `API` are then invoked with this data.

The data that providers supply in event payloads may include a list of `flag keys` changed, error messages, and possibly updated flag values.

```mermaid
graph
P(Provider) -->|emit event| A[API]
A -->|run handlers| AH("API (global) event handlers")
A --> C[Client]
C -->|run handlers| CH(Client event handlers)
```

Kavindu-Dodan marked this conversation as resolved.
Show resolved Hide resolved
### 5.1. Provider events

#### Requirement 5.1.1

> The `provider` **MAY** define a mechanism for signaling the occurrence of one of a set of events, including `PROVIDER_READY`, `PROVIDER_ERROR`, `PROVIDER_CONFIGURATION_CHANGED` and `PROVIDER_STALE`, with a `provider event details` payload.

If available, native event-emitter or observable/observer language constructs can be used.

see: [provider event types](../types.md#provider-events), [`event details`](../types.md#provider-event-details).

#### Requirement 5.1.2

> When a `provider` signals the occurrence of a particular `event`, the associated `client` and `API` event handlers **MUST** run.

see: [provider event types](./../types.md#provider-events) and [event handlers](#52-event-handlers).

#### Requirement 5.1.3

> When a `provider` signals the occurrence of a particular `event`, event handlers on clients which are not associated with that provider **MUST NOT** run.

Providers bound to a named client constitute their own "events scope".

see: [setting a provider](./01-flag-evaluation.md#setting-a-provider)

#### Requirement 5.1.4

> `PROVIDER_ERROR` events **SHOULD** populate the `provider event details`'s `error message` field.

The error message field should contain an informative message as to the nature of the error.

See [event metadata](../types.md#error-event-details)

### 5.2. Event handlers

#### Requirement 5.2.1

> The `client` **MUST** provide a function for associating `handler functions` with a particular `provider event type`.

```java
// run the myClientOnReadyHandler function when the PROVIDER_READY event is fired
client.addHandler(ProviderEvents.Ready, myClientOnReadyHandler);
```

see: [provider events](#51-provider-events), [`provider event types`](../types.md#provider-events)

#### Requirement 5.2.2

> The `API` **MUST** provide a function for associating `handler functions` with a particular `provider event type`.

```java
// run the myGlobalErrorHandler function when the PROVIDER_READY event is fired
OpenFeature.addHandler(ProviderEvents.Error, myGlobalErrorHandler);
```

see: [provider events](#51-provider-events), [`provider event types`](../types.md#provider-events)

#### Requirement 5.2.3

> The `event details` **MUST** contain the `client name` associated with the event.

The `client name` indicates the client/provider pair with which the event is associated.
This is especially relevant for event handlers which are attached to the `API`, not a particular client.

#### Requirement 5.2.4

> The `handler function` **MUST** accept a `event details` parameter.

see: [`event details`](../types.md#event-details)

#### Requirement 5.2.5

> If a `handler function` terminates abnormally, other `handler functions` **MUST** run.

#### Requirement 5.2.6

> Event handlers **MUST** persist across `provider` changes.

If a provider is changed, existing event handlers will still fire.
This means that the order of provider configuration and event handler addition is independent.

#### Requirement 5.2.7

> The `API` and `client` **MUST** provide a function allowing the removal of event handlers.

```java
// remove an existing handler for a PROVIDER_CONFIGURATION_CHANGED event
client.removeHandler(ProviderEvents.ConfigurationChanged, myClientOnChangedHandler);
```

### Event handlers and initialization

Though providers themselves need not implement events, the `flag evaluation API` uses events to convey relevant state changes during configuration and initialization.
_Application authors_ and _application integrators_ use these events to wait for proper initialization of the provider and to do basic monitoring and error handling.

#### Requirement 5.3.1

> If the provider's `initialize` function terminates normally, `PROVIDER_READY` handlers **MUST** run.

See [provider initialization](./02-providers.md#24-initialization) and [setting a provider](./01-flag-evaluation.md#setting-a-provider).

Kavindu-Dodan marked this conversation as resolved.
Show resolved Hide resolved
#### Requirement 5.3.2

> If the provider's `initialize` function terminates abnormally, `PROVIDER_ERROR` handlers **MUST** run.

A failed initialization could represent an unrecoverable error, such as bad credentials or a missing file.
If a failed initialization could also represent a transient error.
A provider which maintains a persistent connection to a remote `flag management system` may attempt to reconnect, and emit `PROVIDER_READY` after a failed initialization.

See [provider initialization](./02-providers.md#24-initialization) and [setting a provider](./01-flag-evaluation.md#setting-a-provider).

#### Requirement 5.3.3

> `PROVIDER_READY` handlers attached after the provider is already in a ready state **MUST** run immediately.

See [provider initialization](./02-providers.md#24-initialization) and [setting a provider](./01-flag-evaluation.md#setting-a-provider).
38 changes: 38 additions & 0 deletions specification/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,41 @@ An enumeration of possible provider states.
| NOT_READY | The provider has not been initialized. |
| READY | The provider has been initialized, and is able to reliably resolve flag values. |
| ERROR | The provider is initialized but is not able to reliably resolve flag values. |

### Provider Event Details

A structure defining a provider event payload, including:

- flags changed (string[], optional)
- message (string, optional)
toddbaert marked this conversation as resolved.
Show resolved Hide resolved
- event metadata ([event metadata](#event-metadata))

### Event Details

A structure defining an event payload, including:

- client name (string, required)
- flags changed (string[], optional)
- message (string, optional)
- event metadata ([event metadata](#event-metadata))

### Event Metadata

A structure supporting the addition of arbitrary event data.
It supports definition of arbitrary properties, with keys of type `string`, and values of type `boolean`, `string`, or `number`.

### Provider Events

An enumeration of provider events.
Kavindu-Dodan marked this conversation as resolved.
Show resolved Hide resolved

| Event | Explanation |
| ------------------------------ | --------------------------------------------------------------------------------------------------- |
| PROVIDER_READY | The provider is ready to perform flag evaluations. |
| PROVIDER_ERROR | The provider signalled an error. |
| PROVIDER_CONFIGURATION_CHANGED | A change was made to the backend flag configuration. |
toddbaert marked this conversation as resolved.
Show resolved Hide resolved
| PROVIDER_STALE | The provider's cached state is not longer valid and may not be up-to-date with the source of truth. |

### Handler Functions

A function or method which can be associated with a `provider event`, and runs when that event occurs.
It declares an `event details` parameter.