Skip to content

Commit

Permalink
chore: section clarity, harden API, providers (#149)
Browse files Browse the repository at this point in the history
**Marking Evaluation API and Providers `hardening`** and a few
non-functional changes here that improve structure.

specifically:

- explicitly mark and number all sections
- section headings have consistent numbering (provider section numbering
was different than the other docs)
- removed "draft" language 

An alternative to marking all of `provider` and `evaluation API` as
hardening would be just marking all existing sections therein as
hardening, which might be better since we'd have to do that anyway if we
added a new experimental section.

see: #146 for more on release
goals.

Signed-off-by: Todd Baert <toddbaert@gmail.com>
  • Loading branch information
toddbaert committed Oct 17, 2022
1 parent 18d2f02 commit c179236
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 64 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# OpenFeature Specification (Draft)
# OpenFeature Specification

[![Roadmap](https://img.shields.io/static/v1?label=Roadmap&message=public&color=green)](https://github.com/orgs/open-feature/projects/1) [![Contributing](https://img.shields.io/static/v1?label=Contributing&message=guide&color=blue)](https://github.com/open-feature/.github/blob/main/CONTRIBUTING.md) [![Code of Conduct](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](https://github.com/open-feature/.github/blob/main/CODE_OF_CONDUCT.md)

Expand Down
56 changes: 28 additions & 28 deletions specification.json
Original file line number Diff line number Diff line change
Expand Up @@ -185,101 +185,101 @@
"children": []
},
{
"id": "Requirement 2.1",
"machine_id": "requirement_2_1",
"id": "Requirement 2.1.1",
"machine_id": "requirement_2_1_1",
"content": "The provider interface MUST define a `metadata` member or accessor, containing a `name` field or accessor of type string, which identifies the provider implementation.",
"RFC 2119 keyword": "MUST",
"children": []
},
{
"id": "Requirement 2.2",
"machine_id": "requirement_2_2",
"id": "Requirement 2.2.1",
"machine_id": "requirement_2_2_1",
"content": "The `feature provider` interface MUST define methods to resolve flag values, with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required) and `evaluation context` (optional), which returns a `flag resolution` structure.",
"RFC 2119 keyword": "MUST",
"children": []
},
{
"id": "Condition 2.3",
"machine_id": "condition_2_3",
"id": "Condition 2.2.2",
"machine_id": "condition_2_2_2",
"content": "The implementing language type system differentiates between strings, numbers, booleans and structures.",
"RFC 2119 keyword": null,
"children": [
{
"id": "Conditional Requirement 2.3.1",
"machine_id": "conditional_requirement_2_3_1",
"id": "Conditional Requirement 2.2.2.1",
"machine_id": "conditional_requirement_2_2_2_1",
"content": "The `feature provider` interface MUST define methods for typed flag resolution, including boolean, numeric, string, and structure.",
"RFC 2119 keyword": "MUST",
"children": []
}
]
},
{
"id": "Requirement 2.4",
"machine_id": "requirement_2_4",
"id": "Requirement 2.2.3",
"machine_id": "requirement_2_2_3",
"content": "In cases of normal execution, the `provider` MUST populate the `flag resolution` structure's `value` field with the resolved flag value.",
"RFC 2119 keyword": "MUST",
"children": []
},
{
"id": "Requirement 2.5",
"machine_id": "requirement_2_5",
"id": "Requirement 2.2.4",
"machine_id": "requirement_2_2_4",
"content": "In cases of normal execution, the `provider` SHOULD populate the `flag resolution` structure's `variant` field with a string identifier corresponding to the returned flag value.",
"RFC 2119 keyword": "SHOULD",
"children": []
},
{
"id": "Requirement 2.6",
"machine_id": "requirement_2_6",
"id": "Requirement 2.2.5",
"machine_id": "requirement_2_2_5",
"content": "The `provider` SHOULD populate the `flag resolution` structure's `reason` field with `\"DEFAULT\",` `\"TARGETING_MATCH\"`, `\"SPLIT\"`, `\"DISABLED\"`, `\"UNKNOWN\"`, `\"ERROR\"` or some other string indicating the semantic reason for the returned flag value.",
"RFC 2119 keyword": "SHOULD",
"children": []
},
{
"id": "Requirement 2.7",
"machine_id": "requirement_2_7",
"id": "Requirement 2.2.6",
"machine_id": "requirement_2_2_6",
"content": "In cases of normal execution, the `provider` MUST NOT populate the `flag resolution` structure's `error code` field, or otherwise must populate it with a null or falsy value.",
"RFC 2119 keyword": "MUST NOT",
"children": []
},
{
"id": "Requirement 2.8",
"machine_id": "requirement_2_8",
"id": "Requirement 2.2.7",
"machine_id": "requirement_2_2_7",
"content": "In cases of abnormal execution, the `provider` MUST indicate an error using the idioms of the implementation language, with an associated `error code` and optional associated `error message`.",
"RFC 2119 keyword": "MUST",
"children": []
},
{
"id": "Condition 2.9",
"machine_id": "condition_2_9",
"id": "Condition 2.2.8",
"machine_id": "condition_2_2_8",
"content": "The implementation language supports generics (or an equivalent feature).",
"RFC 2119 keyword": null,
"children": [
{
"id": "Conditional Requirement 2.9.1",
"machine_id": "conditional_requirement_2_9_1",
"id": "Conditional Requirement 2.2.8.1",
"machine_id": "conditional_requirement_2_2_8_1",
"content": "The `flag resolution` structure SHOULD accept a generic argument (or use an equivalent language feature) which indicates the type of the wrapped `value` field.",
"RFC 2119 keyword": "SHOULD",
"children": []
}
]
},
{
"id": "Requirement 2.10",
"machine_id": "requirement_2_10",
"id": "Requirement 2.3.1",
"machine_id": "requirement_2_3_1",
"content": "The provider interface MUST define a `provider hook` mechanism which can be optionally implemented in order to add `hook` instances to the evaluation life-cycle.",
"RFC 2119 keyword": "MUST",
"children": []
},
{
"id": "Requirement 2.11",
"machine_id": "requirement_2_11",
"id": "Requirement 2.3.2",
"machine_id": "requirement_2_3_2",
"content": "In cases of normal execution, the `provider` MUST NOT populate the `flag resolution` structure's `error message` field, or otherwise must populate it with a null or falsy value.",
"RFC 2119 keyword": "MUST NOT",
"children": []
},
{
"id": "Requirement 2.12",
"machine_id": "requirement_2_12",
"id": "Requirement 2.3.3",
"machine_id": "requirement_2_3_3",
"content": "In cases of abnormal execution, the `evaluation details` structure's `error message` field MAY contain a string containing additional detail about the nature of the error.",
"RFC 2119 keyword": "MAY",
"children": []
Expand Down
6 changes: 3 additions & 3 deletions specification/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Possible statuses are described below:

### Experimental

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

Specification sections that are marked as `Experimental` contain functionality under active development. Breaking changes are allowed and may be made without deprecation notices or warnings with minor version updates. We recommend you use these features in experimental environments and not in production.

Expand All @@ -54,7 +54,7 @@ Put simply:
### Hardening

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

Sections marked as `Hardening` describe functionality with an emphasis on stabilizing existing requirements. Breaking changes require consensus by the [Technical Steering Committee](https://github.com/open-feature/community/blob/main/governance-charter.md#tsc-members) but may still be made with minor version updates. These features are suitable for use in production environments. Feedback is encouraged.

Expand All @@ -64,7 +64,7 @@ Put simply:
### Stable

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

Sections marked as `Stable` do not allow breaking changes without a major version update. They can be used in production with a high degree of confidence.

Expand Down
16 changes: 10 additions & 6 deletions specification/sections/01-flag-evaluation.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ description: The specification that defines the developer facing feature flag ev
toc_max_heading_level: 4
---

# Flag Evaluation API
# 1. Flag Evaluation API

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

## Overview

The `evaluation API` allows for the evaluation of feature flag values, independent of any flag control plane or vendor. In the absence of a [provider](./02-providers.md) the `evaluation API` uses the "No-op provider", which simply returns the supplied default flag value.

### API Initialization and Configuration
### 1.1. API Initialization and Configuration

#### Requirement 1.1.1

Expand Down Expand Up @@ -74,7 +74,7 @@ The name is a logical identifier for the client.
Clients may be created in critical code paths, and even per-request in server-side HTTP contexts. Therefore, in keeping with the principle that OpenFeature should never cause abnormal execution of the first party application, this function should never throw. Abnormal execution in initialization should instead occur during provider registration.

### Client Usage
### 1.2. Client Usage

#### Requirement 1.2.1

Expand All @@ -95,7 +95,9 @@ See [hooks](./04-hooks.md) for details.
client.getMetadata().getName(); // "my-client"
```

#### Flag Evaluation
#### 1.3. Flag Evaluation

[![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

Expand Down Expand Up @@ -138,7 +140,9 @@ See [types](../types.md) for details.

> 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.
#### Detailed Flag Evaluation
#### 1.4. Detailed Flag Evaluation

[![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

Expand Down
38 changes: 20 additions & 18 deletions specification/sections/02-providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ description: The specification that defines the responsibilities and behaviors o
toc_max_heading_level: 4
---

# Provider
# 2. Provider

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

## Overview

Expand All @@ -14,21 +16,21 @@ Providers are the "translator" between the flag evaluation calls made in applica

![Provider](../assets/images/provider.png)

### Feature Provider Interface
### 2.1. Feature Provider Interface

#### Requirement 2.1
#### Requirement 2.1.1

> The provider interface **MUST** define a `metadata` member or accessor, containing a `name` field or accessor of type string, which identifies the provider implementation.
```typescript
provider.getMetadata().getName(); // "my-custom-provider"
```

#### Flag Value Resolution
### 2.2 Flag Value Resolution

`Providers` are implementations of the `feature provider` interface, which may wrap vendor SDKs, REST API clients, or otherwise resolve flag values from the runtime environment.

##### Requirement 2.2
##### Requirement 2.2.1

> The `feature provider` interface **MUST** define methods to resolve flag values, with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required) and `evaluation context` (optional), which returns a `flag resolution` structure.
Expand All @@ -39,11 +41,11 @@ resolveBooleanValue(flagKey, defaultValue, context);

see: [flag resolution structure](../types.md#flag-resolution), [flag value resolution](../glossary.md#flag-value-resolution)

##### Condition 2.3
##### Condition 2.2.2

> The implementing language type system differentiates between strings, numbers, booleans and structures.
###### Conditional Requirement 2.3.1
###### Conditional Requirement 2.2.2.1

> The `feature provider` interface **MUST** define methods for typed flag resolution, including boolean, numeric, string, and structure.
Expand All @@ -61,29 +63,29 @@ ResolutionDetails resolveNumberValue(string flagKey, number defaultValue, contex
ResolutionDetails resolveStructureValue(string flagKey, JsonObject defaultValue, context: EvaluationContext);
```

##### Requirement 2.4
##### Requirement 2.2.3

> In cases of normal execution, the `provider` **MUST** populate the `flag resolution` structure's `value` field with the resolved flag value.
##### Requirement 2.5
##### Requirement 2.2.4

> In cases of normal execution, the `provider` **SHOULD** populate the `flag resolution` structure's `variant` field with a string identifier corresponding to the returned flag value.
For example, the flag value might be `3.14159265359`, and the variant field's value might be `"pi"`.

The value of the variant field might only be meaningful in the context of the flag management system associated with the provider. For example, the variant may be a UUID corresponding to the variant in the flag management system, or an index corresponding to the variant in the flag management system.

##### Requirement 2.6
##### Requirement 2.2.5

> The `provider` **SHOULD** populate the `flag resolution` structure's `reason` field with `"DEFAULT",` `"TARGETING_MATCH"`, `"SPLIT"`, `"DISABLED"`, `"UNKNOWN"`, `"ERROR"` or some other string indicating the semantic reason for the returned flag value.
As indicated in the definition of the [`flag resolution`](../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.

##### Requirement 2.7
##### Requirement 2.2.6

> In cases of normal execution, the `provider` **MUST NOT** populate the `flag resolution` structure's `error code` field, or otherwise must populate it with a null or falsy value.
##### Requirement 2.8
##### Requirement 2.2.7

> In cases of abnormal execution, the `provider` **MUST** indicate an error using the idioms of the implementation language, with an associated `error code` and optional associated `error message`.
Expand All @@ -96,11 +98,11 @@ See [error code](../types.md#error-code) for details.
throw new ProviderError(ErrorCode.INVALID_CONTEXT, "The 'foo' attribute must be a string.");
```

##### Condition 2.9
##### Condition 2.2.8

> The implementation language supports generics (or an equivalent feature).
###### Conditional Requirement 2.9.1
###### Conditional Requirement 2.2.8.1

> The `flag resolution` structure **SHOULD** accept a generic argument (or use an equivalent language feature) which indicates the type of the wrapped `value` field.
Expand All @@ -118,11 +120,11 @@ ResolutionDetails<number> resolveNumberValue(string flagKey, number defaultValue
ResolutionDetails<MyStruct> resolveStructureValue(string flagKey, MyStruct defaultValue, context: EvaluationContext);
```

#### Provider hooks
#### 2.3. Provider hooks

A `provider hook` exposes a mechanism for `provider authors` to register [`hooks`](./04-hooks.md) to tap into various stages of the flag evaluation lifecycle. These hooks can be used to perform side effects and mutate the context for purposes of the provider. Provider hooks are not configured or controlled by the `application author`.

##### Requirement 2.10
##### Requirement 2.3.1

> The provider interface **MUST** define a `provider hook` mechanism which can be optionally implemented in order to add `hook` instances to the evaluation life-cycle.
Expand All @@ -141,10 +143,10 @@ class MyProvider implements Provider {
}
```

#### Requirement 2.11
#### Requirement 2.3.2

> In cases of normal execution, the `provider` **MUST NOT** populate the `flag resolution` structure's `error message` field, or otherwise must populate it with a null or falsy value.
#### Requirement 2.12
#### Requirement 2.3.3

> In cases of abnormal execution, the `evaluation details` structure's `error message` field **MAY** contain a string containing additional detail about the nature of the error.
6 changes: 3 additions & 3 deletions specification/sections/03-evaluation-context.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: The specification that defines the structure and expectations of ev
toc_max_heading_level: 4
---

# Evaluation Context
# 3. Evaluation Context

**Status**: [Experimental](../README.md#document-statuses)

Expand All @@ -14,7 +14,7 @@ The `evaluation context` provides ambient information for the purposes of flag e

The context might contain information about the end-user, the application, the host, or any other ambient data that might be useful in flag evaluation. For example, a flag system might define rules that return a specific value based on the user's email address, locale, or the time of day. The context provides this information. The context can be optionally provided at evaluation, and mutated in [before hooks](./04-hooks.md).

### Fields
### 3.1 Fields

NOTE: Field casing is not specified, and should be chosen in accordance with language idioms.

Expand Down Expand Up @@ -42,7 +42,7 @@ see: [structure](../types.md#structure), [datetime](../types.md#datetime)
The key uniquely identifies a field in the `evaluation context` and it should be unique across all types to avoid any collision when marshalling the `evaluation context` by the provider.

### Merging Context
### 3.2 Merging Context

#### Requirement 3.2.1

Expand Down
Loading

0 comments on commit c179236

Please sign in to comment.