Skip to content

Commit

Permalink
feat: initialization and shutdown (#179)
Browse files Browse the repository at this point in the history
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Weyert de Boer <weyert@gmail.com>
Co-authored-by: Justin Abrahms <justin@abrah.ms>
Co-authored-by: Lukas Reining <lukas.reining@codecentric.de>
Co-authored-by: Jonathan Norris <jonathan@taplytics.com>
Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com>
Co-authored-by: Kavindu Dodanduwa <kavindudodanduwa@gmail.com>
Co-authored-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com>
  • Loading branch information
9 people authored May 8, 2023
1 parent b50883c commit a4ffec3
Show file tree
Hide file tree
Showing 7 changed files with 273 additions and 75 deletions.
97 changes: 80 additions & 17 deletions specification.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,23 @@
"children": []
},
{
"id": "Requirement 1.1.2",
"machine_id": "requirement_1_1_2",
"content": "The `API` MUST provide a function to set the default `provider`, which accepts an API-conformant `provider` implementation.",
"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": []
},
Expand Down Expand Up @@ -213,6 +227,13 @@
"RFC 2119 keyword": "MUST",
"children": []
},
{
"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.",
"RFC 2119 keyword": "MUST",
"children": []
},
{
"id": "Requirement 2.1.1",
"machine_id": "requirement_2_1_1",
Expand Down Expand Up @@ -289,23 +310,23 @@
"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.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",
Expand All @@ -327,6 +348,48 @@
"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 `shutdown` function to perform whatever cleanup is necessary for the implementation.",
"RFC 2119 keyword": "MAY",
"children": []
},
{
"id": "Requirement 3.1.1",
"machine_id": "requirement_3_1_1",
Expand Down
90 changes: 60 additions & 30 deletions specification/sections/01-flag-evaluation.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ The `evaluation API` allows for the evaluation of feature flag values, independe
It's important that multiple instances of the `API` not be active, so that state stored therein, such as the registered `provider`, static global `evaluation context`, and globally configured `hooks` allow the `API` to behave predictably. This can be difficult in some runtimes or languages, but implementors should make their best effort to ensure that only a single instance of the `API` is used.

#### Requirement 1.1.2
### Setting a provider

> The `API` **MUST** provide a function to set the default `provider`, which accepts an API-conformant `provider` implementation.
#### Requirement 1.1.2.1

> The `API` **MUST** define a `provider mutator`, a function to set the default `provider`, which accepts an API-conformant `provider` implementation.
```typescript
// example provider mutator
Expand All @@ -33,6 +35,22 @@ This provider is used if there is not a more specific client name binding. (see

See [provider](./02-providers.md) for details.

#### Requirement 1.1.2.2

> The `provider mutator` function **MUST** invoke the `initialize` function on the newly registered provider before using it to resolve flag values.
The `provider's` readiness can state can be determined from its `status` member/accessor.

See [provider initialization](./02-providers.md#24-initialization).

#### Requirement 1.1.2.3

> 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.
Setting a new provider means the previous provider is no longer in use, and should therefore be disposed of using its `shutdown` function.

see: [shutdown](./02-providers.md#26-shutdown), [setting a provider](#setting-a-provider)

#### Requirement 1.1.3

> 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.
Expand Down Expand Up @@ -105,11 +123,11 @@ See [hooks](./04-hooks.md) for details.
client.getMetadata().getName(); // "my-client"
```

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

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

##### Requirement 1.3.1
#### Requirement 1.3.1

> The `client` **MUST** provide methods for typed flag evaluation, including boolean, numeric, string, and structure, with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), `evaluation context` (optional), and `evaluation options` (optional), which returns the flag value.
Expand All @@ -129,32 +147,31 @@ MyStruct myStruct = client.getObjectValue<MyStruct>('structured-flag', { text: '

See [evaluation context](./03-evaluation-context.md) for details.

##### Condition 1.3.2
#### Condition 1.3.2

> The implementation language differentiates between floating-point numbers and integers.
###### Conditional Requirement 1.3.2.1
##### Conditional Requirement 1.3.2.1

> The client **SHOULD** provide functions for floating-point numbers and integers, consistent with language idioms.
```go
// example in GO
GetIntValue(flag string, defaultValue int64, evalCtx EvaluationContext, options ...EvaluationOption) (int64, error)
```java
int getIntValue(String flag, int defaultValue);

GetFloatValue(flag string, defaultValue float64, evalCtx EvaluationContext, options ...EvaluationOption) (float64, error)
long getFloatValue(String flag, long defaultValue);
```

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

##### Requirement 1.3.3
#### Requirement 1.3.3

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

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

##### Requirement 1.4.1
#### Requirement 1.4.1

> The `client` **MUST** provide methods for detailed flag value evaluation with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), `evaluation context` (optional), and `evaluation options` (optional), which returns an `evaluation details` structure.
Expand All @@ -173,80 +190,93 @@ FlagEvaluationDetails<MyStruct> myStructDetails = client.getObjectDetails<MyStru

```

##### Requirement 1.4.2
#### Requirement 1.4.2

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

> The language supports generics (or an equivalent feature).
###### Conditional Requirement 1.4.3.1
##### Conditional Requirement 1.4.3.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.4

> 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.5

> 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.6

> 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.7

> 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.8

> 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.9

> 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.10

> 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.11

> 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.12

> 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.13

> 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.14

> The implementation language supports a mechanism for marking data as immutable.
###### Conditional Requirement 1.4.14.1
##### Conditional Requirement 1.4.14.1

> Condition: `Flag metadata` **MUST** be immutable.
#### Evaluation Options
### Evaluation Options

##### Requirement 1.5.1
#### Requirement 1.5.1

> 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.
See [hooks](./04-hooks.md) for details.

### 1.6. Shutdown

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

#### Requirement 1.6.1

> The API **MUST** define a `shutdown` function, which, when called, must call the respective `shutdown` function on the active provider.
The precise name of this function is not prescribed by this specification, but should be defined be the SDK.
Relevant language idioms should be considered when choosing the name for this function, in accordance with the resource-disposal semantics of the language in question.

see: [`shutdown`](./02-providers.md#25-shutdown)
Loading

0 comments on commit a4ffec3

Please sign in to comment.