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 static/dynamic context #171

Merged
merged 34 commits into from
Jul 21, 2023
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
796985b
feat!: client/single-context, shutdown, and event specs
toddbaert Jan 10, 2023
9797c40
fixup: update TOC
toddbaert Jan 10, 2023
5a0b1cb
fixup: add links to context-paradigms
toddbaert Jan 10, 2023
199b87c
Update specification/glossary.md
toddbaert Jan 11, 2023
7219c58
Update specification/glossary.md
toddbaert Jan 11, 2023
91915a1
Update specification/sections/01-flag-evaluation.md
toddbaert Jan 11, 2023
4ae0914
Update specification/sections/02-providers.md
toddbaert Jan 11, 2023
a14b868
Update specification/sections/03-evaluation-context.md
toddbaert Jan 11, 2023
bdf9188
Update specification/sections/02-providers.md
toddbaert Jan 11, 2023
44d8a9c
fixup: correct examples, grammer
toddbaert Jan 11, 2023
0cd329e
fixup: update toc
toddbaert Jan 11, 2023
5cc744a
fixup: reduce number thrashing in 1.1
toddbaert Jan 16, 2023
f66e5c9
fixup: pr feedback
toddbaert Jan 24, 2023
6b8ef21
fixup: add STALE reason
toddbaert Jan 25, 2023
76c2f8e
Update specification/types.md
toddbaert Mar 9, 2023
c64a7ca
Update specification/sections/02-providers.md
toddbaert Mar 9, 2023
947b822
Update specification/glossary.md
toddbaert Mar 9, 2023
045772d
Update specification/sections/02-providers.md
toddbaert Mar 9, 2023
ad676f2
fixup: json
toddbaert May 18, 2023
29d812c
fixup: duped number
toddbaert May 18, 2023
b762b34
fixup: re-order so dynamic context is first
toddbaert May 19, 2023
b12bb96
Update specification/glossary.md
toddbaert May 26, 2023
5add151
Update specification/glossary.md
toddbaert May 26, 2023
624b765
Update specification/glossary.md
toddbaert May 26, 2023
fb8d6b0
Update specification/sections/02-providers.md
toddbaert May 26, 2023
a568c07
Update specification/glossary.md
toddbaert May 26, 2023
9b7cb59
fixup: sync json after suggestions
toddbaert May 26, 2023
3252176
fixup: on context set -> changed
toddbaert May 26, 2023
55dcfba
fixup: 261 normative text
toddbaert Jul 4, 2023
74dbeb6
fixup: json
toddbaert Jul 4, 2023
4fa2178
Update specification/sections/03-evaluation-context.md
toddbaert Jul 14, 2023
5753aa8
Update specification/sections/01-flag-evaluation.md
toddbaert Jul 14, 2023
e4a4b38
fixup: add client-side, server-side
toddbaert Jul 14, 2023
c4b8292
fixup: json
toddbaert Jul 14, 2023
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
251 changes: 186 additions & 65 deletions specification.json

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions specification/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ This document defines some terms that are used across this specification.
- [Targeting Key](#targeting-key)
- [Fractional Evaluation](#fractional-evaluation)
- [Rule](#rule)
- [SDK Paradigms](#sdk-paradigms)
- [Dynamic-Context Paradigm](#dynamic-context-paradigm)
- [Static-Context Paradigm](#static-context-paradigm)

<!-- tocstop -->

Expand Down Expand Up @@ -163,3 +166,25 @@ Pseudorandomly resolve flag values using a context property, such as a targeting
### Rule

A rule is some criteria that's used to determine which variant a particular context should be mapped to.

## SDK Paradigms
toddbaert marked this conversation as resolved.
Show resolved Hide resolved

Feature flag frameworks have SDKs which operate in two distinct paradigms: those designed for use with a single user client application (e.g. mobile phones, single-page web apps), and those designed for multi-user applications, such as web server applications. Some parts of the OpenFeature specification diverge depending on the paradigm.

### Dynamic-Context Paradigm

Server-side applications typically perform flag evaluations on behalf of many users, with each request or event being associated with a particular user or client. For this reason, server frameworks typically operate similarly to this:

- the application is initialized with some static context (geography, service name, hostname, etc)
- with each request or event, relevant dynamic context (for example, user session data, unique user identifiers) is provided to flag evaluations

### Static-Context Paradigm

In contrast to server-side or other service-type applications, client side applications typically operate in the context of a single user. Most feature flagging libraries for these applications have been designed with this in mind. Frequently, Client/web libraries operate similarly to this:
toddbaert marked this conversation as resolved.
Show resolved Hide resolved

- an initialization occurs, which fetches evaluated flags in bulk for a given context (user)
- the evaluated flags are cached in the library
- flag evaluations take place against this cache, without a need to provide context (context was already used to evaluate flags in bulk)
- libraries provide a mechanism to update context (e.g. if a user logs in), meaning cached evaluations are no longer valid and must be re-evaluated, frequently involving a network request or I/O operation

Not all client libraries work this way, but generally, libraries that accept dynamic context per evaluation can build providers which conform to this model with relative ease, while the reverse is not true.
toddbaert marked this conversation as resolved.
Show resolved Hide resolved
103 changes: 85 additions & 18 deletions specification/sections/01-flag-evaluation.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,13 @@ client.getMetadata().getName(); // "my-client"

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

#### Requirement 1.3.1
#### Condition 1.3.1

> The implementation uses the dynamic-context paradigm.

see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm)

##### Conditional Requirement 1.3.1.1

> The `client` **MUST** provide methods for typed flag evaluation, including boolean, numeric, string, and structure, with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), `evaluation context` (optional), and `evaluation options` (optional), which returns the flag value.

Expand All @@ -152,10 +158,38 @@ See [evaluation context](./03-evaluation-context.md) for details.

#### Condition 1.3.2

> The implementation language differentiates between floating-point numbers and integers.
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)

> The implementation uses the static-context paradigm.

see: [static-context paradigm](../glossary.md#static-context-paradigm)

##### Conditional Requirement 1.3.2.1

> The `client` **MUST** provide methods for typed flag evaluation, including boolean, numeric, string, and structure, with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), and `evaluation options` (optional), which returns the flag value.

```typescript
// example boolean flag evaluation
boolean myBool = client.getBooleanValue('bool-flag', false);

// example overloaded string flag evaluation with optional params
string myString = client.getStringValue('string-flag', 'N/A', options);

// example number flag evaluation
number myNumber = client.getNumberValue('number-flag', 75);

// example overloaded structure flag evaluation with optional params
MyStruct myStruct = client.getObjectValue<MyStruct>('structured-flag', { text: 'N/A', percentage: 75 }, options);
```

See [evaluation context](./03-evaluation-context.md) for details.
toddbaert marked this conversation as resolved.
Show resolved Hide resolved

#### Condition 1.3.3

> The implementation language differentiates between floating-point numbers and integers.

##### Conditional Requirement 1.3.3.1

> The client **SHOULD** provide functions for floating-point numbers and integers, consistent with language idioms.

```java
Expand All @@ -166,15 +200,21 @@ long getFloatValue(String flag, long defaultValue);

See [types](../types.md) for details.

#### Requirement 1.3.3
#### Requirement 1.3.4

> The `client` **SHOULD** guarantee the returned value of any typed flag evaluation method is of the expected type. If the value returned by the underlying provider implementation does not match the expected type, it's to be considered abnormal execution, and the supplied `default value` should be returned.

### 1.4. Detailed Flag Evaluation
toddbaert marked this conversation as resolved.
Show resolved Hide resolved

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

#### Requirement 1.4.1
#### Condition 1.4.1

> The implementation uses the dynamic-context paradigm.

see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm)

##### Conditional Requirement 1.4.1.1

> The `client` **MUST** provide methods for detailed flag value evaluation with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), `evaluation context` (optional), and `evaluation options` (optional), which returns an `evaluation details` structure.

Expand All @@ -193,69 +233,96 @@ FlagEvaluationDetails<MyStruct> myStructDetails = client.getObjectDetails<MyStru

```

#### Requirement 1.4.2
#### Condition 1.4.2

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

> The implementation uses the static-context paradigm.

see: [static-context paradigm](../glossary.md#static-context-paradigm)

##### Conditional Requirement 1.4.2.1

> The `client` **MUST** provide methods for detailed flag value evaluation with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), and `evaluation options` (optional), which returns an `evaluation details` structure.
toddbaert marked this conversation as resolved.
Show resolved Hide resolved

```typescript
// example detailed boolean flag evaluation
FlagEvaluationDetails<boolean> myBoolDetails = client.getBooleanDetails('bool-flag', false);

// example detailed string flag evaluation
FlagEvaluationDetails<string> myStringDetails = client.getStringDetails('string-flag', 'N/A', options);

// example detailed number flag evaluation
FlagEvaluationDetails<number> myNumberDetails = client.getNumberDetails('number-flag', 75);

// example detailed structure flag evaluation
FlagEvaluationDetails<MyStruct> myStructDetails = client.getObjectDetails<MyStruct>('structured-flag', { text: 'N/A', percentage: 75 }, options);

```

#### Requirement 1.4.3

> The `evaluation details` structure's `value` field **MUST** contain the evaluated flag value.

#### Condition 1.4.3
#### Condition 1.4.4

> The language supports generics (or an equivalent feature).

##### Conditional Requirement 1.4.3.1
##### Conditional Requirement 1.4.4.1

> The `evaluation details` structure **SHOULD** accept a generic argument (or use an equivalent language feature) which indicates the type of the wrapped `value` field.

#### Requirement 1.4.4
#### Requirement 1.4.5

> The `evaluation details` structure's `flag key` field **MUST** contain the `flag key` argument passed to the detailed flag evaluation method.

#### Requirement 1.4.5
#### Requirement 1.4.6

> In cases of normal execution, the `evaluation details` structure's `variant` field **MUST** contain the value of the `variant` field in the `flag resolution` structure returned by the configured `provider`, if the field is set.

#### Requirement 1.4.6
#### Requirement 1.4.7

> In cases of normal execution, the `evaluation details` structure's `reason` field **MUST** contain the value of the `reason` field in the `flag resolution` structure returned by the configured `provider`, if the field is set.

#### Requirement 1.4.7
#### Requirement 1.4.8

> In cases of abnormal execution, the `evaluation details` structure's `error code` field **MUST** contain an `error code`.

See [error code](../types.md#error-code) for details.

#### Requirement 1.4.8
#### Requirement 1.4.9

> In cases of abnormal execution (network failure, unhandled error, etc) the `reason` field in the `evaluation details` **SHOULD** indicate an error.

#### Requirement 1.4.9
#### Requirement 1.4.10

> Methods, functions, or operations on the client **MUST NOT** throw exceptions, or otherwise abnormally terminate. Flag evaluation calls must always return the `default value` in the event of abnormal execution. Exceptions include functions or methods for the purposes for configuration or setup.

Configuration code includes code to set the provider, instantiate providers, and configure the global API object.

#### Requirement 1.4.10
#### Requirement 1.4.11

> In the case of abnormal execution, the client **SHOULD** log an informative error message.

Implementations may define a standard logging interface that can be supplied as an optional argument to the client creation function, which may wrap standard logging functionality of the implementation language.

#### Requirement 1.4.11
#### Requirement 1.4.12

> The `client` **SHOULD** provide asynchronous or non-blocking mechanisms for flag evaluation.

It's recommended to provide non-blocking mechanisms for flag evaluation, particularly in languages or environments wherein there's a single thread of execution.

#### Requirement 1.4.12
#### Requirement 1.4.13

> In cases of abnormal execution, the `evaluation details` structure's `error message` field **MAY** contain a string containing additional details about the nature of the error.

#### Requirement 1.4.13
#### Requirement 1.4.14

> If the `flag metadata` field in the `flag resolution` structure returned by the configured `provider` is set, the `evaluation details` structure's `flag metadata` field **MUST** contain that value. Otherwise, it **MUST** contain an empty record.

This `flag metadata` field is intended as a mechanism for providers to surface additional information about a feature flag (or its evaluation) beyond what is defined within the OpenFeature spec itself. The primary consumer of this information is a provider-specific hook.

#### Condition 1.4.14
#### Condition 1.4.15

> The implementation language supports a mechanism for marking data as immutable.

Expand Down
26 changes: 25 additions & 1 deletion specification/sections/02-providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ The value of the variant field might only be meaningful in the context of the fl

#### Requirement 2.2.5

> The `provider` **SHOULD** populate the `resolution details` structure's `reason` field with `"STATIC"`, `"DEFAULT",` `"TARGETING_MATCH"`, `"SPLIT"`, `"CACHED"`, `"DISABLED"`, `"UNKNOWN"`, `"ERROR"` or some other string indicating the semantic reason for the returned flag value.
> The `provider` **SHOULD** populate the `resolution details` structure's `reason` field with `"STATIC"`, `"DEFAULT",` `"TARGETING_MATCH"`, `"SPLIT"`, `"CACHED"`, `"DISABLED"`, `"UNKNOWN"`, `"STALE"`, `"ERROR"` or some other string indicating the semantic reason for the returned flag value.

As indicated in the definition of the [`resolution details`](../types.md#resolution-details) structure, the `reason` should be a string. This allows providers to reflect accurately why a flag was resolved to a particular value.

Expand Down Expand Up @@ -245,6 +245,30 @@ class MyProvider implements Provider, AutoDisposable {
void dispose() {
// close connections, terminate threads or timers, etc...
}
```

### 2.6. Provider context reconciliation

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

Static-context focused providers may need a mechanism to understand when their cache of evaluated flags must be invalidated or updated. An `on context changed` handler can be defined which performs whatever operations are needed to reconcile the evaluated flags with the new context.

#### Requirement 2.6.1

> The provider **MAY** define an `on context changed` handler, which takes an argument for the previous context and the newly set context, in order to respond to an evaluation context change.

Especially in static-context implementations, providers and underlying SDKs may maintain state for a particular context.
The `on context changed` handler provides a mechanism to update this state, often by re-evaluating flags in bulk with respect to the new context.
Kavindu-Dodan marked this conversation as resolved.
Show resolved Hide resolved

```java
// MyProvider implementation of the onContextChanged function defined in Provider
class MyProvider implements Provider {
//...

onContextChanged(EvaluationContext oldContext, EvaluationContext newContext): void {
// update context-sensitive cached flags, or otherwise react to the change in the global context
}

//...
}
```
Expand Down
44 changes: 42 additions & 2 deletions specification/sections/03-evaluation-context.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,39 @@ The key uniquely identifies a field in the `evaluation context` and it should be

### 3.2 Context levels and merging

#### Requirement 3.2.1
#### Condition 3.2.1

> The implementation uses the dynamic-context paradigm.

see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm)

##### Conditional Requirement 3.2.1.1

> The API, Client and invocation **MUST** have a method for supplying `evaluation context`.

API (global) `evaluation context` can be used to supply static data to flag evaluation, such as an application identifier, compute region, or hostname. Client and invocation `evaluation context` are ideal for dynamic data, such as end-user attributes.

#### Requirement 3.2.2
#### Condition 3.2.2

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

> The implementation uses the static-context paradigm.

see: [static-context paradigm](../glossary.md#static-context-paradigm)

##### Conditional Requirement 3.2.2.1

> The API **MUST** have a method for supplying `evaluation context`.
toddbaert marked this conversation as resolved.
Show resolved Hide resolved

API (global) `evaluation context` can be used to supply data to flag evaluation, such as (but not limited to) user name, email, or user organization membership changes.

##### Conditional Requirement 3.2.2.2

> The Client and invocation **MUST NOT** have a method for supplying `evaluation context`.

In the static-context paradigm, context is global. The client and invocation cannot supply evaluation context.

toddbaert marked this conversation as resolved.
Show resolved Hide resolved
#### Requirement 3.2.3

> Evaluation context **MUST** be merged in the order: API (global; lowest precedence) -> client -> invocation -> before hooks (highest precedence), with duplicate values being overwritten.

Expand All @@ -66,3 +92,17 @@ flowchart LR
client --> invocation
invocation --> hook
```

#### Condition 3.2.4

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

> The implementation uses the static-context paradigm.

see: [static-context paradigm](../glossary.md#static-context-paradigm)

##### Requirement 3.2.4.1

> When the global `evaluation context` is set, the `on context changed` handler **MUST** run.

The SDK implementation must run the `on context changed` handler on the registered provider whenever the global `evaluation context` is mutated.
Loading
Loading