From 796985b3a3f3c023a06ab33d3638027cc835d70c Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Tue, 10 Jan 2023 13:55:16 -0500 Subject: [PATCH 01/34] feat!: client/single-context, shutdown, and event specs Signed-off-by: Todd Baert --- specification.json | 249 +++++++++++++----- specification/glossary.md | 25 ++ specification/sections/01-flag-evaluation.md | 108 ++++++-- specification/sections/02-providers.md | 20 +- .../sections/03-evaluation-context.md | 44 +++- specification/sections/04-hooks.md | 38 ++- 6 files changed, 388 insertions(+), 96 deletions(-) diff --git a/specification.json b/specification.json index 505644cf..d8b99b07 100644 --- a/specification.json +++ b/specification.json @@ -78,21 +78,44 @@ "children": [] }, { - "id": "Requirement 1.3.1", - "machine_id": "requirement_1_3_1", - "content": "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.", - "RFC 2119 keyword": "MUST", - "children": [] + "id": "Condition 1.3.1", + "machine_id": "condition_1_3_1", + "content": "The implementation uses the static-context paradigm.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 1.3.1.1", + "machine_id": "conditional_requirement_1_3_1_1", + "content": "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.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] }, { "id": "Condition 1.3.2", "machine_id": "condition_1_3_2", - "content": "The implementation language differentiates between floating-point numbers and integers.", + "content": "The implementation uses the dynamic-context paradigm.", "RFC 2119 keyword": null, "children": [ { "id": "Conditional Requirement 1.3.2.1", "machine_id": "conditional_requirement_1_3_2_1", + "content": "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.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] + }, + { + "id": "Condition 1.3.3", + "machine_id": "condition_1_3_3", + "content": "The implementation language differentiates between floating-point numbers and integers.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 1.3.3.1", + "machine_id": "conditional_requirement_1_3_3_1", "content": "The client SHOULD provide functions for floating-point numbers and integers, consistent with language idioms.", "RFC 2119 keyword": "SHOULD", "children": [] @@ -100,114 +123,137 @@ ] }, { - "id": "Requirement 1.3.3", - "machine_id": "requirement_1_3_3", + "id": "Requirement 1.3.4", + "machine_id": "requirement_1_3_4", "content": "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.", "RFC 2119 keyword": "SHOULD", "children": [] }, { - "id": "Requirement 1.4.1", - "machine_id": "requirement_1_4_1", - "content": "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.", - "RFC 2119 keyword": "MUST", - "children": [] + "id": "Condition 1.4.1", + "machine_id": "condition_1_4_1", + "content": "The implementation uses the static-context paradigm.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 1.4.1.1", + "machine_id": "conditional_requirement_1_4_1_1", + "content": "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.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] + }, + { + "id": "Condition 1.4.2", + "machine_id": "condition_1_4_2", + "content": "The implementation uses the dynamic-context paradigm.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 1.4.2.1", + "machine_id": "conditional_requirement_1_4_2_1", + "content": "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.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] }, { - "id": "Requirement 1.4.2", - "machine_id": "requirement_1_4_2", + "id": "Requirement 1.4.3", + "machine_id": "requirement_1_4_3", "content": "The `evaluation details` structure's `value` field MUST contain the evaluated flag value.", "RFC 2119 keyword": "MUST", "children": [] }, { - "id": "Condition 1.4.3", - "machine_id": "condition_1_4_3", + "id": "Condition 1.4.4", + "machine_id": "condition_1_4_4", "content": "The language supports generics (or an equivalent feature).", "RFC 2119 keyword": null, "children": [ { - "id": "Conditional Requirement 1.4.3.1", - "machine_id": "conditional_requirement_1_4_3_1", + "id": "Conditional Requirement 1.4.4.1", + "machine_id": "conditional_requirement_1_4_4_1", "content": "The `evaluation details` 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 1.4.4", - "machine_id": "requirement_1_4_4", - "content": "The `evaluation details` structure's `flag key` field MUST contain the `flag key` argument passed to the detailed flag evaluation method.", - "RFC 2119 keyword": "MUST", - "children": [] - }, { "id": "Requirement 1.4.5", "machine_id": "requirement_1_4_5", - "content": "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.", + "content": "The `evaluation details` structure's `flag key` field MUST contain the `flag key` argument passed to the detailed flag evaluation method.", "RFC 2119 keyword": "MUST", "children": [] }, { "id": "Requirement 1.4.6", "machine_id": "requirement_1_4_6", - "content": "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.", + "content": "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.", "RFC 2119 keyword": "MUST", "children": [] }, { "id": "Requirement 1.4.7", "machine_id": "requirement_1_4_7", - "content": "In cases of abnormal execution, the `evaluation details` structure's `error code` field MUST contain an `error code`.", + "content": "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.", "RFC 2119 keyword": "MUST", "children": [] }, { "id": "Requirement 1.4.8", "machine_id": "requirement_1_4_8", - "content": "In cases of abnormal execution (network failure, unhandled error, etc) the `reason` field in the `evaluation details` SHOULD indicate an error.", - "RFC 2119 keyword": "SHOULD", + "content": "In cases of abnormal execution, the `evaluation details` structure's `error code` field MUST contain an `error code`.", + "RFC 2119 keyword": "MUST", "children": [] }, { "id": "Requirement 1.4.9", "machine_id": "requirement_1_4_9", - "content": "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.", - "RFC 2119 keyword": "MUST NOT", + "content": "In cases of abnormal execution (network failure, unhandled error, etc) the `reason` field in the `evaluation details` SHOULD indicate an error.", + "RFC 2119 keyword": "SHOULD", "children": [] }, { "id": "Requirement 1.4.10", "machine_id": "requirement_1_4_10", - "content": "In the case of abnormal execution, the client SHOULD log an informative error message.", - "RFC 2119 keyword": "SHOULD", + "content": "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.", + "RFC 2119 keyword": "MUST NOT", "children": [] }, { "id": "Requirement 1.4.11", "machine_id": "requirement_1_4_11", - "content": "The `client` SHOULD provide asynchronous or non-blocking mechanisms for flag evaluation.", + "content": "In the case of abnormal execution, the client SHOULD log an informative error message.", "RFC 2119 keyword": "SHOULD", "children": [] }, { "id": "Requirement 1.4.12", "machine_id": "requirement_1_4_12", - "content": "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.", - "RFC 2119 keyword": "MAY", + "content": "The `client` SHOULD provide asynchronous or non-blocking mechanisms for flag evaluation.", + "RFC 2119 keyword": "SHOULD", "children": [] }, { "id": "Requirement 1.4.13", "machine_id": "requirement_1_4_13", + "content": "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.", + "RFC 2119 keyword": "MAY", + "children": [] + }, + { + "id": "Requirement 1.4.14", + "machine_id": "requirement_1_4_14", "content": "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.", "RFC 2119 keyword": "MUST", "children": [] }, { - "id": "Condition 1.4.14", - "machine_id": "condition_1_4_14", + "id": "Condition 1.4.15", + "machine_id": "condition_1_4_15", "content": "The implementation language supports a mechanism for marking data as immutable.", "RFC 2119 keyword": null, "children": [ @@ -390,6 +436,13 @@ "RFC 2119 keyword": "MAY", "children": [] }, + { + "id": "Requirement 2.6.1", + "machine_id": "requirement_2_6_1", + "content": "The provider interface MUST define an `on context changed` handler, which takes a argument for the previous context, and the newly set context, and which can be optionally implemented to reconcile any stored state pertaining to the global evaluation context.", + "RFC 2119 keyword": "MUST", + "children": [] + }, { "id": "Requirement 3.1.1", "machine_id": "requirement_3_1_1", @@ -419,19 +472,64 @@ "children": [] }, { - "id": "Requirement 3.2.1", - "machine_id": "requirement_3_2_1", - "content": "The API, Client and invocation MUST have a method for supplying `evaluation context`.", - "RFC 2119 keyword": "MUST", - "children": [] + "id": "Condition 3.2.1", + "machine_id": "condition_3_2_1", + "content": "The implementation uses the static-context paradigm.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 3.2.1.1", + "machine_id": "conditional_requirement_3_2_1_1", + "content": "The API MUST have a method for supplying `evaluation context`.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Conditional Requirement 3.2.1.2", + "machine_id": "conditional_requirement_3_2_1_2", + "content": "The Client and invocation MUST NOT have a method for supplying `evaluation context`.", + "RFC 2119 keyword": "MUST NOT", + "children": [] + } + ] }, { - "id": "Requirement 3.2.2", - "machine_id": "requirement_3_2_2", + "id": "Condition 3.2.2", + "machine_id": "condition_3_2_2", + "content": "The implementation uses the dynamic-context paradigm.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 3.2.2.1", + "machine_id": "conditional_requirement_3_2_2_1", + "content": "The API, Client and invocation MUST have a method for supplying `evaluation context`.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] + }, + { + "id": "Requirement 3.2.3", + "machine_id": "requirement_3_2_3", "content": "Evaluation context MUST be merged in the order: API (global; lowest precedence) - client - invocation - before hooks (highest precedence), with duplicate values being overwritten.", "RFC 2119 keyword": "MUST", "children": [] }, + { + "id": "Condition 3.2.4", + "machine_id": "condition_3_2_4", + "content": "The implementation uses the static-context paradigm.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Requirement 3.2.4.1", + "machine_id": "requirement_3_2_4_1", + "content": "When the global `evaluation context` is set, the `on context changed` handler MUST run.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] + }, { "id": "Requirement 4.1.1", "machine_id": "requirement_4_1_1", @@ -504,56 +602,79 @@ "children": [] }, { - "id": "Requirement 4.3.2", - "machine_id": "requirement_4_3_2", - "content": "The `before` stage MUST run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters and returns either an `evaluation context` or nothing.", - "RFC 2119 keyword": "MUST", - "children": [] + "id": "Condition 4.3.2", + "machine_id": "condition_4_3_2", + "content": "The implementation uses the static-context paradigm.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 4.3.2.1", + "machine_id": "conditional_requirement_4_3_2_1", + "content": "The `before` stage MUST run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters. It has no return value.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] }, { - "id": "Requirement 4.3.3", - "machine_id": "requirement_4_3_3", - "content": "Any `evaluation context` returned from a `before` hook MUST be passed to subsequent `before` hooks (via `HookContext`).", - "RFC 2119 keyword": "MUST", - "children": [] + "id": "Condition 4.3.3", + "machine_id": "condition_4_3_3", + "content": "The implementation uses the dynamic-context paradigm.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 4.3.3.1", + "machine_id": "conditional_requirement_4_3_3_1", + "content": "The `before` stage MUST run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters and returns either an `evaluation context` or nothing.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] }, { "id": "Requirement 4.3.4", "machine_id": "requirement_4_3_4", - "content": "When `before` hooks have finished executing, any resulting `evaluation context` MUST be merged with the existing `evaluation context`.", + "content": "Any `evaluation context` returned from a `before` hook MUST be passed to subsequent `before` hooks (via `HookContext`).", "RFC 2119 keyword": "MUST", "children": [] }, { "id": "Requirement 4.3.5", "machine_id": "requirement_4_3_5", - "content": "The `after` stage MUST run after flag resolution occurs. It accepts a `hook context` (required), `flag evaluation details` (required) and `hook hints` (optional). It has no return value.", + "content": "When `before` hooks have finished executing, any resulting `evaluation context` MUST be merged with the existing `evaluation context`.", "RFC 2119 keyword": "MUST", "children": [] }, { "id": "Requirement 4.3.6", "machine_id": "requirement_4_3_6", - "content": "The `error` hook MUST run when errors are encountered in the `before` stage, the `after` stage or during flag resolution. It accepts `hook context` (required), `exception` representing what went wrong (required), and `hook hints` (optional). It has no return value.", + "content": "The `after` stage MUST run after flag resolution occurs. It accepts a `hook context` (required), `flag evaluation details` (required) and `hook hints` (optional). It has no return value.", "RFC 2119 keyword": "MUST", "children": [] }, { "id": "Requirement 4.3.7", "machine_id": "requirement_4_3_7", + "content": "The `error` hook MUST run when errors are encountered in the `before` stage, the `after` stage or during flag resolution. It accepts `hook context` (required), `exception` representing what went wrong (required), and `hook hints` (optional). It has no return value.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 4.3.8", + "machine_id": "requirement_4_3_8", "content": "The `finally` hook MUST run after the `before`, `after`, and `error` stages. It accepts a `hook context` (required) and `hook hints` (optional). There is no return value.", "RFC 2119 keyword": "MUST", "children": [] }, { - "id": "Condition 4.3.8", - "machine_id": "condition_4_3_8", + "id": "Condition 4.3.9", + "machine_id": "condition_4_3_9", "content": "`finally` is a reserved word in the language.", "RFC 2119 keyword": null, "children": [ { - "id": "Conditional Requirement 4.3.8.1", - "machine_id": "conditional_requirement_4_3_8_1", + "id": "Conditional Requirement 4.3.9.1", + "machine_id": "conditional_requirement_4_3_9_1", "content": "Instead of `finally`, `finallyAfter` SHOULD be used.", "RFC 2119 keyword": "SHOULD", "children": [] diff --git a/specification/glossary.md b/specification/glossary.md index d8ff80c6..06efebbf 100644 --- a/specification/glossary.md +++ b/specification/glossary.md @@ -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) @@ -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 + +Feature flag frameworks tend to come in two categories: those designed for use with a single user client application, and those designed for multi-user applications, such as web server applications. Some parts of the OpenFeature specification diverge depending on which paradigm the implementation seeks to adhere to. + +### Dynamic-Context Paradigm + +Server-side use cases 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 something like 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) is provided to flag evaluations + +### Static-Context Paradigm + +In contrast with 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 something like this: + +- 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) +- Functions are exposed on the libraries that signal the cache is no longer valid, and must be reconciled based on a context change. This frequently involves 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. diff --git a/specification/sections/01-flag-evaluation.md b/specification/sections/01-flag-evaluation.md index 5e5a3492..bc0ae2b4 100644 --- a/specification/sections/01-flag-evaluation.md +++ b/specification/sections/01-flag-evaluation.md @@ -130,7 +130,41 @@ 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 + +[![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.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), 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('structured-flag', { text: 'N/A', percentage: 75 }, options); +``` + +See [evaluation context](./03-evaluation-context.md) for details. + +#### Condition 1.3.2 + +> The implementation uses the dynamic-context paradigm. + +see: [dynamic-context paradigm](../glossary.md#dynamic-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), `evaluation context` (optional), and `evaluation options` (optional), which returns the flag value. @@ -150,15 +184,15 @@ MyStruct myStruct = client.getObjectValue('structured-flag', { text: ' See [evaluation context](./03-evaluation-context.md) for details. -#### Condition 1.3.2 +#### Condition 1.3.3 > The implementation language differentiates between floating-point numbers and integers. -##### Conditional Requirement 1.3.2.1 +##### Conditional Requirement 1.3.3.1 > The client **SHOULD** provide functions for floating-point numbers and integers, consistent with language idioms. -```java +``` int getIntValue(String flag, int defaultValue); long getFloatValue(String flag, long defaultValue); @@ -166,7 +200,7 @@ 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. @@ -174,7 +208,41 @@ See [types](../types.md) for details. [![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 + +[![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.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), and `evaluation options` (optional), which returns an `evaluation details` structure. + +```typescript +// example detailed boolean flag evaluation +FlagEvaluationDetails myBoolDetails = client.getBooleanDetails('bool-flag', false); + +// example detailed string flag evaluation +FlagEvaluationDetails myStringDetails = client.getStringDetails('string-flag', 'N/A', options); + +// example detailed number flag evaluation +FlagEvaluationDetails myNumberDetails = client.getNumberDetails('number-flag', 75); + +// example detailed structure flag evaluation +FlagEvaluationDetails myStructDetails = client.getObjectDetails('structured-flag', { text: 'N/A', percentage: 75 }, options); + +``` + +#### Condition 1.4.2 + +> The implementation uses the dynamic-context paradigm. + +see: [dynamic-context paradigm](../glossary.md#dynamic-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), `evaluation context` (optional), and `evaluation options` (optional), which returns an `evaluation details` structure. @@ -193,69 +261,69 @@ FlagEvaluationDetails myStructDetails = client.getObjectDetails 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. diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index 08f75620..d3a0fff9 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -245,8 +245,24 @@ 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) + +Client-focused providers may need a mechanism to understand when their cache of evaluated flags must be invalidated or updated. An `on-context-set` 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 interface **MUST** define an `on context changed` handler, which takes a argument for the previous context, and the newly set context, and which can be optionally implemented to reconcile any stored state pertaining to the global evaluation context. + +Especially in static-context implementations, providers and underlying SDKs my maintain a cache of evaluated flags 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. + +``` + // run the myOnReadyHandler function when the + this.client.addHandler(ProviderEvents.Ready, myOnReadyHandler); ``` Providers may maintain remote connections, timers, threads or other constructs that need to be appropriately disposed of. diff --git a/specification/sections/03-evaluation-context.md b/specification/sections/03-evaluation-context.md index 6c2929c6..7ca2987d 100644 --- a/specification/sections/03-evaluation-context.md +++ b/specification/sections/03-evaluation-context.md @@ -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 + +[![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.1.1 + +> The API **MUST** have a method for supplying `evaluation context`. + +API (global) `evaluation context` can be used to supply data to flag evaluation, such as user name, email, or shopping cart items. + +##### Conditional Requirement 3.2.1.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. + +#### Condition 3.2.2 + +> The implementation uses the dynamic-context paradigm. + +see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm) + +##### Conditional Requirement 3.2.2.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 +#### 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. @@ -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. \ No newline at end of file diff --git a/specification/sections/04-hooks.md b/specification/sections/04-hooks.md index 9d64cc61..de105f24 100644 --- a/specification/sections/04-hooks.md +++ b/specification/sections/04-hooks.md @@ -73,7 +73,29 @@ Hook context exists to provide hooks with information about the invocation. > Hooks **MUST** specify at least one stage. -#### Requirement 4.3.2 +#### Condition 4.3.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 4.3.2.1 + +> The `before` stage **MUST** run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters. It has no return value. + +```typescript +void before(HookContext, HookHints); +``` + +#### Condition 4.3.3 + +> The implementation uses the dynamic-context paradigm. + +see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm) + +##### Conditional Requirement 4.3.3.1 > The `before` stage **MUST** run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters and returns either an `evaluation context` or nothing. @@ -81,33 +103,33 @@ Hook context exists to provide hooks with information about the invocation. EvaluationContext | void before(HookContext, HookHints); ``` -#### Requirement 4.3.3 +#### Requirement 4.3.4 > Any `evaluation context` returned from a `before` hook **MUST** be passed to subsequent `before` hooks (via `HookContext`). -#### Requirement 4.3.4 +#### Requirement 4.3.5 > When `before` hooks have finished executing, any resulting `evaluation context` **MUST** be merged with the existing `evaluation context`. Evaluation context merge order is defined in [Requirement 3.2.2](./03-evaluation-context.md#requirement-322). -#### Requirement 4.3.5 +#### Requirement 4.3.6 > The `after` stage **MUST** run after flag resolution occurs. It accepts a `hook context` (required), `flag evaluation details` (required) and `hook hints` (optional). It has no return value. -#### Requirement 4.3.6 +#### Requirement 4.3.7 > The `error` hook **MUST** run when errors are encountered in the `before` stage, the `after` stage or during flag resolution. It accepts `hook context` (required), `exception` representing what went wrong (required), and `hook hints` (optional). It has no return value. -#### Requirement 4.3.7 +#### Requirement 4.3.8 > The `finally` hook **MUST** run after the `before`, `after`, and `error` stages. It accepts a `hook context` (required) and `hook hints` (optional). There is no return value. -#### Condition 4.3.8 +#### Condition 4.3.9 > `finally` is a reserved word in the language. -##### Conditional Requirement 4.3.8.1 +##### Conditional Requirement 4.3.9.1 > Instead of `finally`, `finallyAfter` **SHOULD** be used. From 9797c401e04fd2fba4bcbce745918654136fd710 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Tue, 10 Jan 2023 15:15:42 -0500 Subject: [PATCH 02/34] fixup: update TOC Signed-off-by: Todd Baert --- specification/glossary.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/specification/glossary.md b/specification/glossary.md index 06efebbf..7464e683 100644 --- a/specification/glossary.md +++ b/specification/glossary.md @@ -40,8 +40,13 @@ This document defines some terms that are used across this specification. - [Fractional Evaluation](#fractional-evaluation) - [Rule](#rule) - [SDK Paradigms](#sdk-paradigms) +<<<<<<< HEAD - [Dynamic-Context Paradigm](#dynamic-context-paradigm) - [Static-Context Paradigm](#static-context-paradigm) +======= + - [Multi-Context Paradigm](#multi-context-paradigm) + - [Single-Context Paradigm](#single-context-paradigm) +>>>>>>> 66bb575 (fixup: update TOC) From 5a0b1cbfabf07c79ce630c6b9850efcd504fff3e Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Tue, 10 Jan 2023 15:28:21 -0500 Subject: [PATCH 03/34] fixup: add links to context-paradigms Signed-off-by: Todd Baert --- specification.json | 4 ++++ specification/sections/01-flag-evaluation.md | 6 ++++++ specification/sections/03-evaluation-context.md | 4 ++++ specification/sections/04-hooks.md | 2 ++ 4 files changed, 16 insertions(+) diff --git a/specification.json b/specification.json index d8b99b07..65a4160f 100644 --- a/specification.json +++ b/specification.json @@ -619,7 +619,11 @@ { "id": "Condition 4.3.3", "machine_id": "condition_4_3_3", +<<<<<<< HEAD "content": "The implementation uses the dynamic-context paradigm.", +======= + "content": "The implementation uses the multi-context paradigm.", +>>>>>>> a6996aa (fixup: add links to context-paradigms) "RFC 2119 keyword": null, "children": [ { diff --git a/specification/sections/01-flag-evaluation.md b/specification/sections/01-flag-evaluation.md index bc0ae2b4..4bc77ba5 100644 --- a/specification/sections/01-flag-evaluation.md +++ b/specification/sections/01-flag-evaluation.md @@ -138,6 +138,8 @@ client.getMetadata().getName(); // "my-client" see: [static-context paradigm](../glossary.md#static-context-paradigm) +see: [single-context paradigm](../glossary.md#single-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), and `evaluation options` (optional), which returns the flag value. @@ -164,6 +166,8 @@ See [evaluation context](./03-evaluation-context.md) for details. see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm) +see: [multi-context paradigm](../glossary.md#multi-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), `evaluation context` (optional), and `evaluation options` (optional), which returns the flag value. @@ -217,6 +221,8 @@ See [types](../types.md) for details. see: [static-context paradigm](../glossary.md#static-context-paradigm) +see: [single-context paradigm](../glossary.md#single-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), and `evaluation options` (optional), which returns an `evaluation details` structure. diff --git a/specification/sections/03-evaluation-context.md b/specification/sections/03-evaluation-context.md index 7ca2987d..bbe2ff01 100644 --- a/specification/sections/03-evaluation-context.md +++ b/specification/sections/03-evaluation-context.md @@ -52,6 +52,8 @@ The key uniquely identifies a field in the `evaluation context` and it should be see: [static-context paradigm](../glossary.md#static-context-paradigm) +see: [single-context paradigm](../glossary.md#single-context-paradigm) + ##### Conditional Requirement 3.2.1.1 > The API **MUST** have a method for supplying `evaluation context`. @@ -70,6 +72,8 @@ In the static-context paradigm, context is global. The client and invocation can see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm) +see: [multi-context paradigm](../glossary.md#multi-context-paradigm) + ##### Conditional Requirement 3.2.2.1 > The API, Client and invocation **MUST** have a method for supplying `evaluation context`. diff --git a/specification/sections/04-hooks.md b/specification/sections/04-hooks.md index de105f24..607c67cb 100644 --- a/specification/sections/04-hooks.md +++ b/specification/sections/04-hooks.md @@ -81,6 +81,8 @@ Hook context exists to provide hooks with information about the invocation. see: [static-context paradigm](../glossary.md#static-context-paradigm) +see: [single-context paradigm](../glossary.md#single-context-paradigm) + ##### Conditional Requirement 4.3.2.1 > The `before` stage **MUST** run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters. It has no return value. From 199b87c99a57a46d8e873f78633b13fc7c214eb2 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Wed, 11 Jan 2023 11:32:21 -0500 Subject: [PATCH 04/34] Update specification/glossary.md Co-authored-by: Skye Gill Signed-off-by: Todd Baert --- specification/glossary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/glossary.md b/specification/glossary.md index 7464e683..48cbf9ba 100644 --- a/specification/glossary.md +++ b/specification/glossary.md @@ -185,7 +185,7 @@ Server-side use cases typically perform flag evaluations on behalf of many users ### Static-Context Paradigm -In contrast with 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 something like this: +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: - an initialization occurs, which fetches evaluated flags in bulk for a given context (user) - the evaluated flags are cached in the library From 7219c589e1dd3c04a24dd9b73d5a9509dd090d18 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Wed, 11 Jan 2023 11:32:31 -0500 Subject: [PATCH 05/34] Update specification/glossary.md Co-authored-by: Skye Gill Signed-off-by: Todd Baert --- specification/glossary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/glossary.md b/specification/glossary.md index 48cbf9ba..7babd949 100644 --- a/specification/glossary.md +++ b/specification/glossary.md @@ -178,7 +178,7 @@ Feature flag frameworks tend to come in two categories: those designed for use w ### Dynamic-Context Paradigm -Server-side use cases 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 something like this: +Server-side use cases 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) is provided to flag evaluations From 91915a1bfded77f8b1cdd077952a3f5e1670ba19 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Wed, 11 Jan 2023 11:33:34 -0500 Subject: [PATCH 06/34] Update specification/sections/01-flag-evaluation.md Co-authored-by: Skye Gill Signed-off-by: Todd Baert --- specification/sections/01-flag-evaluation.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/specification/sections/01-flag-evaluation.md b/specification/sections/01-flag-evaluation.md index 4bc77ba5..ef1891cd 100644 --- a/specification/sections/01-flag-evaluation.md +++ b/specification/sections/01-flag-evaluation.md @@ -24,7 +24,11 @@ It's important that multiple instances of the `API` not be active, so that state #### Requirement 1.1.2.1 +<<<<<<< HEAD > The `API` **MUST** define a `provider mutator`, a function to set the default `provider`, which accepts an API-conformant `provider` implementation. +======= +> The `API` **MUST** expose a `provider mutator`, a function to set the global `provider` singleton, which accepts an API-conformant `provider` implementation. +>>>>>>> 46c03b2 (Update specification/sections/01-flag-evaluation.md) ```typescript // example provider mutator From 4ae0914ee6cfefbb295fa9b3afff96b29566cc4a Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Wed, 11 Jan 2023 11:35:18 -0500 Subject: [PATCH 07/34] Update specification/sections/02-providers.md Co-authored-by: Skye Gill Signed-off-by: Todd Baert --- specification/sections/02-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index d3a0fff9..4fec9b0d 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -255,7 +255,7 @@ Client-focused providers may need a mechanism to understand when their cache of #### Requirement 2.6.1 -> The provider interface **MUST** define an `on context changed` handler, which takes a argument for the previous context, and the newly set context, and which can be optionally implemented to reconcile any stored state pertaining to the global evaluation context. +> The provider interface **MUST** define an `on context changed` handler, which takes an argument for the previous context, and the newly set context, and which can be optionally implemented to reconcile any stored state pertaining to the global evaluation context. Especially in static-context implementations, providers and underlying SDKs my maintain a cache of evaluated flags 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. From a14b868db8cbc3ee65719c07a96261bc3a39a8d5 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Wed, 11 Jan 2023 11:35:48 -0500 Subject: [PATCH 08/34] Update specification/sections/03-evaluation-context.md Co-authored-by: Skye Gill Signed-off-by: Todd Baert --- specification/sections/03-evaluation-context.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/sections/03-evaluation-context.md b/specification/sections/03-evaluation-context.md index bbe2ff01..d7800b45 100644 --- a/specification/sections/03-evaluation-context.md +++ b/specification/sections/03-evaluation-context.md @@ -58,7 +58,7 @@ see: [single-context paradigm](../glossary.md#single-context-paradigm) > The API **MUST** have a method for supplying `evaluation context`. -API (global) `evaluation context` can be used to supply data to flag evaluation, such as user name, email, or shopping cart items. +API (global) `evaluation context` can be used to supply data to flag evaluation, such as (but not limited to) user name, email, or shopping cart items. ##### Conditional Requirement 3.2.1.2 From bdf91884cac76c0d37dd83b8a92b623b2e149ea4 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Wed, 11 Jan 2023 11:52:39 -0500 Subject: [PATCH 09/34] Update specification/sections/02-providers.md Co-authored-by: Skye Gill Signed-off-by: Todd Baert --- specification/sections/02-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index 4fec9b0d..9bfb366c 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -251,7 +251,7 @@ class MyProvider implements Provider, AutoDisposable { [![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental) -Client-focused providers may need a mechanism to understand when their cache of evaluated flags must be invalidated or updated. An `on-context-set` handler can be defined which performs whatever operations are needed to reconcile the evaluated flags with the new context. +Single-context focused providers may need a mechanism to understand when their cache of evaluated flags must be invalidated or updated. An `on-context-set` handler can be defined which performs whatever operations are needed to reconcile the evaluated flags with the new context. #### Requirement 2.6.1 From 44d8a9c29b384389aedff4355e32c215b4d82727 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Wed, 11 Jan 2023 11:54:48 -0500 Subject: [PATCH 10/34] fixup: correct examples, grammer Signed-off-by: Todd Baert --- specification.json | 857 ------------------- specification/glossary.md | 35 +- specification/sections/01-flag-evaluation.md | 2 +- specification/sections/02-providers.md | 14 +- 4 files changed, 46 insertions(+), 862 deletions(-) delete mode 100644 specification.json diff --git a/specification.json b/specification.json deleted file mode 100644 index 65a4160f..00000000 --- a/specification.json +++ /dev/null @@ -1,857 +0,0 @@ -{ - "rules": [ - { - "id": "Requirement 1.1.1", - "machine_id": "requirement_1_1_1", - "content": "The `API`, and any state it maintains SHOULD exist as a global singleton, even in cases wherein multiple versions of the `API` are present at runtime.", - "RFC 2119 keyword": "SHOULD", - "children": [] - }, - { - "id": "Requirement 1.1.2.1", - "machine_id": "requirement_1_1_2_1", - "content": "The `API` MUST define a `provider mutator`, a function to set the default `provider`, which accepts an API-conformant `provider` implementation.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 1.1.2.2", - "machine_id": "requirement_1_1_2_2", - "content": "The `provider mutator` function MUST invoke the `initialize` function on the newly registered provider before using it to resolve flag values.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 1.1.2.3", - "machine_id": "requirement_1_1_2_3", - "content": "The `provider mutator` function MUST invoke the `shutdown` function on the previously registered provider once it's no longer being used to resolve flag values.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 1.1.3", - "machine_id": "requirement_1_1_3", - "content": "The `API` MUST provide a function to bind a given `provider` to one or more client `name`s. If the client-name already has a bound provider, it is overwritten with the new mapping.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 1.1.4", - "machine_id": "requirement_1_1_4", - "content": "The `API` MUST provide a function to add `hooks` which accepts one or more API-conformant `hooks`, and appends them to the collection of any previously added hooks. When new hooks are added, previously added hooks are not removed.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "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`.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 1.1.6", - "machine_id": "requirement_1_1_6", - "content": "The `API` MUST provide a function for creating a `client` which accepts the following options: - name (optional): A logical string identifier for the client.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 1.1.7", - "machine_id": "requirement_1_1_7", - "content": "The client creation function MUST NOT throw, or otherwise abnormally terminate.", - "RFC 2119 keyword": "MUST NOT", - "children": [] - }, - { - "id": "Requirement 1.2.1", - "machine_id": "requirement_1_2_1", - "content": "The client MUST provide a method to add `hooks` which accepts one or more API-conformant `hooks`, and appends them to the collection of any previously added hooks. When new hooks are added, previously added hooks are not removed.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 1.2.2", - "machine_id": "requirement_1_2_2", - "content": "The client interface MUST define a `metadata` member or accessor, containing an immutable `name` field or accessor of type string, which corresponds to the `name` value supplied during client creation.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Condition 1.3.1", - "machine_id": "condition_1_3_1", - "content": "The implementation uses the static-context paradigm.", - "RFC 2119 keyword": null, - "children": [ - { - "id": "Conditional Requirement 1.3.1.1", - "machine_id": "conditional_requirement_1_3_1_1", - "content": "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.", - "RFC 2119 keyword": "MUST", - "children": [] - } - ] - }, - { - "id": "Condition 1.3.2", - "machine_id": "condition_1_3_2", - "content": "The implementation uses the dynamic-context paradigm.", - "RFC 2119 keyword": null, - "children": [ - { - "id": "Conditional Requirement 1.3.2.1", - "machine_id": "conditional_requirement_1_3_2_1", - "content": "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.", - "RFC 2119 keyword": "MUST", - "children": [] - } - ] - }, - { - "id": "Condition 1.3.3", - "machine_id": "condition_1_3_3", - "content": "The implementation language differentiates between floating-point numbers and integers.", - "RFC 2119 keyword": null, - "children": [ - { - "id": "Conditional Requirement 1.3.3.1", - "machine_id": "conditional_requirement_1_3_3_1", - "content": "The client SHOULD provide functions for floating-point numbers and integers, consistent with language idioms.", - "RFC 2119 keyword": "SHOULD", - "children": [] - } - ] - }, - { - "id": "Requirement 1.3.4", - "machine_id": "requirement_1_3_4", - "content": "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.", - "RFC 2119 keyword": "SHOULD", - "children": [] - }, - { - "id": "Condition 1.4.1", - "machine_id": "condition_1_4_1", - "content": "The implementation uses the static-context paradigm.", - "RFC 2119 keyword": null, - "children": [ - { - "id": "Conditional Requirement 1.4.1.1", - "machine_id": "conditional_requirement_1_4_1_1", - "content": "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.", - "RFC 2119 keyword": "MUST", - "children": [] - } - ] - }, - { - "id": "Condition 1.4.2", - "machine_id": "condition_1_4_2", - "content": "The implementation uses the dynamic-context paradigm.", - "RFC 2119 keyword": null, - "children": [ - { - "id": "Conditional Requirement 1.4.2.1", - "machine_id": "conditional_requirement_1_4_2_1", - "content": "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.", - "RFC 2119 keyword": "MUST", - "children": [] - } - ] - }, - { - "id": "Requirement 1.4.3", - "machine_id": "requirement_1_4_3", - "content": "The `evaluation details` structure's `value` field MUST contain the evaluated flag value.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Condition 1.4.4", - "machine_id": "condition_1_4_4", - "content": "The language supports generics (or an equivalent feature).", - "RFC 2119 keyword": null, - "children": [ - { - "id": "Conditional Requirement 1.4.4.1", - "machine_id": "conditional_requirement_1_4_4_1", - "content": "The `evaluation details` 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 1.4.5", - "machine_id": "requirement_1_4_5", - "content": "The `evaluation details` structure's `flag key` field MUST contain the `flag key` argument passed to the detailed flag evaluation method.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 1.4.6", - "machine_id": "requirement_1_4_6", - "content": "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.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 1.4.7", - "machine_id": "requirement_1_4_7", - "content": "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.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 1.4.8", - "machine_id": "requirement_1_4_8", - "content": "In cases of abnormal execution, the `evaluation details` structure's `error code` field MUST contain an `error code`.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 1.4.9", - "machine_id": "requirement_1_4_9", - "content": "In cases of abnormal execution (network failure, unhandled error, etc) the `reason` field in the `evaluation details` SHOULD indicate an error.", - "RFC 2119 keyword": "SHOULD", - "children": [] - }, - { - "id": "Requirement 1.4.10", - "machine_id": "requirement_1_4_10", - "content": "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.", - "RFC 2119 keyword": "MUST NOT", - "children": [] - }, - { - "id": "Requirement 1.4.11", - "machine_id": "requirement_1_4_11", - "content": "In the case of abnormal execution, the client SHOULD log an informative error message.", - "RFC 2119 keyword": "SHOULD", - "children": [] - }, - { - "id": "Requirement 1.4.12", - "machine_id": "requirement_1_4_12", - "content": "The `client` SHOULD provide asynchronous or non-blocking mechanisms for flag evaluation.", - "RFC 2119 keyword": "SHOULD", - "children": [] - }, - { - "id": "Requirement 1.4.13", - "machine_id": "requirement_1_4_13", - "content": "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.", - "RFC 2119 keyword": "MAY", - "children": [] - }, - { - "id": "Requirement 1.4.14", - "machine_id": "requirement_1_4_14", - "content": "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.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Condition 1.4.15", - "machine_id": "condition_1_4_15", - "content": "The implementation language supports a mechanism for marking data as immutable.", - "RFC 2119 keyword": null, - "children": [ - { - "id": "Conditional Requirement 1.4.14.1", - "machine_id": "conditional_requirement_1_4_14_1", - "content": "Condition: `Flag metadata` MUST be immutable.", - "RFC 2119 keyword": "MUST", - "children": [] - } - ] - }, - { - "id": "Requirement 1.5.1", - "machine_id": "requirement_1_5_1", - "content": "The `evaluation options` structure's `hooks` field denotes an ordered collection of hooks that the client MUST execute for the respective flag evaluation, in addition to those already configured.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 1.6.1", - "machine_id": "requirement_1_6_1", - "content": "The API MUST define a mechanism to propagate a shutdown request to active providers.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "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.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 `resolution details` structure.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "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.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.2.3", - "machine_id": "requirement_2_2_3", - "content": "In cases of normal execution, the `provider` MUST populate the `resolution details` structure's `value` field with the resolved flag value.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 2.2.4", - "machine_id": "requirement_2_2_4", - "content": "In cases of normal execution, the `provider` SHOULD populate the `resolution details` structure's `variant` field with a string identifier corresponding to the returned flag value.", - "RFC 2119 keyword": "SHOULD", - "children": [] - }, - { - "id": "Requirement 2.2.5", - "machine_id": "requirement_2_2_5", - "content": "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.", - "RFC 2119 keyword": "SHOULD", - "children": [] - }, - { - "id": "Requirement 2.2.6", - "machine_id": "requirement_2_2_6", - "content": "In cases of normal execution, the `provider` MUST NOT populate the `resolution details` 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.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.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.2.8.1", - "machine_id": "conditional_requirement_2_2_8_1", - "content": "The `resolution details` 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.2.9", - "machine_id": "requirement_2_2_9", - "content": "The `provider` SHOULD populate the `resolution details` structure's `flag metadata` field.", - "RFC 2119 keyword": "SHOULD", - "children": [] - }, - { - "id": "Requirement 2.2.10", - "machine_id": "requirement_2_2_10", - "content": "`flag metadata` MUST be a structure supporting the definition of arbitrary properties, with keys of type `string`, and values of type `boolean | string | number`.", - "RFC 2119 keyword": "MUST", - "children": [] - } - ] - }, - { - "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.3.2", - "machine_id": "requirement_2_3_2", - "content": "In cases of normal execution, the `provider` MUST NOT populate the `resolution details` 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.3.3", - "machine_id": "requirement_2_3_3", - "content": "In cases of abnormal execution, the `resolution details` structure's `error message` field MAY contain a string containing additional detail about the nature of the error.", - "RFC 2119 keyword": "MAY", - "children": [] - }, - { - "id": "Requirement 2.4.1", - "machine_id": "requirement_2_4_1", - "content": "The `provider` MAY define an `initialize` function which accepts the global `evaluation context` as an argument and performs initialization logic relevant to the provider.", - "RFC 2119 keyword": "MAY", - "children": [] - }, - { - "id": "Requirement 2.4.2", - "machine_id": "requirement_2_4_2", - "content": "The `provider` MAY define a `status` field/accessor which indicates the readiness of the provider, with possible values `NOT_READY`, `READY`, or `ERROR`.", - "RFC 2119 keyword": "MAY", - "children": [] - }, - { - "id": "Requirement 2.4.3", - "machine_id": "requirement_2_4_3", - "content": "The provider MUST set its `status` field/accessor to `READY` if its `initialize` function terminates normally.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 2.4.4", - "machine_id": "requirement_2_4_4", - "content": "The provider MUST set its `status` field to `ERROR` if its `initialize` function terminates abnormally.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 2.4.5", - "machine_id": "requirement_2_4_5", - "content": "The provider SHOULD indicate an error if flag resolution is attempted before the provider is ready.", - "RFC 2119 keyword": "SHOULD", - "children": [] - }, - { - "id": "Requirement 2.5.1", - "machine_id": "requirement_2_5_1", - "content": "The provider MAY define a mechanism to gracefully shutdown and dispose of resources.", - "RFC 2119 keyword": "MAY", - "children": [] - }, - { - "id": "Requirement 2.6.1", - "machine_id": "requirement_2_6_1", - "content": "The provider interface MUST define an `on context changed` handler, which takes a argument for the previous context, and the newly set context, and which can be optionally implemented to reconcile any stored state pertaining to the global evaluation context.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 3.1.1", - "machine_id": "requirement_3_1_1", - "content": "The `evaluation context` structure MUST define an optional `targeting key` field of type string, identifying the subject of the flag evaluation.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 3.1.2", - "machine_id": "requirement_3_1_2", - "content": "The evaluation context MUST support the inclusion of custom fields, having keys of type `string`, and values of type `boolean | string | number | datetime | structure`.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 3.1.3", - "machine_id": "requirement_3_1_3", - "content": "The evaluation context MUST support fetching the custom fields by key and also fetching all key value pairs.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 3.1.4", - "machine_id": "requirement_3_1_4", - "content": "The evaluation context fields MUST have an unique key.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Condition 3.2.1", - "machine_id": "condition_3_2_1", - "content": "The implementation uses the static-context paradigm.", - "RFC 2119 keyword": null, - "children": [ - { - "id": "Conditional Requirement 3.2.1.1", - "machine_id": "conditional_requirement_3_2_1_1", - "content": "The API MUST have a method for supplying `evaluation context`.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Conditional Requirement 3.2.1.2", - "machine_id": "conditional_requirement_3_2_1_2", - "content": "The Client and invocation MUST NOT have a method for supplying `evaluation context`.", - "RFC 2119 keyword": "MUST NOT", - "children": [] - } - ] - }, - { - "id": "Condition 3.2.2", - "machine_id": "condition_3_2_2", - "content": "The implementation uses the dynamic-context paradigm.", - "RFC 2119 keyword": null, - "children": [ - { - "id": "Conditional Requirement 3.2.2.1", - "machine_id": "conditional_requirement_3_2_2_1", - "content": "The API, Client and invocation MUST have a method for supplying `evaluation context`.", - "RFC 2119 keyword": "MUST", - "children": [] - } - ] - }, - { - "id": "Requirement 3.2.3", - "machine_id": "requirement_3_2_3", - "content": "Evaluation context MUST be merged in the order: API (global; lowest precedence) - client - invocation - before hooks (highest precedence), with duplicate values being overwritten.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Condition 3.2.4", - "machine_id": "condition_3_2_4", - "content": "The implementation uses the static-context paradigm.", - "RFC 2119 keyword": null, - "children": [ - { - "id": "Requirement 3.2.4.1", - "machine_id": "requirement_3_2_4_1", - "content": "When the global `evaluation context` is set, the `on context changed` handler MUST run.", - "RFC 2119 keyword": "MUST", - "children": [] - } - ] - }, - { - "id": "Requirement 4.1.1", - "machine_id": "requirement_4_1_1", - "content": "Hook context MUST provide: the `flag key`, `flag value type`, `evaluation context`, and the `default value`.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 4.1.2", - "machine_id": "requirement_4_1_2", - "content": "The `hook context` SHOULD provide: access to the `client metadata` and the `provider metadata` fields.", - "RFC 2119 keyword": "SHOULD", - "children": [] - }, - { - "id": "Requirement 4.1.3", - "machine_id": "requirement_4_1_3", - "content": "The `flag key`, `flag type`, and `default value` properties MUST be immutable. If the language does not support immutability, the hook MUST NOT modify these properties.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 4.1.4", - "machine_id": "requirement_4_1_4", - "content": "The evaluation context MUST be mutable only within the `before` hook.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 4.2.1", - "machine_id": "requirement_4_2_1", - "content": "`hook hints` MUST be a structure supports definition of arbitrary properties, with keys of type `string`, and values of type `boolean | string | number | datetime | structure`..", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Condition 4.2.2", - "machine_id": "condition_4_2_2", - "content": "The implementation language supports a mechanism for marking data as immutable.", - "RFC 2119 keyword": null, - "children": [ - { - "id": "Conditional Requirement 4.2.2.1", - "machine_id": "conditional_requirement_4_2_2_1", - "content": "Condition: `Hook hints` MUST be immutable.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Conditional Requirement 4.2.2.2", - "machine_id": "conditional_requirement_4_2_2_2", - "content": "Condition: The client `metadata` field in the `hook context` MUST be immutable.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Conditional Requirement 4.2.2.3", - "machine_id": "conditional_requirement_4_2_2_3", - "content": "Condition: The provider `metadata` field in the `hook context` MUST be immutable.", - "RFC 2119 keyword": "MUST", - "children": [] - } - ] - }, - { - "id": "Requirement 4.3.1", - "machine_id": "requirement_4_3_1", - "content": "Hooks MUST specify at least one stage.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Condition 4.3.2", - "machine_id": "condition_4_3_2", - "content": "The implementation uses the static-context paradigm.", - "RFC 2119 keyword": null, - "children": [ - { - "id": "Conditional Requirement 4.3.2.1", - "machine_id": "conditional_requirement_4_3_2_1", - "content": "The `before` stage MUST run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters. It has no return value.", - "RFC 2119 keyword": "MUST", - "children": [] - } - ] - }, - { - "id": "Condition 4.3.3", - "machine_id": "condition_4_3_3", -<<<<<<< HEAD - "content": "The implementation uses the dynamic-context paradigm.", -======= - "content": "The implementation uses the multi-context paradigm.", ->>>>>>> a6996aa (fixup: add links to context-paradigms) - "RFC 2119 keyword": null, - "children": [ - { - "id": "Conditional Requirement 4.3.3.1", - "machine_id": "conditional_requirement_4_3_3_1", - "content": "The `before` stage MUST run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters and returns either an `evaluation context` or nothing.", - "RFC 2119 keyword": "MUST", - "children": [] - } - ] - }, - { - "id": "Requirement 4.3.4", - "machine_id": "requirement_4_3_4", - "content": "Any `evaluation context` returned from a `before` hook MUST be passed to subsequent `before` hooks (via `HookContext`).", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 4.3.5", - "machine_id": "requirement_4_3_5", - "content": "When `before` hooks have finished executing, any resulting `evaluation context` MUST be merged with the existing `evaluation context`.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 4.3.6", - "machine_id": "requirement_4_3_6", - "content": "The `after` stage MUST run after flag resolution occurs. It accepts a `hook context` (required), `flag evaluation details` (required) and `hook hints` (optional). It has no return value.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 4.3.7", - "machine_id": "requirement_4_3_7", - "content": "The `error` hook MUST run when errors are encountered in the `before` stage, the `after` stage or during flag resolution. It accepts `hook context` (required), `exception` representing what went wrong (required), and `hook hints` (optional). It has no return value.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 4.3.8", - "machine_id": "requirement_4_3_8", - "content": "The `finally` hook MUST run after the `before`, `after`, and `error` stages. It accepts a `hook context` (required) and `hook hints` (optional). There is no return value.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Condition 4.3.9", - "machine_id": "condition_4_3_9", - "content": "`finally` is a reserved word in the language.", - "RFC 2119 keyword": null, - "children": [ - { - "id": "Conditional Requirement 4.3.9.1", - "machine_id": "conditional_requirement_4_3_9_1", - "content": "Instead of `finally`, `finallyAfter` SHOULD be used.", - "RFC 2119 keyword": "SHOULD", - "children": [] - } - ] - }, - { - "id": "Requirement 4.4.1", - "machine_id": "requirement_4_4_1", - "content": "The API, Client, Provider, and invocation MUST have a method for registering hooks.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 4.4.2", - "machine_id": "requirement_4_4_2", - "content": "Hooks MUST be evaluated in the following order: - before: API, Client, Invocation, Provider - after: Provider, Invocation, Client, API - error (if applicable): Provider, Invocation, Client, API - finally: Provider, Invocation, Client, API", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 4.4.3", - "machine_id": "requirement_4_4_3", - "content": "If a `finally` hook abnormally terminates, evaluation MUST proceed, including the execution of any remaining `finally` hooks.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 4.4.4", - "machine_id": "requirement_4_4_4", - "content": "If an `error` hook abnormally terminates, evaluation MUST proceed, including the execution of any remaining `error` hooks.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 4.4.5", - "machine_id": "requirement_4_4_5", - "content": "If an error occurs in the `before` or `after` hooks, the `error` hooks MUST be invoked.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 4.4.6", - "machine_id": "requirement_4_4_6", - "content": "If an error occurs during the evaluation of `before` or `after` hooks, any remaining hooks in the `before` or `after` stages MUST NOT be invoked.", - "RFC 2119 keyword": "MUST NOT", - "children": [] - }, - { - "id": "Requirement 4.4.7", - "machine_id": "requirement_4_4_7", - "content": "If an error occurs in the `before` hooks, the default value MUST be returned.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 4.5.1", - "machine_id": "requirement_4_5_1", - "content": "`Flag evaluation options` MAY contain `hook hints`, a map of data to be provided to hook invocations.", - "RFC 2119 keyword": "MAY", - "children": [] - }, - { - "id": "Requirement 4.5.2", - "machine_id": "requirement_4_5_2", - "content": "`hook hints` MUST be passed to each hook.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 4.5.3", - "machine_id": "requirement_4_5_3", - "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": [] - } - ] -} \ No newline at end of file diff --git a/specification/glossary.md b/specification/glossary.md index 7babd949..17b7aaf2 100644 --- a/specification/glossary.md +++ b/specification/glossary.md @@ -13,6 +13,7 @@ This document defines some terms that are used across this specification. +<<<<<<< HEAD - [Feature Flag](#feature-flag) - [User Roles](#user-roles) - [Application Author](#application-author) @@ -47,6 +48,38 @@ This document defines some terms that are used across this specification. - [Multi-Context Paradigm](#multi-context-paradigm) - [Single-Context Paradigm](#single-context-paradigm) >>>>>>> 66bb575 (fixup: update TOC) +======= +- [Glossary](#glossary) + - [Feature Flag](#feature-flag) + - [User Roles](#user-roles) + - [Application Author](#application-author) + - [Application Integrator](#application-integrator) + - [Provider Author](#provider-author) + - [Integration Author](#integration-author) + - [Library Author](#library-author) + - [Common](#common) + - [Feature Flag SDK](#feature-flag-sdk) + - [Feature Flag API](#feature-flag-api) + - [Evaluation API](#evaluation-api) + - [Flag Management System](#flag-management-system) + - [Provider](#provider) + - [Integration](#integration) + - [Evaluation Context](#evaluation-context) + - [Evaluating Flag Values](#evaluating-flag-values) + - [Resolving Flag Values](#resolving-flag-values) + - [Flagging specifics](#flagging-specifics) + - [Flag](#flag) + - [Flag Key](#flag-key) + - [Variant](#variant) + - [Values](#values) + - [Targeting](#targeting) + - [Targeting Key](#targeting-key) + - [Fractional Evaluation](#fractional-evaluation) + - [Rule](#rule) + - [SDK Paradigms](#sdk-paradigms) + - [Multi-Context Paradigm](#multi-context-paradigm) + - [Single-Context Paradigm](#single-context-paradigm) +>>>>>>> 7610f5a (fixup: correct examples, grammer) @@ -190,6 +223,6 @@ In contrast to server-side or other service-type applications, client side appli - 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) -- Functions are exposed on the libraries that signal the cache is no longer valid, and must be reconciled based on a context change. This frequently involves a network request or I/O operation. +- functions are exposed on the libraries that signal the cache is no longer valid, and must be reconciled based on a context change, 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. diff --git a/specification/sections/01-flag-evaluation.md b/specification/sections/01-flag-evaluation.md index ef1891cd..7cf61fe7 100644 --- a/specification/sections/01-flag-evaluation.md +++ b/specification/sections/01-flag-evaluation.md @@ -200,7 +200,7 @@ See [evaluation context](./03-evaluation-context.md) for details. > The client **SHOULD** provide functions for floating-point numbers and integers, consistent with language idioms. -``` +```java int getIntValue(String flag, int defaultValue); long getFloatValue(String flag, long defaultValue); diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index 9bfb366c..97ba5b8c 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -260,9 +260,17 @@ Single-context focused providers may need a mechanism to understand when their c Especially in static-context implementations, providers and underlying SDKs my maintain a cache of evaluated flags 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. -``` - // run the myOnReadyHandler function when the - this.client.addHandler(ProviderEvents.Ready, myOnReadyHandler); +```java +// MyProvider implementation of the onContextSet function defined in Provider +class MyProvider implements Provider { + //... + + onContextSet(EvaluationContext oldContext, EvaluationContext newContext): void { + // update context-sensitive cached flags, or otherwise react to the change in the global context + } + + //... +} ``` Providers may maintain remote connections, timers, threads or other constructs that need to be appropriately disposed of. From 0cd329e19fbee8f4563593741e0e5d2a98c0b413 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Wed, 11 Jan 2023 12:08:22 -0500 Subject: [PATCH 11/34] fixup: update toc Signed-off-by: Todd Baert --- specification/glossary.md | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/specification/glossary.md b/specification/glossary.md index 17b7aaf2..a91c09b7 100644 --- a/specification/glossary.md +++ b/specification/glossary.md @@ -13,7 +13,6 @@ This document defines some terms that are used across this specification. -<<<<<<< HEAD - [Feature Flag](#feature-flag) - [User Roles](#user-roles) - [Application Author](#application-author) @@ -41,45 +40,8 @@ This document defines some terms that are used across this specification. - [Fractional Evaluation](#fractional-evaluation) - [Rule](#rule) - [SDK Paradigms](#sdk-paradigms) -<<<<<<< HEAD - [Dynamic-Context Paradigm](#dynamic-context-paradigm) - [Static-Context Paradigm](#static-context-paradigm) -======= - - [Multi-Context Paradigm](#multi-context-paradigm) - - [Single-Context Paradigm](#single-context-paradigm) ->>>>>>> 66bb575 (fixup: update TOC) -======= -- [Glossary](#glossary) - - [Feature Flag](#feature-flag) - - [User Roles](#user-roles) - - [Application Author](#application-author) - - [Application Integrator](#application-integrator) - - [Provider Author](#provider-author) - - [Integration Author](#integration-author) - - [Library Author](#library-author) - - [Common](#common) - - [Feature Flag SDK](#feature-flag-sdk) - - [Feature Flag API](#feature-flag-api) - - [Evaluation API](#evaluation-api) - - [Flag Management System](#flag-management-system) - - [Provider](#provider) - - [Integration](#integration) - - [Evaluation Context](#evaluation-context) - - [Evaluating Flag Values](#evaluating-flag-values) - - [Resolving Flag Values](#resolving-flag-values) - - [Flagging specifics](#flagging-specifics) - - [Flag](#flag) - - [Flag Key](#flag-key) - - [Variant](#variant) - - [Values](#values) - - [Targeting](#targeting) - - [Targeting Key](#targeting-key) - - [Fractional Evaluation](#fractional-evaluation) - - [Rule](#rule) - - [SDK Paradigms](#sdk-paradigms) - - [Multi-Context Paradigm](#multi-context-paradigm) - - [Single-Context Paradigm](#single-context-paradigm) ->>>>>>> 7610f5a (fixup: correct examples, grammer) From 5cc744ae1249a137f099a21a52be7936d5477fa0 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Mon, 16 Jan 2023 14:54:41 -0500 Subject: [PATCH 12/34] fixup: reduce number thrashing in 1.1 Signed-off-by: Todd Baert --- specification/sections/01-flag-evaluation.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specification/sections/01-flag-evaluation.md b/specification/sections/01-flag-evaluation.md index 7cf61fe7..d5eced0e 100644 --- a/specification/sections/01-flag-evaluation.md +++ b/specification/sections/01-flag-evaluation.md @@ -66,7 +66,7 @@ see: [shutdown](./02-providers.md#25-shutdown), [setting a provider](#setting-a- OpenFeature.setProvider("client-name", new MyProvider()); ``` -#### Requirement 1.1.4 +#### Requirement 1.1.3 > The `API` **MUST** provide a function to add `hooks` which accepts one or more API-conformant `hooks`, and appends them to the collection of any previously added hooks. When new hooks are added, previously added hooks are not removed. @@ -77,7 +77,7 @@ OpenFeature.addHooks([new MyHook()]); See [hooks](./04-hooks.md) for details. -#### Requirement 1.1.5 +#### Requirement 1.1.4 > The `API` **MUST** provide a function for retrieving the metadata field of the configured `provider`. @@ -88,7 +88,7 @@ OpenFeature.getProviderMetadata(); See [provider](./02-providers.md) for details. -#### Requirement 1.1.6 +#### Requirement 1.1.5 > The `API` **MUST** provide a function for creating a `client` which accepts the following options: > @@ -103,7 +103,7 @@ OpenFeature.getClient({ The name is a logical identifier for the client. -#### Requirement 1.1.7 +#### Requirement 1.1.6 > The client creation function **MUST NOT** throw, or otherwise abnormally terminate. From f66e5c98ce207aa4f54a47f8604fe7c5ecfd0c77 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Tue, 24 Jan 2023 15:20:46 -0500 Subject: [PATCH 13/34] fixup: pr feedback - improve shutdown description, add links - single/multi context -> static/dynamic context - fixed ambility in onContextSet description Signed-off-by: Todd Baert --- specification.json | 908 ++++++++++++++++++ specification/glossary.md | 2 +- specification/sections/01-flag-evaluation.md | 10 - specification/sections/02-providers.md | 2 +- .../sections/03-evaluation-context.md | 4 - specification/sections/04-hooks.md | 2 - 6 files changed, 910 insertions(+), 18 deletions(-) create mode 100644 specification.json diff --git a/specification.json b/specification.json new file mode 100644 index 00000000..8097ae4d --- /dev/null +++ b/specification.json @@ -0,0 +1,908 @@ +{ + "rules": [ + { + "id": "Requirement 1.1.1", + "machine_id": "requirement_1_1_1", + "content": "The `API`, and any state it maintains SHOULD exist as a global singleton, even in cases wherein multiple versions of the `API` are present at runtime.", + "RFC 2119 keyword": "SHOULD", + "children": [] + }, + { +<<<<<<< HEAD +<<<<<<< HEAD + "id": "Requirement 1.1.2.1", + "machine_id": "requirement_1_1_2_1", + "content": "The `API` MUST define a `provider mutator`, a function to set the default `provider`, which accepts an API-conformant `provider` implementation.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.1.2.2", + "machine_id": "requirement_1_1_2_2", + "content": "The `provider mutator` function MUST invoke the `initialize` function on the newly registered provider before using it to resolve flag values.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.1.2.3", + "machine_id": "requirement_1_1_2_3", + "content": "The `provider mutator` function MUST invoke the `shutdown` function on the previously registered provider once it's no longer being used to resolve flag values.", +======= + "id": "Requirement 1.1.2", + "machine_id": "requirement_1_1_2", +======= + "id": "Requirement 1.1.2.1", + "machine_id": "requirement_1_1_2_1", +>>>>>>> f2b30ad (fixup: reduce number thrashing in 1.1) + "content": "The `API` MUST expose a `provider mutator`, a function to set the global `provider` singleton, which accepts an API-conformant `provider` implementation.", +>>>>>>> 7610f5a (fixup: correct examples, grammer) + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.1.2.2", + "machine_id": "requirement_1_1_2_2", + "content": "The `provider mutator` function MUST run the `initialize` function on the newly registered provider.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.1.3", + "machine_id": "requirement_1_1_3", +<<<<<<< HEAD + "content": "The `API` MUST provide a function to bind a given `provider` to one or more client `name`s. If the client-name already has a bound provider, it is overwritten with the new mapping.", +======= + "content": "The `API` MUST provide a function to add `hooks` which accepts one or more API-conformant `hooks`, and appends them to the collection of any previously added hooks. When new hooks are added, previously added hooks are not removed.", +>>>>>>> f2b30ad (fixup: reduce number thrashing in 1.1) + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.1.4", + "machine_id": "requirement_1_1_4", + "content": "The API MUST provide a function for retrieving the metadata field of the configured `provider`.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.1.5", + "machine_id": "requirement_1_1_5", +<<<<<<< HEAD + "content": "The `API` MUST provide a function for retrieving the metadata field of the configured `provider`.", +======= + "content": "The `API` MUST provide a function for creating a `client` which accepts the following options: - name (optional): A logical string identifier for the client.", +>>>>>>> f2b30ad (fixup: reduce number thrashing in 1.1) + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.1.6", + "machine_id": "requirement_1_1_6", + "content": "The client creation function MUST NOT throw, or otherwise abnormally terminate.", + "RFC 2119 keyword": "MUST NOT", + "children": [] + }, + { + "id": "Requirement 1.2.1", + "machine_id": "requirement_1_2_1", + "content": "The client MUST provide a method to add `hooks` which accepts one or more API-conformant `hooks`, and appends them to the collection of any previously added hooks. When new hooks are added, previously added hooks are not removed.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.2.2", + "machine_id": "requirement_1_2_2", + "content": "The client interface MUST define a `metadata` member or accessor, containing an immutable `name` field or accessor of type string, which corresponds to the `name` value supplied during client creation.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Condition 1.3.1", + "machine_id": "condition_1_3_1", + "content": "The implementation uses the static-context paradigm.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 1.3.1.1", + "machine_id": "conditional_requirement_1_3_1_1", + "content": "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.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] + }, + { + "id": "Condition 1.3.2", + "machine_id": "condition_1_3_2", + "content": "The implementation uses the dynamic-context paradigm.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 1.3.2.1", + "machine_id": "conditional_requirement_1_3_2_1", + "content": "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.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] + }, + { + "id": "Condition 1.3.3", + "machine_id": "condition_1_3_3", + "content": "The implementation language differentiates between floating-point numbers and integers.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 1.3.3.1", + "machine_id": "conditional_requirement_1_3_3_1", + "content": "The client SHOULD provide functions for floating-point numbers and integers, consistent with language idioms.", + "RFC 2119 keyword": "SHOULD", + "children": [] + } + ] + }, + { + "id": "Requirement 1.3.4", + "machine_id": "requirement_1_3_4", + "content": "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.", + "RFC 2119 keyword": "SHOULD", + "children": [] + }, + { + "id": "Condition 1.4.1", + "machine_id": "condition_1_4_1", + "content": "The implementation uses the static-context paradigm.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 1.4.1.1", + "machine_id": "conditional_requirement_1_4_1_1", + "content": "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.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] + }, + { + "id": "Condition 1.4.2", + "machine_id": "condition_1_4_2", + "content": "The implementation uses the dynamic-context paradigm.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 1.4.2.1", + "machine_id": "conditional_requirement_1_4_2_1", + "content": "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.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] + }, + { + "id": "Requirement 1.4.3", + "machine_id": "requirement_1_4_3", + "content": "The `evaluation details` structure's `value` field MUST contain the evaluated flag value.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Condition 1.4.4", + "machine_id": "condition_1_4_4", + "content": "The language supports generics (or an equivalent feature).", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 1.4.4.1", + "machine_id": "conditional_requirement_1_4_4_1", + "content": "The `evaluation details` 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 1.4.5", + "machine_id": "requirement_1_4_5", + "content": "The `evaluation details` structure's `flag key` field MUST contain the `flag key` argument passed to the detailed flag evaluation method.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.4.6", + "machine_id": "requirement_1_4_6", + "content": "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.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.4.7", + "machine_id": "requirement_1_4_7", + "content": "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.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.4.8", + "machine_id": "requirement_1_4_8", + "content": "In cases of abnormal execution, the `evaluation details` structure's `error code` field MUST contain an `error code`.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.4.9", + "machine_id": "requirement_1_4_9", + "content": "In cases of abnormal execution (network failure, unhandled error, etc) the `reason` field in the `evaluation details` SHOULD indicate an error.", + "RFC 2119 keyword": "SHOULD", + "children": [] + }, + { + "id": "Requirement 1.4.10", + "machine_id": "requirement_1_4_10", + "content": "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.", + "RFC 2119 keyword": "MUST NOT", + "children": [] + }, + { + "id": "Requirement 1.4.11", + "machine_id": "requirement_1_4_11", + "content": "In the case of abnormal execution, the client SHOULD log an informative error message.", + "RFC 2119 keyword": "SHOULD", + "children": [] + }, + { + "id": "Requirement 1.4.12", + "machine_id": "requirement_1_4_12", + "content": "The `client` SHOULD provide asynchronous or non-blocking mechanisms for flag evaluation.", + "RFC 2119 keyword": "SHOULD", + "children": [] + }, + { + "id": "Requirement 1.4.13", + "machine_id": "requirement_1_4_13", + "content": "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.", + "RFC 2119 keyword": "MAY", + "children": [] + }, + { + "id": "Requirement 1.4.14", + "machine_id": "requirement_1_4_14", + "content": "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.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Condition 1.4.15", + "machine_id": "condition_1_4_15", + "content": "The implementation language supports a mechanism for marking data as immutable.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 1.4.14.1", + "machine_id": "conditional_requirement_1_4_14_1", + "content": "Condition: `Flag metadata` MUST be immutable.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] + }, + { + "id": "Requirement 1.5.1", + "machine_id": "requirement_1_5_1", + "content": "The `evaluation options` structure's `hooks` field denotes an ordered collection of hooks that the client MUST execute for the respective flag evaluation, in addition to those already configured.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.6.1", + "machine_id": "requirement_1_6_1", +<<<<<<< HEAD + "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` or `dispose` function, which, when called, must call the respective `shutdown`/`dispose` function on the active provider.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.6.2", + "machine_id": "requirement_1_6_2", + "content": "When the API's `provider mutator` is invoked, the `shutdown`/`dispose` function on the active provider MUST run, if defined.", +>>>>>>> bab5359 (fixup: pr feedback) + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "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.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 `resolution details` structure.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "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.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.2.3", + "machine_id": "requirement_2_2_3", + "content": "In cases of normal execution, the `provider` MUST populate the `resolution details` structure's `value` field with the resolved flag value.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 2.2.4", + "machine_id": "requirement_2_2_4", + "content": "In cases of normal execution, the `provider` SHOULD populate the `resolution details` structure's `variant` field with a string identifier corresponding to the returned flag value.", + "RFC 2119 keyword": "SHOULD", + "children": [] + }, + { + "id": "Requirement 2.2.5", + "machine_id": "requirement_2_2_5", + "content": "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.", + "RFC 2119 keyword": "SHOULD", + "children": [] + }, + { + "id": "Requirement 2.2.6", + "machine_id": "requirement_2_2_6", + "content": "In cases of normal execution, the `provider` MUST NOT populate the `resolution details` 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.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.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.2.8.1", + "machine_id": "conditional_requirement_2_2_8_1", + "content": "The `resolution details` 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.2.9", + "machine_id": "requirement_2_2_9", + "content": "The `provider` SHOULD populate the `resolution details` structure's `flag metadata` field.", + "RFC 2119 keyword": "SHOULD", + "children": [] + }, + { + "id": "Requirement 2.2.10", + "machine_id": "requirement_2_2_10", + "content": "`flag metadata` MUST be a structure supporting the definition of arbitrary properties, with keys of type `string`, and values of type `boolean | string | number`.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] + }, + { + "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.3.2", + "machine_id": "requirement_2_3_2", + "content": "In cases of normal execution, the `provider` MUST NOT populate the `resolution details` 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.3.3", + "machine_id": "requirement_2_3_3", + "content": "In cases of abnormal execution, the `resolution details` structure's `error message` field MAY contain a string containing additional detail about the nature of the error.", + "RFC 2119 keyword": "MAY", + "children": [] + }, + { + "id": "Requirement 2.4.1", + "machine_id": "requirement_2_4_1", +<<<<<<< HEAD +<<<<<<< HEAD + "content": "The `provider` MAY define an `initialize` function which accepts the global `evaluation context` as an argument and performs initialization logic relevant to the provider.", + "RFC 2119 keyword": "MAY", + "children": [] + }, + { + "id": "Requirement 2.4.2", + "machine_id": "requirement_2_4_2", + "content": "The `provider` MAY define a `status` field/accessor which indicates the readiness of the provider, with possible values `NOT_READY`, `READY`, or `ERROR`.", + "RFC 2119 keyword": "MAY", + "children": [] + }, + { + "id": "Requirement 2.4.3", + "machine_id": "requirement_2_4_3", + "content": "The provider MUST set its `status` field/accessor to `READY` if its `initialize` function terminates normally.", +======= + "content": "The `provider` interface MUST define an `initialize` function which accepts the global `evaluation context` as a argument, and which can be optionally implemented to perform initialization logic relevant to the provider.", +>>>>>>> 534aa3b (fixup: add more non-normative info on init) +======= + "content": "The `provider` interface MUST define an optional `initialize` function, which accepts the global `evaluation context` as an argument and performs initialization logic relevant to the provider.", +>>>>>>> bab5359 (fixup: pr feedback) + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 2.4.4", + "machine_id": "requirement_2_4_4", + "content": "The provider MUST set its `status` field to `ERROR` if its `initialize` function terminates abnormally.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 2.4.5", + "machine_id": "requirement_2_4_5", + "content": "The provider SHOULD indicate an error if flag resolution is attempted before the provider is ready.", + "RFC 2119 keyword": "SHOULD", + "children": [] + }, + { + "id": "Requirement 2.5.1", + "machine_id": "requirement_2_5_1", +<<<<<<< HEAD + "content": "The provider MAY define a `shutdown` function to perform whatever cleanup is necessary for the implementation.", + "RFC 2119 keyword": "MAY", +======= + "content": "The provider interface MUST define an `on context set` handler, which takes an argument for the previous context, and the newly set context, and which can be optionally implemented to reconcile any stored state pertaining to the global evaluation context.", + "RFC 2119 keyword": "MUST", +>>>>>>> 7610f5a (fixup: correct examples, grammer) + "children": [] + }, + { + "id": "Requirement 2.6.1", + "machine_id": "requirement_2_6_1", + "content": "The provider interface MUST define an `on context changed` handler, which takes a argument for the previous context, and the newly set context, and which can be optionally implemented to reconcile any stored state pertaining to the global evaluation context.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 3.1.1", + "machine_id": "requirement_3_1_1", + "content": "The `evaluation context` structure MUST define an optional `targeting key` field of type string, identifying the subject of the flag evaluation.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 3.1.2", + "machine_id": "requirement_3_1_2", + "content": "The evaluation context MUST support the inclusion of custom fields, having keys of type `string`, and values of type `boolean | string | number | datetime | structure`.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 3.1.3", + "machine_id": "requirement_3_1_3", + "content": "The evaluation context MUST support fetching the custom fields by key and also fetching all key value pairs.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 3.1.4", + "machine_id": "requirement_3_1_4", + "content": "The evaluation context fields MUST have an unique key.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Condition 3.2.1", + "machine_id": "condition_3_2_1", + "content": "The implementation uses the static-context paradigm.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 3.2.1.1", + "machine_id": "conditional_requirement_3_2_1_1", + "content": "The API MUST have a method for supplying `evaluation context`.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Conditional Requirement 3.2.1.2", + "machine_id": "conditional_requirement_3_2_1_2", + "content": "The Client and invocation MUST NOT have a method for supplying `evaluation context`.", + "RFC 2119 keyword": "MUST NOT", + "children": [] + } + ] + }, + { + "id": "Condition 3.2.2", + "machine_id": "condition_3_2_2", + "content": "The implementation uses the dynamic-context paradigm.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 3.2.2.1", + "machine_id": "conditional_requirement_3_2_2_1", + "content": "The API, Client and invocation MUST have a method for supplying `evaluation context`.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] + }, + { + "id": "Requirement 3.2.3", + "machine_id": "requirement_3_2_3", + "content": "Evaluation context MUST be merged in the order: API (global; lowest precedence) - client - invocation - before hooks (highest precedence), with duplicate values being overwritten.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Condition 3.2.4", + "machine_id": "condition_3_2_4", + "content": "The implementation uses the static-context paradigm.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Requirement 3.2.4.1", + "machine_id": "requirement_3_2_4_1", + "content": "When the global `evaluation context` is set, the `on context changed` handler MUST run.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] + }, + { + "id": "Requirement 4.1.1", + "machine_id": "requirement_4_1_1", + "content": "Hook context MUST provide: the `flag key`, `flag value type`, `evaluation context`, and the `default value`.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 4.1.2", + "machine_id": "requirement_4_1_2", + "content": "The `hook context` SHOULD provide: access to the `client metadata` and the `provider metadata` fields.", + "RFC 2119 keyword": "SHOULD", + "children": [] + }, + { + "id": "Requirement 4.1.3", + "machine_id": "requirement_4_1_3", + "content": "The `flag key`, `flag type`, and `default value` properties MUST be immutable. If the language does not support immutability, the hook MUST NOT modify these properties.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 4.1.4", + "machine_id": "requirement_4_1_4", + "content": "The evaluation context MUST be mutable only within the `before` hook.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 4.2.1", + "machine_id": "requirement_4_2_1", + "content": "`hook hints` MUST be a structure supports definition of arbitrary properties, with keys of type `string`, and values of type `boolean | string | number | datetime | structure`..", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Condition 4.2.2", + "machine_id": "condition_4_2_2", + "content": "The implementation language supports a mechanism for marking data as immutable.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 4.2.2.1", + "machine_id": "conditional_requirement_4_2_2_1", + "content": "Condition: `Hook hints` MUST be immutable.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Conditional Requirement 4.2.2.2", + "machine_id": "conditional_requirement_4_2_2_2", + "content": "Condition: The client `metadata` field in the `hook context` MUST be immutable.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Conditional Requirement 4.2.2.3", + "machine_id": "conditional_requirement_4_2_2_3", + "content": "Condition: The provider `metadata` field in the `hook context` MUST be immutable.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] + }, + { + "id": "Requirement 4.3.1", + "machine_id": "requirement_4_3_1", + "content": "Hooks MUST specify at least one stage.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Condition 4.3.2", + "machine_id": "condition_4_3_2", + "content": "The implementation uses the static-context paradigm.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 4.3.2.1", + "machine_id": "conditional_requirement_4_3_2_1", + "content": "The `before` stage MUST run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters. It has no return value.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] + }, + { + "id": "Condition 4.3.3", + "machine_id": "condition_4_3_3", +<<<<<<< HEAD +<<<<<<< HEAD + "content": "The implementation uses the dynamic-context paradigm.", +======= + "content": "The implementation uses the multi-context paradigm.", +>>>>>>> a6996aa (fixup: add links to context-paradigms) +======= + "content": "The implementation uses the dynamic-context paradigm.", +>>>>>>> bab5359 (fixup: pr feedback) + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 4.3.3.1", + "machine_id": "conditional_requirement_4_3_3_1", + "content": "The `before` stage MUST run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters and returns either an `evaluation context` or nothing.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] + }, + { + "id": "Requirement 4.3.4", + "machine_id": "requirement_4_3_4", + "content": "Any `evaluation context` returned from a `before` hook MUST be passed to subsequent `before` hooks (via `HookContext`).", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 4.3.5", + "machine_id": "requirement_4_3_5", + "content": "When `before` hooks have finished executing, any resulting `evaluation context` MUST be merged with the existing `evaluation context`.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 4.3.6", + "machine_id": "requirement_4_3_6", + "content": "The `after` stage MUST run after flag resolution occurs. It accepts a `hook context` (required), `flag evaluation details` (required) and `hook hints` (optional). It has no return value.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 4.3.7", + "machine_id": "requirement_4_3_7", + "content": "The `error` hook MUST run when errors are encountered in the `before` stage, the `after` stage or during flag resolution. It accepts `hook context` (required), `exception` representing what went wrong (required), and `hook hints` (optional). It has no return value.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 4.3.8", + "machine_id": "requirement_4_3_8", + "content": "The `finally` hook MUST run after the `before`, `after`, and `error` stages. It accepts a `hook context` (required) and `hook hints` (optional). There is no return value.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Condition 4.3.9", + "machine_id": "condition_4_3_9", + "content": "`finally` is a reserved word in the language.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 4.3.9.1", + "machine_id": "conditional_requirement_4_3_9_1", + "content": "Instead of `finally`, `finallyAfter` SHOULD be used.", + "RFC 2119 keyword": "SHOULD", + "children": [] + } + ] + }, + { + "id": "Requirement 4.4.1", + "machine_id": "requirement_4_4_1", + "content": "The API, Client, Provider, and invocation MUST have a method for registering hooks.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 4.4.2", + "machine_id": "requirement_4_4_2", + "content": "Hooks MUST be evaluated in the following order: - before: API, Client, Invocation, Provider - after: Provider, Invocation, Client, API - error (if applicable): Provider, Invocation, Client, API - finally: Provider, Invocation, Client, API", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 4.4.3", + "machine_id": "requirement_4_4_3", + "content": "If a `finally` hook abnormally terminates, evaluation MUST proceed, including the execution of any remaining `finally` hooks.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 4.4.4", + "machine_id": "requirement_4_4_4", + "content": "If an `error` hook abnormally terminates, evaluation MUST proceed, including the execution of any remaining `error` hooks.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 4.4.5", + "machine_id": "requirement_4_4_5", + "content": "If an error occurs in the `before` or `after` hooks, the `error` hooks MUST be invoked.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 4.4.6", + "machine_id": "requirement_4_4_6", + "content": "If an error occurs during the evaluation of `before` or `after` hooks, any remaining hooks in the `before` or `after` stages MUST NOT be invoked.", + "RFC 2119 keyword": "MUST NOT", + "children": [] + }, + { + "id": "Requirement 4.4.7", + "machine_id": "requirement_4_4_7", + "content": "If an error occurs in the `before` hooks, the default value MUST be returned.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 4.5.1", + "machine_id": "requirement_4_5_1", + "content": "`Flag evaluation options` MAY contain `hook hints`, a map of data to be provided to hook invocations.", + "RFC 2119 keyword": "MAY", + "children": [] + }, + { + "id": "Requirement 4.5.2", + "machine_id": "requirement_4_5_2", + "content": "`hook hints` MUST be passed to each hook.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 4.5.3", + "machine_id": "requirement_4_5_3", + "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", +<<<<<<< HEAD + "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.", +======= + "content": "The provider MUST define a mechanism for signalling the occurrence of a set of events, including `PROVIDER_READY`, `PROVIDER_ERROR`, `PROVIDER_CONFIGURATION_CHANGED` and `PROVIDER_SHUTDOWN`.", +>>>>>>> 7610f5a (fixup: correct examples, grammer) + "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": [] + } + ] +} \ No newline at end of file diff --git a/specification/glossary.md b/specification/glossary.md index a91c09b7..01828246 100644 --- a/specification/glossary.md +++ b/specification/glossary.md @@ -169,7 +169,7 @@ A rule is some criteria that's used to determine which variant a particular cont ## SDK Paradigms -Feature flag frameworks tend to come in two categories: those designed for use with a single user client application, and those designed for multi-user applications, such as web server applications. Some parts of the OpenFeature specification diverge depending on which paradigm the implementation seeks to adhere to. +Feature flag frameworks have SDKs which operate in two distinct paradigms: those designed for use with a single user client application, and those designed for multi-user applications, such as web server applications. Some parts of the OpenFeature specification diverge depending on which paradigm the implementation seeks to adhere to. ### Dynamic-Context Paradigm diff --git a/specification/sections/01-flag-evaluation.md b/specification/sections/01-flag-evaluation.md index d5eced0e..4e834d38 100644 --- a/specification/sections/01-flag-evaluation.md +++ b/specification/sections/01-flag-evaluation.md @@ -24,11 +24,7 @@ It's important that multiple instances of the `API` not be active, so that state #### Requirement 1.1.2.1 -<<<<<<< HEAD > The `API` **MUST** define a `provider mutator`, a function to set the default `provider`, which accepts an API-conformant `provider` implementation. -======= -> The `API` **MUST** expose a `provider mutator`, a function to set the global `provider` singleton, which accepts an API-conformant `provider` implementation. ->>>>>>> 46c03b2 (Update specification/sections/01-flag-evaluation.md) ```typescript // example provider mutator @@ -142,8 +138,6 @@ client.getMetadata().getName(); // "my-client" see: [static-context paradigm](../glossary.md#static-context-paradigm) -see: [single-context paradigm](../glossary.md#single-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), and `evaluation options` (optional), which returns the flag value. @@ -170,8 +164,6 @@ See [evaluation context](./03-evaluation-context.md) for details. see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm) -see: [multi-context paradigm](../glossary.md#multi-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), `evaluation context` (optional), and `evaluation options` (optional), which returns the flag value. @@ -225,8 +217,6 @@ See [types](../types.md) for details. see: [static-context paradigm](../glossary.md#static-context-paradigm) -see: [single-context paradigm](../glossary.md#single-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), and `evaluation options` (optional), which returns an `evaluation details` structure. diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index 97ba5b8c..36220ecd 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -251,7 +251,7 @@ class MyProvider implements Provider, AutoDisposable { [![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental) -Single-context focused providers may need a mechanism to understand when their cache of evaluated flags must be invalidated or updated. An `on-context-set` handler can be defined which performs whatever operations are needed to reconcile the evaluated flags with the new context. +Static-context focused providers may need a mechanism to understand when their cache of evaluated flags must be invalidated or updated. An `on-context-set` handler can be defined which performs whatever operations are needed to reconcile the evaluated flags with the new context. #### Requirement 2.6.1 diff --git a/specification/sections/03-evaluation-context.md b/specification/sections/03-evaluation-context.md index d7800b45..5303bf35 100644 --- a/specification/sections/03-evaluation-context.md +++ b/specification/sections/03-evaluation-context.md @@ -52,8 +52,6 @@ The key uniquely identifies a field in the `evaluation context` and it should be see: [static-context paradigm](../glossary.md#static-context-paradigm) -see: [single-context paradigm](../glossary.md#single-context-paradigm) - ##### Conditional Requirement 3.2.1.1 > The API **MUST** have a method for supplying `evaluation context`. @@ -72,8 +70,6 @@ In the static-context paradigm, context is global. The client and invocation can see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm) -see: [multi-context paradigm](../glossary.md#multi-context-paradigm) - ##### Conditional Requirement 3.2.2.1 > The API, Client and invocation **MUST** have a method for supplying `evaluation context`. diff --git a/specification/sections/04-hooks.md b/specification/sections/04-hooks.md index 607c67cb..de105f24 100644 --- a/specification/sections/04-hooks.md +++ b/specification/sections/04-hooks.md @@ -81,8 +81,6 @@ Hook context exists to provide hooks with information about the invocation. see: [static-context paradigm](../glossary.md#static-context-paradigm) -see: [single-context paradigm](../glossary.md#single-context-paradigm) - ##### Conditional Requirement 4.3.2.1 > The `before` stage **MUST** run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters. It has no return value. From 6b8ef21898bd5ddc5eaa63ee5d28a61c1b599951 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Wed, 25 Jan 2023 14:41:11 -0500 Subject: [PATCH 14/34] fixup: add STALE reason Signed-off-by: Todd Baert --- specification/sections/02-providers.md | 2 +- specification/types.md | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index 36220ecd..52396415 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -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. diff --git a/specification/types.md b/specification/types.md index c4482dca..ff6ce34d 100644 --- a/specification/types.md +++ b/specification/types.md @@ -55,16 +55,17 @@ A structure which contains a subset of the fields defined in the `evaluation det A set of pre-defined reasons is enumerated below: -| Reason | Explanation | -| --------------- |-------------------------------------------------------------------------------------------------------| -| STATIC | The resolved value is static (no dynamic evaluation). | -| DEFAULT | The resolved value fell back to a pre-configured value (no dynamic evaluation occurred or dynamic evaluation yielded no result). | -| TARGETING_MATCH | The resolved value was the result of a dynamic evaluation, such as a rule or specific user-targeting. | -| SPLIT | The resolved value was the result of pseudorandom assignment. | -| CACHED | The resolved value was retrieved from cache. | -| DISABLED | The resolved value was the result of the flag being disabled in the management system. | -| UNKNOWN | The reason for the resolved value could not be determined. | -| ERROR | The resolved value was the result of an error. | +| Reason | Explanation | +| --------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| STATIC | The resolved value is static (no dynamic evaluation). | +| DEFAULT | The resolved value fell back to a pre-configured value (no dynamic evaluation occurred or dynamic evaluation yielded no result). | +| TARGETING_MATCH | The resolved value was the result of a dynamic evaluation, such as a rule or specific user-targeting. | +| SPLIT | The resolved value was the result of pseudorandom assignment. | +| CACHED | The resolved value was retrieved from cache. | +| DISABLED | The resolved value was the result of the flag being disabled in the management system. | +| UNKNOWN | The reason for the resolved value could not be determined. | +| STALE | The resolved value is non-authoritative or possible out of date | +| ERROR | The resolved value was the result of an error. | > NOTE: The `resolution details` structure is not exposed to the Application Author. It defines the data which Provider Authors must return when resolving the value of flags. From 76c2f8e9d09ac1f855eb464a7a9309bcf33ee6d7 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Thu, 9 Mar 2023 08:40:20 -0500 Subject: [PATCH 15/34] Update specification/types.md Co-authored-by: Justin Abrahms Signed-off-by: Todd Baert --- specification/types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/types.md b/specification/types.md index ff6ce34d..a509cfd1 100644 --- a/specification/types.md +++ b/specification/types.md @@ -64,7 +64,7 @@ A set of pre-defined reasons is enumerated below: | CACHED | The resolved value was retrieved from cache. | | DISABLED | The resolved value was the result of the flag being disabled in the management system. | | UNKNOWN | The reason for the resolved value could not be determined. | -| STALE | The resolved value is non-authoritative or possible out of date | +| STALE | The resolved value is non-authoritative or possibly out of date | | ERROR | The resolved value was the result of an error. | > NOTE: The `resolution details` structure is not exposed to the Application Author. It defines the data which Provider Authors must return when resolving the value of flags. From c64a7caaa5c57a996267e68aa2e39739f32a51e1 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Thu, 9 Mar 2023 08:40:33 -0500 Subject: [PATCH 16/34] Update specification/sections/02-providers.md Co-authored-by: Justin Abrahms Signed-off-by: Todd Baert --- specification/sections/02-providers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index 52396415..a73f2b04 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -257,7 +257,7 @@ Static-context focused providers may need a mechanism to understand when their c > The provider interface **MUST** define an `on context changed` handler, which takes an argument for the previous context, and the newly set context, and which can be optionally implemented to reconcile any stored state pertaining to the global evaluation context. -Especially in static-context implementations, providers and underlying SDKs my maintain a cache of evaluated flags for a particular context. +Especially in static-context implementations, providers and underlying SDKs may maintain a cache of evaluated flags 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. ```java @@ -265,7 +265,7 @@ The `on context changed` handler provides a mechanism to update this state, ofte class MyProvider implements Provider { //... - onContextSet(EvaluationContext oldContext, EvaluationContext newContext): void { + onContextChanged(EvaluationContext oldContext, EvaluationContext newContext): void { // update context-sensitive cached flags, or otherwise react to the change in the global context } From 947b822eac80ac556e88a0974a8d20a7772511c3 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Thu, 9 Mar 2023 08:47:56 -0500 Subject: [PATCH 17/34] Update specification/glossary.md Co-authored-by: Justin Abrahms Signed-off-by: Todd Baert --- specification/glossary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/glossary.md b/specification/glossary.md index 01828246..4cf1bda6 100644 --- a/specification/glossary.md +++ b/specification/glossary.md @@ -169,7 +169,7 @@ A rule is some criteria that's used to determine which variant a particular cont ## SDK Paradigms -Feature flag frameworks have SDKs which operate in two distinct paradigms: those designed for use with a single user client application, and those designed for multi-user applications, such as web server applications. Some parts of the OpenFeature specification diverge depending on which paradigm the implementation seeks to adhere to. +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), 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 From 045772d6937a191b59fd16f4e9317415c9abb6f6 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Thu, 9 Mar 2023 09:14:26 -0500 Subject: [PATCH 18/34] Update specification/sections/02-providers.md Signed-off-by: Todd Baert --- specification/sections/02-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index a73f2b04..0c5cfd4c 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -255,7 +255,7 @@ Static-context focused providers may need a mechanism to understand when their c #### Requirement 2.6.1 -> The provider interface **MUST** define an `on context changed` handler, which takes an argument for the previous context, and the newly set context, and which can be optionally implemented to reconcile any stored state pertaining to the global evaluation context. +> The provider **MAY** define an `on context changed` handler, which takes an argument for the previous context and the newly set context, and reconciles any stored state pertaining to the global evaluation context. Especially in static-context implementations, providers and underlying SDKs may maintain a cache of evaluated flags 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. From ad676f2e4e3cd7f62cd331334681c6ccd2b7571d Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Thu, 18 May 2023 15:44:36 -0400 Subject: [PATCH 19/34] fixup: json Signed-off-by: Todd Baert --- specification.json | 69 +++++----------------------------------------- 1 file changed, 7 insertions(+), 62 deletions(-) diff --git a/specification.json b/specification.json index 8097ae4d..bf7fdceb 100644 --- a/specification.json +++ b/specification.json @@ -8,8 +8,6 @@ "children": [] }, { -<<<<<<< HEAD -<<<<<<< HEAD "id": "Requirement 1.1.2.1", "machine_id": "requirement_1_1_2_1", "content": "The `API` MUST define a `provider mutator`, a function to set the default `provider`, which accepts an API-conformant `provider` implementation.", @@ -27,51 +25,34 @@ "id": "Requirement 1.1.2.3", "machine_id": "requirement_1_1_2_3", "content": "The `provider mutator` function MUST invoke the `shutdown` function on the previously registered provider once it's no longer being used to resolve flag values.", -======= - "id": "Requirement 1.1.2", - "machine_id": "requirement_1_1_2", -======= - "id": "Requirement 1.1.2.1", - "machine_id": "requirement_1_1_2_1", ->>>>>>> f2b30ad (fixup: reduce number thrashing in 1.1) - "content": "The `API` MUST expose a `provider mutator`, a function to set the global `provider` singleton, which accepts an API-conformant `provider` implementation.", ->>>>>>> 7610f5a (fixup: correct examples, grammer) "RFC 2119 keyword": "MUST", "children": [] }, { - "id": "Requirement 1.1.2.2", - "machine_id": "requirement_1_1_2_2", - "content": "The `provider mutator` function MUST run the `initialize` function on the newly registered provider.", + "id": "Requirement 1.1.3", + "machine_id": "requirement_1_1_3", + "content": "The `API` MUST provide a function to bind a given `provider` to one or more client `name`s. If the client-name already has a bound provider, it is overwritten with the new mapping.", "RFC 2119 keyword": "MUST", "children": [] }, { "id": "Requirement 1.1.3", "machine_id": "requirement_1_1_3", -<<<<<<< HEAD - "content": "The `API` MUST provide a function to bind a given `provider` to one or more client `name`s. If the client-name already has a bound provider, it is overwritten with the new mapping.", -======= "content": "The `API` MUST provide a function to add `hooks` which accepts one or more API-conformant `hooks`, and appends them to the collection of any previously added hooks. When new hooks are added, previously added hooks are not removed.", ->>>>>>> f2b30ad (fixup: reduce number thrashing in 1.1) "RFC 2119 keyword": "MUST", "children": [] }, { "id": "Requirement 1.1.4", "machine_id": "requirement_1_1_4", - "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`.", "RFC 2119 keyword": "MUST", "children": [] }, { "id": "Requirement 1.1.5", "machine_id": "requirement_1_1_5", -<<<<<<< HEAD - "content": "The `API` MUST provide a function for retrieving the metadata field of the configured `provider`.", -======= "content": "The `API` MUST provide a function for creating a `client` which accepts the following options: - name (optional): A logical string identifier for the client.", ->>>>>>> f2b30ad (fixup: reduce number thrashing in 1.1) "RFC 2119 keyword": "MUST", "children": [] }, @@ -295,18 +276,7 @@ { "id": "Requirement 1.6.1", "machine_id": "requirement_1_6_1", -<<<<<<< HEAD "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` or `dispose` function, which, when called, must call the respective `shutdown`/`dispose` function on the active provider.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 1.6.2", - "machine_id": "requirement_1_6_2", - "content": "When the API's `provider mutator` is invoked, the `shutdown`/`dispose` function on the active provider MUST run, if defined.", ->>>>>>> bab5359 (fixup: pr feedback) "RFC 2119 keyword": "MUST", "children": [] }, @@ -356,7 +326,7 @@ { "id": "Requirement 2.2.5", "machine_id": "requirement_2_2_5", - "content": "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.", + "content": "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.", "RFC 2119 keyword": "SHOULD", "children": [] }, @@ -427,8 +397,6 @@ { "id": "Requirement 2.4.1", "machine_id": "requirement_2_4_1", -<<<<<<< HEAD -<<<<<<< HEAD "content": "The `provider` MAY define an `initialize` function which accepts the global `evaluation context` as an argument and performs initialization logic relevant to the provider.", "RFC 2119 keyword": "MAY", "children": [] @@ -444,12 +412,6 @@ "id": "Requirement 2.4.3", "machine_id": "requirement_2_4_3", "content": "The provider MUST set its `status` field/accessor to `READY` if its `initialize` function terminates normally.", -======= - "content": "The `provider` interface MUST define an `initialize` function which accepts the global `evaluation context` as a argument, and which can be optionally implemented to perform initialization logic relevant to the provider.", ->>>>>>> 534aa3b (fixup: add more non-normative info on init) -======= - "content": "The `provider` interface MUST define an optional `initialize` function, which accepts the global `evaluation context` as an argument and performs initialization logic relevant to the provider.", ->>>>>>> bab5359 (fixup: pr feedback) "RFC 2119 keyword": "MUST", "children": [] }, @@ -470,20 +432,15 @@ { "id": "Requirement 2.5.1", "machine_id": "requirement_2_5_1", -<<<<<<< HEAD "content": "The provider MAY define a `shutdown` function to perform whatever cleanup is necessary for the implementation.", "RFC 2119 keyword": "MAY", -======= - "content": "The provider interface MUST define an `on context set` handler, which takes an argument for the previous context, and the newly set context, and which can be optionally implemented to reconcile any stored state pertaining to the global evaluation context.", - "RFC 2119 keyword": "MUST", ->>>>>>> 7610f5a (fixup: correct examples, grammer) "children": [] }, { "id": "Requirement 2.6.1", "machine_id": "requirement_2_6_1", - "content": "The provider interface MUST define an `on context changed` handler, which takes a argument for the previous context, and the newly set context, and which can be optionally implemented to reconcile any stored state pertaining to the global evaluation context.", - "RFC 2119 keyword": "MUST", + "content": "The provider MAY define an `on context changed` handler, which takes an argument for the previous context and the newly set context, and reconciles any stored state pertaining to the global evaluation context.", + "RFC 2119 keyword": "MAY", "children": [] }, { @@ -662,15 +619,7 @@ { "id": "Condition 4.3.3", "machine_id": "condition_4_3_3", -<<<<<<< HEAD -<<<<<<< HEAD - "content": "The implementation uses the dynamic-context paradigm.", -======= - "content": "The implementation uses the multi-context paradigm.", ->>>>>>> a6996aa (fixup: add links to context-paradigms) -======= "content": "The implementation uses the dynamic-context paradigm.", ->>>>>>> bab5359 (fixup: pr feedback) "RFC 2119 keyword": null, "children": [ { @@ -805,7 +754,6 @@ { "id": "Requirement 5.1.1", "machine_id": "requirement_5_1_1", -<<<<<<< HEAD "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": [] @@ -814,9 +762,6 @@ "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.", -======= - "content": "The provider MUST define a mechanism for signalling the occurrence of a set of events, including `PROVIDER_READY`, `PROVIDER_ERROR`, `PROVIDER_CONFIGURATION_CHANGED` and `PROVIDER_SHUTDOWN`.", ->>>>>>> 7610f5a (fixup: correct examples, grammer) "RFC 2119 keyword": "MUST", "children": [] }, From 29d812cf859386354269f6c48b35ccd08e84c4bb Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Thu, 18 May 2023 15:49:33 -0400 Subject: [PATCH 20/34] fixup: duped number Signed-off-by: Todd Baert --- specification.json | 16 ++++++++-------- specification/sections/01-flag-evaluation.md | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/specification.json b/specification.json index bf7fdceb..c43b62e9 100644 --- a/specification.json +++ b/specification.json @@ -36,29 +36,29 @@ "children": [] }, { - "id": "Requirement 1.1.3", - "machine_id": "requirement_1_1_3", + "id": "Requirement 1.1.4", + "machine_id": "requirement_1_1_4", "content": "The `API` MUST provide a function to add `hooks` which accepts one or more API-conformant `hooks`, and appends them to the collection of any previously added hooks. When new hooks are added, previously added hooks are not removed.", "RFC 2119 keyword": "MUST", "children": [] }, { - "id": "Requirement 1.1.4", - "machine_id": "requirement_1_1_4", + "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`.", "RFC 2119 keyword": "MUST", "children": [] }, { - "id": "Requirement 1.1.5", - "machine_id": "requirement_1_1_5", + "id": "Requirement 1.1.6", + "machine_id": "requirement_1_1_6", "content": "The `API` MUST provide a function for creating a `client` which accepts the following options: - name (optional): A logical string identifier for the client.", "RFC 2119 keyword": "MUST", "children": [] }, { - "id": "Requirement 1.1.6", - "machine_id": "requirement_1_1_6", + "id": "Requirement 1.1.7", + "machine_id": "requirement_1_1_7", "content": "The client creation function MUST NOT throw, or otherwise abnormally terminate.", "RFC 2119 keyword": "MUST NOT", "children": [] diff --git a/specification/sections/01-flag-evaluation.md b/specification/sections/01-flag-evaluation.md index 4e834d38..6aa3725c 100644 --- a/specification/sections/01-flag-evaluation.md +++ b/specification/sections/01-flag-evaluation.md @@ -62,7 +62,7 @@ see: [shutdown](./02-providers.md#25-shutdown), [setting a provider](#setting-a- OpenFeature.setProvider("client-name", new MyProvider()); ``` -#### Requirement 1.1.3 +#### Requirement 1.1.4 > The `API` **MUST** provide a function to add `hooks` which accepts one or more API-conformant `hooks`, and appends them to the collection of any previously added hooks. When new hooks are added, previously added hooks are not removed. @@ -73,7 +73,7 @@ OpenFeature.addHooks([new MyHook()]); See [hooks](./04-hooks.md) for details. -#### Requirement 1.1.4 +#### Requirement 1.1.5 > The `API` **MUST** provide a function for retrieving the metadata field of the configured `provider`. @@ -84,7 +84,7 @@ OpenFeature.getProviderMetadata(); See [provider](./02-providers.md) for details. -#### Requirement 1.1.5 +#### Requirement 1.1.6 > The `API` **MUST** provide a function for creating a `client` which accepts the following options: > @@ -99,7 +99,7 @@ OpenFeature.getClient({ The name is a logical identifier for the client. -#### Requirement 1.1.6 +#### Requirement 1.1.7 > The client creation function **MUST NOT** throw, or otherwise abnormally terminate. From b762b346a1ffd3be3560a2a9e83e4d206d3031f2 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 19 May 2023 11:25:51 -0400 Subject: [PATCH 21/34] fixup: re-order so dynamic context is first Signed-off-by: Todd Baert --- specification.json | 46 ++++++++--------- specification/sections/01-flag-evaluation.md | 49 +++++++++---------- .../sections/03-evaluation-context.md | 28 +++++------ specification/sections/04-hooks.md | 24 ++++----- 4 files changed, 73 insertions(+), 74 deletions(-) diff --git a/specification.json b/specification.json index c43b62e9..7b289b7c 100644 --- a/specification.json +++ b/specification.json @@ -80,13 +80,13 @@ { "id": "Condition 1.3.1", "machine_id": "condition_1_3_1", - "content": "The implementation uses the static-context paradigm.", + "content": "The implementation uses the dynamic-context paradigm.", "RFC 2119 keyword": null, "children": [ { "id": "Conditional Requirement 1.3.1.1", "machine_id": "conditional_requirement_1_3_1_1", - "content": "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.", + "content": "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.", "RFC 2119 keyword": "MUST", "children": [] } @@ -95,13 +95,13 @@ { "id": "Condition 1.3.2", "machine_id": "condition_1_3_2", - "content": "The implementation uses the dynamic-context paradigm.", + "content": "The implementation uses the static-context paradigm.", "RFC 2119 keyword": null, "children": [ { "id": "Conditional Requirement 1.3.2.1", "machine_id": "conditional_requirement_1_3_2_1", - "content": "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.", + "content": "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.", "RFC 2119 keyword": "MUST", "children": [] } @@ -132,13 +132,13 @@ { "id": "Condition 1.4.1", "machine_id": "condition_1_4_1", - "content": "The implementation uses the static-context paradigm.", + "content": "The implementation uses the dynamic-context paradigm.", "RFC 2119 keyword": null, "children": [ { "id": "Conditional Requirement 1.4.1.1", "machine_id": "conditional_requirement_1_4_1_1", - "content": "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.", + "content": "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.", "RFC 2119 keyword": "MUST", "children": [] } @@ -147,13 +147,13 @@ { "id": "Condition 1.4.2", "machine_id": "condition_1_4_2", - "content": "The implementation uses the dynamic-context paradigm.", + "content": "The implementation uses the static-context paradigm.", "RFC 2119 keyword": null, "children": [ { "id": "Conditional Requirement 1.4.2.1", "machine_id": "conditional_requirement_1_4_2_1", - "content": "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.", + "content": "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.", "RFC 2119 keyword": "MUST", "children": [] } @@ -474,37 +474,37 @@ { "id": "Condition 3.2.1", "machine_id": "condition_3_2_1", - "content": "The implementation uses the static-context paradigm.", + "content": "The implementation uses the dynamic-context paradigm.", "RFC 2119 keyword": null, "children": [ { "id": "Conditional Requirement 3.2.1.1", "machine_id": "conditional_requirement_3_2_1_1", - "content": "The API MUST have a method for supplying `evaluation context`.", + "content": "The API, Client and invocation MUST have a method for supplying `evaluation context`.", "RFC 2119 keyword": "MUST", "children": [] - }, - { - "id": "Conditional Requirement 3.2.1.2", - "machine_id": "conditional_requirement_3_2_1_2", - "content": "The Client and invocation MUST NOT have a method for supplying `evaluation context`.", - "RFC 2119 keyword": "MUST NOT", - "children": [] } ] }, { "id": "Condition 3.2.2", "machine_id": "condition_3_2_2", - "content": "The implementation uses the dynamic-context paradigm.", + "content": "The implementation uses the static-context paradigm.", "RFC 2119 keyword": null, "children": [ { "id": "Conditional Requirement 3.2.2.1", "machine_id": "conditional_requirement_3_2_2_1", - "content": "The API, Client and invocation MUST have a method for supplying `evaluation context`.", + "content": "The API MUST have a method for supplying `evaluation context`.", "RFC 2119 keyword": "MUST", "children": [] + }, + { + "id": "Conditional Requirement 3.2.2.2", + "machine_id": "conditional_requirement_3_2_2_2", + "content": "The Client and invocation MUST NOT have a method for supplying `evaluation context`.", + "RFC 2119 keyword": "MUST NOT", + "children": [] } ] }, @@ -604,13 +604,13 @@ { "id": "Condition 4.3.2", "machine_id": "condition_4_3_2", - "content": "The implementation uses the static-context paradigm.", + "content": "The implementation uses the dynamic-context paradigm.", "RFC 2119 keyword": null, "children": [ { "id": "Conditional Requirement 4.3.2.1", "machine_id": "conditional_requirement_4_3_2_1", - "content": "The `before` stage MUST run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters. It has no return value.", + "content": "The `before` stage MUST run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters and returns either an `evaluation context` or nothing.", "RFC 2119 keyword": "MUST", "children": [] } @@ -619,13 +619,13 @@ { "id": "Condition 4.3.3", "machine_id": "condition_4_3_3", - "content": "The implementation uses the dynamic-context paradigm.", + "content": "The implementation uses the static-context paradigm.", "RFC 2119 keyword": null, "children": [ { "id": "Conditional Requirement 4.3.3.1", "machine_id": "conditional_requirement_4_3_3_1", - "content": "The `before` stage MUST run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters and returns either an `evaluation context` or nothing.", + "content": "The `before` stage MUST run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters. It has no return value.", "RFC 2119 keyword": "MUST", "children": [] } diff --git a/specification/sections/01-flag-evaluation.md b/specification/sections/01-flag-evaluation.md index 6aa3725c..785292c7 100644 --- a/specification/sections/01-flag-evaluation.md +++ b/specification/sections/01-flag-evaluation.md @@ -132,54 +132,54 @@ client.getMetadata().getName(); // "my-client" #### Condition 1.3.1 -[![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. +> The implementation uses the dynamic-context paradigm. -see: [static-context paradigm](../glossary.md#static-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), and `evaluation options` (optional), which returns the flag value. +> 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. ```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); +string myString = client.getStringValue('string-flag', 'N/A', evaluationContext, options); // example number flag evaluation number myNumber = client.getNumberValue('number-flag', 75); // example overloaded structure flag evaluation with optional params -MyStruct myStruct = client.getObjectValue('structured-flag', { text: 'N/A', percentage: 75 }, options); +MyStruct myStruct = client.getObjectValue('structured-flag', { text: 'N/A', percentage: 75 }, evaluationContext, options); ``` See [evaluation context](./03-evaluation-context.md) for details. #### Condition 1.3.2 -> The implementation uses the dynamic-context paradigm. +[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental) -see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm) +> 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), `evaluation context` (optional), and `evaluation options` (optional), which returns the flag value. +> 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', evaluationContext, options); +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('structured-flag', { text: 'N/A', percentage: 75 }, evaluationContext, options); +MyStruct myStruct = client.getObjectValue('structured-flag', { text: 'N/A', percentage: 75 }, options); ``` See [evaluation context](./03-evaluation-context.md) for details. @@ -208,56 +208,55 @@ See [types](../types.md) for details. [![hardening](https://img.shields.io/static/v1?label=Status&message=hardening&color=yellow)](https://github.com/open-feature/spec/tree/main/specification#hardening) - #### Condition 1.4.1 -[![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. +> The implementation uses the dynamic-context paradigm. -see: [static-context paradigm](../glossary.md#static-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), and `evaluation options` (optional), which returns an `evaluation details` structure. +> 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. ```typescript // example detailed boolean flag evaluation FlagEvaluationDetails myBoolDetails = client.getBooleanDetails('bool-flag', false); // example detailed string flag evaluation -FlagEvaluationDetails myStringDetails = client.getStringDetails('string-flag', 'N/A', options); +FlagEvaluationDetails myStringDetails = client.getStringDetails('string-flag', 'N/A', evaluationContext, options); // example detailed number flag evaluation FlagEvaluationDetails myNumberDetails = client.getNumberDetails('number-flag', 75); // example detailed structure flag evaluation -FlagEvaluationDetails myStructDetails = client.getObjectDetails('structured-flag', { text: 'N/A', percentage: 75 }, options); +FlagEvaluationDetails myStructDetails = client.getObjectDetails('structured-flag', { text: 'N/A', percentage: 75 }, evaluationContext, options); ``` #### Condition 1.4.2 -> The implementation uses the dynamic-context paradigm. +[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental) -see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm) +> 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), `evaluation context` (optional), and `evaluation options` (optional), which returns an `evaluation details` structure. +> 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. ```typescript // example detailed boolean flag evaluation FlagEvaluationDetails myBoolDetails = client.getBooleanDetails('bool-flag', false); // example detailed string flag evaluation -FlagEvaluationDetails myStringDetails = client.getStringDetails('string-flag', 'N/A', evaluationContext, options); +FlagEvaluationDetails myStringDetails = client.getStringDetails('string-flag', 'N/A', options); // example detailed number flag evaluation FlagEvaluationDetails myNumberDetails = client.getNumberDetails('number-flag', 75); // example detailed structure flag evaluation -FlagEvaluationDetails myStructDetails = client.getObjectDetails('structured-flag', { text: 'N/A', percentage: 75 }, evaluationContext, options); +FlagEvaluationDetails myStructDetails = client.getObjectDetails('structured-flag', { text: 'N/A', percentage: 75 }, options); ``` diff --git a/specification/sections/03-evaluation-context.md b/specification/sections/03-evaluation-context.md index 5303bf35..0ece4fc1 100644 --- a/specification/sections/03-evaluation-context.md +++ b/specification/sections/03-evaluation-context.md @@ -46,36 +46,36 @@ The key uniquely identifies a field in the `evaluation context` and it should be #### 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. + +#### 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.1.1 +##### Conditional Requirement 3.2.2.1 > The API **MUST** have a method for supplying `evaluation context`. API (global) `evaluation context` can be used to supply data to flag evaluation, such as (but not limited to) user name, email, or shopping cart items. -##### Conditional Requirement 3.2.1.2 +##### 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. -#### Condition 3.2.2 - -> The implementation uses the dynamic-context paradigm. - -see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm) - -##### Conditional Requirement 3.2.2.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.3 > Evaluation context **MUST** be merged in the order: API (global; lowest precedence) -> client -> invocation -> before hooks (highest precedence), with duplicate values being overwritten. diff --git a/specification/sections/04-hooks.md b/specification/sections/04-hooks.md index de105f24..890a314b 100644 --- a/specification/sections/04-hooks.md +++ b/specification/sections/04-hooks.md @@ -75,32 +75,32 @@ Hook context exists to provide hooks with information about the invocation. #### Condition 4.3.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. +> The implementation uses the dynamic-context paradigm. -see: [static-context paradigm](../glossary.md#static-context-paradigm) +see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm) ##### Conditional Requirement 4.3.2.1 -> The `before` stage **MUST** run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters. It has no return value. +> The `before` stage **MUST** run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters and returns either an `evaluation context` or nothing. -```typescript -void before(HookContext, HookHints); +```java +EvaluationContext | void before(HookContext hookContext, HookHints hints); ``` #### Condition 4.3.3 -> The implementation uses the dynamic-context paradigm. +[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental) -see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm) +> The implementation uses the static-context paradigm. + +see: [static-context paradigm](../glossary.md#static-context-paradigm) ##### Conditional Requirement 4.3.3.1 -> The `before` stage **MUST** run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters and returns either an `evaluation context` or nothing. +> The `before` stage **MUST** run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters. It has no return value. -```typescript -EvaluationContext | void before(HookContext, HookHints); +```java +void before(HookContext hookContext, HookHints hints); ``` #### Requirement 4.3.4 From b12bb9617f26bedfc20aa78c865955fe627ae2ce Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 26 May 2023 13:54:21 -0400 Subject: [PATCH 22/34] Update specification/glossary.md Co-authored-by: Pete Hodgson Signed-off-by: Todd Baert --- specification/glossary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/glossary.md b/specification/glossary.md index 4cf1bda6..993b619e 100644 --- a/specification/glossary.md +++ b/specification/glossary.md @@ -169,7 +169,7 @@ A rule is some criteria that's used to determine which variant a particular cont ## SDK Paradigms -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), and those designed for multi-user applications, such as web server applications. Some parts of the OpenFeature specification diverge depending on the paradigm. +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 From 5add151e0bb76bccbb6edc010537154acbe31829 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 26 May 2023 13:54:33 -0400 Subject: [PATCH 23/34] Update specification/glossary.md Co-authored-by: Pete Hodgson Signed-off-by: Todd Baert --- specification/glossary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/glossary.md b/specification/glossary.md index 993b619e..c5f75a14 100644 --- a/specification/glossary.md +++ b/specification/glossary.md @@ -173,7 +173,7 @@ Feature flag frameworks have SDKs which operate in two distinct paradigms: those ### Dynamic-Context Paradigm -Server-side use cases 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: +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) is provided to flag evaluations From 624b765e1091994f9ca88cae668500e61eabbefc Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 26 May 2023 13:55:01 -0400 Subject: [PATCH 24/34] Update specification/glossary.md Co-authored-by: Pete Hodgson Signed-off-by: Todd Baert --- specification/glossary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/glossary.md b/specification/glossary.md index c5f75a14..ec8c24b2 100644 --- a/specification/glossary.md +++ b/specification/glossary.md @@ -185,6 +185,6 @@ In contrast to server-side or other service-type applications, client side appli - 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) -- functions are exposed on the libraries that signal the cache is no longer valid, and must be reconciled based on a context change, frequently involving a network request or I/O operation +- 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. From fb8d6b064b72e0e8a6abc389753162b2666ce9de Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 26 May 2023 13:55:47 -0400 Subject: [PATCH 25/34] Update specification/sections/02-providers.md Co-authored-by: Pete Hodgson Signed-off-by: Todd Baert --- specification/sections/02-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index 0c5cfd4c..c02c7840 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -255,7 +255,7 @@ Static-context focused providers may need a mechanism to understand when their c #### 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, and reconciles any stored state pertaining to the global evaluation context. +> 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 a cache of evaluated flags 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. From a568c0784fc09189ead3e57b2ca770632bfd944b Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 26 May 2023 13:59:37 -0400 Subject: [PATCH 26/34] Update specification/glossary.md Co-authored-by: Jonathan Norris Signed-off-by: Todd Baert --- specification/glossary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/glossary.md b/specification/glossary.md index ec8c24b2..5d96931a 100644 --- a/specification/glossary.md +++ b/specification/glossary.md @@ -176,7 +176,7 @@ Feature flag frameworks have SDKs which operate in two distinct paradigms: those 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) is provided to flag evaluations +- with each request or event, relevant dynamic context (for example, user session data, unique user identifiers) is provided to flag evaluations ### Static-Context Paradigm From 9b7cb59229596f11c26747cf5ee274368801d179 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 26 May 2023 13:59:39 -0400 Subject: [PATCH 27/34] fixup: sync json after suggestions Signed-off-by: Todd Baert --- specification.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification.json b/specification.json index 7b289b7c..ef00cbd3 100644 --- a/specification.json +++ b/specification.json @@ -439,7 +439,7 @@ { "id": "Requirement 2.6.1", "machine_id": "requirement_2_6_1", - "content": "The provider MAY define an `on context changed` handler, which takes an argument for the previous context and the newly set context, and reconciles any stored state pertaining to the global evaluation context.", + "content": "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.", "RFC 2119 keyword": "MAY", "children": [] }, From 3252176e10a08d916fe39af7b22b22f8f59c932c Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 26 May 2023 14:07:11 -0400 Subject: [PATCH 28/34] fixup: on context set -> changed Signed-off-by: Todd Baert --- specification/sections/02-providers.md | 4 ++-- specification/sections/03-evaluation-context.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index c02c7840..4bd62023 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -251,7 +251,7 @@ class MyProvider implements Provider, AutoDisposable { [![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-set` handler can be defined which performs whatever operations are needed to reconcile the evaluated flags with the new context. +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 @@ -261,7 +261,7 @@ Especially in static-context implementations, providers and underlying SDKs may 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. ```java -// MyProvider implementation of the onContextSet function defined in Provider +// MyProvider implementation of the onContextChanged function defined in Provider class MyProvider implements Provider { //... diff --git a/specification/sections/03-evaluation-context.md b/specification/sections/03-evaluation-context.md index 0ece4fc1..96e668a7 100644 --- a/specification/sections/03-evaluation-context.md +++ b/specification/sections/03-evaluation-context.md @@ -68,7 +68,7 @@ see: [static-context paradigm](../glossary.md#static-context-paradigm) > The API **MUST** have a method for supplying `evaluation context`. -API (global) `evaluation context` can be used to supply data to flag evaluation, such as (but not limited to) user name, email, or shopping cart items. +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 From 55dcfba7f7f9ee6c973709bb82e219492fb2acce Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Tue, 4 Jul 2023 14:20:19 -0400 Subject: [PATCH 29/34] fixup: 261 normative text Signed-off-by: Todd Baert --- specification/sections/02-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index 4bd62023..41e6f37a 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -257,7 +257,7 @@ Static-context focused providers may need a mechanism to understand when their c > 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 a cache of evaluated flags for a particular context. +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. ```java From 74dbeb64264b079baaef6bc2fc67fd3e0d7aecd6 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Tue, 4 Jul 2023 14:25:25 -0400 Subject: [PATCH 30/34] fixup: json Signed-off-by: Todd Baert --- specification.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specification.json b/specification.json index ef00cbd3..0ab10dcc 100644 --- a/specification.json +++ b/specification.json @@ -276,7 +276,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 mechanism to propagate a shutdown request to active providers.", "RFC 2119 keyword": "MUST", "children": [] }, @@ -432,7 +432,7 @@ { "id": "Requirement 2.5.1", "machine_id": "requirement_2_5_1", - "content": "The provider MAY define a `shutdown` function to perform whatever cleanup is necessary for the implementation.", + "content": "The provider MAY define a mechanism to gracefully shutdown and dispose of resources.", "RFC 2119 keyword": "MAY", "children": [] }, From 4fa217877428ad28b9343325bc3ec560264f2f1c Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 14 Jul 2023 08:49:25 -0400 Subject: [PATCH 31/34] Update specification/sections/03-evaluation-context.md Co-authored-by: Kavindu Dodanduwa Signed-off-by: Todd Baert --- specification/sections/03-evaluation-context.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/sections/03-evaluation-context.md b/specification/sections/03-evaluation-context.md index 96e668a7..1cbfc3c3 100644 --- a/specification/sections/03-evaluation-context.md +++ b/specification/sections/03-evaluation-context.md @@ -66,7 +66,7 @@ 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`. +> The API **MUST** have a method for setting the global `evaluation context`. 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. From 5753aa88d92ae41f26004f380778986b71c5bbcb Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 14 Jul 2023 08:49:37 -0400 Subject: [PATCH 32/34] Update specification/sections/01-flag-evaluation.md Co-authored-by: Kavindu Dodanduwa Signed-off-by: Todd Baert --- specification/sections/01-flag-evaluation.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specification/sections/01-flag-evaluation.md b/specification/sections/01-flag-evaluation.md index 785292c7..8889636e 100644 --- a/specification/sections/01-flag-evaluation.md +++ b/specification/sections/01-flag-evaluation.md @@ -182,7 +182,6 @@ number myNumber = client.getNumberValue('number-flag', 75); MyStruct myStruct = client.getObjectValue('structured-flag', { text: 'N/A', percentage: 75 }, options); ``` -See [evaluation context](./03-evaluation-context.md) for details. #### Condition 1.3.3 From e4a4b3827db17aad0aad65dbcc8749934540d23c Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 14 Jul 2023 09:17:52 -0400 Subject: [PATCH 33/34] fixup: add client-side, server-side Signed-off-by: Todd Baert --- specification/glossary.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/specification/glossary.md b/specification/glossary.md index 5d96931a..3b903d39 100644 --- a/specification/glossary.md +++ b/specification/glossary.md @@ -22,6 +22,8 @@ This document defines some terms that are used across this specification. - [Library Author](#library-author) - [Common](#common) - [Feature Flag SDK](#feature-flag-sdk) + - [Client-Side SDK](#client-side-sdk) + - [Server-Side SDK](#server-side-sdk) - [Feature Flag API](#feature-flag-api) - [Evaluation API](#evaluation-api) - [Flag Management System](#flag-management-system) @@ -79,6 +81,14 @@ The maintainer of a shared library which is a dependency of many applications or The libraries used by the Application Author to implement feature flags in their application or service. The interfaces defined in these libraries adhere to the Feature Flag API. +### Client-Side SDK + +An SDK which is built for usage in client applications (e.g. single-page web applications), and typically uses the [static-context paradigm](#static-context-paradigm). + +### Server-Side SDK + +An SDK which is built for usage in server applications (e.g. REST services), and typically uses the [dynamic-context paradigm](#dynamic-context-paradigm). + ### Feature Flag API The interfaces and abstractions used by authors (Application, Integration, Provider). @@ -180,7 +190,7 @@ Server-side applications typically perform flag evaluations on behalf of many us ### 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: +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: - an initialization occurs, which fetches evaluated flags in bulk for a given context (user) - the evaluated flags are cached in the library From c4b8292a965a379eb50c16a406158c8ed189154b Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 14 Jul 2023 09:19:06 -0400 Subject: [PATCH 34/34] fixup: json Signed-off-by: Todd Baert --- specification.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification.json b/specification.json index 0ab10dcc..d051c13d 100644 --- a/specification.json +++ b/specification.json @@ -495,7 +495,7 @@ { "id": "Conditional Requirement 3.2.2.1", "machine_id": "conditional_requirement_3_2_2_1", - "content": "The API MUST have a method for supplying `evaluation context`.", + "content": "The API MUST have a method for setting the global `evaluation context`.", "RFC 2119 keyword": "MUST", "children": [] },